GitOps: K8S 通过 FluxCD 自动从 GitLab 部署应用

发布时间 2023-12-23 01:55:05作者: moon~light

FluxCD

FluxCD 是一组 K8S 控制器,用于构建 GitOps,可以监控代码库,并进行自动部署


GitLab 和 GitHub

GitLab 和 GitHub 非常相似,都是代码托管平台,都基于 Git,都提供了命令行和 Web 界面,都支持问题跟踪、代码审查和团队协作等功能

GitLab 和 GitHub 也有不同

GitHub 主要面向开源社区,例如非盈利性组织、开源项目、个人开发者等
GitLab 更适合企业内部或私有项目管理,主要针对商业用户,提供了更多功能,比如 CI/CD

GitHub 本身不开源
GitLab 是开源的

GitHub 只能用官方平台
GitLab 既可以用官方平台,也可以自己部署服务器,能自定义配置和扩展,更灵活,能更好的控制和保护代码

GitHub 开发者和项目更多
GitLab 规模相对较小,更多是企业用户在用

都有免费和付费版本,付费版本提供了更高级的功能,如 CI/CD 集成和高级安全特性

开源项目协作可以放 GitHub
企业项目可以用 GitLab,提供了许多额外功能,如持续集成、部署管道和自动化测试,能够更高效地协作和交付项目

如果考虑安全和保护代码,可以自己搭建 GitLab 服务器

此外托管代码也可以用 BitBucket

配置 GitLab

可以创建免费账号做测试用

登录后可以建自己的项目 https://gitlab.com/<group>/<project>,比如 https://gitlab.com/my-test-group/hello-world

或者 https://gitlab.com/<user>/<project>

这里的 <group> 或 <user> 就是 owner,一个 <group> 可以有多个 <user>

为了访问 GitLab API,需要设置个人访问令牌 (PAT) 使得 FluxCD 能访问 GitLab

在 GitLab 的 Settings -> Access Tokens -> Add new token,选择 api 权限,创建 token,其 role 是 owner

记下 Token 的值比如 glpat-98rjF7hMxfJR1Sz5s-Rb

安装 Flux 命令行工具

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

成功后运行一下

$ flux --version
flux version 2.2.1

正常返回说明安装成功

在 K8S 安装 Flux 控制器

Flux CLI 提供了一个 bootstrap 命令在 Kubernetes 集群上部署 Flux 控制器,配置控制器从 Git 存储库同步状态,并将 Flux 清单推送到 Git 存储库,并将 Flux 配置为从 Git 进行自我更新

export GITLAB_TOKEN=glpat-98rjF7hMxfJR1Sz5s-Rb

sudo flux bootstrap gitlab \
  --deploy-token-auth \
  --hostname=gitlab.com \
  --owner=my-test-group \
  --repository=hello-world \
  --branch=main \
  --path=flux/K8S \
  --personal

可能会要求输入 Token,这里 --owner 填 <group> 或 <user>,而 --path 填的是 Flux 清单要推到 hello-world 这个 project 下的哪个目录

export GITLAB_TOKEN=glpat-98rjF7hMxfJR1Sz5s-Rb

$ sudo flux bootstrap gitlab \
>   --deploy-token-auth \
>   --hostname=gitlab.com \
>   --owner=my-test-group \
>   --repository=hello-world \
>   --branch=main \
>   --path=flux/K8S \
>   --personal
Please enter your GitLab personal access token (PAT):
► connecting to https://gitlab.com
► cloning branch "main" from Git repository "https://gitlab.com/my-test-group/hello-world.git"
✔ cloned repository
► generating component manifests
✔ generated component manifests
✔ committed component manifests to "main" ("ac1caf868a910367fa9fa2b8d22db829a112ff5d")
► pushing component manifests to "https://gitlab.com/my-test-group/hello-world.git"
► installing components in "flux-system" namespace
✔ installed components
✔ reconciled components
► checking to reconcile deploy token for source secret
✔ configured deploy token "flux-system-main-flux-system-./flux/K8S" for "https://gitlab.com/my-test-group/hello-world"
► determining if source secret "flux-system/flux-system" exists
► generating source secret
► applying source secret "flux-system/flux-system"
✔ reconciled source secret
► generating sync manifests
✔ generated sync manifests
✔ committed sync manifests to "main" ("e7ab061ee089de66d4ba59a23fe2f9e1be74493c")
► pushing sync manifests to "https://gitlab.com/my-test-group/hello-world.git"
► applying sync manifests
✔ reconciled sync configuration
◎ waiting for GitRepository "flux-system/flux-system" to be reconciled

✔ GitRepsoitory reconciled successfully
◎ waiting for Kustomization "flux-system/flux-system" to be reconciled
✔ Kustomization reconciled successfully
► confirming components are healthy
✔ helm-controller: deployment ready
✔ kustomize-controller: deployment ready
✔ notification-controller: deployment ready
✔ source-controller: deployment ready
✔ all components are healthy

这里安装的几个 controller 的作用

source-controller:监听 Git Repo 的变化,当代码或配置发生变化时,同步相关资源配置
kustomize-controller:当监控的 Kustomize 配置变化时,更新相应的 K8S 资源
helm-controller:当监控的 Helm Chart 变化时,跟新相应的 Helm release
notification-controller:当事件发生时,比如同步成功或失败,可以通知外部系统,支持 webhooks,自定义脚本等

可以通过 --components 参数来指定要安装的组件,但是需要注意最小安装需要 source-controller 和 kustomize-controller

查看 K8S 的 namespace 可以看到新建了一个 flux-system

flux-system          Active   13m

查看这个 namespace 下面的 pods

$ sudo kubectl get pods -n flux-system
NAME                                       READY   STATUS    RESTARTS   AGE
helm-controller-5584c79984-ncx2t           1/1     Running   0          13m
kustomize-controller-689c96979f-mhpc4      1/1     Running   0          13m
notification-controller-7c6d7dd557-xkrcb   1/1     Running   0          13m
source-controller-68858c9b59-hlhq5         1/1     Running   0          13m

还有 secret 被创建

$ sudo kubectl get secrets -n flux-system
NAME                                  TYPE                                  DATA   AGE
default-token-qxzjm                   kubernetes.io/service-account-token   3      14m
flux-system                           Opaque                                2      14m
helm-controller-token-crpbn           kubernetes.io/service-account-token   3      14m
kustomize-controller-token-hqwgm      kubernetes.io/service-account-token   3      14m
notification-controller-token-f9jl2   kubernetes.io/service-account-token   3      14m
source-controller-token-fxm7g         kubernetes.io/service-account-token   3      14m

到 GitLab 上可以看到多了个 flux/K8S/flux-system 目录
https://gitlab.com/my-test-group/hello-world/-/tree/main/flux/K8S/flux-system?ref_type=heads

下面有三个新建的文件

gotk-components.yaml 
gotk-sync.yaml 
kustomization.yaml 

改变这些文件可以触发 K8S 的 FluxCD 自我更新


这些 controller 还提供了一些 CRD (Custom Resource Definition)

$ sudo kubectl get crd | grep fluxcd
NAME                                              CREATED AT
alerts.notification.toolkit.fluxcd.io             2023-12-19T09:25:59Z
buckets.source.toolkit.fluxcd.io                  2023-12-19T09:25:59Z
gitrepositories.source.toolkit.fluxcd.io          2023-12-19T09:25:59Z
helmcharts.source.toolkit.fluxcd.io               2023-12-19T09:25:59Z
helmreleases.helm.toolkit.fluxcd.io               2023-12-19T09:25:59Z
helmrepositories.source.toolkit.fluxcd.io         2023-12-19T09:25:59Z
kustomizations.kustomize.toolkit.fluxcd.io        2023-12-19T09:25:59Z
ocirepositories.source.toolkit.fluxcd.io          2023-12-19T09:25:59Z
providers.notification.toolkit.fluxcd.io          2023-12-19T09:25:59Z
receivers.notification.toolkit.fluxcd.io          2023-12-19T09:25:59Z

