在进行Istio发布之前,我们必须首先得保证已经安装了Istio,要使用Istio就必须先安装Istioctl

📋 准备工作

开始前,请确保你的环境满足以下条件:

  • 一个运行中的 Kubernetes 集群(如 Minikube、Kind 或云厂商的托管集群)。

  • kubectl 已安装并配置好,能正常与你的集群通信。

🚀 安装步骤

1. 下载并安装 istioctl

istioctl 是 Istio 的命令行工具,用于安装、配置和管理 Istio。

  • 在 Linux 或 macOS 上:你可以通过官方脚本快速下载最新版本。

    curl -L https://istio.io/downloadIstio | sh -

    这会下载最新版本的 Istio 到一个新目录(例如 istio-1.29.2)。进入该目录,并将 istioctl 添加到你的 PATH 环境变量中:

    cd istio-*/
    export PATH=$PWD/bin:$PATH
  • 在 Windows 上:你可以从 Istio 官方 GitHub Release 页面 下载与系统对应的压缩包,解压后,将 bin 目录下的 istioctl.exe 所在路径添加到系统的 PATH 环境变量中。

  • 通过包管理器 (macOS):在 macOS 上,你也可以使用 Homebrew 安装:

    brew install istioctl

安装完成后,可以运行以下命令验证是否成功:

istioctl version
2. 使用 istioctl 安装 Istio

Istio 提供了多种“配置文件”(Profile),方便你快速选择适合不同场景的安装组合。对于学习和测试,demo 配置文件非常合适。

执行以下命令安装 demo 配置文件:

# 为了防止不能正确拉取到相关配置,这里手动进行拉取到本地在安装
docker pull docker.io/istio/pilot:1.31.0

# 如果网关出现不能正常安装的情况,删除网关 Deployment(触发重建)
kubectl delete deployment istio-ingressgateway -n istio-system
kubectl delete deployment istio-egressgateway -n istio-system

# proxyv2 网关代理对象
docker pull docker.io/istio/proxyv2:1.31.0

# 安装istio相关信息
istioctl install --set profile=demo --set values.global.imagePullPolicy=IfNotPresent -y

# 验证网关 Pod 状态
kubectl get pods -n istio-system -w
  • --set profile=demo:指定使用 demo 配置文件。

  • -y:自动确认安装,无需手动输入 y。

命令执行后,你会看到类似下面的成功信息:

text

✔ Istio core installed
✔ Istiod installed
✔ Egress gateways installed
✔ Ingress gateways installed
✔ Installation complete
3. 验证安装

安装完成后,可以通过以下命令验证 Istio 组件是否正常运行:

  • 查看 Pod 状态:Istio 的核心组件默认安装在 istio-system 命名空间中。

kubectl get pods -n istio-system
  • 你会看到 istiod-...、istio-ingressgateway-... 和 istio-egressgateway-... 等 Pod 处于 Running 状态。

  • 使用 istioctl 验证:

    istioctl verify-install

    如果一切正常,该命令会输出 ✔ Istio is installed and verified successfully。

⚙️ 后续配置:启用 Sidecar 自动注入

为了让 Istio 能管理你的应用,需要为应用所在的命名空间启用 Sidecar 代理的自动注入功能。

例如,为 default 命名空间启用自动注入:

kubectl label namespace default istio-injection=enabled

这样,之后在该命名空间下创建的任何 Pod,Istio 都会自动为其注入一个 Envoy 代理容器。其中除了 demo,Istio 还提供了 default(用于生产)、minimal(仅控制平面)等配置文件。你可以通过修改 --set profile=... 来使用它们

接下来我们配置灰度数据

1.后端deployment的灰度配置

# API 版本:定义使用的 K8s API 版本
# apps/v1 是 Deployment 的稳定版本
apiVersion: apps/v1

# 资源类型:Deployment(无状态应用部署控制器)
# Deployment 负责管理 Pod 的副本数、滚动更新、回滚等
kind: Deployment

# 元数据:描述该 Deployment 自身的信息
metadata:
  name: backend-service-v1          # Deployment 的名称,集群内唯一
  labels:                        # 标签(键值对),用于分类和选择
    app: backend                 # 定义该资源属于 "backend" 应用组

