基于 Flagger 和 Nginx-Ingress 实现金丝雀发布

本文涉及的产品
可观测监控 Prometheus 版,每月50GB免费额度
简介: 本文介绍使用 Flagger 和 Nginx-Ingress 实现自动化金丝雀部署。

前言

很久之前我写过一篇介绍使用 Nginx-Ingress 实现蓝绿部署和金丝雀发布的文章,但那篇文章只是介绍了 nginx-ingress 具备这些能力,真正应用还要很多额外的配置和操作,况且现在能实现这些功能的并不只有 nginx-ingress,Service Mesh 工具如:Istio,App Mesh,Linkerd;Ingress Controller 如:Contour,Gloo,NGINX 都能实现,而我们需要的更多是进行金丝雀发布之后指标的监控,流量的调整以及出现问题后的及时回滚。而 Flagger 就是这样一个帮助我们解决上面这些问题的开源工具。

Flagger

Flagger 是一种渐进式交付工具,可自动控制 Kubernetes 上应用程序的发布过程。通过指标监控和运行一致性测试,将流量逐渐切换到新版本,降低在生产环境中发布新软件版本导致的风险。

Flagger 使用 Service Mesh(App Mesh,Istio,Linkerd)或 Ingress Controller(Contour,Gloo,NGINX)来实现多种部署策略(金丝雀发布,A/B 测试,蓝绿发布)。对于发布分析,Flagger 可以查询 Prometheus、Datadog 或 CloudWatch,并使用 Slack、MS Teams、Discord 和 Rocket 来发出告警通知。

本文主要介绍 Flagger 使用 Nginx-Ingress 进行金丝雀发布并监控发布状态,更多内容见官方文档

Flagger NGINX Ingress Controller

前提条件

版本要求

安装 Flagger 需要 Kubernetes 版本高于 v1.14,NGINX ingress 版本高于 0.24

安装 NGINX ingress

$ kubectl create ns ingress-nginx
$ helm upgrade -i nginx-ingress stable/nginx-ingress \
--namespace ingress-nginx \
--set controller.metrics.enabled=true \
--set controller.podAnnotations."prometheus\.io/scrape"=true \
--set controller.podAnnotations."prometheus\.io/port"=10254

安装部署

Flagger 安装

Flagger 提供了 Hlem 和 Kustomize 两种安装方式,这里使用 Helm 3 安装:

$ helm repo add flagger https://flagger.app
$ helm upgrade -i flagger flagger/flagger \
--namespace ingress-nginx \
--set prometheus.install=true \
--set meshProvider=nginx \
--set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
--set slack.channel=flagger \
--set slack.user=flagger

值得注意的事这里我选择了 Slack 作为通知软件,需要在自己的 #channel 内新增一个 APP,并将该 APP 的 urlchanneluser 填入上面的命令中。这里设置的是全局通知,集群中的 Flagger 被触发后都会进行通知,当然也可以为单个 Flagger 配置专门的通知,这里就不做过多介绍,详情见官方文档

示例安装

新建测试 namespace:

$ kubectl create ns test

部署示例 deployment 和 horizontal pod autoscaler:

$ kubectl apply -k github.com/weaveworks/flagger//kustomize/podinfo

部署负载测试器,以便在金丝雀发布时进行流量分析:

$ helm upgrade -i flagger-loadtester flagger/loadtester --namespace=test

部署 ingress,这里的 app.example.com 需要改成你自己的域名,如果是在本地进行测试,则修改本机和负载测试器所在节点的 /ect/hosts,将其指向你的 ADDRESS,否则将无法进行流量分析,导致部署失败。

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: podinfo
  namespace: test
  labels:
    app: podinfo
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: app.example.com
      http:
        paths:
          - backend:
              serviceName: podinfo
              servicePort: 80

将以上内容另存为 podinfo-ingress.yaml,然后应用:

$ kubectl apply -f ./podinfo-ingress.yaml