提供了一些 kind 给 K8S 的 manifest yaml 文件使用

创建 GitRepository

以部署 spark 为例子,假设 spark namespace 和 spark operator 都已经部署了

建一个 spark-git-repo.yaml 文件

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: spark-git-repo
  namespace: spark
spec:
  url: https://gitlab.com/my-test-group/hello-world.git
  timeout: 60s
  interval: 30s
  ref:
    branch: main
  secretRef:
    name: spark-git-repo
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: spark-git-repo
  namespace: spark
stringData:
  username: <GitLab user or email>
  password: <GitLab password>

这里的 interval: 30s 表示 30s 检查一次 GitLab 看代码库有没有更新
还配置了一个 secret 用于登录 GitLab

部署

$ sudo kubectl apply -f spark-git-repo.yaml -n spark
gitrepository.source.toolkit.fluxcd.io/spark-git-repo created
secret/spark-git-repo created

查看 GitRepository 资源

$ sudo kubectl get GitRepository -n spark
NAME             URL                                                AGE   READY   STATUS
spark-git-repo   https://gitlab.com/my-test-group/hello-world.git   42s   True    stored artifact for revision 'main@sha1:e7ab061ee089de66d4ba59a23fe2f9e1be74493c'

这里 e7ab061ee089de66d4ba59a23fe2f9e1be74493c 是 hello-world.git 最新的 commit id

describe 一下可以看到这个 GitRepository 什么时候做的同步

$ sudo kubectl describe GitRepository spark-git-repo -n spark

......

Events:
  Type    Reason                 Age                 From               Message
  ----    ------                 ----                ----               -------
  Normal  NewArtifact            4m17s               source-controller  stored artifact for commit 'Add Flux sync manifests'
  Normal  GitOperationSucceeded  9s (x8 over 3m46s)  source-controller  no changes since last reconcilation: observed revision 'main@sha1:e7ab061ee089de66d4ba59a23fe2f9e1be74493c'

如果 hello-world.git 有变化,就会自动同步,并更新 commit id

提交 app 代码到 GitLab

创建文件 spark-app.yaml 并提交到 hello-world.git 的 /test/spark 路径

apiVersion: "sparkoperator.k8s.io/v1beta2"
kind: SparkApplication
metadata:
  name: spark-pi
  namespace: spark
spec:
  type: Scala
  mode: cluster
  image: "apache/spark:v3.1.3"
  imagePullPolicy: IfNotPresent
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.12-3.1.3.jar"
  sparkVersion: "3.1.3"
  restartPolicy:
    type: Never
  volumes:
    - name: "volume"
      hostPath:
        path: "/tmp"
        type: Directory
  driver:
    cores: 1
    coreLimit: "1200m"
    memory: "512m"
    labels:
      version: 3.1.3
    serviceAccount: spark
    volumeMounts:
      - name: "volume"
        mountPath: "/tmp"
  executor:
    cores: 1
    instances: 1
    memory: "512m"
    labels:
      version: 3.1.3
    volumeMounts:
      - name: "volume"
        mountPath: "/tmp"

等一会就可以看到 GitRepository 的 commit id 变了

$ sudo kubectl get GitRepository -n spark
NAME             URL                                                AGE   READY   STATUS
spark-git-repo   https://gitlab.com/my-test-group/hello-world.git   14m   True    stored artifact for revision 'main@sha1:18ccc9a220188d3199acbf4028a696ee3567b37a'

describe 可以看到新的 event

$ sudo kubectl describe GitRepository spark-git-repo -n spark

......

Events:
  Type    Reason                      Age                   From               Message
  ----    ------                      ----                  ----               -------
  Normal  NewArtifact                 16m                   source-controller  stored artifact for commit 'Add Flux sync manifests'
  Normal  GitOperationSucceeded       3m48s (x24 over 15m)  source-controller  no changes since last reconcilation: observed revision 'main@sha1:e7ab061ee089de66d4ba59a23fe2f9e1be74493c'
  Normal  NewArtifact                 3m15s                 source-controller  stored artifact for commit 'spark-app.yaml'
  Normal  GarbageCollectionSucceeded  2m44s                 source-controller  garbage collected 1 artifacts
  Normal  GitOperationSucceeded       42s (x5 over 2m43s)   source-controller  no changes since last reconcilation: observed revision 'main@sha1:18ccc9a220188d3199acbf4028a696ee3567b37a'

