GitLab Runnerによる簡易的なCICDの設計と実装

Sreake事業部

2024.8.29

はじめに

はじめまして。Sreake事業部インターン生の高島です。2023年10月から長期インターン生としてKubernetes関連技術の習得とSRE技術の調査・検証を行っています。普段は、情報系の大学院生で、数値解析に関する研究をしています。

今回も、前記事に引き続き研修内容についてまとめました。本記事では、課題4の「GitLab Runnerを利用してCI/CDをできるようにする」ということをゴールに記載します。前記事をご覧になってからだとなおわかりやすいと思いますので、そちらも一読いただけると幸いです。

学習事項

CI/CDの設計および実装に必要な知識の習得がメインになります。

  • CI/CDの設計方法
  • GitLab RunnerによるCI/CDパイプラインの設定
  • CI/CD上でコンテナレジストリにpushしたイメージの自動デプロイ

最終的な目標を、「アプリのコードを管理するGitLabリポジトリの変更をトリガーに、CI/CDを一通り行う」として実装を進めていきます。

事前知識と準備

本ブログは以下の内容の理解および準備をしているとスムーズに進行できます。

  • CI/CDに関する基礎知識
  • ArgoCDが構築済みの環境
    • 詳しい構築方法に関しては前記事をご覧ください。
  • GitLabにて管理されるCI/CDを行う対象とするアプリケーション
  • アプリのイメージ格納用のコンテナレジストリ
  • Google Cloud に関する知識

CI/CD

CI/CDとは、反復的に変更されるコードに対してビルド、テスト、デプロイといった工程を継続的に行うことです。CI/CDの各工程を定義して、実行する処理をパイプライン処理といい、今回はこれらをGitLab Runnerを用いて実装していきます。

GitLab Runner

GitLab Runnerは、GitLabを利用して管理されているリポジトリに対して、登録したRunnerを利用してCI/CDを行うツールです。Runnerとは、CI/CDとして行う各ジョブを実行するために用意するテスト環境のことです。

使用するサンプルコード

今回、CI/CDの対象として以下のようなWebアプリケーションを利用します。このアプリは8080ポートで動作し、パスセグメントがない場合にWelcome to 3-Shake!と返す機能と、Health Checkができるようになっているものです。また、このアプリを動作させるためのイメージを作成するためのDockerfileを用意しておきます。

アプリ本体

package main

import (
	"fmt"
	"log"
	"net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, "Welcome to 3-Shake!")
	log.Printf("/hello client from %s\n", req.RemoteAddr)
}

func health(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, "{\"status\": \"ok\"}")
}

func main() {
	http.HandleFunc("/", hello)
	http.HandleFunc("/health", health)
	log.Println("start sample server on :8080")
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal(err)
	}
}

動作テスト用コード

package main

import (
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestHello(t *testing.T) {
	req := httptest.NewRequest(http.MethodGet, "/", nil)
	w := httptest.NewRecorder()
	// サーバを起動
	hello(w, req)
	res := w.Result()

	data, err := io.ReadAll(res.Body)
	defer res.Body.Close()
	if err != nil {
		t.Errorf("unable to read response data : %v", err)
	}
	if res.StatusCode != 200 {
		t.Error("HTTP Status code is not 200")
	}
	if string(data) != "Welcome to 3-Shake!\n" {
		t.Error("incorrect response data")
	}
}

func TestHealth(t *testing.T) {
	req := httptest.NewRequest(http.MethodGet, "/health", nil)
	w := httptest.NewRecorder()
	// サーバを起動
	health(w, req)
	res := w.Result()
	defer res.Body.Close()

	if res.StatusCode != 200 {
		t.Error("HTTP Status code is not 200")
	}
}

Dockerfile

FROM golang:1.20.5-alpine3.18 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .

FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/server ./
CMD ["/app/server"]

また、このアプリをHelmを利用してデプロイできるようにHelm Chartを作成しておきます。helm createで簡単なひな形を作成することができます。上記のサンプルコードであれば、ひな形内のvalueファイルのimage部分とservice部分のパラメータを変更すれば動作させることができます。

コンテナレジストリ (Artifact Registry) の構築

作成したアプリをイメージにして保存しておくためのコンテナレジストリを用意します。今回、Google Cloudを利用して構築を進めているためArtifact Registryを利用します。以下のようにdokcer形式のイメージを保存できるように、terraformを利用して構築しておきます。

resource "google_artifact_registry_repository" "my-repo" {
  location      = var.gcp_region
  repository_id = "my-repo"
  description   = "my docker repository"
  format        = "DOCKER"
}

CI/CDの構築

CI/CDパイプラインの設計

まず、大まかな流れとして、 以下のような流れで作成しようと考えています。

  1. commit
    アプリのコード変更をGitLabリポジトリにコミットおよびプッシュ。
  2. test
    変更したコードに問題がないか、正常に動作するかを確認。今回は、アプリのコード以外にterraformによる基盤構築を想定して、そちらについてもテストを行います。ここからがGitLab Runnerでやることになります。
  3. build
    testで確認したコードについて、Dockerfileを元にイメージをビルド。その後、イメージの脆弱性を確認。
  4. push
    pushするコンテナレジストリに対して認証を行い、ビルドしたイメージをコンテナレジストリにプッシュ。
  5. deploy
    プッシュした最新のイメージを本環境にデプロイ。このジョブは、ArgoCDを利用しておこないます。

Runnerの登録

通常、CI/CDジョブを実行するためのRunnerを登録する必要があります。しかし、今回はGitLab.comがホストするリポジトリおよびGitLab Runnerを利用するため、Runnerの登録をする必要はありません。自身の環境でのGitLab Runnerを利用する場合は、公式サイトをご覧ください。

CI/CDの各ジョブは、.gitlab-ci.ymlというファイルで定義します。このファイルはGitLabで管理するリポジトリのプロジェクトのルートディレクトリに置き、以下のような形式で記述していきます。

stages:
	- test
	- build

test-job: # job名は自由
	stage: test
	script:
		- echo "Starting test job"

build-job:
	stage: build
	script:
		- echo "Starting build job"

上記のスクリプトは、CI/CDパイプラインのステージがtest, buildの2つあり、各ステージで実行するジョブがtest-job, build-jobであることを示します。ステージごとに複数のジョブを設定可能で、各ステージ内のジョブは同時並行的に実行されます。

CI/CDジョブのトリガー設定

基本的に、各ジョブはリポジトリに対してプッシュするたびに実行されます。ただし今回、OJTとして課題を進めているため、1つのプロジェクトに複数のアプリが存在しているようなモノレポ開発に近い状態になっています。この状態で、.gitlab-ci.yamlに直接ジョブを追加し続けると、他のOJTに取り組む人がコミットするたびにCI/CDジョブを実施してしまう他、.gitlab-ci.yamlファイルの内容が膨大になってしまう可能性があります。

このため、以下のような構成でCI/CDパイプラインを作成していきます。各CI/CDジョブは、特定の条件下のみトリガーすることができ、それを利用して構成します。

.gitlab-ci.yamlに以下のように各アプリごとのステージを用意し、各アプリごとに固有の.my-gitlab-ci.yamlのようなCI/CDパイプラインを定義するようにする。

include:
	- local: '${APP1_PATH}/.app1-gitlab-ci.yaml'
	- local: '${APP2_PATH}/.my-gitlab-ci.yaml'
	- local: '${APP3_PATH}/.app3-gitlab-ci.yaml'
	...

stages:
	- app1
	- app2
	...

また、各アプリの.my-gitlab-ci.yamlについて以下のように定義し、特定のファイルの変更またはマージリクエストを行う場合にchild-main.yamlに記載する自身のアプリに対するCI/CDジョブをトリガーさせます。

my-child-pipeline:
  stage: app2
  trigger: # トリガーする子パイプライン
    include:
    - local: '${APP2_PATH}/child_main.yaml'
  rules: # このジョブのトリガー条件
    - changes:
	    - ${APP2_PATH}/*
	    - ${APP2_PATH}/**/*
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

子パイプラインとして自身のアプリについての具体的なCI/CDジョブを定義するchild-main.yamlは、CI/CDジョブの基本的なコード で述べたようにしてジョブを追加していきます。

注意点として、今回は便宜上親子パイプラインを利用して構成していますが、本番開発でこのような設計が適さない可能性があります。下記のページによると、親子パイプラインは、モノレポ開発などの独立したコンポーネントが多いプロジェクトに適しており、単純なプロジェクトでは、全ての設定を一か所に記載するような方法が適しているようです。

参考:GitLab公式 “Pipeline architecture

testの実装

作成したアプリの動作確認から行います。アプリはgoで作られており、動作確認も同言語のため、goコマンドを使用できるようなイメージを利用して以下のようにtestジョブを定義します。

test-job:
	stage: test
	image: golang:1.22
	script:
		- echo "Running tests..."
		- cd ${APP2_PATH}
		- go test

terraformのtestでは、tflintを利用してコードの静的解析を行います。

terraform-lint:
	stage: test
  image:
    name: hashicorp/terraform:1.7
    entrypoint: [""] # entrypointの命令を消去
  before_script:
    - apk update && apk add curl bash
    - curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash
  script:
    - echo "Terraform lint..."
    - tflint --chdir=${TERRAFORM_PATH}

ベースイメージにはtflintがインストールされていないため、before_scriptを利用してインストールを行います。before_scriptとは、ジョブの前に実行されるscriptです。

注意点として、今回terraformによるリソースの多重生成や余分なリソース生成を防ぐために、リントを行う程度のtestでしかジョブを作成していません。下記のページによると、本番開発では通常のアプリと同様に適切なCI/CDパイプラインを考えていく必要があるようです。

参考:AWS公式 “AWS を使用して Terraform 設定を検証する CI/CD パイプラインを作成する CodePipeline

buildの実装

testが正常に終了することが確認できたので、次にアプリケーションのイメージをビルドしていきます。

イメージのビルド後、イメージに脆弱性がないかを確認します。コンテナイメージの脆弱性診断ツールであるtrivyを利用しています。

build-job:
  stage: build
  image: docker:26.0
  services:
    - docker:26.0-dind
  before_script:
    - apk update && apk add curl
    - curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.18.3
  script:
    - echo "Building image..."
    - docker build -t ${IMAGE_NAME} ${APP2_DOCKERFILE_PATH}
    - echo "Checking image by trivy..."
    - trivy image ${IMAGE_NAME}
    - docker save ${IMAGE_NAME} > ${APP2_PATH}/sample_apps_image.tar
  artifacts:
    paths:
      - ${APP2_PATH}/sample_apps_image.tar

今回、dockerコマンドを利用するためにdockerイメージを利用します。イメージにdockerを利用する場合、dockerコンテナ内でdockerデーモンを動かすためのdocker-in-docker(dind)というツールを利用します。ベースイメージの動作に必要なイメージを追加するservicesを利用してdindを連携させます.

脆弱性の確認後、buildしたイメージを次のジョブでも利用するため、圧縮ファイルにして一時保存しておきます。一時保存には、artifactsという指定したファイルを保存するキーワードを資料します。

pushの実装

ビルドしたイメージをコンテナレジストリにプッシュします。今回は、Google Cloud下での開発を進めているため、コンテナレジストリにGoogle Artifact Registry (GAR) を利用します。

GARにプッシュするために、事前にアクセス認証をする必要があります。認証方法はいくつかありますが、今回はJWTとWorkload Identity Federationを利用した方法を利用します。この方法だと鍵などを保存せずともGARに対してイメージをプッシュすることが可能になります。

まず、TerraformでGoogle Cloud側の設定を行います。Workload Identity Poolを作成し、GitLabのIdPと連携します。

resource "google_iam_workload_identity_pool" "gitlab" {
  workload_identity_pool_id = "my-gitlab-pool"
  display_name = "my-gitlab-pool"
}

resource "google_iam_workload_identity_pool_provider" "gitlab-provider" {
  workload_identity_pool_id = google_iam_workload_identity_pool.gitlab.workload_identity_pool_id
  workload_identity_pool_provider_id = "gitlab-provider"
  display_name = "gitlab-provider"
  attribute_mapping = {
    "google.subject" = "assertion.sub"
    "attribute.project_id" = "assertion.project_id"
    "attribute.user_id" = "assertion.user_id"
    "attribute.user_login" = "assertion.user_login"
  }
  oidc {
    allowed_audiences = ["https://gitlab.com"]
    issuer_uri = "https://gitlab.com/"
  }
}

次に、サービスアカウントを作成し、GARにプッシュするために最低限必要な権限を与えておきます。以下のようにforeachを利用して書くとgoogle_project_iam_memberリソースを何度も書かなくてよいため、便利です。

locals {
  roles = ["roles/artifactregistry.reader", "roles/artifactregistry.writer"]
  user_login = var.my-gitlab-account-name
}

resource "google_service_account" "my_gitlab_account" {
  account_id   = "my-gitlab"
  display_name = "my Service Account for GitLab CI"
  project = var.gcp_project_id
}

resource "google_project_iam_member" "members" {
  for_each = toset(local.roles)

  project = var.gcp_project_id
  role = each.value
  member = "serviceAccount:${google_service_account.my_gitlab_account.email}"
}

最後に、Workload Identity Poolとサービスアカウントを連携します。以下の構成では、user_loginというassertionから判断して、認証するようにしています。

data "google_project" "intern_project" {
  project_id = var.gcp_project_id
}

resource "google_service_account_iam_binding" "workload_identity_binding" {
  service_account_id = google_service_account.my_gitlab_account.id
  role = "roles/iam.workloadIdentityUser"
  members = ["principalSet://iam.googleapis.com/projects/${data.google_project.intern_project.number}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.gitlab.workload_identity_pool_id}/attribute.user_login/${local.user_login}"]
}

これでGoogle Cloud側の設定は完了です。次に、CI/CDジョブ中に一時的な認証情報を取得して、GARにアクセスできるようにします。以下のコードのbefore_scriptで処理を行います。最初にID TokenからFEDERATED_TOKENという一時的な認証情報を取得します。その認証情報を利用して、サービスアカウントを利用します。そのサービスアカウントからGARのレジストリにアクセスするという流れになります。

push-job:
  stage: push
  image: docker:26.0
  services:
    - docker:26.0-dind
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  before_script:
    - apk update && apk add curl jq
    - |
      PAYLOAD="$(cat <<EOF
      {
        "audience": "//iam.googleapis.com/projects/${GAR_PROJECT_NUMBER}/locations/global/workloadIdentityPools/${GAR_POOL_ID}/providers/${GAR_PROVIDER_ID}",
        "grantType": "urn:ietf:params:oauth:grant-type:token-exchange",
        "requestedTokenType": "urn:ietf:params:oauth:token-type:access_token",
        "scope": "https://www.googleapis.com/auth/cloud-platform",
        "subjectTokenType": "urn:ietf:params:oauth:token-type:jwt",
        "subjectToken": "${GITLAB_OIDC_TOKEN}"
      }
      EOF
      )"
    - |
      FEDERATED_TOKEN="$(curl --fail "https://sts.googleapis.com/v1/token" \
        --header "Accept: application/json" \
        --header "Content-Type: application/json" \
        --data "${PAYLOAD}" \
        | jq -r '.access_token'
      )"
    - |
      ACCESS_TOKEN="$(curl --fail "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${GAR_SERVICE_ACCOUNT_EMAIL}:generateAccessToken" \
        --header "Accept: application/json" \
        --header "Content-Type: application/json" \
        --header "Authorization: Bearer ${FEDERATED_TOKEN}" \
        --data '{"scope": ["https://www.googleapis.com/auth/cloud-platform"]}' \
        | jq -r '.accessToken'
      )"
    - echo ${ACCESS_TOKEN} | docker login -u oauth2accesstoken --password-stdin https://${GAR_HOST}
  script:
    - echo "Pushing image in container registry..."
    - docker load -i ${APP2_PATH}/sample_apps_image.tar
    - docker tag ${IMAGE_NAME} ${REMOTE_IMAGE_NAME}:latest
    - docker tag ${IMAGE_NAME} ${REMOTE_IMAGE_NAME}:${STABLE_TAB}
    - docker tag ${IMAGE_NAME} ${REMOTE_IMAGE_NAME}:${STABLE_TAB}-${CI_COMMIT_SHORT_SHA}
    - docker push ${REMOTE_IMAGE_NAME}:latest
    - docker push ${REMOTE_IMAGE_NAME}:${STABLE_TAB}
    - docker push ${REMOTE_IMAGE_NAME}:${STABLE_TAB}-${CI_COMMIT_SHORT_SHA}

