使用 Flux 在亚马逊云科技上实现 GitOps

GitOps 是一种基于 Kubernetes 集群实现持续部署的有效方法,且同时满足安全性、权限分离、可审计性和敏捷性等企业级需求。以下内容是基于 Amazon EKS 的 GitOps 最佳实践。
发布时间:2023 年 5 月 19 日
DevOps
GitOps
Flux CD
EKS
Kubernetes
CDK
CodeBuild
CodePipeline
教程
亚马逊云科技

虽然许多企业已经将 Kubernetes 用于其生产,但是面对在不同阶段同时运行的多个 Kuberentes 集群时,这些企业经常困惑于如何在确保业务敏捷性的同时实现持续部署、高安全性、权限分离和审计。使用 GitOps 即可基于 Kubernetes 集群实现持续部署,同时满足安全性和权限分离等企业级需求。

在这篇博客文章中,我们将使用 Amazon CodeCommit、Amazon CodePipeline 和 Flux 在 Amazon EKS 环境中实现 GitOps。我们将详细演示如何在该 Amazon EKS 环境中设置满足生产要求的 GitOps 工作流,并展示微服务应用程序如何在 GitOps 式的 CI/CD 管道中实现持续集成和持续交付。

但是在我们讨论最佳实践和本教程之前,我们首先了解一下什么是 GitOps,以及 GitOps 最佳实践为何如此重要。

Olawale Olaleye
亚马逊云科技使用经验
300 - 高级
完成所需时间
90 分钟
所需费用
支持亚马逊云科技免费套餐
前提条件

注册 / 登录 亚马逊云科技账户

上次更新时间
2023 年 5 月 19 日

什么是 GitOps?

GitOps 是一种为云原生应用程序实现持续部署的方法。它通过使用开发人员已经熟悉的工具(包括 Git 和持续部署工具),专注于在运行基础设施时打造以开发人员为中心的体验。

GitOps 的核心思想是拥有一个 Git 仓库,该仓库始终包含生产环境中当前所需基础设施的声明性描述,以及使生产环境与仓库中描述的状态相匹配的自动化流程。如果要部署新应用程序或更新现有应用程序,只需更新该仓库即可。自动化流程可处理其他所有任务。就像设置了巡航控制系统一样,可帮助您管理生产中的应用程序。

与传统的持续部署系统相比,GitOps 具有以下特性。

传统持续部署系统 GitOps
由推送事件触发,如代码提交、定时任务、手动操作等 系统不断轮询更改
仅部署更改部分 对任何部署都声明整个系统
系统可能会在部署之间存在偏差 系统会纠正任何偏差
需要访问部署环境 部署管道被授权在系统内运行

为什么使用 GitOps?

Git 是系统所需状态的唯一事实来源。支持可重复的自动化部署、集群管理和监控。开发人员通过复用企业中已经非常成熟的 Git 工作流来完成构建、测试、扫描等持续集成步骤。当系统最终状态的声明代码进入 Git 仓库主线分支后,就会使用 GitOps 工具链验证部署、观测告警和修复操作。基于 GitOps 的核心基本原则,我们认为 GitOps 是持续部署 Kubernetes 集群的比较理想的方式。流程如下所示:

基于 Amazon EKS 的 GitOps 最佳实践

本次最佳实践的整个 CI/CD 管道如下图所示。

Amazon CodeCommit 仓库包含三个代码库。第一个是 flux-repo,这是 Flux CD 的配置仓库,用于定义与 Flux 相关的资源。第二个是 microservices-repo,用于保存微服务应用程序配置和部署文件。第三个是用于业务服务的源代码库 app-repo。在这篇文章中,我们将以一个前端项目为例。我们在 CI/CD 管道中使用 Amazon CodePipeline 实现持续集成,构建 Docker 镜像并将其存储到 Amazon ECR,并在 Amazon EKS 环境中以 Pod 方式部署 CD 引擎 Flux。

基本工作流如下:

  1. 编码工程师编写代码并将最终代码推送到 app-repo。
  2. app-repo 中的代码更改触发 Amazon CodePipeline。
  3. Amazon CodePipeline 编辑和打包代码,生成容器镜像,并将其推送到容器镜像仓库 Amazon ECR。
  4. 在 EKS 环境中运行的 CD 引擎 Flux 定期扫描 ECR 容器镜像仓库,并拉取应用程序的容器镜像元数据。
  5. 检测到新版本的容器镜像时,系统会自动将新的容器镜像地址通过 git commit/push 同步到存储在 microservices-repo 中的应用程序部署文件。
  6. Flux 定期从 Flux-repo 拉取应用程序配置和部署文件。由于 Flux-repo 仓库引用 microservices-repo,Flux 会检查集群的工作负载运行状态与 microservices-repo 文件中所描述的期望值是否一致。如果有任何差异,Flux 将自动启用 EKS 集群同步差异,以确保工作负载在预期状态下运行。

