多集群运维(二):应用渐进发布

背景

在云计算和DevOps的时代,管理和维护多个Kubernetes(K8S)集群成为了常见的挑战。不同的集群(如开发、测试、生产环境)拥有各自的特性和需求。为了有效管理这些复杂的集群环境,需要采用精心的规划和合适的自动化工具。

目标

本文档旨在展示如何高效地管理多个K8S集群,尤其是在开发、测试、生产等不同环境中。关键在于运用自动化工具和最佳实践,以实现高效、可靠的运维流程。

目标细节

  1. IaC管理云资源: 使用基础设施即代码(IaC)方法来初始化云资源。 包括配置虚拟私有云(VPC)、防火墙规则、密钥对(keypairs)以及各种用途的虚拟主机(Vhosts),如开发(devops)、监控(monitor)、系统集成测试(sit)、用户接受测试(uat)和生产(prod)环境。
  2. GitHub Action Pipeline自动化: 利用GitHub Action Pipeline来自动化集群环境的配置初始化。涵盖不同环境(devops、monitor、sit、uat和prod)的自动化设置。
  3. 使用FluxCD实现GitOps: 采用FluxCD工具来实现GitOps方式的配置变更和应用发布。 FluxCD将允许团队通过Git仓库管理和同步Kubernetes集群的状态,从而简化版本控制和部署过程。
  4. 使用Flagger实现渐进式应用发布: 采用Flagger工具来管理渐进式应用发布。 支持金丝雀(Canary)、蓝绿(Blue-Green)、A/B测试等常见的发布模式。

集成应用运行状态监控和告警机制,确保发布过程的可靠性和稳定性。通过这种方法,团队可以有效地管理和维护多个K8S集群,同时确保每个环境的稳定性和一致性。自动化工具和流程的应用不仅减少了人为错误的风险,还提高了运维效率和系统的可靠性。

技术选型

选择 Flagger 而非 ArgoCD 或 Kruise Rollout 作为渐进式交付工具的原因可以从几个方面来考虑:

  • 与现有部署模式的兼容性:Flagger 的一个主要优点是它可以无缝地与现有的部署模式配合工作。由于 Flagger 使用旁路配置,它不需要重定义或大幅修改现有的应用部署模式,这意味着它可以在不干扰现有系统的情况下实现灰度发布。这种低侵入性是对于希望在不破坏现有工作流程的情况下引入新技术的组织来说非常有吸引力的。
  • 灰度发布的专长:Flagger 特别擅长灰度发布,包括金丝雀部署、A/B 测试和蓝绿部署等策略。它提供了丰富的功能和灵活的配置选项,使得它在渐进式交付方面特别有效。与 ArgoCD 相比,虽然 ArgoCD 也支持渐进式部署,但它更侧重于整体的应用部署和管理。
  • 成熟度和社区支持:虽然 Kruise Rollout 是一个非常有前途的项目,但它相对于 Flagger 来说还处于发展阶段。Flagger 作为一个更成熟的项目,拥有更广泛的社区支持和更多的实际应用案例,这为用户提供了更多的信心,尤其是在企业级部署中。
  • 集成和兼容性:Flagger 可以很好地与其他工具(如 Nginx,Istio、Linkerd 等服务网格)集成,提供更全面的网络流量控制。这种兼容性使得它可以更容易地融入多样化的技术栈中。

选择 Flagger 的主要原因是其对现有部署模式的低侵入性,灰度发布的专业能力,以及其成熟度和社区支持。虽然 ArgoCD 和 Kruise Rollout 各有其优势,但对于需要平滑过渡和专注于灰度发布策略的组织来说,Flagger 可能是一个更合适的选择。

选择 FluxCD、Flagger 和 ApiSIX Ingress 的组合,从生态系统和扩展能力的角度来看,有几个关键优势:

  • FluxCD:作为 CNCF 项目,它提供紧密的 Kubernetes 集成和自动化部署的基础。
  • Flagger:与 FluxCD 无缝集成,专注于高效、可靠的渐进式交付。
  • ApiSIX Ingress:作为高性能 API 网关,提供了丰富的流量管理功能,适应复杂的微服务架构。

FluxCD、Flagger 和 ApiSIX Ingress 的组合构建了一个强大、灵活且面向未来的云原生应用部署和管理生态系统,适合在 Kubernetes 上运行复杂微服务架构的组织。

准备工作

Demo示例

项目

服务提供商

用途/环境

备注

云服务账号

Google Cloud Platform (GCP)

通用

访问和管理云资源

域名

xx云

开发环境

svc-sit.ink

域名

xx云

测试验证环境

svc-uat.ink

域名

xx云

线上生产环境

svc.ink

云DNS服务

阿里云

域名解析

使用xx云的SaaS服务

CI/CD

GitHub Action

自动化构建、测试、部署

促进持续集成和持续交付流程

CD

Flagger

自动化Canary, Blue-Green, A/B Testing

应用渐进发布

GitOps

FluxCD

配置同步,自动化部署与管理

应用渐进发布

配置仓库

  1. IAC_code: https://github.com/svc-design/iac_modules.git
  2. Playbook:https://github.com/svc-design/playbook.git
  3. GitOps : https://github.com/svc-design/gitops.git
  4. PipeLine : https://github.com/open-source-solution-design/Modern-Container-Application-Reference-Architecture.git
  5. App Chart包: https://github.com/open-source-solution-design/ObservabilityPlatform/tree/main/charts/app