创建一个 Canary 资源:

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  provider: nginx
  # deployment reference
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  # ingress reference
  ingressRef:
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    name: podinfo
  # HPA reference (optional)
  autoscalerRef:
    apiVersion: autoscaling/v2beta1
    kind: HorizontalPodAutoscaler
    name: podinfo
  # the maximum time in seconds for the canary deployment
  # to make progress before it is rollback (default 600s)
  progressDeadlineSeconds: 60
  service:
    # ClusterIP port number
    port: 80
    # container port number or name
    targetPort: 9898
  analysis:
    # 时间间隔 (默认 60s)
    interval: 10s
    # 回滚前的最大失败指标检查次数
    threshold: 10
    # 路由到金丝雀副本的最大流量百分比
    # 百分比 (0-100)
    maxWeight: 50
    # 金丝雀每次递增的百分比
    # 百分比 (0-100)
    stepWeight: 5
    # NGINX Prometheus checks
    metrics:
    - name: request-success-rate
      # minimum req success rate (non 5xx responses)
      # percentage (0-100)
      thresholdRange:
        min: 99
      interval: 1m
    # testing (optional)
    webhooks:
      - name: acceptance-test
        type: pre-rollout
        url: http://flagger-loadtester.test/
        timeout: 30s
        metadata:
          type: bash
          cmd: "curl -sd 'test' http://podinfo-canary/token | grep token"
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 5s
        metadata:
          cmd: "hey -z 1m -q 10 -c 2 http://app.example.com/"

将以上内容另存为 podinfo-canary.yaml,然后应用:

$ kubectl apply -f ./podinfo-canary.yaml

目前可以看到示例应用 podinfo 已经安装完毕,并出现了 podinfopodinfo-primary 两个版本,并且 http://app.example.com/ 已经可以访问:

$ kubectl get deploy,svc,ing -n test
NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/flagger-loadtester   1/1     1            1           29h
deployment.apps/podinfo              0/0     0            0           29h
deployment.apps/podinfo-primary      2/2     2            2           29s

NAME                         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/flagger-loadtester   ClusterIP   10.43.116.74    <none>        80/TCP    29h
service/podinfo              ClusterIP   10.43.155.193   <none>        80/TCP    9s
service/podinfo-canary       ClusterIP   10.43.194.226   <none>        80/TCP    29s
service/podinfo-primary      ClusterIP   10.43.254.13    <none>        80/TCP    29s

NAME                                HOSTS             ADDRESS                       PORTS   AGE
ingress.extensions/podinfo          app.example.com   192.168.1.129,192.168.4.210   80      5h17m
ingress.extensions/podinfo-canary   app.example.com                                 80      9s

这个页面会展示 podinfo 的版本已经其正在访问的 pod 名称:

app.example.com

自动金丝雀发布

现在起发布由 Flagger 控制,在部署新版本后,Flagger 自动将流量按照比例切换到新版本上,同时监控性能指标,例如 HTTP 请求的成功率、请求的平均持续时间和 pod 运行状态,经过分析后提升流量或者回滚,并通知到 Slack。

自动金丝雀发布

通过更新镜像版本触发金丝雀部署:

$ kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.1
deployment.apps/podinfo image updated

可以看到初始化完成后已经有 5% 的流量切换到新版本了

$ kubectl -n test describe canary/podinfo
...
Status:
  Canary Weight:  5
  Conditions:
    Last Transition Time:  2020-07-02T07:21:26Z
    Last Update Time:      2020-07-02T07:21:26Z
    Message:               New revision detected, progressing canary analysis.
    Reason:                Progressing
    Status:                Unknown
    Type:                  Promoted
  Failed Checks:           0
  Iterations:              0
  Last Applied Spec:       c8bdf98d5
  Last Transition Time:    2020-07-02T07:22:05Z
  Phase:                   Progressing
  Tracked Configs:
Events:
  Type     Reason  Age                From     Message
  ----     ------  ----               ----     -------
  Warning  Synced  10m                flagger  podinfo-primary.test not ready: waiting for rollout to finish: observed deployment generation less then desired generation
  Warning  Synced  10m                flagger  podinfo-primary.test not ready: waiting for rollout to finish: 0 of 2 updated replicas are available
  Normal   Synced  10m (x3 over 10m)  flagger  all the metrics providers are available!
  Normal   Synced  10m                flagger  Initialization done! podinfo.test
  Normal   Synced  41s                flagger  New revision detected! Scaling up podinfo.test
  Warning  Synced  31s                flagger  canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 1 updated replicas are available
  Warning  Synced  21s                flagger  canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 2 updated replicas are available
  Warning  Synced  11s                flagger  canary deployment podinfo.test not ready: waiting for rollout to finish: 1 of 2 updated replicas are available
  Normal   Synced  1s                 flagger  Starting canary analysis for podinfo.test
  Normal   Synced  1s                 flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  1s                 flagger  Advance podinfo.test canary weight 5

使用 watch 也能实时看到部署流量的权重,根据上面的设置,新版本权重大于 50% 就认为部署成功,流量将全部切换到新版本,并完成金丝雀部署:

$ watch kubectl get canaries --all-namespaces
Every 2.0s: kubectl get canaries --all-namespaces                                     guoxudongdeMacBook-Pro.local: Thu Jul  2 15:23:35 2020

NAMESPACE   NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
test        podinfo   Progressing   45       2020-07-02T07:23:25Z

开始部署时的 Slack 通知:

Slack 通知

页面上也能看出变化,访问到新版本的概率会越来越高,以蓝色和绿色的圆代表新版本和老版本:

金丝雀发布

发布成功后,会收到 Slack 通知:

Slack 通知

自动回滚

当然,有自动发布就会有自动回滚,下面就通过手动触发状态码 500 异常,演示暂停发布并回滚。

部署一个新版本:

$ kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.2

触发状态码 500 异常:

$ watch curl http://app.example.com/status/500

等待一会儿,就可以看到部署失败并回滚:

$ watch kubectl get canaries --all-namespaces
Every 2.0s: kubectl get canaries --all-namespaces                                     guoxudongdeMacBook-Pro.local: Thu Jul  2 15:45:24 2020

NAMESPACE   NAME      STATUS   WEIGHT   LASTTRANSITIONTIME
test        podinfo   Failed   0        2020-07-02T07:45:16Z

发布失败,也会收到 Slack 通知:

失败 Slack 通知

A/B 测试

除了加权路由,Flagger 还可以根据 HTTP 匹配条件将流量路由到新版本(当然,这个 Nginx-Ingress 的功能,Flagger 只是简化了操作)。可以根据 HTTP header 和 cookie 来定位用户并细分受众,对于需要关联会话的前端应用十分有用。

A/B 测试

修改 Canary 资源:

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  provider: nginx
  # deployment reference
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  # ingress reference
  ingressRef:
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    name: podinfo
  # HPA reference (optional)
  autoscalerRef:
    apiVersion: autoscaling/v2beta1
    kind: HorizontalPodAutoscaler
    name: podinfo
  # the maximum time in seconds for the canary deployment
  # to make progress before it is rollback (default 600s)
  progressDeadlineSeconds: 60
  service:
    # ClusterIP port number
    port: 80
    # container port number or name
    targetPort: 9898
  analysis:
    interval: 1m
    threshold: 10
    iterations: 10
    match:
      # curl -H 'X-Canary: insider' http://app.example.com
      - headers:
          x-canary:
            exact: "insider"
      # curl -b 'canary=always' http://app.example.com
      - headers:
          cookie:
            exact: "canary"
    metrics:
    - name: request-success-rate
      thresholdRange:
        min: 99
      interval: 1m
    webhooks:
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 5s
        metadata:
          cmd: "hey -z 1m -q 10 -c 2 -H 'Cookie: canary=always' http://app.example.com/"

从上面的配置可以看到,将 headers 为 X-Canary: insider 或 cookie 为 canary=always 的请求路由到新版本。

部署一个新版本:

$ kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.3

可以收到 Slack 通知:

A/B 测试 Slack 通知

正常访问,还是访问到老的 v3.1.1 版:

$ curl http://app.example.com
{
  "hostname": "podinfo-primary-5dc6b76bd5-8sbh8",
  "version": "3.1.1",
  "revision": "7b6f11780ab1ce8c7399da32ec6966215b8e43aa",
  "color": "#34577c",
  "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
  "message": "greetings from podinfo v3.1.1",
  "goos": "linux",
  "goarch": "amd64",
  "runtime": "go1.13.1",
  "num_goroutine": "11",
  "num_cpu": "6"
}

请求添加指定 header,访问到新的 v3.1.3 版:

$ curl -H 'X-Canary: insider' http://app.example.com
{
  "hostname": "podinfo-58bdd78d6f-m9bsc",
  "version": "3.1.3",
  "revision": "7b6f11780ab1ce8c7399da32ec6966215b8e43aa",
  "color": "#34577c",
  "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
  "message": "greetings from podinfo v3.1.3",
  "goos": "linux",
  "goarch": "amd64",
  "runtime": "go1.13.1",
  "num_goroutine": "10",
  "num_cpu": "6"
}

请求添加指定 cookie,访问到新的 v3.1.3 版:

$ curl -b 'canary=always' http://app.example.com
{
  "hostname": "podinfo-58bdd78d6f-m9bsc",
  "version": "3.1.3",
  "revision": "7b6f11780ab1ce8c7399da32ec6966215b8e43aa",
  "color": "#34577c",
  "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
  "message": "greetings from podinfo v3.1.3",
  "goos": "linux",
  "goarch": "amd64",
  "runtime": "go1.13.1",
  "num_goroutine": "10",
  "num_cpu": "6"
}

