Skip to main content

Command Palette

Search for a command to run...

Istio 大入门 - Ingress

Updated
4 min read

本文涉及细节较多,需读者对 Docker、Kubernetes 以及 Istio 有一定了解和实践经验,否则可能难于操作。

Istio 从 v1alpha3 开始,用 Ingress Gateway 组件替代了符合 Kubernetes 规范的 Ingress Controller,因此对入站流量具有了更大的控制能力,但是用法也有了较大不同。

安装:在使用 Helm 进行 Istio 部署的时候,需要使用下面的设置来启用 Ingress Gateway:

gateways:
  enabled: true
  istio-ingressgateway:
    enabled: true

基本概念

VirtualService 是流量控制的核心组件,起着承上(Gateway)启下(DestinationRule)的作用。这三种对象在本文中涉及的主要职责:

  • Gateway 对象:
    • 选择 Gateway Ingress Controller。
    • 设置端点开放方式:域名、端口、入站协议等。
  • DestinationRule
    • 定义负载均衡行为。
    • 定义服务版本子集。
  • VirtualService
    • 根据 Gateway 选择服务版本。

根据 VirtualService 对象文档中的说明,VirtaulService 对象中的 gateways 字段的类型为 string[],包括一或多个字符串值。如果省略该字段内容,会隐式的赋值为 ["mesh"]mesh 网关指代所有的网格内通信。如果要对外提供服务,就需要定义 Gateway 对象,并在 gateways 字段中进行赋值。

注意,一旦在 gateways 中填写了非缺省的对象名称,要继续对内部通信进行流量控制,必须显式的将内置的 mesh 对象名称也加入到列表之中。

准备工作

环境准备

  1. 安装和部署 Kubernetes 以及 Istio,并启用 Ingress Gateway 支持。
  2. 将备用的两个域名(假设为 flask.example2.com 以及 flask.example1.com)指向 istio-ingressgateway 服务的外部 IP。
  3. 为测试域名生成 HTTPS 证书。

域名准备

工作负载

  1. 首先编写一个基于 Flask 的 Python 脚本,并打入镜像,脚本功能很简单,显示环境变量中的 "VERSION"。
  2. 接下来编写 Dockerfile,安装 Python3、Flask,并进行构建和推送。
  3. 编写 Kubernetes 工作负载文件,常见的 Deployment + Service 模式。
  4. 在 Kubernetes 上运行工作负载。

以上涉及代码可以在页面尾部获取。

目标规则定义

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: flask
spec:
  host: flask
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

这里根据工作负载中的标签,将 flask 服务拆分为 v1v2 两个版本。

在外网开放服务

这里我们设计,将所有外网请求经过 flask.example1.com 路由到 v2 版本上。

首先要编写一个 Gateway 定义:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: flask-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - flask.example1.com
        - flask.example2.com

这里定义了一个 Gateway,其中要求 80 端口响应两个域名的请求。接下来定义一个 VirtualService

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
    name: flask
spec:
    hosts:
    - "flask.example1.com"
    gateways:
    - flask-gateway
    http:
    - route:
        - destination:
            host: flask
            subset: v2

然后使用 curl 进行访问:curl http://flask.example1.com 会发现返回了字符串 "v2",证明我们的定义生效了,该域名指向了 v2 服务。

再用 curl 访问第二个域名 curl http://flask.example2.com,会看到返回了 404,这是因为我们的 Gateway 虽然接收了请求,但是并没有服务路由完成这一请求。那么可以再定义一个 VirtualService,让 flask.example2.com 域名指向 v1

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
    name: flask1
spec:
    hosts:
    - "flask.example2.com"
    gateways:
    - flask-gateway
    http:
    - route:
        - destination:
            host: flask
            subset: v1

再次使用 curl http://flask.example2.com,访问,会看到返回结果变成了 v1,证明我们的服务定义生效了。

将一个域名服务升级为 tls

随意修改一个需求,要求开放 flask.example2.com 为 http,而 flask.example1.com 升级为 https,既然涉及 https,就要用到证书,官方文档(中文版哦)陈述如下:

具体说来就是使用 kubectl 命令在命名空间 istio-system 中创建一个 secret 对象,命名为 istio-ingressgateway-certs。Istio 网关会自动载入这个 secret。 这里的 secret 必须 在 istio-system 命名空间中,并且命名为 istio-ingressgateway-certs,否则就不会被正确载入,也就无法 Istio gateway 中使用了。

接着是使用命令为 flask.example1.com 的证书创建 Secret

$ kubectl create -n istio-system secret tls \
    istio-ingressgateway-certs \
    --key key.pem --cert cert.pem