最佳实践表

我们已经介绍了 GitOps 的概念和 CI/CD 管道的架构,下面将使用一个案例通过以下四个模块来完成此实践:

  1. 使用基础设施即代码 (IaC) 部署云基础设施
  2. 在 Amazon EKS 集群上部署 Flux CD
  3. 使用 Flux CD 部署 GitOps 工作流
  4. 使用 GitOps 工作流实现基于镜像的自动部署

1. 使用 IaC 部署云基础设施

DevOps 的一个基本原则是以开发人员对待代码的方式来对待基础设施。IaC 使用代码部署云基础设施和治理云环境。编码工程师能够使用配置文件或代码定义基础设施,并通过编码创建基础设施,以确保一致性和可重复性。借助 IaC,编码工程师还可以管理资源的生命周期,例如在版本控制仓库中托管基础设施定义,以及使用与应用程序编码兼容的持续集成/持续部署 (CI/CD) 更改 IaC 的定义,使环境(如开发、测试、生产)与 IaC 代码更改同步。此外,在发生故障时可以自动回滚,并具有偏差检测功能,有助于识别与预期状态的差异。

在云中,编码工程师可以通过 Amazon 云开发工具包 (CDK),使用 Python、Java 和 TypeScript 构建基础设施模型。CDK 提供了称为构造的高级组件,这些组件使用经过验证的默认值预配置云资源。通过 CDK,工程师还能够根据组织的要求编写和共享自定义构造,从而加快项目的进度。

1.1 使用 CDK CLI 创建项目

使用 cdk init 创建一个 TypeScript CDK 项目,过程中将创建文件夹结构并安装 TypeScript CDK 项目所需的模块。

cdk init --language typescript

1.2 使用 EKS Blueprints 创建 EKS 集群

EKS Blueprints 可以帮助您构建完整的 EKS 集群,这些集群完全由部署和运行工作负载所需的操作软件引导。通过 EKS Blueprints,您可以将 EKS 环境所需状态的配置(如控制面、工作节点和 Kubernetes 附加组件)描述为 IaC 蓝图。配置蓝图后,您可以使用它通过持续部署自动化在多个亚马逊云科技账户和区域之间创建一致的环境。

同时,您可以使用 EKS Blueprints 通过 Amazon EKS 附加组件以及各种流行的开源附加组件轻松引导 EKS 集群,这些开源附加组件包括 PrometheusKarpenterNginxTraefik、Amazon Load Balancer Controller、Fluent BitKedaArgoCD 等。EKS Blueprints 还可以帮助您实施在同一集群中运行多个团队的工作负载所需的相关安全控制。

创建 quickstart 目录,然后运行以下代码,安装项目依赖项。

mkdir quickstart
cd quickstart
npm install @aws-quickstart/eks-blueprints

打开 lib/quickstart-stack.ts,然后添加以下 EKS Blueprints 代码。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as blueprints from '@aws-quickstart/eks-blueprints';
import { KubernetesVersion } from 'aws-cdk-lib/aws-eks';

export class QuickstartStack extends cdk.Stack {
 constructor(scope: Construct, id: string, props?: cdk.StackProps) {
 super(scope, id, props);
 const account = props?.env?.account!;
 const region = props?.env?.region!;

 const clusterProvider = new blueprints.GenericClusterProvider({
 version: KubernetesVersion.V1_23,
 managedNodeGroups: [
 {
 id: "default-ng",
 desiredSize: 2,
 minSize: 1,
 maxSize: 2,
 }
 ]
 });

 const cluster = blueprints.EksBlueprint.builder()
 .clusterProvider(clusterProvider)
 .account(account)
 .region(region)
 .addOns(
 new blueprints.AwsLoadBalancerControllerAddOn,
 )
 .teams();
 }
}

在上一步中,我们创建了一个 EKS 集群,定义了其节点组,并添加了 AwsLoadBalancerController 插件。

 
注意
我们建议通过 clusterProvider 自定义集群参数,并通过 EKS Blueprints 内置的 addOns 添加插件。
 

虽然使用 CDK 命令行工具部署堆栈很方便,但我们建议设置一个自动化管道,负责部署和更新 EKS 基础设施。这样,您可以更轻松地跨区域部署开发、测试和生产环境。