在浏览器中访问也能得到相同的结果:

添加 cookie 在浏览器中访问

结语

最早了解 Flagger 其实是因为其与 Istio 的关系,Flagger 默认的 meshProvider 就是 Istio。但是在深入了解后,发现其对市面上常见的 Service Mesh 和 Ingress Controller 都有较好的支持,通过与 Prometheus 以及负载测试器的配合可以进行细粒度的分析,从而提高了发布质量,同时还降低了人工操作出错的可能性。

最近 OAM 社区也放出了基于 Flagger 的部署 Trait 的示例,相信之后与 OAM 结合使用可以在持续部署和应用管理领域发挥更大的作用。

想了解 OAM 可以查看我之前的文章:《以应用为中心:开放应用模型(OAM)初探》

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
2月前
|
应用服务中间件 BI nginx
Nginx的location配置详解
【10月更文挑战第16天】Nginx的location配置详解
|
2月前
|
缓存 负载均衡 安全
Nginx常用基本配置总结:从入门到实战的全方位指南
Nginx常用基本配置总结:从入门到实战的全方位指南
285 0
|
2月前
|
应用服务中间件 Linux nginx
Jetson 环境安装(四):jetson nano配置ffmpeg和nginx(亲测)之编译错误汇总
这篇文章是关于在Jetson Nano上配置FFmpeg和Nginx时遇到的编译错误及其解决方案的汇总。
96 4
|
16天前
|
存储 负载均衡 中间件
Nginx反向代理配置详解,图文全面总结,建议收藏
Nginx 是大型架构必备中间件,也是大厂喜欢考察的内容,必知必会。本篇全面详解 Nginx 反向代理及配置,建议收藏。
Nginx反向代理配置详解,图文全面总结,建议收藏
|
29天前
|
应用服务中间件 API nginx
nginx配置反向代理404问题
【10月更文挑战第18天】本文介绍了使用Nginx进行反向代理的配置方法,解决了404错误、跨域问题和302重定向问题。关键配置包括代理路径、请求头设置、跨域头添加以及端口转发设置。通过调整`proxy_set_header`和添加必要的HTTP头,实现了稳定的服务代理和跨域访问。
147 1
nginx配置反向代理404问题
|
13天前
|
应用服务中间件 网络安全 nginx
轻松上手Nginx Proxy Manager:安装、配置与实战
Nginx Proxy Manager (NPM) 是一款基于 Nginx 的反向代理管理工具,提供直观的 Web 界面,方便用户配置和管理反向代理、SSL 证书等。本文档介绍了 NPM 的安装步骤,包括 Docker 和 Docker Compose 的安装、Docker Compose 文件的创建与配置、启动服务、访问 Web 管理界面、基本使用方法以及如何申请和配置 SSL 证书,帮助用户快速上手 NPM。
67 1
|
2月前
|
编解码 Ubuntu 应用服务中间件
Jetson 环境安装(三):jetson nano配置ffmpeg和nginx(亲测)
本文介绍了在NVIDIA Jetson Nano上配置FFmpeg和Nginx的步骤,包括安装、配置和自启动设置。
168 1
Jetson 环境安装(三):jetson nano配置ffmpeg和nginx(亲测)
|
1月前
|
缓存 负载均衡 应用服务中间件
Nginx配置
【10月更文挑战第22天】在实际配置 Nginx 时,需要根据具体的需求和环境进行调整和优化。同时,还需要注意配置文件的语法正确性和安全性。
48 7
|
2月前
|
前端开发 JavaScript 应用服务中间件
终极 Nginx 配置指南
本文介绍了Nginx的基本配置及其优化方法。首先,通过删除注释简化了Nginx的默认配置文件,使其更易于理解。接着,文章将Nginx配置文件分为全局块、events块和http块三部分进行详细解释。此外,还提供了如何快速上线网站、解决前端history模式404问题、配置反向代理、开启gzip压缩、设置维护页面、在同一IP上部署多个网站以及实现动静分离的具体配置示例。最后,附上了Nginx的基础命令,包括安装、启动、重启和关闭等操作。
|
2月前
|
负载均衡 应用服务中间件 nginx
Nginx的6大负载均衡策略及权重轮询手写配置
【10月更文挑战第9天】 Nginx是一款高性能的HTTP服务器和反向代理服务器,它在处理大量并发请求时表现出色。Nginx的负载均衡功能可以将请求分发到多个服务器,提高网站的吞吐量和可靠性。以下是Nginx支持的6大负载均衡策略:
171 7