Flux 如何监听镜像标签更新实现 GitOps

WilliamGates
发布于 2023-11-24 11:33
浏览
0收藏

前面我们在使用 Flux 进行 Gitops 实践的过程中,我们每次都需要在 CI 流水线去手动更新 Git 代码仓库中的 Values 文件的镜像版本,这样就会比较麻烦,和 Argo CD 类似,Flux 也提供了一个 ​​Image Automation​​ 控制器的功能。

原理

当新的容器镜像可用时,​​image-reflector-controller​​​ 和 ​​image-automation-controller​​ 可以协同工作来更新 Git 存储库。

  • ​image-reflector-controller​​ 扫描镜像存储库并反射到 Kubernetes 资源中的镜像元数据。
  • ​image-automation-controller​​ 根据扫描的最新镜像更新 YAML 文件,并将更改提交到指定的 Git 存储库。

Flux 如何监听镜像标签更新实现 GitOps-鸿蒙开发者社区

但是需要注意的是默认情况下 Flux 不会自动安装 ​​image-reflector-controller​​​ 和 ​​image-automation-controller​​​,所以我们需要手动安装这两个控制器,可以通过 ​​--components-extra​​ 参数来指定要安装的组件,如下所示:

# gh-token 为 GitLab PAT(例如:glpat-p17PB_dgWEDfWiUSAqUo)
flux bootstrap git \
  --url=http://gitlab.k8s.local/cnych/flux \
  --username=cnych \
  --password=<gh-token> \
  --token-auth=true \
  --path=clusters/my-cluster \
  --allow-insecure-http=true \
  --components-extra image-reflector-controller,image-automation-controller

这两个控制器安装完成后,我们就可以使用 Flux 配置容器镜像扫描和部署发布了。对于容器镜像,可以将 Flux 配置为:

  • 扫描镜像仓库并获取镜像标签
  • 根据定义的策略(semver、calver、regex)选择最新的标签
  • 替换 Kubernetes 清单中的标签(YAML 格式)
  • 检出分支、提交并将更改推送到远程 Git 存储库
  • 在集群中应用更改并变更容器镜像

对于生产环境,此功能允许你自动部署应用程序补丁(CVE 和错误修复),并在 Git 历史记录中保留所有部署的记录。

下面我们来思考下对于生产环境和测试环境 CI/CD 的工作流是怎样的?

生产环境 CI/CD 工作流

  • DEV:将错误修复推送到应用程序存储库
  • DEV:修改补丁版本并发布,例如​​v1.0.1​
  • CI:构建并推送标记为​​registry.domain/org/app:v1.0.1​​ 的容器镜像
  • CD:从镜像仓库中提取最新的镜像元数据(Flux 镜像扫描)
  • CD:将应用程序清单中的镜像标签更新为​​v1.0.1​​(Flux 集群到 Git 调谐)
  • CD:将​​v1.0.1​​ 部署到生产集群(Flux Git 到集群调谐)

对于 Staging 环境,此功能允许你部署分支的最新版本,而无需在 Git 中手动编辑应用程序部署清单。

Staging 环境 CI/CD 工作流

  • DEV:将代码更改推送到应用程序存储库主分支
  • CI:构建并推送标记为​​${GIT_BRANCH}-${GIT_SHA:0:7}-$(date +%s)​​ 的容器镜像
  • CD:从镜像仓库中提取最新的镜像元数据(Flux 镜像扫描)
  • CD:将应用程序清单中的镜像标记更新为​​main-2d3fcbd-1611906956​​(Flux 集群到 Git 调谐)
  • CD:将​​main-2d3fcbd-1611906956​​ 部署到 Staging 环境集群(Flux Git 到集群调谐)

示例

比如这里的示例使用的镜像地址为 ​​cnych/devops-demo​​​,我们可以先创建一个 ​​ImageRepository​​ 来告诉 Flux 扫描哪个镜像仓库以查找新标签:

$ flux create image repository k8s-demo \
--namespace=default \
--image=cnych/devops-demo \
--interval=30s \
--export > k8s-demo-registry.yaml

不知道 ​​ImageRepository​​​ 如何编写,我们可以通过上面的 ​​flux​​ 命令来帮我们生成即可,上面的命令会生成如下所示的资源清单文件:

apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: k8s-demo
  namespace: default
spec:
  image: docker.io/cnych/devops-demo
  interval: 30s

对于私有镜像,可以使用 ​​kubectl create secret docker-registry​​​ 在与 ​​ImageRepository​​ 相同的命名空间中创建 Kubernetes Secret。同样使用的密码需要通过 Docker Hub 后台创建 Access Token 来获取。

Flux 如何监听镜像标签更新实现 GitOps-鸿蒙开发者社区

然后,可以通过引用 ​​ImageRepository​​ 中的 Kubernetes Secret 来配置 Flux 以使用凭据:

kind: ImageRepository
spec:
  secretRef:
    name: regcred

然后就可以应用上面的资源清单文件了:

$ kubectl apply -f k8s-demo-registry.yaml
imagerepository.image.toolkit.fluxcd.io/k8s-demo created
$ kubectl get imagerepository
NAME       LAST SCAN              TAGS
k8s-demo   2023-09-24T03:11:05Z   21

可以看到 ​​ImageRepository​​​ 对象已经创建成功了,并且扫描到了 21 个镜像标签。当然我们也可以通过 ​​kubectl describe imagerepository k8s-demo​​​ 命令来查看 ​​ImageRepository​​ 的各种状态:

$ kubectl describe imagerepository k8s-demo
# ......
Status:
  Canonical Image Name:  index.docker.io/cnych/devops-demo
  Conditions:
    Last Transition Time:  2023-09-24T03:11:05Z
    Message:               successful scan: found 21 tags
    Observed Generation:   1
    Reason:                Succeeded
    Status:                True
    Type:                  Ready
  Last Scan Result:
    Latest Tags:
      main-25de3e1a-1695469144
      f4fc1ec
      e0b13c5
      dc9922d
      d9b7083
      d3fc3d5
      ae94ccf
      a6268b3
      9813cf9
      8ede4fb
    Scan Time:  2023-09-24T03:12:09Z
    Tag Count:  21
  Observed Exclusion List:
    ^.*\.sig$
  Observed Generation:  1
Events:
  Type    Reason     Age                From                        Message
  ----    ------     ----               ----                        -------
  Normal  Succeeded  87s                image-reflector-controller  successful scan: found 21 tags
  Normal  Succeeded  23s (x2 over 55s)  image-reflector-controller  no new tags found, next scan in 30s

如果你要告诉 Flux 在过滤标签时使用哪个 ​​semver​​​ 版本范围的标签,则开源创建一个 ​​ImagePolicy​​​ 对象。比如选择标签为 ​​${GIT_BRANCH}-${GIT_SHA:0:7}-$(date +%s)​​​ 的最新主分支构建,则可以使用以下 ​​ImagePolicy​​:

kind: ImagePolicy
spec:
  filterTags:
    pattern: "^main-[a-fA-F0-9]+-(?P<ts>.*)"
    extract: "$ts"
  policy:
    numerical:
      order: asc

选择最新的稳定版本(semver):

kind: ImagePolicy
spec:
  policy:
    semver:
      range: ">=1.0.0"

选择 1.x 范围内的最新稳定补丁版本 (semver):

kind: ImagePolicy
spec:
  policy:
    semver:
      range: ">=1.0.0 <2.0.0"

选择最新版本,包括预发行版 (semver):

kind: ImagePolicy
spec:
  policy:
    semver:
      range: ">=1.0.0-0"

由于 ​​ImagePolicy​​ 对象的策略只支持三种:

  • ​SemVer​​:语义版本
  • ​Alphabetical​​:字母顺序
  • ​Numerical​​:数字顺序

而前面我们的镜像标签是根据 git commit id 来生成的,不符合这里的规范,所以我们可以更改下镜像的 Tag 生成策略:

def myRepo = checkout scm
def gitCommit = myRepo.GIT_COMMIT.substring(0,8)
def gitBranch = myRepo.GIT_BRANCH
gitBranch = gitBranch.replace("origin/", "")

def unixTime = (new Date().time.intdiv(1000))
def imageTag = "${gitBranch}-${gitCommit}-${unixTime}"

然后我们可以通过如下所示的 ​​ImagePolicy​​ 对象来告诉 Flux 如何过滤镜像标签:

# k8s-demo-image-policy.yaml
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
  name: k8s-demo
spec:
  imageRepositoryRef:
    name: k8s-demo
  filterTags:
    pattern: "^main-[a-fA-F0-9]+-(?P<ts>.*)"
    extract: "$ts"
  policy:
    numerical:
      order: asc

直接应用该资源对象即可:

$ kubectl apply -f k8s-demo-image-policy.yaml
$ kubectl get imagepolicy
NAME       LATESTIMAGE
k8s-demo   docker.io/cnych/devops-demo:main-25de3e1a-1695469144

可以看到 ​​ImagePolicy​​​ 对象创建后过滤到了最新的镜像标签为 ​​main-25de3e1a-1695469144​​。

接下来我们就可以创建一个 ​​ImageUpdateAutomation​​​ 对象来告诉 Flux 将镜像更新写入哪个 Git 存储库,但是这里还有一个问题就是我们的应用是通过 Helm Chart 来部署的,​​ImageUpdateAutomation​​ 如何知道要把我们更新后的镜像标签写入到哪个 Values 文件中呢?写入到哪个位置呢?

这就需要用到 marker 功能了,用来标记 Image Automation Controller 自动更新的位置。比如我们这里使用的是 Helm Chart 来部署应用,决定使用哪个版本的镜像是通过 ​​my-values.yaml​​ 这个 Values 文件来指定的:

# my-values.yaml
image:
  repository: cnych/devops-demo
  tag: d3fc3d5
  pullPolicy: IfNotPresent
  imagePullSecrets:
    - name: docker-auth
# ......

所以我们需要在该文件中添加一个 marker 来告诉 Flux 将镜像标签写入到哪个位置,这个镜像策略的 marker 标记的格式有如下几种:

  • ​{"$imagepolicy": "<policy-namespace>:<policy-name>"}​
  • ​{"$imagepolicy": "<policy-namespace>:<policy-name>:tag"}​
  • ​{"$imagepolicy": "<policy-namespace>:<policy-name>:name"}​

这些标记作为注解内联放置在目标 YAML 中,​​Setter​​ 策略是指 Flux 可以在调谐期间找到并替换的 kyaml setter。

我们这里重新修改下 ​​my-values.yaml​​ 文件,添加上 marker 标记:

# my-values.yaml
image:
  repository: cnych/devops-demo # {"$imagepolicy": "default:k8s-demo:name"}
  tag: d3fc3d5 # {"$imagepolicy": "default:k8s-demo:tag"}
  pullPolicy: IfNotPresent
  imagePullSecrets:
    - name: docker-auth
# ......

我们在上面的 Values 文件中的 ​​image.repository​​​ 和 ​​image.tag​​ 字段添加了 marker 标记,分别使用的是上面创建的 ImagePolicy 对象的 name 和 tag,这样 Flux 就知道要将镜像标签写入到哪个位置了。

接下来我们再创建一个 ​​ImageUpdateAutomation​​ 对象来告诉 Flux 将镜像更新写入哪个 Git 存储库,同样开源使用 flux 命令来创建:

flux create image update k8s-demo \
--namespace=default \
--interval=1m \
--git-repo-ref=k8s-demo \
--git-repo-path="./helm/my-values.yaml" \
--checkout-branch=main \
--push-branch=main \
--author-name=fluxcdbot \
--author-email=fluxcdbot@youdianzhishi.com \
--commit-template="{{range .Updated.Images}}{{println .}}{{end}}" \
--export > k8s-demo-automation.yaml