CodePipelineStack 是一种用于持续交付 Amazon CDK 应用程序的结构。当 Amazon CDK 应用程序的源代码上传到 Git 时,堆栈会自动构建、测试和部署新版本。如果添加了任何应用程序阶段或堆栈,系统会自动进行重新配置以部署这些新阶段或堆栈。

 
注意
我们建议使用 CDK 代码定义基础设施,并使用管道管理多个集群之间的更改,这也是 GitOps 的最佳实践。
 

然后,执行 cdk deploy 命令,部署堆栈。

最后,我们使用命令验证应用程序负载均衡器是否已成功安装。

$ kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
aws-load-balancer-controller-6bd49cfb7-2cvlk 1/1 Running 0 5m31s
aws-load-balancer-controller-6bd49cfb7-7lcwd 1/1 Running 0 5m31s
aws-node-9hsvz 1/1 Running 0 99m
coredns-66cb55d4f4-gdcqg 1/1 Running 0 106m
coredns-66cb55d4f4-gmzjt 1/1 Running 0 106m
kube-proxy-wr5jn 1/1 Running 0 99m

1.3 小结

在这一部分,我们介绍了 IaC 的概念,使用 CDK 创建了一个自定义 EKS 集群,同时安装了 Amazon Application Load Balancer 插件。为后续访问微服务的网页做好了前期准备。这一部分的小结如下:

  • 使用 cdk init 初始化了 CDK 项目。
  • 使用 EKS Blueprints 快速定义了 EKS 集群,同时添加了 Amazon Application Load Balancer 插件。

2. 在 Amazon EKS 集群上部署 Flux CD

Flux CD 是一个持续交付工具,最初由 Weaveworks 开发,然后加入 CNCF 开源项目。现因其易于设置且能够感知 Kubernetes 变化而得到广泛应用。还有一个重要的特性是,它允许团队以声明方式管理 Kubernetes 部署。Flux CD 通过定期轮询仓库,将存储在源代码库中的 Kubernetes 清单文件与 Kubernetes 集群同步。Flux CD 可以确保 Kubernetes 集群始终与源代码库中定义的配置保持同步,因此您无需担心 kube ctl 的运行状态,也无需使用其他工具和服务监控工作负载。下面,我们开始安装 Flux。

2.1 安装 Flux CLI

Flux CLI 是一个适用于所有平台的二进制可执行文件,可以从 GitHub 发布页面下载。

curl -s https://fluxcd.io/install.sh | sudo bash

2.2 准备 Amazon CodeCommit 凭证

我们将创建一个用户,并使用 CodeCommit 作为 Git 源,以及访问 Amazon CodeCommit 所需的 HTTPS Git 凭证。

2.3 在集群上安装 Flux

克隆准备好的 GitOps 代码。项目结构如下:

.
├── apps // Define Application
│ ├── base // Application Infrastructure Layer
│ └── overlays // Application Overlays Layer
├── clusters // Cluster configuration portal
│ └── dev-cluster
├── infrastructure // Infrastructure Shared Components
│ ├── base // Infrastructure Infrastructure layer
│ └── overlays // Infrastructure Overlays layer
└── README.md
 
注意
我们建议将 Flux 相关资源分为基础设施层、集群管理层和应用程序层。我们利用 Kustomization(base 和 overlays)支持多集群部署。
 

在 Git 仓库下使用 flux bootstrap,在 Kubernetes 集群上安装 Flux 并将其配置为自我管理。如果集群上存在 Flux 组件,则引导命令将根据需要执行升级。引导程序是幂等的,因此该命令可以安全地运行任意次数。将以下命令中的 username 和 password 替换为 Amazon CodeCommit 的 HTTPS Git 凭证。

flux bootstrap git \ 
 --url=https://git-codecommit.us-west-2.amazonaws.com/v1/repos/gitops \ 
 --username=__replace_with_your_Git_credential_username__ \ 
 --password=__replace_with_your_Git_credential_password__ \ 
 --token-auth=true \ 
 --path="./clusters/dev-cluster" \ 
 --components-extra=image-reflector-controller,image-automation-controller
 
警告
启用镜像自动更新功能,并在引导 Flux 时添加 --components-extra=image-reflector-controller,image-automation-controller 参数。否则,Flux 默认不会安装 image-reflector-controller 和 image-automation-controller,且自动镜像更新等配置也不会生效。
 

