GitOps 介绍

基础设施即代码 IaC

在理解 GitOps 之前,需要先理解什么是基础设施即代码。

基础设施即代码(Infrastructure as Code,简称IaC)是一种软件工程实践,它将基础设施的管理和配置过程像管理代码一样进行版本控制、自动化和可追溯。这意味着基础设施的创建、配置和管理不再依赖手动操作,而是通过编码的方式进行定义和管理,以实现更高效、可靠、可复用和可扩展的基础设施管理。

在传统的基础设施管理中,管理员通常需要手动完成服务器、网络、存储等基础设施的配置和管理工作。这样做可能会导致配置的不一致性、人为错误和时间消耗较多。

通过 IaC,管理员可以使用编程语言(如 Ansible)来定义基础设施的状态和配置,然后使用工具或平台将这些定义转换为实际的基础设施。这些定义文件可以保存在版本控制系统如 Git 中,确保配置的可追溯性和可重现性。当需要进行更新或扩展时,只需修改代码并重新执行即可,从而实现基础设施的快速部署和变更管理。

IaC 的优势包括:

  1. 可重复性和一致性:基础设施代码能够确保在不同环境中的一致性,消除了手动配置带来的不稳定和错误。
  2. 自动化:自动化配置和管理基础设施,减少了人工操作和减轻了管理员的负担。
  3. 版本控制:基础设施代码可以像软件代码一样进行版本管理,方便回滚、跟踪和团队协作。
  4. 快速部署和扩展:由于基础设施的定义已经以代码的形式存在,因此可以快速部署新的基础设施实例或扩展现有基础设施。
  5. 文档化:基础设施代码本身就是对基础设施架构和配置的文档化,降低了维护和管理的复杂性。

广义上的 IaC 不仅仅只关于基础设施,还包含了网络安全配置等等,所以广义上的 IaC 又叫 X as Code。比如你想在某公有云中创建一台服务器,配置网络,部署 Kubernetes 集群以及各种工作负载,你只需要定义好 Ansible 的声明式配置,以及 Kubernetes 的配置清单即可,免去繁杂的手动操作。

什么是 GitOps

GitOps = IaC + Git + CI/CD

翻译过来就是, Git 版本控制管理 IaC 的 CI/CD,核心是使用 Git 仓库来管理基础设施和应用的配置以 Git 仓库作为基础设施和应用的单一事实来源,从其他地方修改配置(比如手动改线上配置)一概不予通过。

借助 IaC,目标环境当前所需基础设施的期望状态以声明式配置的方式作为代码保存在 Git 仓库中,借助于 GitOps,如果集群的实际状态与 Git 仓库中定义的期望状态不匹配,会根据期望状态来调整当前的状态,最终使实际状态符合期望状态。

GitOps vs DevOps

从广义上来看,GitOps 与 DevOps 并不冲突,GitOps 是一种技术手段,而 DevOps 是一种文化。GitOps 是一种实现持续交付(Continuous Delivery)、持续部署(Continuous Deployment)和基础设施即代码(IaC)的工具和框架,它是符合和满足 DevOps 文化的。

从狭义上来看,GitOps 与 DevOps 有以下几个区别:

  1. GitOps 是以目标为导向的。它使用 Git 来维护期望状态,并不断调整实际状态,最终与期望状态相匹配。而 DevOps 更多关注的是最佳实践,这些实践可以是利用其他任何工具实现。
  2. GitOps 采取声明式的操作方法,而 DevOps 同时接受声明式和命令式的方法,所以 DevOps 除了适用于容器环境之外,还适用于虚拟机和裸机环境。
  3. 针对 CD 流水线,GitOps 采用 Pull 模式,GitOps 系统的各个组件(如 Kuberntes 集群)从 Git 仓库拉取(拉取动作由代理 agent 完成)状态和配置信息,使集群状态调整为和 Git 中描述的一致。而传统 Devops 中使用 Push 模式,配置更改和代码部署是由开发团队主动推送到目标服务器或平台上的。


GitOps

DevOps

优点

  1. 声明式配置:基于声明的方式来定义基础设施和应用程序的配置,以代码的形式存储在Git仓库中。这样可以确保配置的版本控制、可追溯性和一致性。
  2. 自动化持续部署:强调使用自动化流程来同步状态,每当存储库中的配置发生更改时,系统会自动将更改部署到集群中,实现全自动的持续部署。
  3. 可观察性:通过Git存储库中的历史记录和变更日志,可以轻松追踪每个部署的更改,提高了系统的可观察性和故障排除能力。
  4. 一致性和可重复性:确保基础设施和应用程序状态与Git存储库中定义的期望状态保持一致,避免了手动配置可能引入的不稳定性和错误。
  1. 快速交付:强调自动化、持续集成和持续交付,可以实现更快速的软件交付和部署。
  2. 灵活性:可以根据团队的需求和偏好,选择合适的工具和流程,具有较大的灵活性。
  3. 协作:强调开发和运维团队之间的协作,促进了团队之间的合作和沟通。


缺点

  1. 学习曲线:采用GitOps需要对Git、版本控制和基础设施即代码等概念有一定的了解,可能对一些团队成员造成学习曲线。
  2. 依赖Git存储库:Git存储库成为了整个系统的“单一事实的来源”,如果Git存储库不可用或发生故障,可能会影响整个部署流程。
  1. 人为干预:DevOps中的部分过程可能需要人为干预和手动操作,可能增加了出错的风险。
  2. 配置漂移:由于手动操作的存在,可能导致配置的漂移,使得实际状态和预期状态不一致。


综合

综合来看,GitOps强调自动化和基础设施即代码,适用于需要高度可追溯性和自动化的场景,而DevOps更注重协作和快速交付,适用于需要灵活性和快速反馈的场景。在实际应用中,可以根据团队的需求和项目的特点选择合适的方法论或结合两者的优势来实现高效的软件开发和部署

Push vs Pull

在上面 GitOps 和 Devops 的对比中,简单提到了 GitOps 采用 Pull 模式,而传统 Devops 采用 push 模式,下面展开介绍一下这两种模式的区别。

Push 模式

目前大多数 CI/CD 工具都使用基于 Push 的部署模式,例如最常见的 Jenkins。这种模式一般都会在 CI 流水线运行完成后执行一个命令(比如 kubectl apply)将应用部署到目标环境中。因此这种 CD 模式的相比 pull 模式有如下缺点:

  • 需要安装配置额外工具(比如 kubectl);
  • 需要 Kubernetes 对其进行授权;
  • 无法自动感知部署状态,也就无法感知期望状态与实际状态的偏差,需要再通过查询集群信息的方式来感知。

Pull 模式

Pull 模式会在目标环境中安装一个 Agent,例如在 Kubernetes 集群中就靠自定义的 Operator 来充当这个 Agent。Operator 会周期性地监控目标环境的实际状态,并与 Git 仓库中的期望状态进行比较,如果实际状态不符合期望状态,Operator 就会更新基础设施的实际状态以匹配期望状态。

目前基于 Pull 模式的 CD 工具有 Argo CDFlux CD 以及 ks-devops

GitOps 设计原则

声明式

必须通过声明式来描述系统的期望状态。例如 Kubernetes,众多现代云原生工具都是声明式的,Kubernetes 只是其中的一种。

版本控制/不可变

因为所有的状态声明都存储在 Git 仓库中,并且把 Git 仓库作为单一事实来源,那么所有的操作都是从 Git 仓库里驱动的,而且保留了完整的版本历史,方便回滚。有了 Git 优秀的安全保障,对代码的作者和出处实施强有力的安全保障。

自动应用变更

Git 仓库中声明的期望状态发生了任何变更,都可以立即应用到系统中,而且不需要安装配置额外工具(比如 kubectl),也不需要配置 Kubernetes 的认证授权。

持续的协调

协调 Reconciliation 其实最早是 Kubernetes 里的一个概念,表示的是确保系统的实际状态与期望状态一致的过程。具体的实现方式是在目标环境中安装一个 agent,一旦实际状态与期望状态不匹配,agent 就会进行自动修复。这里的修复比 Kubernetes 的故障自愈更高级,即使是手动修改了集群的编排清单,集群也会被恢复到 Git 仓库中的清单所描述的状态。

GitOps 的工作流

鉴于以上这些设计原则,GitOps 的工作流如下:

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施

  1. 首先,团队中的任何一个成员都可以 clone 仓库对配置进行更改,然后提交 Pull Request。
  2. 接下来会运行 CI 流水线,一般会做这么几件事情:验证配置文件、执行自动化测试、检测代码的复杂性、构建镜像、将镜像推送到镜像仓库等等。
  3. CI 流水线运行完成后,团队中拥有合并代码权限的人将会将这个 Pull Request 合并到主分支中 。一般拥有这个权限的都是研发人员、安全专家或者高级运维工程师。
  4. 最后会运行 CD 流水线,将变更应用到目标系统中(比如自建 Kubernetes 集群或者其他云服务商) 。

GitOps 是对现有 DevOps 文化的补充,相比传统模式,GitOps 的整个过程自动化且透明,部署过程清晰可见,可以查看和跟踪对系统进行的任何变更,提高了生产力、安全性和合规性。而传统的模式只能由工程师在自己的电脑上手动操作这一切,其他人不知道发生了什么,也无法对其操作进行 Review。而且在 GitOps 中,整个系统都是通过声明式来描述的,天然适合云原生环境,因为 Kubernetes 也是这么设计的

GitOps 实战

从零入门

容器镜像:云原生基石

在进行 GitOps 的实战之前,首先需要了解如何将业务代码构建为容器镜像,以及容器镜像是如何部署到 K8S 中的,此处不对 Docker 和 K8S 做更多的展开,有兴趣的可以参考关于 Docker 和 K8S 的赋能介绍。下面以一个简单的例子来介绍从业务代码到成功部署到 K8S 中的过程。

构建容器镜像

接下来,将下面这段最简单的 Python 业务代码制作成镜像。

from flask import Flask
import os
app = Flask(__name__)
app.run(debug=True)

@app.route('/')
def hello_world():
    return 'Hello, my first docker images! ' + os.getenv("HOSTNAME") + ''

创建 Python 的依赖文件 requirements.txt ,用它来安装所依赖的 Flask 框架。执行下面的命令来创建文件并将 Flask==2.2.2 内容写入该文件

echo "Flask==2.2.2" >> requirements.txt

需要一个文件来描述镜像是如何被构建的,这个文件叫做 Dockerfile,将以下内容保存为 Dockerfile 文件。

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

RUN apt-get update && apt-get install -y procps vim apache2-utils && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

在当前目录下查看所需的文件是否都创建完毕,并执行 docker build 命令,这样就可以开始制作镜像了。

# 查看文件
ls
# 构建镜像
docker build -t hello-world-flask .

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_02

……(省略)……

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_03

最后,为了以后在任意主机都能使用该镜像,最好是将该镜像上次至 harbor 仓库

docker tag hello-world-flask:latest 10.8.59.115/library/hello-world-flask:latest
docker push 10.8.59.115/library/hello-world-flask:latest
部署容器镜像

想要将容器镜像部署到 K8S 中,需要定义用来向 K8S 描述“期望最终状态”的文件,叫做 K8S Manifest,也可以称之为清单文件。Manifest 就好比餐厅的菜单,客户只管点菜,做菜的过程客户不需要感知。一般都使用 yaml 格式的文件来描述清单文件。

apiVersion: v1
kind: Pod
metadata:
  name: hello-world-flask
spec:
  containers:
    - name: flask
      image: 10.8.59.115/library/hello-world-flask:latest
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 5000

而有了 K8S Manifest 后,我们需要使用 Kubectl 命令行工具来将 K8S Manifest 提交给 K8S,控制其创建和删除。

# 创建
kubectl apply -f flask-pod.yaml
# 删除
kubectl delete -f flask-pod.yaml
业务的自愈和扩容

前面我们成功的将业务代码构建成了容器镜像,并部署到了 K8S 中,那么借助 K8S 来运行业务有什么好处呢?其中最重要的一点,是业务的自愈和扩容,其常见的架构如下图所示

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_04

  • Pod 会被 Deployment 工作负载管理起来,例如创建和销毁等;
  • Service 相当于弹性伸缩组的负载均衡器,以加权轮训的方式将流量转发到多个 Pod 副本上;
  • Ingress 相当于集群的外网访问入口。

对于任意的一个 Service,如果其后端 Pod 中任意发生故障,K8S 能感知到业务 Pod 故障,立刻进行了故障转移并隔离了有故障的 Pod,将请求转发到了其他健康的 Pod 中,随后重启有故障的 Pod,最后将重启后的 Pod 加入到了负载均衡并开始接收外部请求。这些过程都是自动化完成的。

除了自愈功能以外,K8s 还为我们提供了自动扩容的能力。

kubectl autoscale deployment hello-world-flask --cpu-percent=50 --min=2 --max=10

这其中,–cpu-percent 表示 CPU 使用率阈值,当 CPU 超过 50% 时将进行自动扩容,–min 代表最小的 Pod 副本数,–max 代表最大扩容的副本数。也就是说,自动扩容会根据 CPU 的使用率在 2 个副本和 10 个副本之间进行扩缩容。

借助GitOps实现应用秒级自动发布和回滚

传统发布方式

在传统 Devops 的发布方式中,可以通过使用 Jenkins 等工具,将下面三种操作放到 CD 流水线中实现应用的更新发布

  1. kubectl set image
  1. kubectl set image deployment/hello-world-flask hello-world-flask=<new-image-name>
  1. kubectl edit
  1.   直接修改 K8S 集群中保存的工作负载

  2. kubectl edit deployment hello-world-flask
  1. kubectl apply
  1.   先修改本地 yaml 文件,再使用下面命令重新部署

  2. kubectl apply -f new-hello-world-flask.yaml
GitOps 发布工作流

但是,随着项目的发展,我们需要发布流程更加自动化、安全、可追溯。这时候,应该考虑用 GitOps 的方式来发布应用

要实现 GitOps 工作流,首先需要一个能够帮助我们监听 Git 仓库变化,自动部署的工具。下面以 FluxCD 为例,一步步构建出一个 GitOps 工作流

  1. 安装 FluxCD
  1.   下面提供的是一个已经整理好的 FluxCD 部署 yaml 文件,直接应用即可

  2. kubectl apply -f https://ghproxy.com/https://raw.githubusercontent.com/lyzhang1999/resource/main/fluxcd/fluxcd.yaml
  3.   由于安装 FluxCD 的工作负载比较多,可以使用 kubectl wait 来等待安装完成

  4. kubectl wait --for=cnotallow=available --timeout=300s --all deployments -n flux-system
  1. 创建代码仓库
  1.   在本地创建 fluxcd-demo 目录:

  2. mkdir fluxcd-demo && cd fluxcd-demo
  3.   在 fluxcd-demo 目录下创建 deployment.yaml 文件

  4. apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: hello-world-flask
      name: hello-world-flask
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: hello-world-flask
      template:
        metadata:
          labels:
            app: hello-world-flask
        spec:
          containers:
          - image: 10.8.59.115/library/hello-world-flask:latest
            name: hello-world-flask
  5.   创建本地仓库

  6. # 列出当前目录文件
    ls
    # 初始化 git 文件夹
    git init
    # 提交当前文件夹
    git add -A && git commit -m "Add deployment"
    # 设置主分支为 main
    git branch -M main
    # 设置远端仓库地址
    git remote add origin http://10.8.59.16/wuchangtai/fluxcd-demo.git
    # 推送本地仓库至远端
    git push -u origin main
  1. 为 FluxCD 设置仓库信息
  1.   FluxCD 中使用 source.toolkit.fluxcd.io/v1beta2/GitRepository 自定义资源来定义仓库连接信息,这就是一种 IaC 的实际应用的体现

  2. apiVersion: source.toolkit.fluxcd.io/v1beta2
    kind: GitRepository
    metadata:
      name: hello-world-flask
    spec:
      interval: 5s # 每5s主动拉取一次仓库内容
      ref:
        branch: main # 目标仓库分支
      url: http://10.8.59.16/wuchangtai/fluxcd-demo.git # 目标仓库 url
  3.   使用 kubectl apply 将其 GitRepository 对象部署到集群内

  4. kubectl apply -f fluxcd-repo.yaml
  5. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_05

  1. 为 FluxCD 设置部署策略
  1.   与仓库配置同理,部署策略也是以清单文件的方式,用代码的形式保存

  2. apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
    kind: Kustomization
    metadata:
      name: hello-world-flask
    spec:
      interval: 5s                  # 每 5 秒钟运行一次工作负载差异对比
      path: ./                      # 目标文件的位置
      prune: true
      sourceRef:                    # 仓库信息,指向前面创建的仓库清单文件
        kind: GitRepository
        name: hello-world-flask
      targetNamespace: default      # 目标命名空间
  3. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_06

  1. 查看业务是否自动部署
  1.   前面我们所有的操作都没有手动部署过 deployment.yaml 文件,但是,设置好仓库和部署策略后,我们可以查看当前集群,会发现我们的应用已经自动部署起来了

  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_07

  1. 业务的更新发布
  1.   如果此时刚好有新版本发布,只需要下面的简单步骤,就可以借助 GitOps 很容易的实现应用的更新发布

  2. 修改本地代码
  3. ......
        spec:
          containers:
          - image: lyzhang1999/hello-world-flask:v1 # 修改此处,这里直接借用网上的镜像
            name: hello-world-flask
    ......
  4. 修改后,将修改 push 到 git
  5. git add -A && git commit -m "Update image tag to v1"
    git push origin main
  6. 查看触发重新部署的事件
  7. kubectl describe kustomization hello-world-flask
  8. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_08

  9.   从返回的结果可以看出,镜像版本修改为了 v1,并且,FluxCD 最后一次部署仓库的 Commit ID 是 8260f5a0ac1e4ccdba64e074d1ee2c154956f12d,这对应了我们最后一次的提交记录

  10. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_09

  1. 业务的回滚
  1.   如果此时需要进行版本的回滚,根据 GitOps 设计原则中的版本控制原则,Git 仓库是描述期望状态的唯一可信源,因此也只需对 Git 仓库进行回滚即可

  2. 找到上一次的提交记录。我们可以使用 git log 来查看它
  3. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_10

  4. 使用 git reset 来回滚到上一次提交,并强制推送到 Git
  5. # 回滚
    git reset --hard 469d1aecc95ae01937aae0842dd0787155f1e75a
    # 回滚后强制推送(如果报错不允许推送至受保护的分支,请在 git仓库中-setting-repository-Protected branches 开启允许强制推送
    git push origin main -f


核心基础

容器化和K8S最佳实践

构建多平台镜像

在构建镜像时,Docker 会默认构建本机对应平台的镜像,例如常见的 AMD64 平台,这在大多数情况是适用的。但是,当使用不同平台的设备时,因为构建和运行设备的 CPU 平台存在差异,可能会导致镜像无法运行。

为了真正实现跨平台的“一次构建,到处运行”目标,Docker 提供了构建多平台镜像的方法:buildx。

# 创建构建器
docker buildx create --name builder
# 将创建的 builder 设置为默认构建器
docker buildx use builder
# 初始化构建器
docker buildx inspect --bootstrap

初始化成功后,会返回支持的平台

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_11

下面尝试构建多平台镜像

# clone 示例代码
git clone http://10.8.59.16/wuchangtai/multi-arch-image.git
# 使用 buildx 一次性构建多平台镜像
docker buildx build --platform linux/amd64,linux/arm64 -t 10.8.59.115/gitops_test/multi-arch:latest --push .

查看 Dockerfile,第 2 行增加的 --platform=$BUILDPLATFORM 参数会接受来自 buildx 构建命令传递的平台参数来使用不同平台版本的基础镜像进行构建

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:1.18 as build
ARG TARGETOS TARGETARCH
WORKDIR /opt/app
COPY go.* ./
RUN go mod download
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /opt/app/example .

FROM ubuntu:latest
WORKDIR /opt/app
COPY --from=build /opt/app/example ./example
CMD ["/opt/app/example"]
尽量缩减镜像体积

在现实生产过程中,我们可能面临一系列问题,例如,由于对镜像构建过程不够熟悉,很容易出现构建慢、构建镜像过大等问题,这会导致推送镜像变得缓慢,同时也会导致在 Kubernetes 更新应用镜像版本时拉取镜像的过程也变得缓慢,从而影响整体应用发布效率。下面介绍两种缩减镜像体积的镜像构建思路

  1. 替换基础镜像
  1.   构建的镜像的大小很大程度是由引入的基础镜像的大小决定的,选用合适的基础镜像可以有效减小体积

  1. 使用多阶段构建
  1.   将镜像构建过程拆分成编译过程和运行过程,最终镜像是第二阶段生成的镜像,删减了编译所需的相关文件

选择合适业务基础镜像

可以将基础镜像分成两类:通用镜像和专用镜像

  1. 通用镜像:
  1.   可以把通用镜像理解成 Linux 发行版,他们是一个全新安装的 Linux 系统,除了系统工具以外,不包含任何特定语言的编译和运行环境。一般来说,通用镜像有下面两种使用场景:

  2. 使用通用镜像构建符合业务特定要求的镜像,例如特定的工具链和依赖,特定的构建环境和安全工具等。
  3. 单纯作为业务的运行环境,常见于多阶段构建。
  1. 专用镜像:
  1.   专用镜像提供了特定语言的编译和运行环境,绝大多数语言都有 Docker 官方维护的专用镜像。专用镜像一般有下面两种使用场景:

  2. 作为解释型语言的运行镜像使用,例如 python:latest、php:8.1-fpm-buster 等
  3. 作为编译型语言多阶段构建中编译阶段的基础镜像使用,例如 golang:latest
使用命名空间隔离团队和环境

在不同规模的组织下对命名空间进行分配和管理,有如下的建议:

  1. 单一业务应用和团队
  1.   当团队规模在几人到十几人,业务应用只有 1-2 个时,可以参考下图这种命名空间的规划方案

  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_12

  1. 多业务应用和团队
  1.   在大型的组织架构下,往往会有多个业务应用和多个团队组成,他们开发的可能是同一个大型业务,也可能是独立的应用。在这种情况下,我建议你将团队和命名空间关联起来,并且在区分环境时使用不同的集群

  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_13

为业务选择合适的工作负载类型

K8S 的工作负载包括:ReplicaSet、Deployment、StatefulSet、DaemonSet、Job、CronJob。在实际工作中,最常用到的是 Deployment,一般来说,工作负载的选择有以下参考标准

  • 对于无状态应用,Deployment 是最佳的选择
  • 实际的业务场景,几乎不会将业务应用以 StatefulSet 和 DaemonSet 这两种工作负载的方式进行部。但是 StatefulSet 可以用来部署中间件,例如 Postgres、MySQL、MongoDB、etcd 等;DaemonSet 可以用来部署日志和监控组件。这些中间件或扩展功能组件,一般都有成熟的 Helm chart,建议直接拿来使用
  • Job 和 CronJob 更适合处理一次性的任务,可以把一些批处理任务迁移到 K8S 中
解决服务发现

当应用迁移到 K8s 时,一般会将业务系统中的每一个微服务以某种 K8s 工作负载的形式进行部署,比如最常见的 Deployment。也就是说,在 K8s 环境下,微服务之间的调用可以理解为是工作负载之间的调用。换句话说,在 K8s 环境下,微服务之间的调用实际上是 Pod 之间的调用。

K8S 使用 Service 解决 Pod 之间的通信问题,Service 将一组来自同一个业务负载创建的 Pod 组合在一起,并提供 DNS 的访问能力。也就是说,Pod 之间可以通过 Service 域名来进行访问,无论 Service 背后一组的 Pod 如何变化,对于其他服务来说都是透明的,服务只需要关注访问 Service 即可,不用关心这个服务是由哪个 Pod 提供的。

将集群业务暴露给外部

在 Kubernetes 环境下,对外暴露服务则需要通过 Service 来实现。由于 Service 的 IP 默认是一个集群内的 IP,无法从外部访问,所以我们需要为 Service 赋予外网 IP 地址,有以下三种方式:

  • NodePort 类型可以通过 Kubernetes 节点外网 IP + 端口号的方式提供外网访问能力。
  • LoadBalancer 类型则需要依赖云厂商的负载均衡器
  • 引入了 Ingress 来暴露服务
保障业务资源需求和弹性扩容

在 kubernetes 环境下,业务进程是运行在 Pod 的容器当中,而容器实际上又是运行在 kubernetes 集群的节点当中的,当有多个 Pod 被调度到同一台节点时,如何避免资源竞争,保障每一个业务所需的资源呢?

通过为工作负载配置资源的请求(Request) 和限制(Limit),可以避免工作负载调度在了资源不足的节点,避免资源相互抢占的问题。

为业务配置水平扩容(HPA)策略。HPA 可以基于 CPU 和内存指标或自定义指标对 Pod 进行横向扩缩容,同时也是保障业务可用性的重要手段。

检查业务真实的健康状态

kubernetes 提供了三种健康检查探针。

  • Readiness 就绪探针可以让 kubernetes 感知到业务的真实可用状态,以此来控制什么时候将 Pod 加入到 EndPoints 列表中接收外部流量
  • Liveness 存活探针则更加彻底,当它感知到 Pod 不健康时,会重启 Pod
  • StartupProbe 探针主要针对一些服务启动慢的业务,例如大型的 Java 服务,建议为其配置 StartupProbe,以确保在服务启动完成后再对它进行常规的就绪和存活健康检查。

使用 GitLab CI 构建镜像

  • Pipeline
  •   Pipeline 指的是流水线,在 GitLab 中,当有业务代码新提交推送到仓库中时,会自动触发流水线。流水线包含一组 Stage 和 Job 的定义,它们负责执行具体的逻辑。在 GitLab 中,Pipeline 是通过仓库根目录下的 .gitlab-ci.yml 文件来定义的。

  • Stage
  •   Stage 字面上的意思是“阶段”。在 GitLab CI 中,至少需要包含一个 Stage,上面这张图中有三个 Stage,分别是 Stage1、Stage2 和 Stage3,不同的 Stage 是按照定义的顺序依次执行的。如果其中一个 Stage 失败,则整个 Pipeline 都将失败,后续的 Stage 也都不会再继续执行。

  • Job
  •   Job 字面上的意思是“任务”。实际上,Job 的作用是定义具体需要执行的 Shell 脚本,同时,Job 可以被关联到一个 Stage 上。当 Stage 执行时,它所关联的 Job 也会并行执行。以自动构建镜像为例,我们可能需要在 1 个 Job 中定义 2 个 Shell 脚本步骤,它们分别是:

  • 运行 docker build 构建镜像运行
  • docker push 来推送镜像
创建 CICD 流水线
  1. 这里借助示例应用来进行实验,先克隆示例应用到本地
git clone https://github.com/lyzhang1999/kubernetes-example.git
cd kubernetes-example
  1. 在 gitlab 上创建新的代码仓库

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_14

  1. 将本地代码仓库的远程地址修改为我们自己新建的仓库地址
git remote set-url origin http://10.8.59.16/wuchangtai/kubernetes-example.git
  1. 将本地仓库推送到自己的 gitlab 仓库中
git branch -M main
git push -u origin main
  1. 进入仓库,选择 CI/CD-Editor,将下面的内容填入,之后会在项目下生成 .gitlab-ci.yml 文件

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_15

# 定义有哪些阶段,在这个 Pipeline 中,定义了一个 build 阶段
stages:
  - build
  -
  -
  
# 定义了运行镜像,GitLab CI 使用 docker:20.10.16 镜像来启动一个容器,并在容器内运行 Pipeline
image: docker:20.10.16

# 这是定义的变量,是Harbor仓库登录的验证信息。
variables:
  USERNAME: admin
  PASSWORD: Cloud@9000
  HARBORIP: 10.8.59.115

# 和 image 字段定义的容器相互协作,这两个容器可以相互访问
services:
  - docker:20.10.16-dind

# Pipeline 最开始的 Shell 脚本, 它将会在 Job 运行之前执行
before_script:
  - docker login -u $USERNAME -p $PASSWORD $HARBORIP

# 定义了一个 Job,“build_and_push” 实际上是 Job 的名称
build_and_push:
  # 定义了 Job 所属的 Stage
  stage: build
  # 定义了执行的具体的 Shell 脚本
  script:
    - docker build -t $HARBORIP/gitops_test/frontend:$CI_COMMIT_SHORT_SHA ./frontend
    - docker push $HARBORIP/gitops_test/frontend:$CI_COMMIT_SHORT_SHA
    - docker build -t $HARBORIP/gitops_test/backend:$CI_COMMIT_SHORT_SHA ./backend
    - docker push $HARBORIP/gitops_test/backend:$CI_COMMIT_SHORT_SHA
  1. 点击 Commit changes,保存并提交,然后在 Pipeline 选项中查看流水线

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_16

但是当前流水线状态时 pending 的,点击流水线查看详情,可以看到当前 pending 的原因是没有任何运行中的 runner 可以用来运行当前的 job

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_17

安装 GitLab Runner

GitLab Runner 是一个开源项目,用于运行 Job 并将结果发送回 GitLab,它与 GitLab CI 一起使用。

几乎可以在任何机器上安装gitlab-runner,安装的方式这里推荐如下几种:

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_18

使用二进制的方式运行Runner
  1. 安装 gitlab runner
# 安装 Linux x86-64 二进制版本
sudo curl -L --output /usr/local/bin/gitlab-runner "https://s3.dualstack.us-east-1.amazonaws.com/gitlab-runner-downloads/latest/binaries/gitlab-runner-linux-amd64"
sudo chmod +x /usr/local/bin/gitlab-runner

# 创建一个Linux 用户,CI user
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash

sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start

安装完成后可以看到服务已经启动

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_19

  1. 在gitlab的工程中获取CI流程注册信息(若无setting选项,可能是该登录用户没有管理员权限)

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_20

  1. 向 gitlab 注册 gitlab runner
sudo gitlab-runner register
# 输入以下信息
# gitlab 地址
Enter the GitLab instance URL (for example, https://gitlab.com/):
# 输入注册 token,可以在 gitlab 页面中的 CI/CD settings 中找到
Enter the registration token
# 输入 runner 名称
Enter a description for the runner:  
# 设置 runner 的 tag,用来区分不同的 runner 
Enter tags for the runner (comma-separated):  
# 输入可选维护说明
Enter optional maintenance note for the runner:  
# 输入 runner 的类型,这里选择 shell
Enter an executor: docker-windows, shell, ssh, docker-autoscaler, kubernetes, custom, parallels, virtualbox, docker+machine, instance, docker:

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_21

成功后可以在gitlab工程中setting-CICD-Runners菜单中看到

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_22

修改配置文件/etc/gitlab-runner/config.toml,添加指定shell

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_23

修改后执行命令gitlab-runner restart重启


【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_24


【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_25




使用 Docker 的方式运行 Runner
  1. 安装 gitlab runner
  1. # 创建一个卷
    docker volume create gitlab-runner-01-config
    # 安装 runner
    docker run -d --name gitlab-runner-01 --restart always -v /var/run/docker.sock:/var/run/docker.sock -v gitlab-runner-01-config:/etc/gitlab-runner gitlab/gitlab-runner:latest
  1. 向 gitlab 注册 gitlab runner
  1. # 注册 runner
    docker run --rm -it -v gitlab-runner-01-config:/etc/gitlab-runner gitlab/gitlab-runner:latest register
  2.   注册过程中,会要求输入以下内容:

  3. # gitlab 地址
    Enter the GitLab instance URL (for example, https://gitlab.com/):
    # 输入注册 token,可以在 gitlab 页面中的 CI/CD settings 中找到
    Enter the registration token
    # 输入 runner 名称
    Enter a description for the runner:  
    # 设置 runner 的 tag,用来区分不同的 runner 
    Enter tags for the runner (comma-separated):  
    # 输入可选维护说明
    Enter optional maintenance note for the runner:  
    # 输入 runner 的类型,这里选择 docker
    Enter an executor: docker-windows, shell, ssh, docker-autoscaler, kubernetes, custom, parallels, virtualbox, docker+machine, instance, docker:
    # 输入 runner 默认使用的镜像
    Enter the default Docker image (for example, ruby:2.7):
  4.   当看到输出 Runner registered successfully 后,去 gilab 的项目页面,选择 Settings-CI/CD-Runners,可以查看到刚刚注册的新的 runner

  5. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_26

  6.   修改安装的 runner 的配置文件,配置文件的详解请见 Advanced configuration | GitLab

  7. # 查看卷的挂载地址
    docker inspect volume gitlab-runner-01-config
    # 根据地址修改挂载目录下的 config.toml 文件,将 docker.sock 作为挂载,方法后续 pipeline 中用到的镜像可以访问 docker
    cat /var/lib/docker/volumes/gitlab-runner-01-config/_data/config.toml
    ……
    concurrent = 1
    check_interval = 0v
    shutdown_timeout = 0
    
    [session_server]
      session_timeout = 1800
    
    [[runners]]
      name = "gitlab-runner-01-test"
      url = "http://10.8.59.16"
      id = 14
      token = "z_Y3syn8v1xvM9yim1ij"
      token_obtained_at = 2023-07-24T07:18:47Z
      token_expires_at = 0001-01-01T00:00:00Z
      executor = "docker"
      [runners.cache]
        MaxUploadedArchiveSize = 0
      [runners.docker]
        tls_verify = false
        image = "ruby:2.7"
        privileged = false
        disable_entrypoint_overwrite = false
        oom_kill_disable = false
        disable_cache = false
        volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
        shm_size = 0
    ……
  8.   修改后重启 gitlab-runner

  9. docker restart gitlab-runner-01
  1. 修改项目的 CI/CD-Editor,并查看 Job 运行情况
  1.   通过 tags 指定运行流水线使用的 runner,tag 就是注册 runner 时填写的 tag

  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_27

  3.   点击 Commit changes 后,查看 Job 的运行情况

  4. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_28

  5.   登录 harbor 仓库查看镜像是否上传成功

  6. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_29


使用 Helm Chart 定义应用

什么是 Helm

Helm 是一种真正意义上的 Kubernetes 应用的包管理工具,它对最终用户屏蔽了 Kubernetes 对象概念,将复杂度左移到了应用开发者侧,终端用户只需要提供安装参数,就可以将应用安装到 Kubernetes 集群内

Helm 的结构

Chart 是 Helm 的一种应用封装格式,它由一些特定文件和目录组成。为了方便 Helm Chart 存储、分发和下载,它采用 tgz 的格式对文件和目录进行打包。

通过 Helm 命令安装应用时,Helm 会先从指定的仓库下载 tgz 格式的 Helm Chart,然后再进行安装。

一个标准的 Helm Chart 目录结构是下面这样的。

$ ls
Chart.yaml templates values.yaml
  • Chart.yaml 文件是 Helm Chart 的描述文件,例如名称、描述和版本等。
  • templates 目录用来存放模板文件,可以把它视作 Kubernetes Manifest,但它和 Manifest 最大的区别是,helm 中的模板文件可以包含变量变量的值则来自于 values.yaml 文件定义的内容
  • values.yaml 文件是安装参数定义文件,它不是必需的。在 Helm Chart 被打包成 tgz 包时,如果 templates 目录下的 Kubernetes Manifest 包含变量,那么你需要通过它来提供默认的安装参数。

作为最终用户,当安装某一个 Helm Chart 的时候,也可以提供额外的 YAML 文件来覆盖默认值。为同一个 Helm Chart 提供不同的安装参数,就可以得到具有配置差异的多套环境。

Helm Release

Helm Release 实际上是一个“安装”阶段的概念,它指的是一次安装的唯一标识(名称)。Helm Chart 本质是一个应用安装包,它可以在同一个集群中甚至是同一个命名空间下安装多次,所以就需要为每次安装都起一个唯一的名字,这就是 Helm Release Name。

创建示例应用
  1. 创建 helm 目录结构
  1.   使用前面使用 GitLab CI 构建镜像中用到的示例项目 kubernetes-example,在目录下创建 helm 目录,并在 helm 目录下创建 templates 目录、Chart.yaml 以及 values.yaml 文件,现在,helm 目录结构是下面这样。

  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_30

  1. 配置 chart.yaml
  1. apiVersion: v2                              # 指定所使用的 Helm chart 的API版本
    name: kubernetes-example                    # 名称
    description: A Helm chart for Kubernetes    # 描述
    type: application                           # 类型,固定为 application,代表 Kubernetes 应用
    version: 0.1.0                              # 当前打包的 chart 包的版本
    appVersion: "0.1.0"                         # 应用的版本
  1. 模板变量和 value.yaml
  1.   先将示例中的清单文件拷贝到 templates 目录下

  2. cp -r deploy/ helm/templates/
  3.   此时直接使用 helm install 部署应用和直接使用 kubectl apply 是一样的,因为此时是一个纯静态的 Helm Chart。要将这个静态的 Helm Chart 改造成参数动态可控制的,需要用到模板变量和 values.yaml。下面是针对本示例抽象后并填入 value.yaml 中的几个变量。

  4. frontend:
      image: lyzhang1999/frontend
      tag: latest
      autoscaling:
        averageUtilization: 90
    
    backend:
      image: lyzhang1999/backend
      tag: latest
      autoscaling:
        averageUtilization: 90
    
    database:
      enabled: true
      uri: pg-service
      username: postgres
      password: postgres
  5.   变量抽取完成后,还需要让原来的清单文件能够读取到这些变量,这就需要在清单文件中使用模板变量,举一个最简单的例子,要读取 values.yaml 中的 frontend.image 字段,可以通过 “{{ .Values.frontend.image }}” 模板变量来获取值。

  1. 安装应用
  1.   使用 helm 部署应用非常简单,只需要运行如下命令即可

  2. helm install my-kubernetes-example ./helm --namespace example --create-namespace
  3.   my-kubernetes-example 是前面提到的 Helm Release name,用来特指本次安装

  4.   ./helm 是指定目标 chart 的位置

  5.   --namespace 参数指定命名空间,因为空间并非提前创建好的,所以使用 --create-namespace 来创建

  1. 变量的动态部署
  1.   前面为 Helm Chart 创建的 values.yaml 实际上是默认值,在发布到不同环境时,变量的值可能会发生变化,此时可以直接修改 value.yaml 后部署,也可以使用下面两种方式来动态部署

  2. 使用 --set 参数为模板变量赋值
  •     使用 helm install 命令时,可以使用 --set 参数对模板变量赋值,该参数赋值会覆盖 value.yaml 中的默认值

  • helm install my-kubernetes-example ./helm --namespace helm-staging --create-namespace --set frontend.autoscaling.averageUtilization=60
  1. 使用 -f 参数,指定不同的参数定义文件
  •     可以为同一个 chart 创建多份参数定义文件,默认情况下使用名为 value.yaml 的文件

  • helm install my-kubernetes-example ./helm -f ./helm/values-prod.yaml --namespace helm-prod --create-namespace


在 Helm Chart 编写完成之后,便能够在本地安装它了,同时还可以将其推送到仓库与他人分享,下面以 harbor 仓库为例,介绍如何推送应用到仓库

# 登录到 harbor 仓库
docker login -u <username> -p <password> <harbor-registry-url>
# 为本地 helm 添加远程仓库信息, <repo-name> 是 Helm 仓库的名称,可以自定义
helm repo add <repo-name> <harbor-registry-url>/<project-name>

进阶提升

生产级工作流

使用 ArgoCD 快速打造生产可用的 GitOps 工作流

在最开始的时候,我们尝试了使用了 FluxCD 来构建 GitOps 流水线。FluxCD 的主要特点是比较轻量,但同时也缺少友好的 UI 控制台。相比较而言,在社区和维护方面,ArgoCD 更为活跃。在生产环境下,推荐使用 ArgoCD 来构建 GitOps 工作流。

安装 ArgoCD
  1. 创建 Argocd 命名空间
  1. kubectl create namespace argocd
  1. 部署 ArgoCD 和 ArgoCD Image Updater(官方部署清单文件)
  1. # 安装 ArgoCD
    kubectl apply -n argocd -f https://ghproxy.com/https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
    # 安装 ArgoCD Image Updater
    kubectl apply -n argocd -f https://ghproxy.com/https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yaml
  1. 通过 watch 等待部署完成
  1. kubectl wait --for=condition=Ready pods --all -n argocd --timeout 300s
  2.   由于 Kubernetes 版本存在差异,如果在安装过程中发现 argocd-repo-server 工作负载一直无法启动,可以尝试删除 argocd-repo-server 的 Deployment 中 seccompProfile 的内容

  1. 外暴露 argoCD 的服务
  1.   通过 NodePort 的方式对外暴露 argoCD 的服务,使用下面的命令修改 svc 为 NodePort 类型

  2. # 修改 svc
    kubectl edit svc argocd-server -n argocd
    # 查看暴露的端口
    kubectl get svc argocd-server -n argocd
  3. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_31

  1. 安装 ArgoCD CLI 并登录 ArgoCD
  1. curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
    sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
    rm argocd-linux-amd64
  2.   访问 ArgoCD 之前,要先执行 argocd login 命令登录,默认用户名为 admin,默认密码使用下面的命令来获取

  3.   端口信息,使用HTTP的端口

  4. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_32

  5. # 获取默认密码
    kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
    # 登录 argocd,端口为上面使用 Nodeport 暴露的端口
    argocd login 10.8.59.117:32696 --insecure
    Username: admin
    Password:

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_33

  1. 访问 ArgoCD 的 web UI
  1.   使用浏览器打开:http://[ip]:[port],这样就可以访问 ArgoCD 控制台了,如下图所示。

  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_34

  3.   ArgoCD 的默认账号为 admin,密码可以通过下面的命令来获取。

  4. kubectl -n argocd get secret argocd-initial-admin-secret -o jsnotallow="{.data.password}" | base64 -d
基于 ArgoCD 的 GitOps 工作流

在开发和发布分工明确的团队中,推荐将源码和应用定义(helm chart)分离,考虑到安全性和发布的严谨性,也尽量不要通过 CI 直接修改应用定义。

更合理的研发规范设计应该是这样的:业务开发负责编写代码和 Dockerfile,并通过 CI 生成制品,也就是 Docker 镜像,并对生成的制品负责。而基础架构部门或者 SRE 团队则对应用定义负责。在发布环节,开发可以随时控制要发布的镜像版本,而无需关注其他的应用细节,他们之间的工作流程图如下:

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_35

开发和 SRE 团队各司其职,只操作和自己相关的 Git 仓库,互不干扰,借助 ArgoCD Image Updater,可以让 ArgoCD 自动监控镜像仓库的更新情况,一旦工作负载的镜像版本有更新,ArgoCD 就会自动将工作负载升级为新的镜像版本,并且还可以自动将镜像的版本号回写到 Helm Chart 仓库中,保持应用定义和集群状态的一致性。

工作流创建实践
  1. 私有 CI 流程复用前面使用 GitLab CI 构建镜像中所创建的流程
  1.   http://10.8.59.16/wuchangtai/kubernetes-example/-/pipelines

  1. 基于上面介绍的工作流原则,先为应用定义单独创建仓库,还是基于前面的示例应用 kuberntes-example
  1. # 将 helm chart 单独拷贝出来
    cp -r ./kubernetes-example/helm ./kubernetes-example-helm
    # 将 helm chart 初始化为本地 git 仓库
    cd kubernetes-example-helm && git init
    # 在 gitlab 创建新的仓库并命名为 kubernetes-example-helm,并将本地仓库推送到远端
    git add .
    git commit -m "first commit"
    git branch -M main
    git remote add origin http://10.8.59.16/wuchangtai/kubernetes-example-helm.git
    git push -u origin main
  1. 创建 Image Pull Secret
  1.   由于 ArgoCD 会主动监控镜像仓库来检查是否存在新版本,如果使用的是私有镜像仓库,那么需要创建 Secret 对象,以便为 ArgoCD 提供访问镜像仓库的权限

  2. kubectl create -n argocd secret docker-registry harbor-115-secret \
      --docker-username admin \
      --docker-password Cloud@9000 \
      --docker-server "https://10.8.59.115"
  1. 为 ArgoCD Image Updater(监听image变化)配置 kubernetes-example-helm 仓库的访问权限
  1. # 操作前需要先使用 argocd login 登录
    argocd login 127.0.0.1:30143 --insecure
    # 配置访问权限
    argocd repo add http://10.8.59.16/wuchangtai/kubernetes-example-helm.git --username $USERNAME --password $PASSWORD
  2.   这里还需要修改 ArgoCD Image Updater 的配置文件来跳过对 harbor 仓库自签名证书合法性的校验,因为我们使用的私有 harbor 仓库,其证书是非受信的,无法通过验证

  3. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_36

  4.   通过下面的方法,修改 ArgoCD Image Updater 的 cm 来设置

  5. kubectl edit cm -n argocd argocd-image-updater-config
  6. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_37

  7.   修改完成后,通过删除原有的 pod 重新创建来应用配置的修改

  8. kubectl delete pods argocd-image-updater-xxxxxx-xxxx
  1. 创建 ArgoCD Application
  1.   ArgoCD Application 实际上就是 ArgoCD 为定义应用而设计的一种特殊资源对象,同样是采用 yaml 格式的方式来定义的,将下面的内容保存为 application.yaml 文件并用 kubectl apply -n argocd 命令部署。

  2. apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: example
      annotations:
        # 配置符合更新条件的镜像 Tag,这里以正则表达式匹配以 main 开头的 tag
        argocd-image-updater.argoproj.io/backend.allow-tags: regexp:^main
        # 配置 Helm Chart values.yaml 中定义镜像名称的变量
        argocd-image-updater.argoproj.io/backend.helm.image-name: backend.image
        # 配置 Helm Chart values.yaml 中定义镜像 tag 的变量
        argocd-image-updater.argoproj.io/backend.helm.image-tag: backend.tag
        # 为 backend 镜像指定镜像拉取凭据
        argocd-image-updater.argoproj.io/backend.pull-secret: pullsecret:argocd/harbor-115-secret
        # 配置符合更新条件的镜像 Tag,这里以正则表达式匹配以 main 开头的 tag
        argocd-image-updater.argoproj.io/frontend.allow-tags: regexp:^main
        # 配置 Helm Chart values.yaml 中定义镜像名称的变量
        argocd-image-updater.argoproj.io/frontend.helm.image-name: frontend.image
        # 配置 Helm Chart values.yaml 中定义镜像 tag 的变量
        argocd-image-updater.argoproj.io/frontend.helm.image-tag: frontend.tag
        # 为 frontend 镜像指定镜像拉取凭据
        argocd-image-updater.argoproj.io/frontend.pull-secret: pullsecret:argocd/harbor-115-secret
        # 指定需要监听的镜像
        argocd-image-updater.argoproj.io/image-list: frontend=10.8.59.115/gitops_test/frontend, backend=10.8.59.115/gitops_test/backend
        # 镜像更新策略:latest 并不代表监听 latest 镜像版本,而是以最新推送的镜像作为更新策略。此外,semver 策略可以识别最高语义化版本的标签,digest 策略可以用来区分同一 Tag 下不同镜像 digest 的变更
        argocd-image-updater.argoproj.io/update-strategy: latest
        # 表示将镜像版本回写到应用仓库
        argocd-image-updater.argoproj.io/write-back-method: git
    spec:
      destination:
        namespace: gitops-example-updater          # 目标 namespace 是该应用要部署到的目的空间
        server: https://kubernetes.default.svc     # 目标 k8s 集群地址,这里是部署在本地集群
      project: default                             # 这里的 project 是 application 资源本身部署的空间
      source:
        path: .
        repoURL: http://10.8.59.16/wuchangtai/kubernetes-example-helm.git
        targetRevision: main
      syncPolicy:
        automated: {}
        syncOptions:
          - CreateNamespace=true
  1. 修改源码验证工作流
  1.   尝试修改 frontend/src/App.js 文件,修改完成后,将代码推送到 GitHub 的 main 分支;通过查看 CI 页面,可以看到每次修改推送都能成功触发 CI 流程

  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_38

  3.   与此同时,ArgoCD Image Updater 将会每 2 分钟从镜像仓库检索 frontend 和 backend 的镜像版本,一旦发现有新的并且以 main 开头的镜像版本,它将自动使用新版本来更新集群内工作负载的镜像。

  4. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_39

  5. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_40

  6.   同时,会将新的镜像信息回写到应用定义的仓库中,在回写时,ArgoCD Image Updater 并不会直接修改仓库的 values.yaml 文件,而是会创建一个专门用于覆盖 Helm Chart values.yaml 的 .argocd-source-example.yaml 文件。当看到这个文件时,说明 ArgoCD Image Updater 已经触发了镜像更新,并且成功将镜像版本回写到了镜像仓库。同时,这个文件记录了详细的覆盖 values.yaml 值的策略。

  7. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_41

  8.   这样,当 ArgoCD 在做自动同步时,会将这份文件的内容覆盖 values.yaml 对应的值,比如 frontend.tag 的值会被覆盖为 main-c275db32,这样,回写后的 Helm Chart 和集群内资源对象就仍然能够保持一致性。



高级发布策略

蓝绿发布
手动蓝绿发布

蓝绿发布核心思想是:为应用提供两套环境,并且可以很方便地对它们进行流量切换

对同一个应用部署两个版本的环境,称之为蓝绿环境,在没有切换流量之前,“蓝色”环境负责接收外部请求流量。需要进行流量切换时,只要调整 Ingress 策略就可以让“绿色”环境接收外部流量

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_42

下面通过实验来演示蓝绿发布的过程,环境中需要提前安装好 ingress

  1. 创建 ingress rule
  1.   现在,这个 ingress 向集群外暴露了名为 blue-service 的服务,同时通过 bluegreen.demo 这个域名去访问,如果用的是 Linux 或者 MacOS 系统,请将域名和IP对应关系添加到 /etc/hosts 文件。如果用的是 Windows 系统,将其添加到 C:\Windows\System32\Drivers\etc\hosts 文件

  2. apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: demo-ingress
    spec:
      rules:
      - host: "bluegreen.demo"
        http:
          paths:
          - pathType: Prefix
            path: "/"
            backend:
              service:
                name: blue-service
                port:
                  number: 80
  1. 部署蓝色环境
  1. # deployment for blue
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: blue
      labels:
        app: blue
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: blue
      template:
        metadata:
          labels:
            app: blue
        spec:
          containers:
          - name: demo
            image: argoproj/rollouts-demo:blue
            imagePullPolicy: Always
            ports:
            - containerPort: 8080
    ---
    # service for blue
    apiVersion: v1
    kind: Service
    metadata:
      name: blue-service
      labels:
        app: blue
    spec:
      ports:
      - protocol: TCP
        port: 80
        targetPort: 8080
      selector:
        app: blue
      type: ClusterIP
  1. 访问蓝色环境后端
  1. # 先查找 ingress-controller 暴露的端口
    kubectl get svc -A | grep ingress-controller
  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_43

  3.   使用前面配置的 ingrees 规则中的域名+端口号访问,在这个页面里,浏览器每秒钟会向后端发出 50 个请求,蓝色的方块代表后端返回接口的内容为 blue,对应 blue 版本的镜像,代表蓝色环境。

  4. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_44

  1. 部署绿色环境
  1. # deployment for green
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: green
      labels:
        app: green
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: green
      template:
        metadata:
          labels:
            app: green
        spec:
          containers:
          - name: demo
            image: argoproj/rollouts-demo:green
            imagePullPolicy: Always
            ports:
            - containerPort: 8080
    ---
    # service for green
    apiVersion: v1
    kind: Service
    metadata:
      name: green-service
      labels:
        app: green
    spec:
      ports:
      - protocol: TCP
        port: 80
        targetPort: 8080
      selector:
        app: green
      type: ClusterIP
  1. 手动切换环境到绿色
  1.   手动切换的过程实际上就是修改 ingress rule 的过程,将 demo-ingress 的内容修改为如下后并 apply:

  2. apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: demo-ingress
    spec:
      rules:
      - host: "bluegreen.demo"
        http:
          paths:
          - pathType: Prefix
            path: "/"
            backend:
              service:
                name: green-service    # 从 blue-service 切换到  green-service
                port:
                  number: 80
  3.   回到页面上观察,页面上的后端逐渐从蓝色替换为绿色,并最终全部变成绿色

  4. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_45


自动蓝绿发布

上面是通过创建 Kubernetes 原生对象并修改 Ingress 策略的方式来完成蓝绿发布的。首先在更新过程中,一般只关注镜像版本的变化,而不会去操作 Ingress 策略;其次,这种方式不利于将蓝绿发布和 GitOps 流水线进行整合。因此我们需要通过 Argo Rollout 工具来自动化蓝绿发布的过程。

Argo Rollout 是一款专门提供 Kubernetes 高级部署能力的自动化工具,它可以独立运行,同时也可以和 ArgoCD 协同在 GitOps 流水线中来使用。

  1. 安装 Argo Rollout
  1. # 创建命名空间
    kubectl create namespace argo-rollouts
    # 部署清单文件
    kubectl apply -n argo-rollouts -f https://ghproxy.com/https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
    # 等待就绪
    kubectl wait --for=condition=Ready pods --all -n argo-rollouts --timeout=300s
  1. 创建 rollout 对象
  1.   同样是 IaC 的原则,将创建负载和发布的过程,抽象成 yaml 格式的资源对象(CRDs)

  2. apiVersion: argoproj.io/v1alpha1
    kind: Rollout    
    metadata:
      name: bluegreen-demo
      labels:
        app: bluegreen-demo
    spec:
      replicas: 3
      revisionHistoryLimit: 1
      selector:
        matchLabels:
          app: bluegreen-demo
      template:
        metadata:
          labels:
            app: bluegreen-demo
        spec:
          containers:
          - name: bluegreen-demo
            image: argoproj/rollouts-demo:blue
            imagePullPolicy: Always
            ports:
            - name: http
              containerPort: 8080
              protocol: TCP
            resources:
              requests:
                memory: 32Mi
                cpu: 5m
      # 定义部署策略
      strategy:
        # 策略类型: blueGreen
        blueGreen:
          # 自动实实施蓝绿发布
          autoPromotionEnabled: true
          # 关联蓝绿发布的 service
          activeService: bluegreen-demo
  1. 创建 Service 和 ingress rule
  1. apiVersion: v1
    kind: Service
    metadata:
      name: bluegreen-demo
      labels:
        app: bluegreen-demo
    spec:
      ports:
      - port: 80
        targetPort: http
        protocol: TCP
        name: http
      selector:
        app: bluegreen-demo              # 根据 label,关联到前面 rollout 对象管理的 Pod
    ---
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: bluegreen-demo
    spec:
      rules:
      - host: "bluegreen.auto"            # 同理,将该域名添加到 hosts 文件中
        http:
          paths:
          - pathType: Prefix
            path: "/"
            backend:
              service:
                name: bluegreen-demo
                port:
                  number: 80
  1. 查看页面
  1.   与手动发布类似,当前所有请求都是发往蓝色环境

  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_46

  1. 切换到蓝色环境
  1.   现在,假设需要更新到绿色环境,在 Argo Rollout 的帮助下,只需要修改 Rollout 对象中的镜像版本就可以了,流量切换过程将由 Argo Rollout 自动控制。

  2. containers:
    - name: bluegreen-demo
      image: argoproj/rollouts-demo:green
  3. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_47

  4.   结合前面的 ArgoCD 和 ArgoCD images updater,当监控到镜像仓库中有新的版本镜像生成,则更新应用定义仓库中 Rollout 对象的 images 字段,即可实现完全自动化的蓝绿发布

  1. 原理简介
  1.   当修改 Rollout 对象的镜像版本后,Argo Rollout 将会重新创建一个新的 ReplicaSet 对象,名称为 bluegreen-demo-7d6459646d,新的 ReplicaSet 也会在创建 Pod 时额外为 Pod 打上 rollouts-pod-template-hash=7d6459646d 标签。这时候蓝绿环境的 ReplicaSet 同时存在。

  2.   当绿色环境的 Pod 全部就绪之后,Argo Rollout 会将 Service 原来的选择器删除,并添加 rollouts-pod-template-hash=7d6459646d 的选择器,这样就将 Service 指向了绿色环境的 Pod,从而达到了切换流量的目的。同时,Argo Rollout 还会将蓝色环境的 ReplicaSet 副本数缩容为 0,但并不删除它,而是把它作为灾备

  3. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_48




金丝雀发布

上面介绍的蓝绿发布有个缺点,每次更新,所有流量都将全部一次性切换,无法对新环境进行小规模的流量验证。

通常,在发布新版本时,应该先将一小部分生产流量转发到新的环境,以此来验证新环境的表现,而大部分流量仍然指向稳定的老版本。这种发布方式就叫做金丝雀发布,又叫做灰度发布

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_49

通过手动的方式实现金丝雀发布,需要手动创建生产和金丝雀两个环境,手动配置 Ingress 策略,如果想要调整金丝雀环境的流量比例,可能需要多次修改 Ingress 策略。因此,最好还是借助 Argo rollout 来实现自动化的金丝雀发布。

  1. 创建金丝雀发布 rollout 对象
  1. apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: canary-demo
      labels:
        app: canary-demo
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: canary-demo
      template:
        metadata:
          labels:
            app: canary-demo
        spec:
          containers:
          - name: canary-demo
            image: argoproj/rollouts-demo:blue
            imagePullPolicy: Always
            ports:
            - name: http
              containerPort: 8080
              protocol: TCP
            resources:
              requests:
                memory: 32Mi
                cpu: 5m
      strategy:
        canary:                                    # 策略类型: blueGreen
          canaryService: canary-demo-canary        # 金丝雀环境 Service
          stableService: canary-demo               # 稳定环境 Service
          canaryMetadata:                          # 将额外的标签增加到 Pod 中,区分不同环境的 Pod
            labels:
              deployment: canary
          stableMetadata:                          # 将额外的标签增加到 Pod 中,区分不同环境的 Pod
            labels:
              deployment: stable
          trafficRouting:
            nginx:                                 # 使用 Ingress-Nginx 来管理流量
              stableIngress: canary-demo           # 指定 Ingress rule 名称
              additionalIngressAnnotations:        # 配置特定的金丝雀流量识别策略
                canary-by-header: X-Canary         # 请求头出现 X-Canary 将流量转发到金丝雀环境
          steps:                                   # 描述如何进行自动化金丝雀发布
            - setWeight: 20                        # 流量比例设置为 20%
            - pause: {}                            # 描述如何进行自动化金丝雀发布
            - setWeight: 50                        # 将流量比例配置为 50% 并持续 30 秒
            - pause:
                duration: 30s
            - setWeight: 70                        # 将流量比例配置为 70% 并持续 30 秒
            - pause:
                duration: 30s
  1. 创建 ingress rule 和 service
  1.   分别为稳定环境创建 Service/canary-demo 和金丝雀环境创建 Service/canary-demo-canary

  2. apiVersion: v1
    kind: Service
    metadata:
      name: canary-demo
      labels: 
        app: canary-demo
    spec:
      ports:
      - port: 80
        targetPort: http
        protocol: TCP
        name: http
      selector:
        app: canary-demo
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: canary-demo-canary
      labels: 
        app: canary-demo
    spec:
      ports:
      - port: 80
        targetPort: http
        protocol: TCP
        name: http
      selector:
        app: canary-demo
  3.   创建 Ingress 对象,通过域名 canary.auto 访问,指向 canary-demo 这个 service

  4. apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: canary-demo
      labels:
        app: canary-demo
      annotations:
        kubernetes.io/ingress.class: nginx
    spec:
      rules:
        - host: canary.auto
          http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: canary-demo
                    port:
                      name: http
  5.   查看生产环境的情况

  6. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_50

  1. 自动化金丝雀发布
  1.   和蓝绿发布类似,此时,如果有业务更新需要,只需要修改 rollout 对象的 image 字段即可

  2. containers:
    - name: canary-demo
      image: argoproj/rollouts-demo:green
  3.   查看生产环境情况,后端逐渐增加了发往绿色环境,也就是金丝雀环境的请求,大概比率在20%左右

  4. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_51

  1. 控制发布阶段
  1.   前面在发布策略中设计了多个阶段,现在发布阶段一直停留在比率为 20% 的阶段,下面要让发布继续,这时候就需要用到 Argo rollout 的 kubectl 插件,安装后可以使用 kubectl argo 相关命令行

  2. # 安装插件
    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/bin/kubectl-argo-rollouts
    # 检查是否安装成功
    kubectl argo rollouts version
  3. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_52

  4.   使用 kubectl argo 命令行批准发布

  5. kubectl argo rollouts promote canary-demo
  6.   查看环境中请求的情况,可以看到请求比率逐渐从20% 提升到 50%,持续一段时间(30s)后,再提升到了 70%,最后全部转换成绿色环境

  7. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_53

  1. 发布原理简介
  1.   当修改了 Rollout 的镜像版本并进行金丝雀发布时,Rollout 对象会创建一个新的金丝雀环境的 ReplicaSet 对象,并修改 Service 的标签选择器来匹配到金丝雀环境的 Pod。

  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_54

  3.   然后,再生成一个额外的 Ingress 对象匹配需要转发到金丝雀环境的流量,包括按权重以及匹配 Header 的规则

  4. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_55

  5.   当金丝雀发布的所有阶段都完成之后,Argo Rollout 还会自动将金丝雀环境提升为生产环境。具体的做法是,修改金丝雀环境的 Ingress 策略,将 nginx.ingress.kubernetes.io/canary-weight 注解的值修改为 0,同时将旧生产环境的 Service 的选择器修改为匹配金丝雀环境的 Pod,最后再将旧的生产环境的 ReplicaSet 缩容为 0

  6. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_56

发布过程可视化

在前面的蓝绿发布和金丝雀发布中,可以看到利用 Argo rollout 可以很简单的实现发布过程的自动化。除了使用命令行操作外,还可以使用可视化界面查看发布过程,需要安装 Argo rollout Dashboard

  1. 使用 kubectl argo 命令启动 dashboard
  1. kubectl argo rollouts dashboard
  2. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_57

  3.   通过 http://IP:3100/rollouts 地址访问 dashboard

  4. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_58

  5. 【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_59

Kubernetes源码实战

通过KinD+DinP技术,完成KinP的部署

暂时无法在飞书文档外展示此内容

构建自定义KIND镜像

准备工作
  1. 镜像

由于kind命令执行编译过程中无法指定镜像仓库和镜像名,故需要准备如下镜像,目前都已经push到115的harbor上,运行前手动拉取并更改tag

#编译kubernetes源码所需要的镜像
docker pull 10.8.59.115/k8s.gcr.io/build-image/setcap:buster-v2.0.3
docker tag 10.8.59.115/k8s.gcr.io/build-image/setcap:buster-v2.0.3 k8s.gcr.io/build-image/setcap:buster-v2.0.3

docker pull 10.8.59.115/k8s.gcr.io/build-image/go-runner:v2.3.1-go1.16.9-buster.0
docker tag 10.8.59.115/k8s.gcr.io/build-image/go-runner:v2.3.1-go1.16.9-buster.0 k8s.gcr.io/build-image/go-runner:v2.3.1-go1.16.9-buster.0

docker pull 10.8.59.115/k8s.gcr.io/build-image/debian-iptables:buster-v1.6.5
docker tag 10.8.59.115/k8s.gcr.io/build-image/debian-iptables:buster-v1.6.5 k8s.gcr.io/build-image/debian-iptables:buster-v1.6.5

docker pull 10.8.59.115/k8s.gcr.io/build-image/kube-cross:v1.21.0-go1.16.9-buster.0
docker tag 10.8.59.115/k8s.gcr.io/build-image/kube-cross:v1.21.0-go1.16.9-buster.0 k8s.gcr.io/build-image/kube-cross:v1.21.0-go1.16.9-buster.0

#kind 构建node-image所需要的镜像
docker pull 10.8.59.115/kind/base:eucs-v1.0
docker tag 10.8.59.115/kind/base:eucs-v1.0 kind/base:eucs-v1.0
  1. Kubernetes源码

将gitlab上的源码拷贝至如下目录

mkidr -p /root/go/src/k8s.io
执行命令构建

kind build node-image --base-image kind/base:eucs-v1.0 --image kindest/node:eucs-v5.0.0.0

根据基础镜像kind/base:eucs-v1.0,编译出node镜像kindest/node:eucs-v5.0.0.0,node镜像中包含了Kubernetes所有相关的源码

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_60

上图红框内为编译Kubernetes源码,除了kubelet、kubectl、kubeadm为二进制文件外,其余控制面的组件都为镜像tar包,归档在kubernetes/_output/release-images/amd64路径下

编译过程中会出现如下报错内容,是由于kind构建镜像时必须从外网拉取pause、etcd、coredns镜像至base镜像(kind/base:eucs-v1.0)内,现已将上述所有镜像全部打包至base镜像内,故可以忽略

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_61

构建完成的打印如下

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_flask_62

现在可以使用上述镜像创建集群

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_63

进入集群(通过docker exec -it <docker_name> bash)可以看到自定义的代码已经生效

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_64

参考资料

https://zhuanlan.zhihu.com/p/482667236

FAQ

kind构建失败,出现无法拉取镜像的问题

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_65

解决方法:

  1. 通过下述方法构建github CI流程,拉取外网镜像后推送至个人docker hub仓库

分享一个很香的k8s.gcr.io Docker镜像拉取方法_k8s 拉取 docker hub 镜像-CSDN博客

  1. 根据Dockerfile的关键字FROM和后续手动build的参数提示获取镜像名称为k8s.gcr.io/build-image/kube-cross:v1.21.0-go1.16.9-buster.0
  2. 拉取镜像到本地后继续执行kind build node-image

Kubernetes编译源码失败,出现无法拉取镜像的问题

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_基础设施_66

解决方法:

  1. 将下述运行所需的镜像拉取到本地
#编译kubernetes源码所需要的镜像
docker pull 10.8.59.115/k8s.gcr.io/build-image/setcap:buster-v2.0.3
docker tag 10.8.59.115/k8s.gcr.io/build-image/setcap:buster-v2.0.3 k8s.gcr.io/build-image/setcap:buster-v2.0.3

docker pull 10.8.59.115/k8s.gcr.io/build-image/go-runner:v2.3.1-go1.16.9-buster.0
docker tag 10.8.59.115/k8s.gcr.io/build-image/go-runner:v2.3.1-go1.16.9-buster.0 k8s.gcr.io/build-image/go-runner:v2.3.1-go1.16.9-buster.0

docker pull 10.8.59.115/k8s.gcr.io/build-image/debian-iptables:buster-v1.6.5
docker tag 10.8.59.115/k8s.gcr.io/build-image/debian-iptables:buster-v1.6.5 k8s.gcr.io/build-image/debian-iptables:buster-v1.6.5

docker pull 10.8.59.115/k8s.gcr.io/build-image/kube-cross:v1.21.0-go1.16.9-buster.0
docker tag 10.8.59.115/k8s.gcr.io/build-image/kube-cross:v1.21.0-go1.16.9-buster.0 k8s.gcr.io/build-image/kube-cross:v1.21.0-go1.16.9-buster.0
  1. 修改编译的脚本build/lib/release.sh,将永远拉取镜像的默认值改为n

【GitOps】一文读懂|GitOps全景架构与最佳实践,快收藏!_git_67