最后修改我们的 Gateway 定义:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: flask-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - flask.example2.com
    - port:
        number: 443
        name: https-rocks
        protocol: HTTPS
      tls:
        mode: SIMPLE
        serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
        privateKey: /etc/istio/ingressgateway-certs/tls.key
      hosts:
        - flask.example1.com

然后再次使用 curl 分别访问 https://flask.example1.com/version 以及 http://flask.example2.com/version,会发现返回了预期结果,而一旦错用 httphttps,就会出现异常,这是因为我们的 Gateway 限制了端口。

这里如此操作的原因可以在他的部署中找到:

$ kubectl get deployment istio-ingressgateway \
    -o yaml -n istio-system

返回的 YAML 中会有这么一段

...
- mountPath: /etc/istio/ingressgateway-ca-certs
    name: ingressgateway-ca-certs
    readOnly: true
...
volumes:
- name: ingressgateway-certs
secret:
    defaultMode: 420
    optional: true
    secretName: istio-ingressgateway-certs
...

上方代码可以看到,Ingress Gateway 中用可选方式加载了一个名称为 istio-ingressgateway-certsSecret,并 Mount 到了 /etc/istio/ingressgateway-ca-certs 目录之中,这就是上文中要求我们固定 Secret 名称和命名空间的原因。众所周知,tls 类型的 Secret 加载后会成为 tls.keytls.crt 两个文件,因此在我们的 Gateway 定义中就使用了 /etc/istio/ingressgateway-certs/tls.crt 这样的文件名。

所有域名都升级为 tls

根据上一节的描写,不难发现按照官方文档,一个 Gateway 是无法处理两个域名的 https 的:

  1. tls secret 只能包含一个证书对。
  2. 泛域名证书可以完成这一任务,但因为 Envoy 的限制,这里无法同时使用两个泛域名。

讨论到这里就很明显了,关键在于如何加载多个证书对,可以修改前面所说的加载指令为加载多个 Secret,或者干脆换成 Configmap,当然这样会引起服务中断,Configmap 用于存放证书也略显粗糙——好在我们还可以换用 Generic 类型的证书,这里我们可以删除原有 Secret 重新创建 :

kubectl delete secret istio-ingressgateway-certs \
    -n istio-system

kubectl create secret generic  \
    istio-ingressgateway-certs \
    -n istio-system \
    --from-file=rocks-crt.pem \
    --from-file=rocks-key.pem \
    --from-file=xyz-crt.pem \
    --from-file=xyz-key.pem

接下来改造我们的 Gateway 定义:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: flask-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 443
      name: https-rocks
      protocol: HTTPS
    tls:
      mode: SIMPLE
      serverCertificate: /etc/istio/ingressgateway-certs/rocks-crt.pem
      privateKey: /etc/istio/ingressgateway-certs/rocks-key.pem
    hosts:
    - "flask.example1.com"
  - port:
      number: 443
      name: https-xyz
      protocol: HTTPS
    tls:
      mode: SIMPLE
      serverCertificate: /etc/istio/ingressgateway-certs/xyz-crt.pem
      privateKey: /etc/istio/ingressgateway-certs/xyz-key.pem
    hosts:
    - "flask.example2.com"

这样一来,我们开放了两个 HTTPS 端口,各自使用不同的证书,分别都可以通过 curl 命令获得正确结果了。

补充

tls.mode 实际还支持双向和透传两种方式,都可以在流量控制参考中找到相关内容。

涉及链接

  1. Istio 流量控制参考:https://istio.io/docs/reference/config/istio.networking.v1alpha3/
  2. 用 HTTPS 加密 Gateway:https://istio.io/zh/docs/tasks/traffic-management/secure-ingress/

代码

Python 脚本

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

from flask import Flask
import os

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "Hello, World!"

@app.route("/version")
def getversion():
    return os.getenv("VERSION")
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80, debug=True)

Dockerfile

FROM alpine
RUN  apk add --update --no-cache python3 && \
  mkdir /web && \
  pip3 install Flask
COPY server.py /web
CMD "/web/server.py"

Kubernetes 工作负载

apiVersion: v1
kind: Service
metadata:
  name: flask
  labels:
    app: flask
spec:
  ports:
    - name: http
      port: 80
  selector:
    app: flask
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: flask-v1
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: flask
        version: v1
    spec:
      containers:
        - image: 'abc:25000/python-flask-server:v2'
          imagePullPolicy: IfNotPresent
          name: flask
          env:
            - name: VERSION
              value: v1
          ports:
            - containerPort: 80
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: flask-v2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: flask
        version: v2
    spec:
      containers:
        - image: 'abc:25000/python-flask-server:v2'
          imagePullPolicy: IfNotPresent
          name: flask
          env:
            - name: VERSION
              value: v2
          ports:
            - containerPort: 80

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts