Argo Rollouts 体验

作者: ygqygq2 分类: 系统/运维,自动化,虚拟化/容器 发布时间: 2022-05-16 18:13

1. Argo Rollouts 简单介绍

Argo Rollouts 是一个 Kubernetes 控制器和一组 CRDs,它们提供了先进的部署功能,例如蓝绿色、金丝雀、金丝雀分析、实验和渐进式交付功能。

2. Argo Rollouts 安装

我使用 helm 安装官方 charts 仓库中的 argo-rollouts。安装过程略…

3. Argo Rollouts Kubectl 插件安装

cd /tmp
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x ./kubectl-argo-rollouts-linux-amd64
sudo mv ./kubectl-argo-rollouts-linux-amd64 /usr/local/sbin/kubectl-argo-rollouts
kubectl argo rollouts version

4. Argo Rollouts 架构

详细介绍请查看官方文档:https://argoproj.github.io/argo-rollouts/architecture/

架构图

5. 官方示例体验

本文环境:
kubernetes: v1.23.4
istio: 1.13.3

我们使用官方getting-started测试。
其它请参考更详细的官方示例

6. 示例体验

6.1 基本使用

6.1.1 部署 rollout

部署一个 rollout 资源,然后查看运行情况

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  replicas: 5
  strategy:
    canary:
      steps:
      - setWeight: 20
      - pause: {}
      - setWeight: 40
      - pause: {duration: 10}
      - setWeight: 60
      - pause: {duration: 10}
      - setWeight: 80
      - pause: {duration: 10}
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: rollouts-demo
  template:
    metadata:
      labels:
        app: rollouts-demo
    spec:
      containers:
      - name: rollouts-demo
        image: argoproj/rollouts-demo:blue
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        resources:
          requests:
            memory: 32Mi
            cpu: 5m
kubectl apply -f docs/getting-started/basic/rollout.yaml -n demo
kubectl apply -f docs/getting-started/basic/service.yaml -n demo
kubectl argo rollouts get rollout rollouts-demo --watch -n demo
查看运行情况
查看资源情况

6.1.2 更新 rollout

rollout spec 模板和 deployment 一样,有内容修改即会触发一个新版本,kubectl argo rollouts 插件支持一个命令,set image 直接修改镜像:

kubectl argo rollouts set image rollouts-demo \
  rollouts-demo=argoproj/rollouts-demo:yellow -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo -w  
更新镜像
达到20%流量暂停

可以看到 rollout 处理暂停状态,现在有 20% 的 POD 为新版本,其它为旧版本。这符合定义的 20% 金丝雀流量。

6.1.3 恢复 rollout

当前 rollout 是暂停状态,当暂停没有设置持续时间时,它将无限期保持暂停状态,直到恢复。手动恢复 rollout 插件提供了一个命令 promote

kubectl argo rollouts promote rollouts-demo -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo -w
rollout恢复
后续更新步骤
pod 达到持续时间 rollout 执行下一步
全部步骤完成

promote 命令也支持跳过后续所有步骤和分析,参数为 --full

一旦所有步骤都成功完成,新的 ReplicaSet 将被票房为 “stable”,无论何时,只要在更新期间中止部署(通过失败的canary或用户手动执行),部署 就会退回 “stable” 版本。

6.1.4 中断 rollout

接下来将演示手动中止 rollout,设置一个“红色”容器版本,然后等待部署到暂停步骤:

kubectl argo rollouts set image rollouts-demo \
  rollouts-demo=argoproj/rollouts-demo:red -n demo

因为 rollout 在暂停状态,“red” 容器版本处于 “canary” 状态,abort rollout 将放大 ReplicaSet 的“稳定”版本,并缩小任何其他版本。尽管 ReplicaSet 的稳定版本可能正在运行并且是健康的,但是总体的部署仍然被认为是降级的,因为所需的版本(“red”镜像)不是实际正在运行的版本。