参考:
GitLab公式 “Configure OpenID Connect with GCP Workload Identity Federation
Google Cloud公式 Youtube “What is Workload Identity Federation?

認証ができたら、GARのレジストリにプッシュしていきます。この際、タグをつけてプッシュしていきますが、タグをつける際、下記のページを参考にしてタグを付けています。

参考:
Microsoft公式 “コンテナー イメージのタグ付けとバージョン管理に関する推奨事項

このページでは以下のように大きく2種類のアプローチでのタグ付けを取り上げています。

  • 安定したタグ
    イメージがタグとして利用したバージョン(例:1.0, v2 )の目的を果たすように安定して機能することを示すために利用するタグを指します。 このタグをつけたからと言い、内容を固定したり、更新をしないでいいというわけではなく、安定を維持するためにセキュリティやツールの互換に関する更新を適応する必要があります。
  • 一意のタグ
    レジストリにプッシュしたすべてのイメージに対して、他のタグとかぶらないように一意につけるタグを示します。 複数ノードでスケーリングできる環境などにおいて、安定したタグを利用するとスケーリング時に元ノードとは異なる更新後の同タグ名のイメージをデプロイする可能性があります。一意のタグを利用することで、各ノードのデプロイされたイメージの一貫性を保つことができます。 一意のタグの例として、時刻、Gitコミットハッシュ、ビルドIDなどがあげられ、今回はコミットハッシュを利用しました。