该命令生成的 ​​ImageUpdateAutomation​​ 对象如下所示:

---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: k8s-demo
  namespace: default
spec:
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        email: fluxcdbot@youdianzhishi.com
        name: fluxcdbot
      messageTemplate: "{{range .Updated.Images}}{{println .}}{{end}}"
    push:
      branch: main
  interval: 1m0s
  sourceRef:
    kind: GitRepository
    name: k8s-demo
  update:
    path: ./helm/my-values.yaml
    strategy: Setters # 指定如何对 git 存储库进行更新,目前只有 Setters 一种策略

同样直接更新该资源对象即可:

$ kubectl apply -f k8s-demo-automation.yaml
imageupdateautomation.image.toolkit.fluxcd.io/k8s-demo created
$ kubectl get imageupdateautomation
NAME       LAST RUN
k8s-demo   2023-09-23T10:58:06Z

现在我们去修改我们的应用程序代码,然后提交,通过 Jenkins Pipeline 构建并推送镜像到镜像仓库即可,正常情况下 ImageRepository 对象会自动扫描到新的镜像标签,然后 ImagePolicy 对象会过滤到最新的镜像标签,最后通过 ImageUpdateAutomation 对象会自动将镜像标签更新到 Git 代码仓库中的 Values 文件中,然后 Flux 就会自动更新应用了。

$ kubectl describe imageupdateautomation k8s-demo
# ......
Status:
  Conditions:
    Last Transition Time:    2023-09-24T03:34:45Z
    Message:                 no updates made; last commit 7bcce72 at 2023-09-24T03:37:48Z
    Reason:                  ReconciliationSucceeded
    Status:                  True
    Type:                    Ready
  Last Automation Run Time:  2023-09-24T03:38:45Z
  Last Push Commit:          7bcce72bf66268c6df9800b30dd2877091f8116b
  Last Push Time:            2023-09-24T03:37:48Z
  Observed Generation:       1
Events:
  Type    Reason  Age    From                         Message
  ----    ------  ----   ----                         -------
  Normal  info    4m11s  image-automation-controller  committed and pushed commit '62263c95f9ecc727939831d4905f65289ebc4ebf' to branch 'main'
docker.io/cnych/devops-demo:main-25de3e1a-1695469144
  Normal  info  67s  image-automation-controller  committed and pushed commit '7bcce72bf66268c6df9800b30dd2877091f8116b' to branch 'main'
docker.io/cnych/devops-demo:main-e21d5f90-1695526572

从上面的事件中可以看到,Flux 已经自动将镜像标签更新到了 Git 代码仓库中的 Values 文件中了,然后 Flux 就会自动更新应用了。当然可以查看 Git 仓库中的提交记录来验证:

Flux 如何监听镜像标签更新实现 GitOps-鸿蒙开发者社区

当然这个 git commit 的信息模板是可以根据自己的需求来定义的,这个模板可以使用 Go Template 和大部分 Sprig 库 的语法,比如:

kind: ImageUpdateAutomation
metadata:
  name: flux-system
spec:
  git:
    commit:
      messageTemplate: |
        Automated image update
        Automation name: {{ .AutomationObject }}

        Files:
        {{ range $filename, $_ := .Updated.Files -}}
        - {{ $filename }}
        {{ end -}}

        Objects:
        {{ range $resource, $_ := .Updated.Objects -}}
        - {{ $resource.Kind | lower }} {{ $resource.Name | lower  }}
        {{ end -}}

        Images:
        {{ range $image, $_ := .Updated.Images -}}
         {{ if contains "1.0.0" $image -}}
          - {{ $image }}
         {{ else -}}
          [skip ci] wrong image
         {{ end -}}        
        {{ end -}}
      author:
        email: fluxcdbot@users.noreply.github.com
        name: fluxcdbot


文章转载自公众号:k8s技术圈

分类
标签
已于2023-11-24 11:33:56修改
收藏
回复
举报
回复
    相关推荐