kubectl argo rollouts abort rollouts-demo
abort rollout
kubectl argo rollouts get rollout rollouts-demo -n demo -w
结果为降级的

为了使 rollout 再次被认为是健康的而不是降级的,有必要将所需的状态更改为以前的、稳定的版本。这通常涉及运行 kubectl apply 来应用以前的 rollout,以下我们只需使用前面的 “yellow” 镜像重新运行 set image 命令。

kubectl argo rollouts set image rollouts-demo \
  rollouts-demo=argoproj/rollouts-demo:yellow -n demo
恢复稳定版本

可以看到,rollout 重新恢复健康,并且没有任何新的 ReplicaSet 创建。

6.1.5 小结

基本示例中的 rollout 没有使用入口控制器或服务网络来路由流量。它使用普通的 kubernetes service 网络(即 kube-proxy)来实现基于新旧副本计数最接近大致 canary 权重。为了实现理想细粒度的 canary,需要一个入口控制器或服务网格。

6.2 Argo Rollouts 配合 Istio 使用

前提:kubernetes 集群已经安装 Istio。(本文使用 istio 1.13)

当 Istio 被用作流量路由时,Rollout canary 策略必须定义以下强制字段:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  strategy:
    canary:
      # 引用一个服务,该服务由控制器更新指向金丝雀副本集
      canaryService: rollouts-demo-canary
      # 引用一个服务,该服务由控制器更新指向稳定副本集
      stableService: rollouts-demo-stable
      trafficRouting:
        istio:
          virtualServices:
          # 可以配置一个或多个 virtualServices
          # 引用一个虚拟服务,控制器用金丝雀权重更新该服务
          - name: rollouts-demo-vsvc1
            # 如果 VirtualService 中有一个单一的 HTTP 路由,可选,否则为必选
            routes:
            - http-primary
            # 如果 VirtualService 中有单个 HTTPS/TLS 路由,则为可选,否则为必选
            tlsRoutes:
            # 下面的字段是可选的,但是如果定义的话,它们应该与您的 VirtualService 中至少一个 TLS 路由匹配规则完全匹配
            - port: 443 # 仅在您希望匹配包含此端口的 VirtualService 中的任何规则时需要
              # 只有在您想匹配包含所有这些 SNI 主机的 VirtualService 中的任何规则时才需要
              sniHosts:
              - reviews.bookinfo.com
              - localhost
          - name: rollouts-demo-vsvc2
            # 如果 VirtualService 中有一个单一的 HTTP 路由,可选,否则为必选
            routes:
              - http-secondary
            # 如果 VirtualService 中有单个 HTTPS/TLS 路由,则为可选,否则为必选
            tlsRoutes:
              # 下面的字段是可选的,但是如果定义的话,它们应该与您的 VirtualService 中至少一个 TLS 路由匹配规则完全匹配
              - port: 443 # 仅在您希望匹配包含此端口的 VirtualService 中的任何规则时需要
                # 只有在您想匹配包含所有这些 SNI 主机的 VirtualService 中的任何规则时才需要
                sniHosts:
                  - reviews.bookinfo.com
                  - localhost

以下为示例使用的 yaml 文件:

# rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  replicas: 1
  strategy:
    canary:
      canaryService: rollouts-demo-canary
      stableService: rollouts-demo-stable
      trafficRouting:
        istio:
          virtualServices:
          - name: rollouts-demo-vsvc1 # At least one virtualService is required
            routes:
            - primary # At least one route is required
          - name: rollouts-demo-vsvc2
            routes:
            - secondary # At least one route is required
      steps:
      - setWeight: 5
      - pause: {}
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: rollouts-demo
  template:
    metadata:
      labels:
        app: rollouts-demo
        istio-injection: enabled
    spec:
      containers:
      - name: rollouts-demo
        image: argoproj/rollouts-demo:blue
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        resources:
          requests:
            memory: 32Mi
            cpu: 5m