これらのアプローチから、今回はコンテナレジストリにイメージを新しくプッシュするたびに3種類のタグをつけています。

  • 最新タグ
    最新のイメージを表す一般的なタグ (今回の設定:latest)
  • バージョンタグ
    アプリの基本機能の変化がないことを示すタグ (今回の設定:1.0)
  • コミットハッシュ付きタグ
    各イメージに対して一意に設定するタグ (今回の設定:1.0-e352f…)

特に、次のdeployの工程で一意のタグをつけておくことが重要になるため、このようなタグのつけ方を適切に設定する必要があります。

これで、GitLab Runnerを利用した設定は終了です。以下のようにそれぞれのジョブにチェックが入って正常に終了していれば問題ありません。

deployの実装

最後に、GARにプッシュされてイメージを参照してデプロイを行います。ここからはGitLab Runnerではなく、ArgoCD Image Updaterというツールを利用してArgoCD側でデプロイのジョブを行います。ArgoCD Image Updaterは、コンテナレジストリ内の変更を確認し、新しいイメージがプッシュされたら自動的にそのイメージをデプロイし直すツールです。以上から、今回のCI/CDのシステム上の全体的な流れは以下のようになります。

上記の図の①~③は前セクションまでで完了したので、④、⑤の部分を実装していきます。ArgoCD Image UpdaterからGARへのアクセス方法はシークレットを利用するなど複数ありますが、今回もWorkload Identityを利用したアクセスをします。まず、ArgoCD Image UpdaterがGARにアクセスするために利用するサービスアカウントを用意しておきます。前セクションで作成したサービスアカウントとは異なり、ArgoCD Image Updaterはイメージの読み込みしか必要ないので、書き込み権限は与えないようにします。