# 规格:定义 Deployment 的期望状态
spec:
  # 副本数:期望运行的 Pod 数量
  # 设置为 1 表示只运行 1 个实例;生产环境可设为 3 或更多
  replicas: 1

  # 选择器:定义该 Deployment 管理哪些 Pod
  # 必须匹配下面 template 中定义的 labels
  selector:
    matchLabels:
      app: backend               # 只管理带有 app=backend 标签的 Pod
      # 带有版本号,后面也用于灰度发布的判断
      version: v1

  # 模板:定义 Pod 的蓝图(类似于 Docker 的容器配置)
  template:
    metadata:
      labels:                    # Pod 的标签,必须匹配上面的 selector
        app: backend
        version: v1
    spec:                        # Pod 规格
      containers:                # 容器列表(Pod 可以包含多个容器)
        - name: client-agent-backend-pod     # 容器名称,在 Pod 内唯一
          image: client-agent-backend:1.3.5   # 容器镜像名(本地镜像,不带仓库地址)
          # 镜像拉取策略:
          # - Always:每次都从远程仓库拉取
          # - IfNotPresent:本地没有才拉取(开发环境推荐)
          # - Never:只使用本地镜像
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8090    # 容器内部监听的端口
              # protocol: TCP       # 默认 TCP,可以省略
          # 环境变量(可选):传递给容器的配置
          # env:
          # - name: SPRING_PROFILES_ACTIVE
          #   value: "prod"
          # - name: NACOS_SERVER_ADDR
          #   value: "nacos-service:8848"
          # 资源限制(可选):CPU 和内存限制
          # resources:
          #   requests:            # 最低保证资源
          #     memory: "512Mi"
          #     cpu: "250m"
          #   limits:              # 最大可用资源
          #     memory: "1Gi"
          #     cpu: "500m"
          # ========== 通过 ConfigMap 和 Secret 注入配置 ==========
          envFrom:
            # 1. 从 ConfigMap 读取非敏感配置(数据库地址、Redis 地址等)
            - configMapRef:
                name: backend-config
            # 2. 从 Secret 读取敏感信息(密码)
            - secretRef:
                name: db-secret
---
# --- 分隔符:表示这是一个独立的资源 ---


# API 版本:定义使用的 K8s API 版本
# apps/v1 是 Deployment 的稳定版本
apiVersion: apps/v1

# 资源类型:Deployment(无状态应用部署控制器)
# Deployment 负责管理 Pod 的副本数、滚动更新、回滚等
kind: Deployment

# 元数据:描述该 Deployment 自身的信息
metadata:
  name: backend-service-v2          # Deployment 的名称,集群内唯一
  labels:                        # 标签(键值对),用于分类和选择
    app: backend                 # 定义该资源属于 "backend" 应用组

# 规格:定义 Deployment 的期望状态
spec:
  # 副本数:期望运行的 Pod 数量
  # 设置为 1 表示只运行 1 个实例;生产环境可设为 3 或更多
  replicas: 1

  # 选择器:定义该 Deployment 管理哪些 Pod
  # 必须匹配下面 template 中定义的 labels
  selector:
    matchLabels:
      app: backend               # 只管理带有 app=backend 标签的 Pod
      # 带有版本号,后面也用于灰度发布的判断
      version: v2

  # 模板:定义 Pod 的蓝图(类似于 Docker 的容器配置)
  template:
    metadata:
      labels:                    # Pod 的标签,必须匹配上面的 selector
        app: backend
        version: v2
    spec:                        # Pod 规格
      containers:                # 容器列表(Pod 可以包含多个容器)
        - name: client-agent-backend-pod     # 容器名称,在 Pod 内唯一
          image: client-agent-backend:1.3.6  # 容器镜像名(本地镜像,不带仓库地址)
          # 镜像拉取策略:
          # - Always:每次都从远程仓库拉取
          # - IfNotPresent:本地没有才拉取(开发环境推荐)
          # - Never:只使用本地镜像
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8090    # 容器内部监听的端口
              # protocol: TCP       # 默认 TCP,可以省略
          # 环境变量(可选):传递给容器的配置
          # env:
          # - name: SPRING_PROFILES_ACTIVE
          #   value: "prod"
          # - name: NACOS_SERVER_ADDR
          #   value: "nacos-service:8848"
          # 资源限制(可选):CPU 和内存限制
          # resources:
          #   requests:            # 最低保证资源
          #     memory: "512Mi"
          #     cpu: "250m"
          #   limits:              # 最大可用资源
          #     memory: "1Gi"
          #     cpu: "500m"
          # ========== 通过 ConfigMap 和 Secret 注入配置 ==========
          envFrom:
            # 1. 从 ConfigMap 读取非敏感配置(数据库地址、Redis 地址等)
            - configMapRef:
                name: backend-config
            # 2. 从 Secret 读取敏感信息(密码)
            - secretRef:
                name: db-secret