# services.yaml
apiVersion: v1
kind: Service
metadata:
  name: rollouts-demo-canary
spec:
  ports:
  - port: 80
    targetPort: http
    protocol: TCP
    name: http
  selector:
    app: rollouts-demo
    # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.:
    # rollouts-pod-template-hash: 7bf84f9696

---
apiVersion: v1
kind: Service
metadata:
  name: rollouts-demo-stable
spec:
  ports:
  - port: 80
    targetPort: http
    protocol: TCP
    name: http
  selector:
    app: rollouts-demo
    # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.:
    # rollouts-pod-template-hash: 789746c88d
# multipleVirtualsvc.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: rollouts-demo-vsvc1
spec:
  gateways:
  - rollouts-demo-gateway
  hosts:
  - rollouts-demo-vsvc1.local
  http:
  - name: primary
    route:
    - destination:
        host: rollouts-demo-stable
        port:
          number: 15372
      weight: 100
    - destination:
        host: rollouts-demo-canary
        port:
          number: 15372
      weight: 0
  tls:
  - match:
    - port: 3000
      sniHosts:
      - rollouts-demo-vsvc1.local  # 修改此处,否则 istio 1.13 版本报错
    route:
    - destination:
        host: rollouts-demo-stable
      weight: 100
    - destination:
        host: rollouts-demo-canary
      weight: 0

---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: rollouts-demo-vsvc2
spec:
  gateways:
  - rollouts-demo-gateway
  hosts:
  - rollouts-demo-vsvc2.local
  http:
  - name: secondary
    route:
    - destination:
        host: rollouts-demo-stable
        port:
          number: 15373
      weight: 100
    - destination:
        host: rollouts-demo-canary
        port:
          number: 15373
      weight: 0
  tls:
  - match:
    - port: 3000
      sniHosts:
      - rollouts-demo-vsvc2.local  # 修改此处,否则 istio 1.13 版本报错
    route:
    - destination:
        host: rollouts-demo-stable
      weight: 100
    - destination:
        host: rollouts-demo-canary
      weight: 0
# gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: rollouts-demo-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

部署

kubectl apply -f docs/getting-started/istio/rollout.yaml -n demo
kubectl apply -f docs/getting-started/istio/services.yaml -n demo
kubectl apply -f docs/getting-started/istio/multipleVirtualsvc.yaml -n demo
kubectl apply -f docs/getting-started/istio/gateway.yaml -n demo
kubectl get ro,svc,virtualservice,gateway,pod -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo
资源情况
rollout状态

执行更新

kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo
更新

rollouts-demo-vsvc1、rollouts-demo-vsvc2 都有更新

更新了weight
更新了weight

当 Rollout 执行步骤时,将调整 HTTP 和/或 TLS 路由的目标权重,以匹配步骤的当前 setWeight

6.3 Argo Rollouts 配合 Nginx Ingress 使用

前提:kubernetes 集群已经安装 Nginx Ingress

当 NGINX Ingress 被用作流量路由时,Rollout 探测器策略必须定义以下强制字段:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  strategy:
    canary:
      # 引用一个服务,控制器将更新该服务以指向金丝雀副本集
      canaryService: rollouts-demo-canary
      # 引用一个服务,控制器将更新该服务以指向稳定副本集
      stableService: rollouts-demo-stable
      trafficRouting:
        nginx:
          # 引用一个入口,该入口有一个指向稳定服务的规则 (e.g. rollouts-demo-stable)
          # 这个入口将被克隆成一个新名字,以便实现 NGINX 流量分割。.
          stableIngress: rollouts-demo-stable
...

canary.trafficRouting.nginx.stableIngress 中引用的入口必须具有一个主机规则,该规则具有一个针对 canary.stablervice 下引用的服务的后端。在我们的示例中,stable Service 命名为: rollouts-demo-stable:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: rollouts-demo-stable
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: rollouts-demo.local
    http:
      paths:
      - path: /
        backend:
          # 引用服务名称,也在 rolloutspec.strategy.canary.stablervice 字段中指定
          serviceName: rollouts-demo-stable
          servicePort: 80