resource "google_service_account" "aiu_account" {
  account_id   = "my-aiu"
  display_name = "Service Account for Argocd image updater"
  project = var.gcp_project_id
}

resource "google_project_iam_member" "aiu_member" {
  project = var.gcp_project_id
  role = "roles/artifactregistry.reader"
  member = "serviceAccount:${google_service_account.aiu_account.email}"
}

resource "google_service_account_iam_binding" "aiu_binding" {
  service_account_id = google_service_account.aiu_account.id
  role = "roles/iam.workloadIdentityUser"
  members = ["serviceAccount:${var.gcp_project_id}.svc.id.goog[argocd/argocd-image-updater]"]
}

次に、ArgoCD Image Updaterをインストールします。ArgoCDにアプリケーションリソースとしてHelm Chartからデプロイします。ArgoCD Image UpdaterはArgoCDと同じ名前空間に入れることが推奨されているため、同じになるようにします。ArgoCD Image UpdaterがWorkload Identity機能に対応していないので、auth.shを実行してアクセストークンを作成するようにします。

applications:
- name: argocd-image-updater
  namespace: argocd
  ...
  source:
    repoURL: 'https://argoproj.github.io/argo-helm'
    chart: argocd-image-updater
    targetRevision: 0.9.6
    helm:
      values: |
        config:
          logLevel: "info"
          registries:
          - name: GAR Asia
            api_url: https://asia-northeast1-docker.pkg.dev
            prefix: asia-northeast1-docker.pkg.dev
            credentials: ext:/auth/auth.sh
            credsexpire: 30m
            ping: no
        serviceAccount:
          annotations:
            iam.gke.io/gcp-service-account: my-aiu@project.iam.gserviceaccount.com
        volumes:
          - name: auth
            configMap:
              defaultMode: 0755
              name: argocd-image-updater-authscripts
        volumeMounts:
          - name: auth
            mountPath: /auth
        authScripts:
          enabled: true
          scripts:
            auth.sh: |
              #!/bin/sh
              ACCESS_TOKEN=$(wget --header 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token -q -O - | grep -Eo '"access_token":.*?[^\\]",' | cut -d '"' -f 4)
              echo "oauth2accesstoken:$ACCESS_TOKEN"
  destination:
	  server: "https://kubernetes.default.svc"
  ...