使用 git pull 检查引导程序推送的更新。Git 仓库的 clusters/dev-cluster/flux-system 目录下将显示三个新文件:

  • gotk-components.yaml:定义 Flux 的六个控制器,分别是 helm、Kustomize、source、notification、image-automation 和 image-reflector。
  • gotk-sync.yaml:Flux 的 Git 源,集群中的 Source Controller 监控 GitOps 仓库中的代码更改并执行相应的更改。
  • kustomization.yaml:多集群配置。

使用 flux get kustomizations --watch 检查 Flux 是否安装成功。输出将如下所示:

$ flux get kustomizations --watch
NAME REVISION SUSPENDED READY MESSAGE 
flux-system master/83b7e66 False True Applied revision: master/83b7e66
infrastructure master/83b7e66 False True Applied revision: master/83b7e66

使用 kubectl -n flux-system get pod,services 检查 flux-system 部署的组件。输出将如下所示:

$ kubectl -n flux-system get pod,services
NAME READY STATUS RESTARTS AGE
pod/helm-controller-88f6889c6-sblvd 1/1 Running 0 11m
pod/image-automation-controller-6c5f7bbbd9-8xskk 1/1 Running 0 11m
pod/image-reflector-controller-78949bb4b4-bqn4m 1/1 Running 0 11m
pod/kustomize-controller-784bd54978-s82dq 1/1 Running 0 11m
pod/notification-controller-648bbb9db7-627zs 1/1 Running 0 11m
pod/source-controller-79f7866bc7-gdt95 1/1 Running 0 11m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/notification-controller ClusterIP 172.20.60.72 <none> 80/TCP 11m
service/source-controller ClusterIP 172.20.133.44 <none> 80/TCP 11m
service/webhook-receiver ClusterIP 172.20.196.178 <none> 80/TCP 11m

2.4 小结

在这一部分,我们使用 flux bootstrap 命令在 Kubernetes 集群上安装了 Flux,并介绍了三个非常重要的配置文件:gotk-components.yaml、gotk-sync.yaml 和 kustomization.yaml。这一部分的小结如下:

  • 安装了 Flux 客户端
  • 创建了 IAM 用户和 CodeCommit 凭证
  • 在 Amazon EKS 集群上安装了 Flux 并启用了镜像自动更新功能

3. 使用 Flux CD 部署 GitOps 工作流

对于 GitOps CI/CD 管道,Git 中的代码更改会引发 EKS 集群及其上所运行工作负载的配置修改和状态更改(Git 中的代码更改由 git push 或 pull 请求触发,GitOps 推荐使用 pull 请求)。而传统的 CI/CD 管道通过 CI 引擎触发 kubectl create/apply 或 helm install/update 来部署集群。因此,GitOps CI/CD 管道更高效、更简洁。

 
注意
Flux 定期从仓库中拉取配置和部署文件,并将集群的当前应用程序负载状态与文件中描述的预期状态进行比较。当检测到差异时,Flux 会自动将差异同步到 EKS 集群,从而确保工作负载始终按预期运行。
 

我们将展示一个特定的应用程序“sock shop”以及一些实践练习,说明如何在 GitOps CI/CD 管道上实现持续集成和交付。

3.1 关于 Sock Shop

我们将以 sock shop 网店面向用户的部分作为示例。本例使用 Spring Boot、Go Kit 和 Node 进行构建,并打包到 Docker 容器中。作为“微服务标准演示”,它将展示:

  • 微服务部署最佳实践(包括错误示例)
  • 跨平台部署的功能
  • 持续集成/部署的优势
  • DevOps 和微服务的互补性
  • 适用于各种编排平台的“真实的”可测试应用程序

sock shop 项目由 8 个前端和后端微服务组成,其中前端是 NodeJS 创建的网页。在本文中,使用项目名称 front-end。前端通过 HTTP 请求访问几个后端服务,其中包括订单、支付、用户管理、产品管理和购物车等。后端服务的数据存储在 MongoDB 和 MySQL 中。

参考架构如下:

3.2 关于 Kustomize

除了设置 GitOps 工作流,我们还需要了解如何配置 Kubernetes。随着系统复杂性和环境复杂性的增加,基于资源清单 (YAML) 的传统管理方式变得越来越难以维护。复杂的业务使用场景、多个环境(开发、测试、预发布、生产)以及大量的 YAML 资源清单需要维护和管理。尽管 Helm 可以解决部分痛点,如统一管理分散的资源文件、应用程序分发、升级、回滚等,但 Helm 在处理环境之间的微小差异时会变得更加困难。另外,还需要掌握复杂的 DSL 语法(模板语法),入门门槛很高。因此,声明性配置管理工具 Kustomize 应运而生。Kustomize 可以帮助团队管理不同环境或不同团队要使用的大量 Kubernetes YAML 资源。还可以帮助团队以轻量级的方式管理环境之间的细微差异,使得资源配置可以复用,不仅减少了复制和更改工作量,同时也极大降低了配置错误。整个应用程序配置过程不需要额外学习模板语法。

