如何使用 Argo Rollouts 和服务网格实现自动可控的金丝雀发布

公众号关注 「奇妙的 Linux 世界」

设为「星标」,每天带你玩转 Linux !

23a9bed0f64d38d2a01c2d82290daa84.png

金丝雀发布是服务治理中的重要功能,在发布时可以可控地将部分流量导入新版本的服务中;其余的流量则由旧版本处理。发布过程中,可以逐步增加新版本服务的流量。通过测试,可以决定是回滚还是升级所有实例,停用旧版本。

有了金丝雀发布,使用真实流量对服务进行测试,通过对流量的控制可以有效的降低服务发布的风险。

本文将介绍如何将使用 Argo Rollouts 和服务网格 osm-edge 来进行应用的自动、可控的金丝雀发布,不涉及工作原理的分析。对工作原理有兴趣的同学可以留言,可以再做一篇原理的介绍。

Argo Rollouts

Argo Rollouts[1] 包括一个 Kubernetes 控制器[2] 和一组 CRD[3],提供如蓝绿色、金丝雀、金丝雀分析、体验等高级部署功能和 Kubernetes 的渐进交付功能。

Argo Rollouts 与 入口控制器[4] 和服务网格集成,利用其流量管理能力,在发布期间逐步将流量转移到新版本。此外,Rollouts 可以查询和解析来自各种提供商的指标,以验证关键 KPI,并在更新期间推动自动推进或回滚。

Argo Rollouts 支持服务网格标准 SMI(Service Mesh Interface)[5] 的 TrafficSplit API[6],通过 TrafficSplit API 的使用来控制金丝雀发布时的流量控制。

服务网格 osm-edge

服务网格是处理服务间网络通信的基础设施组件,旨在从平台层面提供可观性、安全以及可靠性特性,以进程外的方式提供原本由部分应用层逻辑承载的基础能力,真正实现与业务逻辑的分离。

osm-edge[7] 是面向云边一体的轻量化服务网格,采用实现了服务网格标准 SMI(Service Mesh Interface)[8] 的  osm(Open Service Mesh)[9] 作为控制平面,采用  Pipy[10]  作为数据平面,具有高性能、低资源、简单、易用、易扩展、广泛兼容(支持 x86/arm64/龙芯/RISC-V)的特点。

环境准备

K3s

export INSTALL_K3S_VERSION=v1.23.8+k3s2
curl -sfL https://get.k3s.io | sh -s - --disable traefik --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config

安装服务网格

推荐使用 CLI 进行安装。

system=$(uname -s | tr [:upper:] [:lower:])
arch=$(dpkg --print-architecture)
release=v1.1.2
curl -L https://github.com/flomesh-io/osm-edge/releases/download/${release}/osm-edge-${release}-${system}-${arch}.tar.gz | tar -vxzf -
./${system}-${arch}/osm version
cp ./${system}-${arch}/osm /usr/local/bin/

CLI 下载之后就可以通过下面的命令进行安装了,这里默认开启 宽松流量策略模式[11] 并安装 Ingress[12]

export osm_namespace=osm-system
export osm_mesh_name=osm

osm install \
    --mesh-name "$osm_mesh_name" \
    --osm-namespace "$osm_namespace" \
    --set=osm.enablePermissiveTrafficPolicy=true \
    --set=fsm.enabled=true

确认 pod 正常启动并运行。

kubectl get pods -n osm-system
NAME                                           READY   STATUS    RESTARTS   AGE
fsm-repo-d785b55d-r92r5                        1/1     Running   0          2m20s
osm-bootstrap-646497898f-cdjq4                 1/1     Running   0          3m12s
osm-controller-7bbdcf748b-jdhsw                2/2     Running   0          3m12s
fsm-manager-7f9b665bd9-s8z6p                   1/1     Running   0          3m12s
fsm-bootstrap-57cb75d586-vvvzl                 1/1     Running   0          3m01s
osm-injector-86798c9ddb-gfqb4                  1/1     Running   0          3m12s
fsm-ingress-pipy-5bc7f4d6f6-7th6g              1/1     Running   0          3m12s
fsm-cluster-connector-local-7464b77ffd-cphxf   1/1     Running   0          4m22s

安装 Argo Rollouts

kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

Argo Rollouts 中默认使用 SMI TrafficSplit 的 v1alpha1 版本,通过下面的命令指定其使用 v1alpha2 的版本。