---
# --- 分隔符:表示这是一个独立的资源 ---

# API 版本:Service 的 API 版本
apiVersion: v1

# 资源类型:Service(服务发现与负载均衡)
# Service 为 Pod 提供一个稳定的访问入口(IP 和 DNS 名称)
kind: Service

metadata:
  name: backend-service          # Service 名称
  # 重要:这个名称就是前端 Nginx 中 proxy_pass 要填的地址!
  # 例如:http://backend-service:8090

spec:
  # 选择器:将流量转发给带有 app=backend 标签的 Pod
  selector:
    app: backend

  ports:
    - protocol: TCP              # 协议类型
      port: 8090                 # Service 暴露的端口(其他服务访问时用的端口)
      targetPort: 8090           # 转发到 Pod 的端口(必须匹配 containerPort)

  # Service 类型:
  # - ClusterIP(默认):仅在集群内部可访问
  # - NodePort:在每个节点的固定端口上暴露,外部可通过 <NodeIP>:<NodePort> 访问
  # - LoadBalancer:云环境下提供外部负载均衡器
  type: ClusterIP

2.后端destinationRule和VirturalService的配置,这个配置后才能进行流量转发

# 定义版本子集
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: backend-service
spec:
  host: backend-service          # 必须与 Service 名称一致
  subsets:
    - name: v1                   # 对应 Pod 的 version: v1
      labels:
        version: v1
    - name: v2                   # 对应 Pod 的 version: v2
      labels:
        version: v2
---

# 配置流量路由
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: backend-service
spec:
  hosts:
    # 必须与 Service 名称一致,同时这里必须和前端nginx转发过来配置的host要相同
    - backend-service             
    - backend-service.default.svc.cluster.local
  http:
    - route:
        - destination:
            host: backend-service
            subset: v1            # 指向 DestinationRule 中定义的 v1
          weight: 90              # 90% 的流量到 v1(稳定版)
        - destination:
            host: backend-service
            subset: v2            # 指向 DestinationRule 中定义的 v2
          weight: 10              # 10% 的流量到 v2(灰度版)

3.后端configmap的配置

apiVersion: v1
kind: ConfigMap
metadata:
  name: backend-config
data:
  # Spring Boot 标准配置(注意命名规则)
  SPRING_DATASOURCE_URL: "jdbc:mysql://host.docker.internal:3306/smart_cs?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true"
  SPRING_DATASOURCE_USERNAME: "root"

4.配置在deployment里面提到的db-sercet

kubectl create secret generic db-secret --from-literal=SPRING_DATASOURCE_PASSWORD=123456

下面是前端的配置

1.前端configmap的配置,这里主要配置nginx

# API 版本
apiVersion: v1

# 资源类型:ConfigMap(配置管理)
# ConfigMap 用于存储非敏感配置数据,可以挂载到 Pod 中
kind: ConfigMap

metadata:
  name: frontend-nginx-config    # ConfigMap 名称

# data:存储配置数据(键值对)
# 键名 default.conf 会成为挂载后的文件名
data:
  default.conf: |                # | 表示多行字符串(保留换行)
    # Nginx 服务器配置
    server {
        # 监听端口
        listen 80;
        # 服务器域名(可以填实际域名,开发环境用 localhost)
        server_name localhost;
        # 静态文件根目录
        root /usr/share/nginx/html;
        # 默认首页文件
        index index.html;

        # ========== 反向代理配置:处理 API 请求 ==========
        # location /api/ 匹配所有以 /api/ 开头的请求路径
        location /api/ {
            # proxy_pass:将请求转发到后端服务
            # 注意:http://backend-service:8090 中的 backend-service
            # 是 K8s 中 Service 的名称,K8s 内置 DNS 会解析它
            # 末尾的 / 很重要:表示保留 /api/ 前缀
            # 例如:/api/login -> http://backend-service:8090/api/login
            proxy_pass http://backend-service.default.svc.cluster.local:8090/api/;

            # 代理请求头设置(保证后端能拿到真实信息)
            # 传递原始 Host 头
            # proxy_set_header Host $host;
            # 这里用于对接后端的访问host
            proxy_set_header Host backend-service;
            # 传递真实客户端 IP
            proxy_set_header X-Real-IP $remote_addr;
            # 传递代理链上的所有 IP
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            # 传递原始协议(http/https)
            proxy_set_header X-Forwarded-Proto $scheme;
            # 强制关闭 keepalive,这里不关闭的话有可能会影响流量的分配
            proxy_set_header Connection "";   
        }

        # ========== SPA 路由配置 ==========
        # location / 匹配所有未被上面 location 匹配的请求
        location / {
            # try_files:按顺序尝试处理请求
            # $uri          -> 尝试作为文件(如 /about 找 about.html)
            # $uri/         -> 尝试作为目录(如 /about/ 找 about/index.html)
            # /index.html   -> 如果都找不到,返回 index.html
            # 这样前端路由(Vue Router/React Router)就能接管页面渲染
            try_files $uri $uri/ /index.html;
        }

        # ========== 静态资源缓存优化 ==========
        # 匹配常见静态资源文件(js, css, 图片等)
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
            # 设置缓存过期时间为 1 年(因为静态资源通常带 hash)
            expires 1y;
            # 设置缓存头为 public(允许 CDN 缓存)和 immutable(内容不变)
            add_header Cache-Control "public, immutable";
        }
    }