Kustomize 通过以下方式解决了上述问题:

  • Kustomize 通过 Base 和 Overlays 维护不同环境的应用程序配置。
  • Kustomize 利用 Patch 复用 Base 配置和实现,并通过 Overlays 描述和 Base 应用程序配置之间的差异部分实现资源复用。
  • Kustomize 管理的都是 Kubernetes 原生 YAML 文件,无需学习 DSL 语法。

根据官网的描述,Kustomize 已经成为 Kubernetes 的原生配置管理工具,用户无需模板即可自定义应用程序配置。Kustomize 使用 K8s 原生概念帮助创建并复用资源配置 (YAML),允许用户以一个应用程序描述文件 (YAML) 为基础 (Base YAML),然后通过 Overlays 为最终部署的应用程序生成所需的描述文件。

3.3 多集群配置

了解配置管理工具 Kustomize 后,我们使用 Kustomization(Base 和 Overlays)来实现多集群部署转换。

我们在微服务项目中创建了两个目录:base 目录用于存储完整的资源配置 (YAML) 文件,overlays 目录用于存储不同环境或集群的差异配置。

例如,本例中微服务的完整配置文件是 complete-demo.yaml,我们将其复制到 base 目录下。

p deploy/kubernetes/complete-demo.yaml deploy/kubernetes/base/complete-demo.yaml

然后,通过 kustomization.yaml 引用该文件:

# deploy/kubernetes/base/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
 - ./complete-demo.yaml

对于开发环境,如果有差异设置需求,例如更改服务端口和副本的数量,只需在 overlays/development/kustomization.yaml 文件中配置差异设置即可,而无需复制和修改现有的 complete-demo.yaml 文件。

 
注意
在服务部署期间,Flux 会根据环境自动合并 base 和 overlays 配置。我们建议定义多个环境(如开发、测试和生产)的不同配置。在本例中,我们不赞成使用多仓库和多分支策略,而是更倾向于在 Git 仓库中使用不同路径来管理多个环境中的集群。这种方式有助于简化代码维护和合并的过程,同时也是 Flux 最佳实践。
 

3.4 使用 GitOps 工作流部署微服务

完成对微服务的多集群支持后,我们需要让 Flux 感知微服务的配置已更改,因此我们将在 Flux 仓库 (flux-repo) 中注册微服务仓库 (microservices-repo) 的 CodeCommit 地址。

3.4.1 添加微服务仓库地址

返回 Flux 仓库中的应用程序层/apps 目录下:

.
├── base
│ ├── kustomization.yaml
│ └── sock-shop
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ ├── rbac.yaml
│ └── tenant.yaml
└── overlays
 └── development
 ├── kustomization.yaml
 └── sock-shop
 └── kustomization.yaml

打开 apps/base/sock-shop/ 下的 tenant.yaml 文件,并将 MICRO_SERVICES_REPO 替换为微服务的地址:https://git-codecommit.xxx.amazonaws.com/v1/repos/microservices-repo。

apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
 name: sock-shop-tenant
 namespace: sock-shop
spec:
 interval: 1m
 url: __MICRO_SERVICES_REPO__
 ref:
 branch: main
 secretRef:
 name: microservices-basic-access-auth
......

3.4.2 添加 CodeCommit 凭证

找到“准备 Amazon CodeCommit 凭证”中设置的账户和密码。在执行命令之前,将数据值转换为 Base64 编码。

然后,打开文件 base/sock-shop/basic-access-auth.yaml,并将 BASE64_USERNAME 和 BASE64_PASSWORD 替换为生成的 Base64 编码:

---
apiVersion: v1
kind: Secret
metadata:
 name: microservices-basic-access-auth
 namespace: sock-shop
type: Opaque
data:
 username: __BASE64_USERNAME__
 password: __BASE64_PASSWORD__

3.4.3 部署

在 Flux 配置仓库中添加微服务的 Git 地址后,Flux 将自动扫描微服务的配置更改。提交代码后,Flux 将检查集群中是否部署了微服务以及是否与 Git 仓库的定义相匹配,如果未部署或者不匹配,Flux 将自动在集群中部署微服务。

提交代码后,执行命令 flux get kustomizations -watch,然后等待 Flux 更新。当所有 kustomization 的 READY 状态都为 True 时,部署即完成。