kubectl patch deployment argo-rollouts -n argo-rollouts --type=json -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args", "value": ["--traffic-split-api-version=v1alpha2"]}]'

确认 pod 正常启动并运行。

kubectl get pods -n argo-rollouts
NAME                             READY   STATUS    RESTARTS   AGE
argo-rollouts-58b9bc98f7-cj79l   1/1     Running   0          1m5s
安装 kubectl argo 插件

使用 kubectl argo 插件可以通过命令行对发布进行操作。

在 macOS 下,其他平台参考 官方安装文档[13]

brew install argoproj/tap/kubectl-argo-rollouts

通过下面的命令启动 Argo Rollouts Dashboard,在浏览器中打开 http://localhost:3100/rollouts 就可访问 Dashboard。

kubectl argo rollouts dashboard

接下来就是部署示例应用。

部署应用

示例应用使用常见的 bookstore 系统,包含下面几个组件。这里只对 bookstore 应用进行金丝雀发布,为了方便演示,除了 bookstore 以外的几个应用继续使用 Deployment 的方式部署,只有 bookstore 使用 Argo Rollouts 的 `Rollout` CRD[14]

  • bookbuyer  是一个 HTTP 客户端,它发送请求给  bookstore。这个流量是 允许 的。

  • bookthief  是一个 HTTP 客户端,很像  bookbuyer,也会发送 HTTP 请求给  bookstore。这个流量应该被 阻止

  • bookstore  是一个服务器,负责对 HTTP 请求给与响应。同时,该服务器也是一个客户端,发送请求给  bookwarehouse  服务。这个流量是被 允许 的。

  • bookwarehouse  是一个服务器,应该只对  bookstore  做出响应。bookbuyer  和  bookthief  都应该被其阻止。

  • mysql  是一个 MySQL 数据库,只有  bookwarehouse  可以访问。

这里舍弃了 bookthief 应用。

创建命名空间

创建命名空间 rollouts-demo ,并纳入到网格管理中。

kubectl create namespace rollouts-demo
kubectl config set-context --current --namespace rollouts-demo
osm namespace add rollouts-demo

创建 Service

kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: bookbuyer
  namespace: rollouts-demo
  labels:
    app: bookbuyer
spec:
  ports:
  - port: 14001
    name: bookbuyer-port
  selector:
    app: bookbuyer
---
apiVersion: v1
kind: Service
metadata:
  name: bookstore
  namespace: rollouts-demo
  labels:
    app: bookstore
spec:
  ports:
  - port: 14001
    name: bookstore-port
  selector:
    app: bookstore
---
apiVersion: v1
kind: Service
metadata:
  name: bookstore-v1
  namespace: rollouts-demo
  labels:
    app: bookstore
    version: v1
spec:
  ports:
  - port: 14001
    name: bookstore-port
  selector:
    app: bookstore
---
apiVersion: v1
kind: Service
metadata:
  name: bookstore-v2
  namespace: rollouts-demo
  labels:
    app: bookstore
    version: v2
spec:
  ports:
  - port: 14001
    name: bookstore-port
  selector:
    app: bookstore
---
apiVersion: v1
kind: Service
metadata:
  name: bookwarehouse
  namespace: rollouts-demo
  labels:
    app: bookwarehouse
spec:
  ports:
  - port: 14001
    name: bookwarehouse-port
  selector:
    app: bookwarehouse
---
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: rollouts-demo
spec:
  ports:
  - port: 3306
    targetPort: 3306
    name: client
    appProtocol: tcp
  selector:
    app: mysql
  clusterIP: None
EOF

部署 Deployment 应用

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bookbuyer
  namespace: rollouts-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: bookbuyer
      version: v1
  template:
    metadata:
      labels:
        app: bookbuyer
        version: v1
    spec:
      nodeSelector:
        kubernetes.io/os: linux
      containers:
        - name: bookbuyer
          image: flomesh/bookbuyer:latest
          imagePullPolicy: Always
          command: ["/bookbuyer"]
          env:
            - name: "BOOKSTORE_NAMESPACE"
              value: rollouts-demo
            - name: "BOOKSTORE_SVC"
              value: bookstore
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bookwarehouse
  namespace: rollouts-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: bookwarehouse
  template:
    metadata:
      labels:
        app: bookwarehouse
        version: v1
    spec:
      nodeSelector:
        kubernetes.io/os: linux
      containers:
        - name: bookwarehouse
          image: flomesh/bookwarehouse:latest
          imagePullPolicy: Always
          command: ["/bookwarehouse"]
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: rollouts-demo
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      nodeSelector:
        kubernetes.io/os: linux
      containers:
      - image: mariadb:10.7.4
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: mypassword
        - name: MYSQL_DATABASE
          value: booksdemo
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - mountPath: /mysql-data
          name: data
        readinessProbe:
          tcpSocket:
            port: 3306
          initialDelaySeconds: 15
          periodSeconds: 10
      volumes:
        - name: data
          emptyDir: {}
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 250M
EOF

部署 bookstore Rollout

我们为 bookstore 的金丝雀发布设置了三个阶段:10%、50%、90% 流量,每个阶段后都会进入暂停状态(也可以设置暂停时间、条件等等)。

前面创建 Service 时,我们为 bookstore 创建了如下三个服务,这里正好会用到:

  • 根服务 bookstore

  • 稳定版服务 bookstore-v1

  • 金丝雀版本 bookstore-v2

kubectl apply -f - <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: bookstore
  namespace: rollouts-demo
spec:
  replicas: 1
  strategy:
    canary:
      canaryService: bookstore-v2
      stableService: bookstore-v1
      trafficRouting:
        smi:
          rootService: bookstore
      steps:
      - setWeight: 10
      - pause: {}
      - setWeight: 50
      - pause: {}
      - setWeight: 90
      - pause: {}
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: bookstore
  template:
    metadata:
      labels:
        app: bookstore
    spec:
      nodeSelector:
        kubernetes.io/os: linux
      containers:
        - name: bookstore
          image: addozhang/bookstore-v1:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 14001
              name: web
          command: ["/bookstore"]
          args: ["--port", "14001"]
          env:
            - name: BOOKWAREHOUSE_NAMESPACE
              value: rollouts-demo
EOF

部署完成后,此时 bookstore-v1 的版本会运行,并不会部署新的版本,此时三个 Service 都会选择到稳定版本的 pod。

kubectl get endpoints -n rollouts-demo -l app=bookstore
NAME           ENDPOINTS                           AGE
bookstore-v1   10.42.1.34:14001                    54s
bookstore      10.42.1.34:14001                    54s
bookstore-v2   10.42.1.34:14001                    54s

查看 TrafficSplit 的设定,访问 bookstore 的所有流量都会进入 bookstore-v1 的 endpoint 中:

kubectl get trafficsplit bookstore -n rollouts-demo -o yaml
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
  name: bookstore
  namespace: rollouts-demo
spec:
  backends:
  - service: bookstore-v2
    weight: 0
  - service: bookstore-v1
    weight: 100
  service: bookstore

查看 Argo Rollouts Dashboard,可以看到刚刚创建的 Rollout

9584ab4b6ec772cbc3ae9371c89d870b.png
init-rollout

点开可以看到详细信息,包括当前的 revision,已经我们设置的发布步骤。

53d6db2f72f178dd22182947706647e1.png

rollout-dashboard-rollout-detail

创建 bookbuyer Ingress

为了方便查看运行效果,为 bookbuyer 创建 Ingress。然后就可以通过 http://ingress_host:80 来访问 bookbuyer 应用了,也可以通过 http://ingress_host:80/reset 来重置应用页面的计数器,方便灰度发布的效果确认。

kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: bookbuyer
  namespace: rollouts-demo
spec:
  ingressClassName: pipy
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: bookbuyer
            port:
              number: 14001
---
kind: IngressBackend
apiVersion: policy.openservicemesh.io/v1alpha1
metadata:
  name: bookbuyer
  namespace: rollouts-demo
spec:
  backends:
  - name: bookbuyer
    port:
      number: 14001
      protocol: http
  sources:
  - kind: Service
    namespace: osm-system
    name: fsm-ingress-pipy-controller
EOF

发布测试

笔者的 ingress IP 是 192.168.1.12,因此在浏览器中打开 http://192.168.1.12。此时,所有的流量都到了 bookstore-v1

2485f1e604154f68e1e52f4e5005f4e6.png
bookbuyer

执行应用升级

通过下面的命令,部署 bookstore v2 版本开始金丝雀发布。除了命令行,也可以在 Dashboard 的发布详情页面上修改容器的镜像。

kubectl argo rollouts set image bookstore bookstore=addozhang/bookstore-v2:latest

然后通过下面的命令可以查看稳定版和金丝雀版本的实例状态。

kubectl argo rollouts get rollout bookstore
ade1810690d5fbbfc671f8a982968ba9.png
argo-rollout-started

重置 bookstore 应用页面的计数器后,可以发现 v1 和 v2 流量接近 9:1。

21a184585890be51b6ce04aeb8b61237.png
argo-rollout-in-progress

Dashboard 的发布详情页上,会显示 v2 版本的 revision,右侧 steps 列表中可以看到当前处于第一个 pause 阶段。

9abd6868547a5142d9f8140f9ac01331.png

argo-dashboard-rollout-in-progress

推进到 50% 流量

验证完成后,就可以将发布推进到下一阶段。可以使用下面的命令,也可在 Dashboard 详情页上点击 PROMOTE 按钮。

kubectl argo rollouts promote bookstore
rollout 'bookstore' promoted

还是先清空应用页面计数器,然后查看效果。

fa5b776f8fe2be31bc7e56b5efe75a35.png

argo-rollout-promote-50-percent
b53476567af38b31e57bd2d6795025bf.png
argo-dashboard-rollout-promote-50-percent

推进到 100% 流量

接下来,可以继续推荐到下一阶段 90%。也可以通过下面的命令直接进入到最后的阶段 100%,在 Dashboard 详情页上点击 PROMOTE-FULL 也可达到同样的效果。

kubectl argo rollouts promote bookstore --full
rollout 'bookstore' fully promoted
e71c12c9b9a3de6dc83a36a22068eb1b.png
argo-dashboard-rollout-promote-100-percent

其他操作

在发布的任何一个阶段,可以通过命令或者页面上的操作回到上一个阶段,或者退出发布。

#回到上一阶段
kubectl argo rollouts undo bookstore
#退出发布
kubectl argo rollouts abort bookstore

总结

服务网格以进程外无侵入的方式为服务提供了丰富的治理功能,从平台层面提升系统的可观测性、安全以及可靠性。金丝雀发布是服务网格的主要应用场景之一,大大减低了应用发布带来的风险,将不稳定性限制在可控的范围内。

同时 Argo Rollouts 也提供了丰富的设置,来控制发布的流程,满足不同的使用需求。

参考资料

[1] 

Argo Rollouts: https://argoproj.github.io/argo-rollouts/

[2] 

Kubernetes控制器: https://kubernetes.io/docs/concepts/architecture/controller/

[3] 

CRD: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/

[4] 

入口控制器: https://kubernetes.io/docs/concepts/services-networking/ingress/

[5] 

SMI(Service Mesh Interface): https://smi-spec.io

[6] 

TrafficSplit API: https://github.com/servicemeshinterface/smi-spec/blob/main/apis/traffic-split/v1alpha2/traffic-split.md

[7] 

osm-edge: https://osm-edge-docs.flomesh.io

[8] 

SMI(Service Mesh Interface): https://smi-spec.io

[9] 

osm(Open Service Mesh): https://openservicemesh.io

[10] 

Pipy: https://github.com/flomesh-io/pipy

[11] 

宽松流量策略模式: https://osm-edge-docs.flomesh.io/zh/docs/guides/traffic_management/permissive_mode/

[12] 

Ingress: https://osm-edge-docs.flomesh.io/zh/docs/demos/ingress_fsm/

[13] 

官方安装文档: https://argoproj.github.io/argo-rollouts/installation/#kubectl-plugin-installation

[14] 

Rollout CRD: https://argoproj.github.io/argo-rollouts/features/specification/

本文转载自:「云原生指北」,原文:https://url.hi-linux.com/9LKst,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。

c784806c2b845de4ebd6295fc4f38377.gif

最近,我们建立了一个技术交流微信群。目前群里已加入了不少行业内的大神,有兴趣的同学可以加入和我们一起交流技术,在 「奇妙的 Linux 世界」 公众号直接回复 「加群」 邀请你入群。

026ead7094a757ee9864003edc0892ac.png

你可能还喜欢

点击下方图片即可阅读

a56a1a2cf84f1f4d21aa398ba3263ac0.png

SREWorks: 阿里开源,一站式运维 SaaS 应用套件和云原生运维开发平台

18e20d2851d8be94555edbb7321a2edc.png
点击上方图片,『美团|饿了么』外卖红包天天免费领

2c625bfde580cd3358c921bacf98cb27.png

更多有趣的互联网新鲜事,关注「奇妙的互联网」视频号全了解!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值