以下为示例使用的 yaml 文件:

# rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  replicas: 1
  strategy:
    canary:
      canaryService: rollouts-demo-canary
      stableService: rollouts-demo-stable
      trafficRouting:
        nginx:
          stableIngress: rollouts-demo-stable
      steps:
      - setWeight: 5
      - pause: {}
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: rollouts-demo
  template:
    metadata:
      labels:
        app: rollouts-demo
    spec:
      containers:
      - name: rollouts-demo
        image: argoproj/rollouts-demo:blue
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        resources:
          requests:
            memory: 32Mi
            cpu: 5m
# services.yaml
apiVersion: v1
kind: Service
metadata:
  name: rollouts-demo-canary
spec:
  ports:
  - port: 80
    targetPort: http
    protocol: TCP
    name: http
  selector:
    app: rollouts-demo
    # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.:
    # rollouts-pod-template-hash: 7bf84f9696

---
apiVersion: v1
kind: Service
metadata:
  name: rollouts-demo-stable
spec:
  ports:
  - port: 80
    targetPort: http
    protocol: TCP
    name: http
  selector:
    app: rollouts-demo
    # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.:
    # rollouts-pod-template-hash: 789746c88d
# ingress.yaml  这里使用 kubernetes 1.23.4 版本
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rollouts-demo-stable
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: rollouts-demo.local
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          # Reference to a Service name, also specified in the Rollout spec.strategy.canary.stableService field
          service:
            name: rollouts-demo-stable
            port: 
              number: 80

部署

kubectl apply -f docs/getting-started/nginx/rollout.yaml
kubectl apply -f docs/getting-started/nginx/services.yaml
kubectl apply -f docs/getting-started/nginx/ingress.yaml  # 内容为上文
资源情况
rollout状态

执行更新

kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo
变化

此时,Rollout 的探测器和稳定版本都在运行,5% 的流量指向 canary。需要注意的一点是,尽管只运行两个 pod,但是这款产品能够实现5% 的 canary 权重。这是能够实现的,因为流量分配发生在 ingrress 控制器(相对于加权副本数和 kube-proxy)。

在检查 rollout 控制器所生成的 ingress 副本时,我们看到它比原来的有以下变化:

  1. 2 个额外的 NGINX 指定 canary 注释添加到注释中。
  2. Ingress 规则将有一个规则,将后端指向 canary 服务。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "5"
  creationTimestamp: "2022-05-16T09:04:45Z"
  generation: 1
  name: rollouts-demo-rollouts-demo-stable-canary
  namespace: demo
  ownerReferences:
  - apiVersion: argoproj.io/v1alpha1
    blockOwnerDeletion: true
    controller: true
    kind: Rollout
    name: rollouts-demo
    uid: 1e9e8f18-2e6d-4c02-97dc-c22fb7ebcab8
  resourceVersion: "50355684"
  uid: 350850c3-98af-4a7f-8c85-b30c2c8b8656
spec:
  rules:
  - host: rollouts-demo.local
    http:
      paths:
      - backend:
          service:
            name: rollouts-demo-canary
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific

随着 Rollout 在步骤中的进展,将调整 canary-weight 注释以匹配步骤的当前 setWeight。NGINX 入口控制器检查原始入口、金丝雀入口和金丝雀权重注释,以确定在两个入口之间分配的流量百分比。

7. 总结

Argo Rollouts 提供强大的发布功能,确实可以在一定程度上代替 deployment,能基本上满足各种发布需求,它做不到的一些功能也可以配合 istio 。
总之,随着 Argo 系列的产品逐渐完善,其将会在基于 kubernetes 的 CI/CD 中越发占有一席之地。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据