LoginSignup
0
0

Argo CDによるGitOps

Last updated at Posted at 2023-08-25

Argo CDを使用したGitOpsの設計、導入を行ったので概要を書いていきます。

使用したツール、環境
manifest管理: Kustomize
GitOpsツール: Argo CD, Argo CD Image Updater
CI/CD: GitHub Actions
Container Registry: GitHub Container Registry
monorepo: Nx
k8s: GKE

全体構成図

リポジトリはk8sマニフェスト用とアプリケーション用(モノレポ)の2つです。

gitops.jpg

モノレポappsのimageのBuild & Push

mainブランチに変更があった時(PRをマージした時)とReleasesをつくった時にGitHua Actionsが走る様になっています。

  1. 変更のあるappを取得
    A. PRマージ時
    npx nx affected --target=build-image --base=origin/main~1 --head=HEAD
    nx affectedで変更に対して影響のあったappだけを取得しています。

    B. Releases作成時
    npx nx affected --target=build-image --base=$previous_tag --head=$latest_tag
    nx affectedで1つ前のタグと最新のタグの差分を取得しています

  2. matrixを設定
    GitHub Actionsのmatrixを使います。
    取得したapp全てに対してcall-build-and-push-image job経由でimage buildのworkflowを走らせます。

  3. imageをbuild & push
    Build and push image workflowでimageにtagをつけてcontainer registoryにpushします。

set-matrix.yaml
name: Set Matrix

on:
  push:
    branches:
      - 'main'
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+'

jobs:
  set-matrix:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    timeout-minutes: 10

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Get node version
        id: version
        run: |
          node_ver=$( node --version )
          echo "node_version=${node_ver:1}" >> $GITHUB_OUTPUT

      - name: Use the node_modules cache
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ steps.version.outputs.node_version }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-${{ steps.version.outputs.node_version }}

      - name: Install dependencies
        run: npm ci
 
      - name: Get affected nx apps
        if: ${{ github.ref == 'refs/heads/main' }}
        run: npx nx affected --target=build-image --base=origin/main~1 --head=HEAD

      - name: Get affected nx apps (tag)
        if: startsWith(github.ref, 'refs/tags/v')
        run: |
          latest_tag=${{ github.ref_name }}
          previous_tag=$(git describe --abbrev=0 --tags $latest_tag^)
          npx nx affected --target=build-image --base=$previous_tag --head=$latest_tag

      # nx affected --target=build-image で取得したappのリストをmatrixで使える形に変換(ゴリ押し気味なので他の方法を検討中)
      - name: Set matrix
        id: set-matrix
        run: |
          file_path="apps.txt"
          if [[ ! -f "$file_path" ]] || [[ ! -s "$file_path" ]]; then
            echo "matrix={}" >> $GITHUB_OUTPUT
          else
            mapfile -t apps_array < apps.txt
            json_string="{\"app\":["
            for ((i=0; i<${#apps_array[@]}; i++)); do
              json_string+="\"${apps_array[i]}\""
              if [ $i -lt $((${#apps_array[@]} - 1)) ]; then
                json_string+=", "
              fi
            done
            json_string+="]}"
            echo "matrix=${json_string}" >> $GITHUB_OUTPUT
          fi

  call-build-and-push-image:
    name: Call build and push image
    needs: set-matrix
    if: ${{ needs.set-matrix.outputs.matrix != '{}' }}
    strategy:
      matrix: ${{ fromJson(needs.set-matrix.outputs.matrix) }}
    uses: ./.github/workflows/build-and-push-image.yaml
    with:
      app: ${{ matrix.app }}
build-and-push-image.yaml
name: Build and push image

on:
  workflow_call:
    inputs:
      app:
        required: true
        type: string

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    timeout-minutes: 10

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Log in to the Container registry
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ inputs.app }}
          tags: |
            type=sha
            type=semver,pattern={{version}}

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          file: tools/${{ inputs.app }}/Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

k8s manifest

Kustomizeを使って環境ごとにディレクトリを分けて管理します。
productionやstagingはbaseのディレクトリ構成とほぼ同じです。

k8s/
├── base/
│   ├── apps/
│   │   ├── app1/
│   │   │   ├── deployment.yaml
│   │   │   ├── service.yaml
│   │   │   ├── ...
│   │   │   └── kustomization.yaml
│   │   ├── app2/
│   │   ├── app3/
│   │   ├── ...
│   │   └── kustomization.yaml
│   ├── hoge/
│   │   ├── huga.yaml
│   │   └── kustomization.yaml
│   └── ...
│
└── overlays/
    ├── production/
    │   ├── ...
    │   └── kustomization.yaml
    └── staging/
        ├── ...
        └── kustomization.yaml

Argo CD Image Updaterによってk8sのimageをアップデート

Argo CD Image Updaterがimageの更新を検知してk8sへ変更を反映させます。

Argo CD Image Updaterがimageの更新を検知して自動でmanifestを更新しにいきます。

ApplicationSetのannotationsの主要な部分は以下のようになります。

staging.yaml
annotations:
  argocd-image-updater.argoproj.io/write-back-method: git
  argocd-image-updater.argoproj.io/1update-strategy: latest
  argocd-image-updater.argoproj.io/ignore-tags: latest
  argocd-image-updater.argoproj.io/allow-tags: regexp:sha-[a-z0-9]+
production.yaml
annotations:
  argocd-image-updater.argoproj.io/write-back-method: git
  argocd-image-updater.argoproj.io/update-strategy: semver
  argocd-image-updater.argoproj.io/ignore-tags: latest
  argocd-image-updater.argoproj.io/git-branch: image-updater{{range .Images}}-{{.Alias}}-{{.NewTag}}{{end}}
argocd-image-updater.argoproj.io/write-back-method

GitOpsをしたかったのでgitです。アプリケーションのマニフェストをもとにパラメータをオーバーライドします。

argocd-image-updater.argoproj.io/ignore-tags

docker/metadata-actionでtag eventの時にlatestをつける設定にしていますが、image updaterには無視させたいのでlatestを指定します。latestタグをつける設定を省けばこちらは不要。

argocd-image-updater.argoproj.io/update-strategy

stagingは最新のimageに更新するようにします。(sha-abc01234 のようなtagをつけるので argocd-image-updater.argoproj.io/allow-tagsも設定)
productionはsemantic versioningが最新のimageに更新するようにします。

argocd-image-updater.argoproj.io/git-branch

productionはmanifestを自動更新したくなかったため、mainとは別にコミットブランチを作成&ブランチへのコミット作成をトリガーにGitHub ActionsでbotがPRを作成するようにしています。

create-pr.yaml
name: Create PR

on:
  push:
    branches:
      - 'image-updater-*'

jobs:
  create-pr:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3
      - name: Create pull request
        run: |
          gh pr create \
          --base main \
          --head ${{ github.ref_name }} \
          --title 'chore: image update' \
          --body 'This is an automated PR for image update.' \
          --reviewer ${{ vars.IMAGE_UPDATE_REVIEWER }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

まとめ

変更などあれば更新していこうと思います。

参考にさせていただいた記事

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0