これでArgoCD Image UpdaterのPodができているはずなので、一度動作確認を行います。Pod内に入り、コンテナレジストリ内のlatestタグを無視して、最新のimageであるタグを捜索します。うまくいけば、最新のタグの文字列を取得してきます。

注意点として、ArgoCD Image Updaterは各イメージの内部情報は見ずにタグから違いを判断します。このため、最新のイメージにlatestというタグのみをつけ続けて最新のイメージを捜索するようにすると、イメージの内部情報を更新しても常にlatestというタグは変化しないため、変更を確認して再デプロイしてくれません。このため、各イメージに一意のタグをつけておき、ArgoCD Image Updaterで捜索する場合はlatestタグを無視して捜索させます。

$ kubectl exec -it <ArgoCD Image Updater Pod> # ArgoCD Image UpdaterのPodに入る
Pod:~$ argocd-image-updater test asia-northeast1-docker.pkg.dev/project/my-repo/app2-image --credentials ext:/auth/auth.sh --registries-conf-path /app/config/registries.conf  --update-strategy latest --allow-tags 'regexp:^[0-9a-f]{5,40}$' --ignore-tags 'latest'

最後に、annotationを利用してArgoCD Image Updaterと連携させてアプリをデプロイします。アプリのデプロイの際にHelmを利用する場合、イメージ名とイメージタグ名を直接Helmのvalueファイルに書き込むことができます。この時、適切なvalueファイルのパラメータを設定する必要があります。

以下のコードでは、利用するイメージにmy-imageという名前を付けて設定し、Helm Chartを利用して構築します。この際、Helmのvalueファイルのimageパラメータ内のrepositoryとtagに、ArgoCD Image Updaterで取得したイメージ名とイメージタグ名を渡すようにしています。

applications:
- name: app2
	...
  additionalAnnotations:
    argocd-image-updater.argoproj.io/write-back-method: argocd
    argocd-image-updater.argoproj.io/my-image.update-strategy: latest
    argocd-image-updater.argoproj.io/image-list: my-image=asia-northeast1-docker.pkg.dev/project/my-repo/app2-sample
    argocd-image-updater.argoproj.io/my-image.allow-tags: 'regexp:^1\\.0-[0-9a-f]{5,40}$'
    argocd-image-updater.argoproj.io/my-image.ignore-tags: latest
    argocd-image-updater.argoproj.io/my-image.force-update: 'true'
    argocd-image-updater.argoproj.io/my-image.helm.image-name: image.repository
    argocd-image-updater.argoproj.io/my-image.helm.image-tag: image.tag
  source:
	  ...

これでCI/CDの一通りの設定は完了です。イメージの自動更新がなされるかを確認します。コード更新前の結果を表示させた後、更新して再デプロイされるまで待ち、変更されたかを確認します。以下では、表示される「Welcome to 3-Shake!」の ! の数を増やして確認しています。

$ # コード更新前
$ kubectl exec -it ws4-app-sample -- curl localhost:8080
Welcome to 3-Shake!
$ # コード更新および上記のcicdパイプライン完了後 
$ kubectl exec -it ws4-app-sample -- curl localhost:8080
Welcome to 3-Shake!!!!!!

参考:
ArgoCD Image Updater公式 ”Getting Started
ArgoCD Image Updater公式 “Configuring images for update
argocd-image-updaterをWorkload Identityで使う

おわりに

GitLab RunnerとArgoCD Image Updaterを利用して、アプリに対して一通りのCI/CDを構築する方法を説明してきました。この研修から、より認証方法やコンテナ内での処理など所々足りていなかった知識をつけることができました。

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