2.前端deployment配置

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-service
  labels:
    app: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: nginx-frontend
          image: client-agent-front:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80       # Nginx 默认监听 80 端口

          # ========== 挂载 ConfigMap 覆盖 Nginx 配置 ==========
          # 将 ConfigMap 中的 default.conf 挂载到容器内的配置文件
          volumeMounts:
            - name: nginx-config-volume      # 引用的 volume 名称
              mountPath: /etc/nginx/conf.d/default.conf  # 容器内的目标路径
              subPath: default.conf           # 只挂载这个文件,不覆盖整个目录

      # ========== 定义 Volume ==========
      volumes:
        - name: nginx-config-volume
          configMap:
            name: frontend-nginx-config    # 引用前面创建的 ConfigMap
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  selector:
    app: frontend
  ports:
    - protocol: TCP
      port: 80                 # Service 端口
      targetPort: 80           # 转发到 Pod 的端口
      # NodePort 端口(可选,不指定则自动分配,范围 30000-32767)
      nodePort: 31322
  type: NodePort               # 对外暴露(宿主机可访问)

完成上面配置后,我们就可以通过启动服务来验证了

# 启动后端
kubectl apply -f backend-configmap.yaml

kubectl apply -f backend-deployment.yaml

# 重启服务
kubectl rollout restart deployment/backend-service-v2 -n default

# 启动前端
kubectl apply -f frontend-configmap.yaml

kubectl apply -f frontend-deployment.yaml

除了按流量比例,Istio 还支持基于 HTTP 头(Header)、URI 等请求内容的路由,非常适合进行 A/B 测试。以下配置将所有携带 user: test 请求头的请求路由到 v2 版本,其他请求则全部路由到 v1 版本。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: backend-service
spec:
  hosts:
  - backend-service
  http:
  - match:
    - headers:
        user:
          exact: test           # 匹配请求头 user: test
    route:
    - destination:
        host: backend-service
        subset: v2
  - route:                      # 剩余所有请求
    - destination:
        host: backend-service
        subset: v1

工作原理与核心要点

  • 工作原理:VirtualService 定义了“流量要去哪里”(路由规则),而 DestinationRule 定义了“到了之后怎么办”(负载均衡、连接池等)。Envoy 代理根据这些规则动态分发流量。

  • 版本标签一致性:Deployment 中的 version 标签值,必须与 DestinationRule 中 subsets 下的 name 和 labels.version 完全一致。

  • 权重总和为100:在 VirtualService 中,所有路由目的地的 weight 值之和必须严格等于 100。

  • 零侵入:灰度发布的所有逻辑都在 Istio 控制面配置,应用代码无需任何改动。

🔧 工具与自动化

手动调整 VirtualService 的权重比较繁琐,你可以使用以下工具实现自动化灰度发布:

  • Flagger:一个专门用于 Kubernetes 的渐进式交付工具,可与 Istio 集成,根据监控指标(如请求成功率、延迟)自动进行灰度发布、分析和回滚。

  • Argo Rollouts:提供了比原生 Deployment 更高级的部署策略(如蓝绿、金丝雀),并能与 Istio 集成,通过其 Analysis 功能实现基于指标的自动化发布。

  • PipeCD:一个持续交付平台,支持声明式的金丝雀发布策略,可与 Istio 集成实现自动化部署。

更多推荐