查询 sock-shop 命名空间中的 Pod 和服务,如下所示:

$ kubectl get pod,service -n sock-shop
NAME READY STATUS RESTARTS AGE
pod/carts-b4d4ffb5c-z4jrj 1/1 Running 0 5m28s
pod/carts-db-6c6c68b747-jl5pd 1/1 Running 0 5m28s
pod/catalogue-759cc6b86-qdmvc 1/1 Running 0 5m28s
pod/catalogue-db-96f6f6b4c-zgp5z 1/1 Running 0 5m28s
pod/front-end-5c89db9f57-cvbdl 1/1 Running 0 5m28s
pod/orders-7664c64d75-lqwbm 1/1 Running 0 5m28s
pod/orders-db-659949975f-qv7pl 1/1 Running 0 5m28s
pod/payment-7bcdbf45c9-szrfq 1/1 Running 0 5m28s
pod/queue-master-5f6d6d4796-nkktx 1/1 Running 0 5m28s
pod/rabbitmq-5bcbb547d7-gzhn4 2/2 Running 0 5m28s
pod/session-db-7cf97f8d4f-9mz6v 1/1 Running 0 5m28s
pod/shipping-7f7999ffb7-95rlc 1/1 Running 0 5m27s
pod/user-68df64db9c-kh247 1/1 Running 0 5m27s
pod/user-db-6df7444fc-jlkp9 1/1 Running 0 5m27s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/carts ClusterIP 172.20.33.124 <none> 80/TCP 5m29s
service/carts-db ClusterIP 172.20.75.163 <none> 27017/TCP 5m29s
service/catalogue ClusterIP 172.20.92.254 <none> 80/TCP 5m29s
service/catalogue-db ClusterIP 172.20.242.255 <none> 3306/TCP 5m29s
service/front-end LoadBalancer 172.20.55.188 k8s-sockshop-frontend-12345678910-012345678910abc.elb.us-east-1.amazonaws.com 80:30001/TCP 5m29s
service/orders ClusterIP 172.20.8.252 <none> 80/TCP 5m29s
service/orders-db ClusterIP 172.20.40.212 <none> 27017/TCP 5m29s
service/payment ClusterIP 172.20.6.218 <none> 80/TCP 5m29s
service/queue-master ClusterIP 172.20.153.80 <none> 80/TCP 5m29s
service/rabbitmq ClusterIP 172.20.99.37 <none> 5672/TCP,9090/TCP 5m29s
service/session-db ClusterIP 172.20.37.111 <none> 6379/TCP 5m29s
service/shipping ClusterIP 172.20.43.252 <none> 80/TCP 5m29s
service/user ClusterIP 172.20.220.174 <none> 80/TCP 5m29s
service/user-db ClusterIP 172.20.70.57 <none> 27017/TCP 5m29s

访问 Amazon Load Balancer 的 DNS 名称。

3.5 小结

在这一部分,我们介绍了一个微服务业务应用程序:Sock Shop 网店,并完成了其多集群配置。我们还基于 Flux 搭建了标准的 GitOps 工作流,当配置文件发生更改时,系统会自动将更改同步到目标集群,从而在 EKS 集群中完成微服务部署。同时,我们介绍了一个实用的 K8s 配置管理工具 Kustomize,以及如何管理应用程序的资源文件。这一部分的小结如下:

  • 介绍了 Sock Shop
  • 学习了配置管理工具 Kustomize(base 和 overlays)以及如何修改微服务多集群部署
  • 构建了 GitOps 工作流并部署了微服务

4. 使用 GitOps 工作流实现基于镜像的自动部署

我们将以 Sock Shop 的前端微服务为例,演示使用 GitOps 工作流完成代码更改、镜像构建和自定义发布的详细流程。

4.1 定义 CodePipeline CI

Front-end 是 Node.js 的一个纯前端服务,支持 Docker 镜像打包(详情请参阅 3.1 章节中的 sock-shop 架构)。将 buildspec.yml 文件添加到 front-end 项目源代码中,以定义在 CodePipeline 中执行的 CI 流程:

version: 0.2

phases:
 pre_build:
 commands:
 - echo Logging in to Amazon ECR...
 - aws --version
 - AWS_ACCOUNT_ID=`echo $REPOSITORY_URI|cut -d"." -f1`
 - AWS_DEFAULT_REGION=`echo $REPOSITORY_URI|cut -d"." -f4`
 - echo $AWS_ACCOUNT_ID $AWS_DEFAULT_REGION
 - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $REPOSITORY_HOST
 - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
 - IMAGE_TAG=main-$COMMIT_HASH-$CODEBUILD_BUILD_NUMBER
 - echo $IMAGE_TAG
 build:
 commands:
 - echo Build started on `date`
 - echo Building the Docker image...
 - docker build -t $REPOSITORY_URI:latest .
 - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
 post_build:
 commands:
 - echo Build completed on `date`
 - echo Pushing the Docker images...
 - docker push $REPOSITORY_URI:latest
 - docker push $REPOSITORY_URI:$IMAGE_TAG

