Service Mesh 实战 · 用 Istio 实现 Spring Cloud 微服务的灰度发布

基本概念:

服务网格(Service Mesh)是一个基础设施层,它为应用程序提供零信任安全、可观察性和高级流量管理等功能, 而无需更改代码。

Istio 是最受欢迎、最强大、最值得信赖的服务网格。 Istio 可确保云原生和分布式系统具有弹性,帮助现代企业在保持连接和保护的同时跨不同平台维护其工作负载。 它启用安全和治理控制,包括 mTLS 加密、策略管理和访问控制、 支持网络功能,例如金丝雀部署、A/B 测试、负载平衡、故障恢复, 并增加对整个资产流量的可观察性。

https://istio.io/latest/zh/about/service-mesh/

灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。

百度百科

这里也解释一下我的 Spring  Cloud 微服务,作为本次实战的演示项目包含 api-gateway、auth、account、notification 四个服务。其中api-gateway是服务网关,其他三个是应用服务有调用关系。应用服务都提供了 /ping 接口,会返回服务名和版本号以及调用的下游服务信息。

图片

图片

关注公众号“懂点代码的朋友”回复关键字:hello-istio 获取项目源代码

项目环境:

Kubernetes (v1.29.2)

Istio (1.22.1)

开始部署操作

创建一个新的namespace

kubectl create ns hello-istio

开启istio自动注入

kubectl label namespace hello-istio istio-injection=enabled

如果没开启自动注入最后在kiali将会看到下图中的“Missing Sidecar”提示,执行开启自动注入后重启所有 deployment 即可。

图片

构建Docker 镜像

以 api-gateway 为例,进入项目目录执行

docker build -t api-gateway:0.0.1 .

部署到 Kubernetes

在项目根目录直接执行

kubectl -n hello-istio apply -f deployment.yaml

项目中 deployment.yaml 文件内容如下,会部署4个服务的 Service 以及4个服务的多个版本 Deployment。(正常在灰度之前只会有4个服务的一个版本,这里为了快速实战演示直接一次部署了多个版本)

# 给 notification、account、auth、api-gateway 配置 Service

apiVersion: v1
kind: Service
metadata:
  labels:
    app: notification
    service: notification
  name: notification
spec:
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: notification
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: account
    service: account
  name: account
spec:
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: account
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: auth
    service: auth
  name: auth
spec:
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: auth
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: api-gateway
    service: api-gateway
  name: api-gateway
spec:
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: api-gateway


# 部署 v1 版本的 notification、account、auth、api-gateway 服务

# v1 notification
apiVersion: apps/v1
kind: Deployment
metadata:
  name: notification
  labels:
    app: notification
    version: v1
spec:
  selector:
    matchLabels:
      app: notification
      version: v1
  replicas: 1
  template:
    metadata:
      labels:
        app: notification
        version: v1
    spec:
      containers:
        - name: notification
          image: notification:0.0.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          env:
          - name: JAVA_OPTS
            value: "-Xmx256M -Xms256M -Dserver.port=8080 -Dinfo.app.version=v1.0"
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
            limits:
              cpu: "200m"
              memory: "512Mi"
---
# v1 account
apiVersion: apps/v1
kind: Deployment
metadata:
  name: account
  labels:
    app: account
    version: v1
spec:
  selector:
    matchLabels:
      app: account
      version: v1
  replicas: 1
  template:
    metadata:
      labels:
        app: account
        version: v1
    spec:
      containers:
        - name: account
          image: account:0.0.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          env:
          - name: JAVA_OPTS
            value: "-Xmx256M -Xms256M -Dserver.port=8080 -Dinfo.app.version=v1.0 -Dnotification.url=http://notification"
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
            limits:
              cpu: "200m"
              memory: "512Mi"
---
# v1 auth
apiVersion: apps/v1
kind: Deployment
metadata:
  name: auth
  labels:
    app: auth
    version: v1
spec:
  selector:
    matchLabels:
      app: auth
      version: v1
  replicas: 1
  template:
    metadata:
      labels:
        app: auth
        version: v1
    spec:
      containers:
        - name: auth
          image: auth:0.0.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          env:
          - name: JAVA_OPTS
            value: "-Xmx256M -Xms256M -Dserver.port=8080 -Dinfo.app.version=v1.0 -Dnotification.url=http://notification -Daccount.url=http://account"
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
            limits:
              cpu: "200m"
              memory: "512Mi"
---
# v1 api-gateway
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-gateway
  labels:
    app: api-gateway
    version: v1
