KubeSphere + Argo CD,實現真正的 GitOps!


來自社區用戶 willqy 的分享

Argo CD 簡介

Argo CD 是用於 Kubernetes 的聲明性 GitOps 持續交付工具,應用程序定義,配置和環境應為聲明性的,並應受版本控制,應用程序部署和生命周期管理應該是自動化、可審核且易於理解。

Argo CD 遵循 GitOps 模式,該模式使用 Git 倉庫作為定義所需應用程序狀態的真實來源。

Argo CD 可在指定的目標環境中自動部署所需的應用程序狀態,應用程序部署可以在 Git 提交時跟蹤對分支,標簽的更新,或固定到清單的特定版本。

官網:https://argoproj.github.io/

Argo CD 架構圖:

Argo CD 被實現為 Kubernetes 控制器,該控制器持續監視正在運行的應用程序,並將當前的活動狀態與所需的目標狀態(在 Git 存儲庫中指定)進行比較。當已部署應用程序的運行狀態偏離目標狀態時將被 Argo CD 視為 OutOfSync。

Argo CD 報告並可視化差異,同時提供了自動或手動將實時狀態同步回所需目標狀態的功能。在 Git 存儲庫中對所需目標狀態所做的任何修改都可以自動應用並同步到指定的目標環境中。

Argo CD 支持的 Kubernetes 配置清單包括 helm charts、kustomize 或純 YAML/json 文件等。

本篇文章涉及內容:

  • 使用 KubeSphere DevOps 實現 CI 部分, CD 部分由 Argo CD 完成;
  • Argo CD 持續監測 Git 倉庫某個目錄下 yaml 文件變動,自動將 yaml 文件部署到 K8s 集群;
  • Argo CD 持續監測 Harbor 鏡像倉庫某個鏡像 tag 變動,自動將最新鏡像部署到 K8s 集群。

基本原理圖:

准備 Git 代碼倉庫

准備 2 個 Git 倉庫,一個源碼倉庫,一個 yaml 文件倉庫,源碼和 yaml 文件分離。

源碼倉庫可參考以下鏈接,離線環境原因,這里選擇第二個示例 spring-demo:

yaml 文件倉庫可參考以下鏈接,這里命名為 argocd-gitops:

yaml 倉庫下創建 javademo 目錄,並創建 2 個簡單的 yaml 文件:

[root@jenkins git]# tree argocd-gitops/
argocd-gitops/
├── javademo
│   ├── javademo-deployment.yaml
│   └── javademo-svc.yaml

javademo-deployment.yaml 示例,當前鏡像 tag 可隨意指定,執行 CI 時會實時替換該參數:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: javademo
spec:
  replicas: 1
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: javademo
  template:
    metadata:
      labels:
        app: javademo
    spec:
      containers:
      - image: 10.39.140.196:8081/apps/javademo:replace
        name: javademo
        ports:
        - containerPort: 8080

javademo-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: javademo
spec:
  type: NodePort
  ports:
  - port: 8012
    targetPort: 8080
  selector:
    app: javademo

部署 Argo CD

Argo CD 有多種部署方式,可以直接部署 yaml 文件.

kubectl create namespace Argo CD
kubectl apply -n Argo CD -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

這里使用 helm 方式部署,可直接指定 Argo CD server service 類型 nodePort:

helm repo add argo https://argoproj.github.io/argo-helm

helm install Argo CD \
  --namespace=Argo CD --create-namespace \
  --set server.service.type=NodePort \
  argo/argo-cd

查看運行的 Pod:

[root@master ~]# kubectl -n Argo CD get pods
NAME                                             READY   STATUS    RESTARTS   AGE
argocd-application-controller-5db8c6f8f9-qnmtr   1/1     Running   0          8h
argocd-dex-server-84b5cbfbc9-fc7rf               1/1     Running   0          8h
argocd-redis-7c7c79dcd9-hjhgr                    1/1     Running   0          8h
argocd-repo-server-5fb9cbb945-9xmc7              1/1     Running   0          8h
argocd-server-8d8cb6488-pjwt4                    1/1     Running   0          8h

如果使用 KubeSphere 部署 Argo CD,首先需要配置 Argo CD helm 倉庫,進入企業空間,選擇應用模板上傳離線 helm chart 包,或在應用倉庫配置公網 helm repo 地址。

完成后進入項目,點擊部署新應用,選擇 Argo CD helm chart 進行部署即可:

安裝 Argo CD CLI

要與 Argo CD API Server 進行交互,我們需要安裝 CLI 命令:

wget https://github.com/argoproj/argo-cd/releases/download/v1.7.10/argocd-linux-amd64
cp argocd-linux-amd64 /usr/local/bin/Argo CD
chmod +x /usr/local/bin/Argo CD

Argo CD version

如果上面 Argo CD 使用 yaml 方式部署,修改 serivce 類型為 nodeport,以便訪問 Argo CD API Server

kubectl patch svc argocd-server -n Argo CD -p '{"spec": {"type": "NodePort"}}'

查看 Argo CD server service,記錄 nodeport 信息:

[root@master ~ ]# kubectl -n Argo CD get svc
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
argocd-dex-server       ClusterIP   10.99.232.27     <none>        5556/TCP,5557/TCP,5558/TCP   5d
argocd-metrics          ClusterIP   10.107.37.4      <none>        8082/TCP                     5d
argocd-redis            ClusterIP   10.106.160.6     <none>        6379/TCP                     5d
argocd-repo-server      ClusterIP   10.100.101.100   <none>        8081/TCP,8084/TCP            5d
argocd-server           NodePort    10.106.141.243   <none>        80:31195/TCP,443:32079/TCP   5d
argocd-server-metrics   ClusterIP   10.109.81.234    <none>        8083/TCP                     5d

Argo CD 默認登錄用戶為 admin,初始密碼為 argocd-server Pod 名稱,獲取 Pod 名稱

podName=`kubectl get pods -n Argo CD -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2`

使用 Argo CD CLI 登錄,以 nodeIP 和 nodePort 作為 Argo CD server 登錄地址:

Argo CD login 10.39.140.248:31195 --username admin --password $podName

修改默認密碼:

Argo CD account update-password \
  --current-password $podName \
  --new-password Argo CD@123

瀏覽器登錄 Argo CD UI:

https://10.39.140.248:31195

部署 Argo CD 應用

登陸 Argo CD UI 后,選擇 NEW APP 創建 application,選擇 EDIT AS AYML:

粘貼以下內容,SAVE 后點擊左上 CREATE,當然也可以直接使用 kubectl apply 命令執行以下內容,效果相同。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: javademo
  namespace: Argo CD
  finalizers:
    - resources-finalizer.Argo CD.argoproj.io
spec:
  project: default
  source:
    path: javademo
    repoURL: http://10.39.140.196:10080/gogs/argocd-gitops.git
    targetRevision: HEAD
  destination:
    namespace: apps
    server: https://kubernetes.default.svc
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
    - Validate=false
    - CreateNamespace=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

參數說明:

  • metadata 字段:指定了應用名稱,命名空間必須指定 Argo CD,添加 finalizers 字段可在刪除應用時級聯刪除相關 k8s 資源;
  • source 字段:指定了 yaml 文件所在 git 倉庫 URL,及要監測的 yaml 文件存放目錄,該目錄下文件有任何變更 Argo CD 都會自動將其更新部署到 k8s 集群;
  • destination 字段:指定監測的 yaml 文件要部署到哪個 k8s 集群及哪個命名空間下;
  • syncPolicy 字段:指定自動同步策略和頻率,不配置時需要手動觸發同步。

另外如果使用私有 Git 倉庫,需要創建憑證,這里的憑證是 Argo CD 訪問 yaml 文件 Git 倉庫的憑證:

等效的 Argo CD cli 命令:

Argo CD repo add http://10.39.140.196:10080/gogs/argocd-gitops --username gogs --password xxxxxx

創建后 Argo CD 會自動將 Git 倉庫 javademo 目錄下的 yaml 文件部署到 K8s 集群,此時應用無法正常啟動,因為 yaml 文件中的鏡像 tag 還不存在,拉取鏡像會失敗:

也可以使用 Argo CD CLI 查看部署的應用:

[root@master ~]# Argo CD app get javademo
Name:               javademo
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          apps
URL:                https://10.39.140.248:31195/applications/javademo
Repo:               http://10.39.140.196:10080/gogs/argocd-gitops.git
Target:             HEAD
Path:               javademo
SyncWindow:         Sync Allowed
Sync Policy:        Automated (Prune)
Sync Status:        Synced to HEAD (1b96380)
Health Status:      Progressing

GROUP  KIND        NAMESPACE  NAME      STATUS  HEALTH       HOOK  MESSAGE
       Service     apps       javademo  Synced  Healthy            service/javademo unchanged