可以看到新的 commit id 和 commit message

创建 Kustomization

现在 GitRepository spark-git-repo 会自动同步 GitLab 上 hello-world.git 的 /test/spark 代码,但还不会自动部署

自动部署还需要创建 Kustomization 或 HelmRelease

这里以 Kustomization 为例,建一个 spark-kustomization.yaml 文件

apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: spark-kustomization
  namespace: spark
spec:
  interval: 1m
  path: "./test/spark"
  prune: true
  timeout: 2m
  sourceRef:
    kind: GitRepository
    name: spark-git-repo

sourceRef 设置从 spark-git-repo 这个 GitRepository 获取代码

path 表示检查的是这个 GitRepository (前面配置为 https://gitlab.com/my-test-group/hello-world.git) 的 ./test/spark 目录

interval: 1m 表示每分钟检查一次,看 ./test/spark 目录下的代码有没有变化

部署 Kustomization

$ sudo kubectl apply -f spark-kustomization.yaml -n spark
Warning: v1beta2 Kustomization is deprecated, upgrade to v1
kustomization.kustomize.toolkit.fluxcd.io/spark-kustomization created

查看 Kustomization

$ sudo kubectl get Kustomization -n spark
NAME                  AGE   READY   STATUS
spark-kustomization   35s   True    Applied revision: main@sha1:18ccc9a220188d3199acbf4028a696ee3567b37a

describe Kustomization

$ sudo kubectl describe Kustomization spark-kustomization -n spark

Events:
  Type    Reason                   Age   From                  Message
  ----    ------                   ----  ----                  -------
  Normal  Progressing              57s   kustomize-controller  SparkApplication/spark/spark-pi created
  Normal  ReconciliationSucceeded  57s   kustomize-controller  Reconciliation finished in 50.650416ms, next run in 1m0s

因为前面提交了 spark-app.yaml 到 hello-world.git 的 /test/spark 目录,所以 Kustomization 检查到后就 apply 了这个文件

查看 spark 这个 namespace,这个 app 确实被执行了

$ sudo kubectl get pods -n spark
NAME                                         READY   STATUS      RESTARTS   AGE
my-release-spark-operator-6f5bbf76f6-zcnln   1/1     Running     0          6h46m
spark-pi-driver                              0/1     Completed   0          3m10s

再 describe Kustomization

$ sudo kubectl describe Kustomization spark-kustomization -n spark

Events:
  Type    Reason                   Age    From                  Message
  ----    ------                   ----   ----                  -------
  Normal  Progressing              2m18s  kustomize-controller  SparkApplication/spark/spark-pi created
  Normal  ReconciliationSucceeded  2m18s  kustomize-controller  Reconciliation finished in 50.650416ms, next run in 1m0s
  Normal  ReconciliationSucceeded  76s    kustomize-controller  Reconciliation finished in 59.993991ms, next run in 1m0s

可以看到约 1 分钟后又检查了一次,但因为代码没变化,所以没操作

如果提交代码更新了 GitLab 上的 spark-app.yaml 文件,spark-kustomization 又会检查到并重新部署

通常提交的是 kustomization.yaml 文件,这样就可以自动部署很多应用和配置了

tenant namespace

前面的简单测试,flux-cd 和 spark-app 代码都放到了 hello-world 这个 project,实际应用应该放在不同 project 比较合适

同时前面把 GitRepository 和 Kustomization 还有 spark-app 都放到了 spark 这个 namespace 下面,实际应用中可以创建一个名为 tenants 的 namespace 统一放所有的 GitRepository 和 Kustomization,然后不同类型或模块的 app 再放在各自的 namespace 比如 spark namespace、flink namespace