如果任何前端代码发生更改,此 CI 流程将自动构建镜像并将其上传到 ECR 仓库 weaveworksdemos/front-end。镜像标签的格式为 [branch]-[commit]-[build number]

4.2 镜像自动更新

在开发测试等可持续集成的敏捷环境中,更新 GitOps 仓库或使用脚本管理新的服务镜像可能会非常麻烦。幸好 Flux 提供了强大的自动镜像升级功能,可帮助您解决这一烦恼。若要使用该功能,只需在配置中启用镜像更新组件即可。如果还未启用,不必担心,只需在重复 flux bootstrap 命令时添加参数 --components-extra=image-reflector-controller,image-automation-controller 即可启用。

要实现基于镜像的自动更新,需要执行以下步骤:

  • 注册前端微服务的镜像仓库,以便让 Flux 定期扫描 front-end 项目对应的 ECR 镜像仓库。
  • 配置镜像仓库访问凭证。Flux 需要访问 ECR 镜像仓库的凭证,才能读取镜像信息。
  • 设置镜像更新策略。在大多数情况下,我们不希望每次更改任一镜像版本都会触发 CD,只希望指定的分支(主)的代码更改才会触发 CD。若要满足此需求,则需要一个特殊的更新策略。

接下来,我们将逐一完成上述操作。

4.2.1 将镜像策略添加到 Git 仓库前端

在 microservices-repo 项目中,我们将在开发环境中使用 Kustomization overlays,将前端微服务替换为自定义的更新版本。修改文件 deploy/kubernetes/overlays/development/kustomization.yaml。(注意:将 ACCOUNT_ID 替换为自己的 ACCOUNT_ID)。

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
 - ../../base
images:
 - name: weaveworksdemos/front-end
 newName: __ACCOUNT_ID__.dkr.ecr.us-west-2.amazonaws.com/weaveworksdemos/front-end # {"$imagepolicy": "sock-shop:sock-shop-front-end:name"}
 newTag: latest # {"$imagepolicy": "sock-shop:sock-shop-front-end:tag"}
 
警告:注释 $imagepolicy 为必需项,用于定位。如果 Flux 发现镜像版本发生更改,将根据此注释定位并修改文件内容。
 

4.2.2 在 Flux-repo 下注册微服务前端

在 flux-repo 项目中,创建新文件 apps/overlays/development/sock-shop/registry.yaml,并将 ACCOUNT_ID 替换为自己的 ACCOUNT_ID。

---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageRepository
metadata:
 name: sock-shop-front-end
 namespace: sock-shop
spec:
 image: __ACCOUNT_ID__.dkr.ecr.xxxx.amazonaws.com/weaveworksdemos/front-end
 interval: 1m0s

4.2.3 配置 Amazon ECR 访问凭证

在 Flux 中,可以使用两种方法配置 Amazon ECR 访问凭证。

  • 自动身份验证机制(image-reflector-controller 自行检索凭证,仅适用于:Amazon ECR、GCP GCR、Azure ACR)
  • 使用 CronJob 定期刷新凭证(通过密钥存储在集群中)
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
 - gotk-components.yaml
 - gotk-sync.yaml
patches:
 - patch: |-
 - op: add
 path: /spec/template/spec/containers/0/args/-
 value: --aws-autologin-for-ecr
 target:
 version: v1
 group: apps
 kind: Deployment
 name: image-reflector-controller
 namespace: flux-system
 
注意
我们使用 Amazon ECR,因此选择自动身份验证机制,修改 clusters/dev-cluster/flux-system/kustomization.yaml 并通过 patch 机制添加 --aws-autologin-for-ecr 参数。相比于使用 CronJob 定期生成凭证,这种方法更简单、更有效。
 

4.2.4 设置镜像更新策略

添加文件 gitops/apps/overlays/development/sock-shop/policy.yaml。以下规则将匹配 master-d480788-1、master-d480788-2 和 master-d480788-3 等镜像版本。

---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
 name: sock-shop-front-end
spec:
 imageRepositoryRef:
 name: sock-shop-front-end
 filterTags:
 pattern: '^main-[a-fA-F0-9]+-(?P<buidnumber>.*)'
 extract: '$buidnumber'
 policy:
 numerical:
 order: asc

添加文件 gitops/apps/overlays/development/sock-shop/image-automation.yaml。Flux 的自动镜像配置将为应用程序配置指定一个 Git 仓库,包括分支、路径等信息。

---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
 name: sock-shop-front-end
spec:
 git:
 checkout:
 ref:
 branch: main
 commit:
 author:
 email: fluxcdbot@users.noreply.github.com
 name: fluxcdbot
 messageTemplate: '{{range .Updated.Images}}{{println .}}{{end}}'
 push:
 branch: main
 interval: 5m0s
 sourceRef:
 kind: GitRepository
 name: sock-shop-tenant
 namespace: sock-shop
 update:
 path: ./deploy/kubernetes/overlays/development
 strategy: Setters

4.3 发布和验证

我们将通过修改前端源代码,验证镜像自动更新的整个流程。

4.3.1 更新前端代码

更改前端页面的页脚,然后修改文件:front-end/public/footer.html。

4.3.2 检查 CodePipeline

前端的代码更改会自动触发 CodePipeline 运行。

4.3.3 确认 ECR 镜像版本更改

待 CodePipeline 完成后,登录 Amazon ECR 控制台,检查 weaveworksdemos/front-end 镜像版本:

4.3.4 验证 Flux 镜像信息

通过 Flux CLI,检查 ImageRepository 和 ImagePolicy 是否成功检索到最新版本。

$ flux get images all --all-namespaces
NAMESPACE NAME LAST SCAN SUSPENDED READY MESSAGE 
sock-shop imagerepository/sock-shop-front-end 2022-09-18T14:46:45Z False True successful scan, found 20 tags

NAMESPACE NAME LATEST IMAGE READYMESSAGE 
sock-shop imagepolicy/sock-shop-front-end 267314483271.dkr.ecr.us-west-2.amazonaws.com/weaveworksdemos/front-end:master-1f49071-24 True Latest image tag for '267314483271.dkr.ecr.us-west-2.amazonaws.com/weaveworksdemos/front-end' resolved to: master-1f49071-24

NAMESPACE NAME LAST RUN SUSPENDED READY MESSAGE 

4.3.5 微服务源代码自动更新

Flux 自动更新前端镜像版本。可以看到最新提交人是 fluxcdbot,并且镜像标签已成功修改为最新版本:master-1f49071-24。

4.3.6 验证 Pod 镜像版本

使用 kubectl get pod -n sock-shop | grep front-end 验证 Pod 名称。使用 kubectl describe pod/front-end-759476784b-9r2rt -n sock-shop | grep Image 检查 Pod 详细信息:确认镜像版本已更新。输出结果如下所示:

$ kubectl describe pod/front-end-759476784b-9r2rt -n sock-shop | grep Image:
 Image: 267314483271.dkr.ecr.us-west-2.amazonaws.com/weaveworksdemos/front-end:master-1f49071-24

4.3.7 确认静态页面是最新版本

4.4 小结

在这一部分,我们详细介绍了基于镜像的自动部署全流程。总而言之,我们利用了 Flux 对镜像仓库的持续监控能力。检测到镜像版本发生更改时,系统会自动修改 Git 仓库中的镜像配置,然后连接到上一部分中的标准 GitOps 工作流完成自动部署。这一部分的小结如下:

  • 通过 CodePipeline 实现了 CI 流程,完成了前端代码的持续集成。
  • 通过注释 Flux 定位并修改了业务配置文件。
  • 配置了 Flux 的镜像更新策略,使 Flux 能够监控镜像的特定版本并完成自动部署。

总结

本文重点讨论如何在云上的 Amazon EKS 集群中使用 FluxCD 自动发布微服务,并介绍了 GitOps 管道最佳实践。GitOps 是一种持续交付方法,包含一系列最佳实践。构建 CI/CD 工具没有严格的限制,只要符合 GitOps 的基本原则即可。希望您能够从本文中获得了一些启发去构建自己的 GitOps 技术堆栈。面对更加多元且复杂的生产场景,我们需要不断优化这些实践,例如:

  • 对于关键的在线生产系统,如何实现安全、增量的灰度发布?
  • Sealed Secrets 引入额外的私钥管理需求时,如何改进云上的 GitOps 密钥管理?
  • 如何协同管理 IaC 和 EKS GitOps 的编码?
  • 如何更有效地开发 Kubernetes 清单 (YAML)?

我们将在后面的博客文章中逐一探讨这些问题。

参考资料