apps   Deployment  apps       javademo  Synced  Progressing        deployment.apps/javademo unchanged

在 KubeSphere UI 查看 Pod 狀態,一直在重試拉取鏡像:

使用 kubectl 命令查看,狀態為 ImagePullBackOff :

[root@master ~]# kubectl -n apps get pods
NAME                       READY   STATUS             RESTARTS   AGE
javademo-64d46bff8-6dgjn   0/1     ImagePullBackOff   0          13m

[root@master ~]# kubectl -n apps get svc
NAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
javademo   ClusterIP   10.111.56.180   <none>        8012/TCP   33m

KubeSphere 創建流水線

創建 CI 流水線,使用 KubeSphere DevOps 完成源碼編譯、鏡像構建並推送到 Harbor 倉庫,最后以 git commit 方式更新 yaml 倉庫中 image 字段。

由於此時 Argo CD 持續監測 yaml 倉庫配置文件變動,當 CI 部分執行 git push 時便會觸發 Argo CD 更新 yaml 文件到 K8s 集群。

在 KubeSphere DevOps 工程下創建一條空流水線,命名為 javademo,進入流水線,選擇編輯 Jenkinsfile,復制以下內容:

pipeline {

    environment {
        GIT_URL='http://10.39.140.196:10080/gogs/spring-demo.git'
        GIT_CREDENTIAL_ID = 'git-id'
        GIT_BRANCH = 'master'
        REGISTRY = '10.39.140.196:8081/apps/javademo'
        REGISTRY_CREDENTIAL_ID = 'harbor-id'
    }

    agent {
        node {
            label 'maven'
        }
    }

    stages {

        stage('SCM Checkout') {
            steps {
                git branch: "${GIT_BRANCH}", credentialsId: "${GIT_CREDENTIAL_ID}", url: "${GIT_URL}"
            }
        }

        stage('source build') {
            steps {
                container('maven') {
                    sh 'mvn clean package'

                }
            }
        }

        stage('docker build & push') {
            steps {
                script {
                    env.COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    env.TIMESTRAP = sh(returnStdout: true, script: 'date +%Y%m%d%H%M%S').trim()
                    env.DOCKER_TAG = "dev_${TIMESTRAP}_${COMMIT_ID}_${BUILD_NUMBER}"
                }
                container('maven') {
                    withCredentials([usernamePassword(passwordVariable : 'DOCKER_PASSWORD' ,usernameVariable : 'DOCKER_USERNAME' ,credentialsId : "$REGISTRY_CREDENTIAL_ID" ,)]) {
                        sh 'docker build -t $REGISTRY:$DOCKER_TAG .'
                        sh 'echo "$DOCKER_PASSWORD" | docker login $REGISTRY -u "$DOCKER_USERNAME" --password-stdin'
                        sh 'docker push $REGISTRY:$DOCKER_TAG'
                    }
                }
            }
        }

        stage('update docker tag') {
            environment {
                BUILD_USER = 'admin'
                BUILD_USER_EMAIL = 'admin@Argo CD.com'
                YAML_REPO_URL='http://${username}:${password}@10.39.140.196:10080/gogs/argocd-gitops.git'
            }

            steps {
                withCredentials([usernamePassword(passwordVariable : 'password' ,usernameVariable : 'username' ,credentialsId : "$GIT_CREDENTIAL_ID" ,)]) {
                    sh """
                        git config --global user.name "$BUILD_USER"
                        git config --global user.email "$BUILD_USER_EMAIL"
                        git clone ${YAML_REPO_URL} && cd argocd-gitops
                        sed -i "s#$REGISTRY.*#${REGISTRY}:${DOCKER_TAG}#g" javademo/javademo-deployment.yaml
                        git add -A && git commit -m "update tag: ${DOCKER_TAG}" && git push ${YAML_REPO_URL}
                    """
                }
            }
        }
    }
}

注意修改相關參數,流水線中引用了 2 個憑證:

  • GIT_CREDENTIAL_ID 為內網 gogs git 倉庫賬號密碼
  • REGISTRY_CREDENTIAL_ID 為 harbor 倉庫賬號密碼

運行流水線前需要在 DevOps 工程下提前創建好相關憑證,后續需要在 jenkinsfile 中引用。

最終流水線如下,點擊運行,等待流水線執行完成,查看狀態為成功:

查看流水線構建日志,可以看到執行了以下過程,其中最后 update docker tag 步驟,執行了 2 個關鍵操作,sed 命令替換鏡像 tag,然后執行 git push 更新 yaml 倉庫。

查看推送到 Harbor 倉庫的鏡像:

Argo CD 監測到 yaml 文件變更后更新至 K8s 集群:

Argo CD UI 查看使用的鏡像:

登錄 KubeSphere UI 查看應用狀態為運行中:

在 Git 倉庫直接修改 yaml 文件配置,同樣能夠觸發 Argo CD 同步,例如將 service 類型改為 nodePort:

等待 Argo CD 自動同步配置更新到 K8s 集群,瀏覽器以 nodeport 方式訪問 java web 應用:

部署 Argo CD Image Updater

上面演示了基於 Git 倉庫變更作為應用部署的事實來源,下面演示另一種方式,以鏡像 tag 變更作為應用部署的事實來源。Argo CD 提供了Argo CD Image Updater小工具,用於實現該操作。

Argo CD image updater 是一種自動更新由 Argo CD 管理的 Kubernetes 工作負載容器鏡像的工具。

該工具目前還在開發中,並且有以下特性和局限性:

  • 只能更新由 Argo CD 管理並由HelmKustomize工具生成的應用程序的鏡像;
  • 對廣泛使用的容器倉庫的默認支持:dockerhub、harbor 私有鏡像倉庫等;
  • 能夠使用匹配器功能過濾鏡像倉庫返回的標簽列表;
  • 鏡像拉取 secrets 必須存在於 Argo CD Image Updater 在其中運行(或可以訪問)的同一 Kubernetes 群集中。當前不可能從其他集群中獲取這些 secrets。
  • 在當前版本中,Argo CD Image Updater 不會將任何更改寫回到 Git 存儲庫。

官方文檔:

https://argocd-image-updater.readthedocs.io/en/stable/

Argo CD Image Updater 部署略顯繁瑣,部署操作如下:

1、在 Argo CD 中創建本地用戶

創建 Argo CD 鏡像更新程序需要訪問 Argo CD API Server 的憑據,使用一個 image-updater 具有適當 API 權限的帳戶,將以下用戶定義添加到 argocd-cm:

# kubectl -n Argo CD edit cm argocd-cm
data:
  accounts.image-updater: apiKey

為用戶創建訪問令牌,將令牌的值復制到某個地方,稍后將需要它。

Argo CD account generate-token --account image-updater --id image-updater

2、在 Argo CD 中授予 RBAC 權限

image-updater用戶配置適當的 RBAC 權限,Argo CD Image Updater 需要應用程序的updateget權限。

# kubectl -n Argo CD edit cm argocd-rbac-cm
data:
  policy.default: role:readonly
  policy.csv: |
    p, role:image-updater, applications, get, */*, allow
    p, role:image-updater, applications, update, */*, allow
    g, image-updater, role:image-updater

3、 安裝 Argo CD Image Updater

yaml 文件下載:https://github.com/argoproj-labs/argocd-image-updater/tree/master/manifests

kubectl create ns argocd-image-updater
kubectl apply -n argocd-image-updater -f manifests/install.yaml

4、 配置鏡像倉庫

即使您不打算使用私有鏡像倉庫,您也需要至少配置一個 empty registries.conf

# kubectl -n argocd-image-updater edit cm argocd-image-updater-config
data:
  registries.conf: ""

沒有此條目argocd-image-updater Pod 將無法啟動。

如果使用私有鏡像倉庫可參考以下配置,以 Harbor 鏡像倉庫為例:

data:
  Argo CD.insecure: "true"
  log.level: debug
  registries.conf: |
    registries:
    - name: harbor
      api_url: http://10.39.140.196:8081
      prefix: 10.39.140.196:8081
      ping: yes
      insecure: yes

5、 配置 API 訪問令牌密鑰

當從清單安裝到 Kubernetes 集群時,Argo CD Image Updater 將從名為Argo CD_TOKEN的環境變量中讀取訪問 Argo CD API 所需的令牌,該環境變量是從名為 Argo CD.token 的 secret 字段中設置的argocd-image-updater-secret

Argo CD.token 的值應設置為您上面生成的訪問令牌的 base64 編碼值。作為一種捷徑,您可以使用 kubectl 生成密鑰,並將其應用於現有資源:

YOUR_TOKEN=xxx
kubectl create secret generic argocd-image-updater-secret \
  --from-literal Argo CD.token=$YOUR_TOKEN --dry-run -o yaml |
  kubectl -n argocd-image-updater apply -f -

更改后,必須重新啟動 argocd-image-updater Pod,即運行

kubectl -n argocd-image-updater rollout restart deployment argocd-image-updater

新建 yaml 倉庫 Kustomize 文件

由於 image updater 僅支持 helm 或 Kustomize 類型 yaml,這里新建一個基於 Kustomize 的 yaml 目錄,修改 yaml 中的參數不要與之前的沖突即可:

[root@jenkins git]# tree argocd-gitops/kustomize-javademo/
argocd-gitops/kustomize-javademo/
├── javademo-deployment.yaml
├── javademo-svc.yaml
└── kustomization.yaml

javademo-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: javademo-tag
spec:
  replicas: 1
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: javademo-tag
  template:
    metadata:
      labels:
        app: javademo-tag
    spec:
      containers:
      - image: 10.39.140.196:8081/apps/javademo:replace
        name: javademo-tag
        ports:
        - containerPort: 8080

javademo-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: javademo-tag
spec:
  ports:
  - port: 8012
    targetPort: 8080
  selector:
    app: javademo-tag

kustomization.yaml

amePrefix: kustomize-

resources:
- javademo-deployment.yaml
- javademo-svc.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

登錄 Argo CD UI 新建一個 Argo CD 應用,和之前相比增加了 annotations 參數,指定要監測的鏡像地址,更新策略為 latest,另外修改了 source path:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    argocd-image-updater.argoproj.io/image-list: javademo=10.39.140.196:8081/apps/javademo
    argocd-image-updater.argoproj.io/javademo.update-strategy: latest
  name: javademo-tag
  namespace: Argo CD
  finalizers:
    - resources-finalizer.Argo CD.argoproj.io
spec:
  destination:
    namespace: apps
    server: https://kubernetes.default.svc
  project: default
  source:
    path: kustomize-javademo
    repoURL: http://10.39.140.196:10080/gogs/argocd-gitops.git
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
    - Validate=false
    - CreateNamespace=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

登錄 KubeSphere UI 重新創建一條 CI 流水線,刪除 update docker tag 步驟即可,已經不需要基於 git push 來觸發應用部署了:

pipeline {

    environment {
        GIT_URL='http://10.39.140.196:10080/gogs/spring-demo.git'
        GIT_CREDENTIAL_ID = 'git-id'
        GIT_BRANCH = 'master'
        REGISTRY = '10.39.140.196:8081/apps/javademo'
        REGISTRY_CREDENTIAL_ID = 'harbor-id'
    }

    agent {
        node {
            label 'maven'
        }
    }

    stages {

        stage('SCM Checkout') {
            steps {
                git branch: "${GIT_BRANCH}", credentialsId: "${GIT_CREDENTIAL_ID}", url: "${GIT_URL}"
            }
        }

        stage('source build') {
            steps {
                container('maven') {
                    sh 'mvn clean package'

                }
            }
        }

        stage('docker build & push') {
            steps {
                script {
                    env.COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    env.TIMESTRAP = sh(returnStdout: true, script: 'date +%Y%m%d%H%M%S').trim()
                    env.DOCKER_TAG = "dev_${TIMESTRAP}_${COMMIT_ID}_${BUILD_NUMBER}"
                }
                container('maven') {
                    withCredentials([usernamePassword(passwordVariable : 'DOCKER_PASSWORD' ,usernameVariable : 'DOCKER_USERNAME' ,credentialsId : "$REGISTRY_CREDENTIAL_ID" ,)]) {
                        sh 'docker build -t $REGISTRY:$DOCKER_TAG .'
                        sh 'echo "$DOCKER_PASSWORD" | docker login $REGISTRY -u "$DOCKER_USERNAME" --password-stdin'
                        sh 'docker push $REGISTRY:$DOCKER_TAG'
                    }
                }
            }
        }
    }
}

查看流水線日志,鏡像成功推送到 Harbor 倉庫:

Harbor 倉庫鏡像 tag 更新,Argo CD image updater 自動將最新 tag 更新到 K8s 集群。

查看鏡像 tag:

以后每次 Harbor 倉庫生成最新鏡像,Argo CD 都會自動將其更新到 K8s 集群。

本文由博客一文多發平台 OpenWrite 發布!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM