配置Service Mesh(Istio)在dockers desktop的k8s上灰度发布
在进行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 集成实现自动化部署。
更多推荐

所有评论(0)