应用代码仓库

  • C: https://github.com/scaffolding-design/c.git
  • GO: https://github.com/scaffolding-design/go.git
  • Python: https://github.com/scaffolding-design/python.git
  • Rust: https://github.com/scaffolding-design/rust.git
  • JS: https://github.com/scaffolding-design/javascript.git

参考操作

基础环境初始化可以参考:

https://cloud.tencent.com/developer/article/2373761

这重点介绍如何配置 FluxCD、Flagger 和 ApiSIX Ingress 的参考解决方案。以下是具体操作步骤:

使用Pipeline初始化集群Addon组件

为了在Kubernetes集群中部署必要的插件,我们使用自动化脚本。以下脚本用于安装和配置 FluxCD、Flagger 和 ApiSIX Ingress。

代码语言:shell
复制
playbook/roles/k3s-addon/files/setup-ingress-apisix.sh
playbook/roles/k3s-addon/files/setup-flagger.sh
playbook/roles/k3s-addon/files/setup-fluxcd.sh

特别注意,Flagger需要配置 meshProvider=apisix 来确保与 ApiSIX 正确集成。这可以通过以下 Helm 命令实现:

代码语言:yaml
复制
helm upgrade -i flagger flagger/flagger       \
--namespace ingress                                \
--set prometheus.install=false                    \
--set meshProvider=apisix

这确保了 Flagger 在金丝雀、蓝绿和A/B测试等策略中能够利用 ApiSIX 提供的路由能力。

修改App chart包,添加Canary发布支持

为了支持渐进式发布,我们需要在应用的 Helm chart 中添加 Canary 对象的定义。以下是 canary.yaml 的示例模板,它定义了如何进行金丝雀发布:

charts/app/templates/canary.yaml

代码语言:yaml
复制
{{- if .Values.canary.enabled }}
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: {{ template "app.fullname" . }}
  labels:
    app: {{ template "app.name" . }}
    chart: {{ template "app.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  provider: {{ .Values.canary.provider }}
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ template "app.name" . }}
  # ingress reference
  ingressRef:
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    name: {{ template "app.fullname" . }}
  progressDeadlineSeconds: 60
  service:
    port: {{ .Values.service.port }}
    targetPort: {{ .Values.service.port }}
  analysis:
    interval: {{ .Values.canary.analysis.interval }}
    threshold: {{ .Values.canary.analysis.threshold }}
    maxWeight: {{ .Values.canary.analysis.maxWeight }}
    stepWeight: {{ .Values.canary.analysis.stepWeight }}
    metrics:
    - name: request-success-rate
      threshold: {{ .Values.canary.thresholds.successRate }}
      interval: 1m
    - name: request-duration
      threshold: {{ .Values.canary.thresholds.latency }}
      interval: 1m
   {{- if .Values.canary.metric_template.enabled }}
      - name: "404s percentage"
        templateRef:
          name: not-found-percentage
        thresholdRange:
          max: 5
        interval: 1m
   {{- endif }}
    webhooks:
      {{- if .Values.canary.acceptance-test.enabled }}
      - name: acceptance-test
        type: pre-rollout
        url: {{ .Values.canary.load_test_url }}
        timeout: 10s
        metadata:
          type: bash
          cmd: "{{ .Values.canary.acceptance-test.cmd }}"
      {{- end }}
      {{- if .Values.canary.loadtest.enabled }}
      - name: load-test
        timeout: 5s
        metadata:
          cmd: "{{ .Values.canary.loadtest.cmd }}"
      {{- end }}
---
{{- if .Values.canary.metric_template.enabled }}
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
  name: not-found-percentage
  namespace: canary
spec:
  provider:
    type: prometheus
    address: http://flagger-prometheus.apisix:9090
  query: |
    sum(
      rate(
        apisix_http_status{
          route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+",
          code!~"4.."
        }[{{ interval }}]
      )
    )
    /
    sum(
      rate(
        apisix_http_status{
          route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+"
        }[{{ interval }}]
      )
    ) * 100
  {{- end }}
{{- end }}

这个配置允许在部署应用时通过启用 canary 参数来触发渐进式发布。

更新GitOps仓库,开启 canary.enabled 参数

最后一步是更新 GitOps 仓库,开启应用的 canary.enabled 参数。这样,FluxCD 在同步仓库状态到集群时,可以自动触发渐进式发布流程。示例配置如下:

gitops/apps/python-demo/release.yaml

代码语言:yaml
复制
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: stable
  namespace: demo-python
spec:
  chart:
    spec:
      chart: app
      version: "0.1.1"
      sourceRef:
        kind: HelmRepository
        name: stable
        namespace: demo-python
  interval: 1m
  values:
    image:
      repository: artifact.onwalk.net/base/scaffolding-design/python
      tag: "d72ba38f7a3a76b71eb50f00fe46a94497e6ecaa"
    ingress:
      className: "apisix"
    canary:
      enabled: true

通过上述步骤,我们能够实现在多集群环境中的应用渐进式发布,确保新版本的平滑和安全部署。此方法结合了自动化、监控和灵活的发布策略,使得管理多个Kubernetes集群变得更加高效和可靠。

参考

  • https://openkruise.io/rollouts/introduction/
  • https://github.com/fluxcd/flagger/blob/main/kustomize/base/prometheus/prometheus.yml
  • https://github.com/fluxcd/flagger/tree/main/charts/grafana
  • https://grafana.com/grafana/dashboards/15158-flagger-canary-status/
  • https://docs.flagger.app/usage/deployment-strategies