spec:
  selector:
    matchLabels:
      app: api-gateway
      version: v1
  replicas: 1
  template:
    metadata:
      labels:
        app: api-gateway
        version: v1
    spec:
      containers:
        - name: api-gateway
          image: api-gateway:0.0.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          env:
          - name: JAVA_OPTS
            value: "-Xmx256M -Xms256M -Dserver.port=8080 -Dspring.profiles.active=k8s"
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
            limits:
              cpu: "200m"
              memory: "512Mi"
---

# 部署 1 个 v2 版本的 account 服务
# v2 account
apiVersion: apps/v1
kind: Deployment
metadata:
  name: account-v2
  labels:
    app: account
    version: v2
spec:
  selector:
    matchLabels:
      app: account
      version: v2
  replicas: 1
  template:
    metadata:
      labels:
        app: account
        version: v2
    spec:
      containers:
        - name: account-v2
          image: account:0.0.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          env:
          - name: JAVA_OPTS
            value: "-Xmx256M -Xms256M -Dserver.port=8080 -Dinfo.app.version=v2.0 -Dnotification.url=http://notification"
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
            limits:
              cpu: "200m"
              memory: "512Mi"
---

# 部署 1 个 v2 和 1 个 v3 版本的 notification 服务
# v2 notification
apiVersion: apps/v1
kind: Deployment
metadata:
  name: notification-v2
  labels:
    app: notification
    version: v2
spec:
  selector:
    matchLabels:
      app: notification
      version: v2
  replicas: 1
  template:
    metadata:
      labels:
        app: notification
        version: v2
    spec:
      containers:
        - name: notification-v2
          image: notification:0.0.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          env:
          - name: JAVA_OPTS
            value: "-Xmx256M -Xms256M -Dserver.port=8080 -Dinfo.app.version=v2.0"
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
            limits:
              cpu: "200m"
              memory: "512Mi"
---
# v3 notification
apiVersion: apps/v1
kind: Deployment
metadata:
  name: notification-v3
  labels:
    app: notification
    version: v3
spec:
  selector:
    matchLabels:
      app: notification
      version: v3
  replicas: 1
  template:
    metadata:
      labels:
        app: notification
        version: v3
    spec:
      containers:
        - name: notification-v3
          image: notification:0.0.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          env:
          - name: JAVA_OPTS
            value: "-Xmx256M -Xms256M -Dserver.port=8080 -Dinfo.app.version=v3.0"
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
            limits:
              cpu: "200m"
              memory: "512Mi"

部署之后 Kubernetes Dashboard 可看到如下效果

图片

图片

测试服务访问

也可在 pod 中执行 curl 命令进行测试,下面是在打通网络的环境下直接用浏览器访问的效果。

curl http://api-gateway.hello-istio/auth/ping

图片

接入 Istio 服务网格

服务部署完成后开始将整个微服务接入到 Istio 服务网格,在此之前服务使用的都是 Kubernetes 的服务发现,通过 Service进行的服务调用。

打开 Kiali 面板

为方便后续查看和操作先打开  kiali 面板

istioctl dashboard kiali

执行接入 Istio 服务网格

kubectl -n hello-istio apply -f hello-istio-gateway.yaml

hello-istio-gateway.yaml 文件内容如下(准备自己的域名和域名解析):

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: hello-istio-gateway
spec:
  # The selector matches the ingress gateway pod labels.
  # If you installed Istio using Helm following the standard documentation, this would be "istio=ingress"
  selector:
    istio: ingressgateway # use istio default controller
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "istio.ready321.cn"
---

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: api-gateway
spec:
  hosts:
    - "istio.ready321.cn"
  gateways:
    - hello-istio-gateway
  http:
    - route:
        - destination:
            host: api-gateway
            port:
              number: 80

图片

测试接入 Istio 之后的效果,此时可以通过刚才配置的域名进行访问,在 Kiali 的Traffic Graph 中可以看到相关的调用链路。

图片

图片

此时还没有进行流量的版本控制所以可以看到各个版本均有流量访问

图片

实施灰度发布

设置全部使用v1版本

这里为了简单直接在 kiali 中进行操作设置

图片

图片

测试效果,已均为v1版本。

图片

图片

控制5%的流量走新版本

设置 account 服务 v1:95%,v2:5%

图片

设置 notification 服务 v1:95% ,v2:3%,v3:2%

图片

多次测试访问后可查看效果

图片

至此灰度的控制已经完成,后续根据情况调整版本的流量访问。在新版本确认无误后可以结束灰度期,全部使用新版本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值