Skip to content

CI/CD Adapterを作成し、アプリケーションのヘルスチェックをする

本チュートリアルでは、Qmonus Value Streamを使って、CI/CD Adapterを作成してCI/CDパイプラインを追加する手順を解説します。 実施する前に、Infrastructure Adapterをカスタマイズし、機能を追加するのチュートリアルが完了していることをご確認ください。

以下のステップを通して、CI/CD Adapterを作成し、パイプラインが実行できることを確認していきます。

  • TektonのTask 定義と Pipeline 定義を記述したCI/CD Adapterを作成します。
  • QVS Configに作成した CI/CD Adapterを組み込みます。
  • AssemblyLineにパイプラインを追加し、実行して動作を確認します。

1. CI/CD Adapterの作成(CLI)

本チュートリアルでは、デプロイ後のアプリケーションに対してヘルスチェックを実施するCI/CD Adapterを作成します。 このCI/CD Adapterは、flask-demoアプリケーションをデプロイするAssemblyLineにてdeployのフェーズ後に実行されます。

作成するCI/CD Adapterは、以下の2つのTekton Pipelineと、それらを構成するTask定義から成り立ちます。

  • resolveIPAddressPipeline: デプロイしたアプリケーションのIngressに割り当てられたグローバルIPアドレスを取得するパイプライン
  • curlHealthCheckPipeline: 取得したIPアドレスに対してcurlコマンドを使ってヘルスチェックを実施するパイプライン

以下のような手順にて、各Task定義とPipeline定義を順番に作成していきます。 CI/CD AdapterにおけるTekton PipelineとTaskの定義について詳しくは、CI/CD Adapter Specificationを参照してください。

Task定義の作成

まずは、kubectlコマンドを使ってIngressに割り当てられているグローバルIPアドレスを取得するTask定義を作成します。

Task定義用のフォルダを作成後に、package名をresolveIPAddressTaskとしたTask定義を用意して、パイプライン内でIngressのIPアドレスを取得するコマンドを以下のような内容で作成します。

bash
# ローカルリポジトリへ移動
cd <repository name>

# 本チュートリアル用のブランチをmainブランチから作成
git switch -c create-cicd-adapter main

# .valuestreamディレクトリに移動
cd .valuestream

# CI/CD Adapter Task定義用ディレクトリを作成する
mkdir -p pipeline/tasks

# IngressのIPアドレス取得のTask定義を作成する
vim pipeline/tasks/resolveIPAddressTask.cue
go
package resolveIPAddressTask

import (
	"qmonus.net/adapter/official/pipeline/schema"
)

#Builder: schema.#TaskBuilder
#Builder: {
	name: "resolve-ip-address"

	params: {     // Qmonus Value StreamのDeployment・Deployment Configからパラメータを受け取るためのParam名
		appName: desc:              "Application Name of QmonusVS"
		k8sNamespace: desc:         "Namespace of a deploy resource"
		kubeconfigSecretName: desc: "The secret name of Kubeconfig"
	}

	results: {
		ipAddress: { // Tekton内の処理結果を外部に返すためのResult名
			description: "External IP Address"
		}
	}

	steps: [{
		name:  "resolve-external-ip-address"   // Tekton TaskのStep名
		image: "gcr.io/cloud-builders/kubectl" // kubectlコマンドが実行できるコンテナイメージ
		script: """
			#!/usr/bin/env sh
			# Ingressリソースから割り当てられたIPアドレスを取得し、Resultsに保存
			kubectl -n $(params.k8sNamespace) --kubeconfig $KUBECONFIG get ingress $(params.appName) -o=jsonpath='{.status.loadBalancer.ingress[0].ip}' | tee tekton/results/ipAddress
			"""
		env: [
			{
				name:  "KUBECONFIG"
				value: "/secret/kubeconfig"
			}]
		volumeMounts: [
			{
				mountPath: "/secret"
				name:      "user-kubeconfig"
				readOnly:  true
			},
		]
	}]
	volumes: [
		{
			name: "user-kubeconfig"
			secret: {
				items: [{
					key:  "kubeconfig"
					path: "kubeconfig"
				}]
				secretName: "$(params.kubeconfigSecretName)"
			}
		},
	]
}

kubeconfig について

上記の Task 定義では、Kubernetes クラスタへのアクセスに必要な kubeconfig を Secret としてマウントしています。 この Secret は、Qmonus Value Stream が Deployment の設定から自動的に作成・管理するため、ユーザが手動で作成する必要はありません。
この kubeconfig をマウントすることで、Task 内で kubectl コマンドを使用して Kubernetes クラスタにアクセスできるようになります。

続いて、package名をcurlHealthCheckTaskとしたTask定義を用意して、パイプライン内でヘルスチェックのコマンドを以下のような内容で作成します。

bash
# ヘルスチェックのTask定義を作成する
vim pipeline/tasks/curlHealthCheckTask.cue
go
package curlHealthCheckTask

import (
	"qmonus.net/adapter/official/pipeline/schema"
)

#Builder: schema.#TaskBuilder
#Builder: {
	name: "curlHealthCheckTask"     // Pipeline定義で指定するBuilderスキーマ名

	params: { // 外部からパラメータを受け取るためのParam名
		ipAddress: desc: "Global IP address of a deploy resource"
	}

	steps: [{
		name:  "curl-health-check" // Tekton TaskのStep名
		image: "curlimages/curl"   // curlコマンドが実行できるコンテナイメージ
		script: """
			#!/usr/bin/env sh
			# /healthエンドポイントにHTTPリクエストを送信してアプリケーションの正常性を確認
			curl --fail http://$(params.ipAddress)/health
			"""
	}]
}

Pipeline定義の作成

次に、Pipeline定義用のフォルダを作成し、上記のTaskを含むPipeline定義を作成します。 まずは、package名をresolveIPAddressPipelineとしたPipeline定義を用意し、先ほど作成したresolveIPAddressTaskパッケージを指定する内容で作成します。 また、resultsで、Taskの実行結果であるIPアドレスを外部に返すように設定します。

bash
# CI/CD Adapter Pipeline定義用ディレクトリを作成する
mkdir pipeline/sample

# Pipeline定義を作成する
vim pipeline/sample/resolveIPAddressPipeline.cue
go
package resolveIPAddressPipeline

import (
	"github.com/qmonus/sample/pipeline/tasks:resolveIPAddressTask"
)

DesignPattern: {
	name: "sample:resolveIPAddressPipeline"  // sampleフォルダ配下のパッケージ名を記載する

	pipelines: {
		"resolve-ip-address-after-deploy": {
			tasks: {
				"resolve-ip-address": resolveIPAddressTask.#Builder
			}
			results: {
				"ipAddress": tasks["resolve-ip-address"].results.ipAddress
			}
		}
	}
}

続いて、package名をcurlHealthCheckPipelineとしたPipeline定義を用意し、先ほど作成したcurlHealthCheckTaskパッケージを指定する内容で作成します。

bash
# Pipeline定義を作成する
vim pipeline/sample/curlHealthCheckPipeline.cue
go
package curlHealthCheckPipeline

import (
	"github.com/qmonus/sample/pipeline/tasks:curlHealthCheckTask"
)

DesignPattern: {
	name: "sample:curlHealthCheckPipeline"  // sampleフォルダ配下のパッケージ名を記載する

	pipelines: {
		"curl-health-check-after-resolve-ip-address": {
			tasks: {
				// 先に作ったcurlHealthCheckTaskパッケージの#Builderスキーマを指定する
				"curl-health-check": curlHealthCheckTask.#Builder
			}
		}
	}
}

この時点でのフォルダ構成は以下のようになります。

<repository name>/
├── .valuestream/
│   ├── cue.mod/
│   │   └── module.cue
│   ├── local/
│   │   ├── flask-app.cue
│   │   └── hpa.cue
│   ├── output/
│   │   ├── manifest.yaml
│   │   └── pipeline.yaml
│   ├── pipeline/
│   │   ├── sample/
│   │   │   ├── curlHealthCheckPipeline.cue
│   │   │   └── resolveIPAddressPipeline.cue
│   │   └── tasks/
│   │       ├── curlHealthCheckTask.cue
│   │       └── resolveIPAddressTask.cue
│   ├── assemblyline-dev.yaml
│   ├── params.json
│   ├── qvs.yaml
│   └── sample-manifests.yaml
├── app.py
├── Dockerfile
└── requirements.txt

2. QVS Configの編集(CLI)

作成したCI/CD Adapterを、QVS Config(qvs.yaml)に組み込みます。

bash
# qvs.yamlを編集する
vim qvs.yaml # 以下のdiffのように変更する
yaml
params:
  - name: k8sNamespace
    type: string
  - name: imageName
    type: string
  - name: hpaStatus
    type: string

modules:
  - name: github.com/qmonus/sample
    local:
      path: .
  - name: qmonus.net/adapter/official

designPatterns:
# Infrastructure Adapter
  - pattern: github.com/qmonus/sample/local:flaskapp
    params:
      k8sNamespace: $(params.k8sNamespace)
      imageName: $(params.imageName)
      hpaStatus: $(params.hpaStatus)

# CI/CD Adapter
  - pattern: qmonus.net/adapter/official/pipeline/build:buildkitGcp
  - pattern: qmonus.net/adapter/official/pipeline/deploy:simple
  - pattern: github.com/qmonus/sample/pipeline/sample:resolveIPAddressPipeline
  - pattern: github.com/qmonus/sample/pipeline/sample:curlHealthCheckPipeline
yaml
params:
  - name: k8sNamespace
    type: string
  - name: imageName
    type: string
  - name: hpaStatus
    type: string

modules:
  - name: github.com/qmonus/sample
    local:
      path: .
  - name: qmonus.net/adapter/official

designPatterns:
# Infrastructure Adapter
  - pattern: github.com/qmonus/sample/local:flaskapp
    params:
      k8sNamespace: $(params.k8sNamespace)
      imageName: $(params.imageName)
      hpaStatus: $(params.hpaStatus)

# CI/CD Adapter
  - pattern: qmonus.net/adapter/official/pipeline/build:buildkitGcp
  - pattern: qmonus.net/adapter/official/pipeline/deploy:simple
  - pattern: github.com/qmonus/sample/pipeline/sample:resolveIPAddressPipeline
  - pattern: github.com/qmonus/sample/pipeline/sample:curlHealthCheckPipeline

3. Pipeline Manifestの生成および登録 (CLI)

作成したCI/CD Adapterを指定したQVS Configから Pipeline Manifestを生成し、Qmonus Value Streamへ登録します。

bash
# CI/CD Adapterをコンパイルする
qvsctl pipeline compile -m . -c qvs.yaml --prefix flask-demo -o output/pipeline.yaml

# qvsctl auth コマンド使用して認証する(出力されたurlへアクセスしてください)
qvsctl auth

# Project Nameを取得する
qvsctl project list

# 取得したProject Nameを環境変数に設定する
export projectName=<PROJECT NAME>

# Tekton Pipeline/Task ManifestをApplyする
qvsctl pipeline apply -p ${projectName} -f output/pipeline.yaml

新たに2つのpipelineflask-demo-resolve-ip-address-after-deployflask-demo-curl-health-check-after-resolve-ip-addressと2つのtaskflask-demo-resolve-ip-addressflask-demo-curlhealthchecktaskが生成されることを確認してください。

4. AssemblyLineの修正(CLI)

AssemblyLineに、resolve-ip-addresscurl-health-checkのStageを定義し、作成したPipelineおよびDeploymentを指定します。 resolve-ip-addressで取得したipAddressは、$(stages.resolve-ip-address.results.ipAddress)としてcurl-health-checkのStageで利用します。 また、resultsipAddressを追加して、Pipelineの実行結果として確認できるようにします。

bash
# assemblyline-dev.yamlを編集する
vim assemblyline-dev.yaml
yaml
apiVersion: vs.axis-dev.io/v1
kind: AssemblyLine
metadata:
  name: flask-demo-dev
spec:
  params:
    - name: gitRevision
      type: string
  stages:
    - name: build
      spec:
        pipeline: flask-demo-build
        deployment:
          app: flask-demo
          name: flask-demo-dev
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)
    - name: deploy
      spec:
        pipeline: flask-demo-deploy
        deployment:
          app: flask-demo
          name: flask-demo-dev
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)
          - name: imageName
            value: $(stages.build.results.imageFullNameTag)
      runAfter:
        - build
    - name: resolve-ip-address
      spec:
        pipeline: flask-demo-resolve-ip-address-after-deploy
        deployment:
          app: flask-demo
          name: flask-demo-dev
      runAfter:
        - deploy
    - name: curl-health-check
      spec:
        pipeline: flask-demo-curl-health-check-after-resolve-ip-address
        deployment:
          app: flask-demo
          name: flask-demo-dev
        params:
          - name: ipAddress
            value: $(stages.resolve-ip-address.results.ipAddress)
      runAfter:
        - resolve-ip-address
  results:
    - name: gitRevision
      value: $(inputs.gitRevision)
    - name: imageName
      value: $(stages.build.results.imageFullNameTag)
    - name: ipAddress
      value: $(stages.resolve-ip-address.results.ipAddress)
  artifacts:
    - path: manifests
yaml
apiVersion: vs.axis-dev.io/v1
kind: AssemblyLine
metadata:
  name: flask-demo-dev
spec:
  params:
    - name: gitRevision
      type: string
  stages:
    - name: build
      spec:
        pipeline: flask-demo-build
        deployment:
          app: flask-demo
          name: flask-demo-dev
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)
    - name: deploy
      spec:
        pipeline: flask-demo-deploy
        deployment:
          app: flask-demo
          name: flask-demo-dev
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)
          - name: imageName
            value: $(stages.build.results.imageFullNameTag)
      runAfter:
        - build
    - name: resolve-ip-address
      spec:
        pipeline: flask-demo-resolve-ip-address-after-deploy
        deployment:
          app: flask-demo
          name: flask-demo-dev
      runAfter:
        - deploy
    - name: curl-health-check
      spec:
        pipeline: flask-demo-curl-health-check-after-resolve-ip-address
        deployment:
          app: flask-demo
          name: flask-demo-dev
        params:
          - name: ipAddress
            value: $(stages.resolve-ip-address.results.ipAddress)
      runAfter:
        - resolve-ip-address
  results:
    - name: gitRevision
      value: $(inputs.gitRevision)
    - name: imageName
      value: $(stages.build.results.imageFullNameTag)
    - name: ipAddress
      value: $(stages.resolve-ip-address.results.ipAddress)
  artifacts:
    - path: manifests

5. AssemblyLineの登録 (CLI)

修正したAssemblyLineをQmonus Value StreamのProjectへ登録します。

bash
# AssemblyLineをApplyして更新する
qvsctl pipeline apply -p ${projectName} -f assemblyline-dev.yaml

git add、 git commit、 git pushを行い、リモートリポジトリに変更を反映します。 またコミットIDをAssemblyLineを実行するときに指定するため控えておきます。

bash
# リモートリポジトリにpushする
git add --all
git commit -m "Add CI/CD Adapter for health check"
git push origin create-cicd-adapter

# コミットIDを控える
git log -1

6. AssemblyLineの実行(GUI)

編集したAssemblyLineを実行し、CI/CD Adapterによるヘルスチェックが正常に動作することを確認します。

  1. 左メニューより、AssemblyLineを選択します。
  2. 画面中央に表示されるflask-demo-devを選択します。
  3. 以下のInput Parameterを入力し、 RUN ボタンを押下してAssemblyLineを実行します。
    • gitRevision: 手順5でのgit commit時のコミットID

7. ヘルスチェックの結果確認(GUI)

AssemblyLine の実行後、ヘルスチェックが正常に完了したことを確認します。

  1. 実行したAssemblyLineの詳細画面から、curl-health-checkステージを選択します。
  2. Taskの実行ログを確認します。
  3. 以下のようなログが出力されていれば、ヘルスチェックは成功です。
    "status": "healthy"

8. ブランチをマージする

動作確認が完了したら、作業ブランチをmainブランチにマージします。 GitHub の対象リポジトリからマージを実施してください。

  1. GitHubの対象リポジトリにアクセスします。
  2. 画面上部のPull requestsタブをクリックします。
  3. create-cicd-adapterブランチのPull Request画面を開きます。
  4. Pull Request画面で変更内容を確認し、問題がなければMerge pull requestボタンをクリックします。
  5. Confirm mergeボタンをクリックしてマージを完了します。
  6. マージ完了後、Delete branchボタンをクリックして作業ブランチを削除します(任意)。

リソースの削除

チュートリアルで作成したリソースは、QVS Config(qvs.yaml)のInfrastructure Adapterの定義をコメントアウトし、リモートリポジトリに変更を反映したCommit IDでAssemblyLineを実行することで削除できます。(※リソースは削除されますが、curlHealthCheckPipelineのヘルスチェック機能により、Healthcheckのステージは失敗します。)

詳しくは、デプロイしたリソースの削除を参照してください。

解説

本チュートリアルでは、CI/CD Adapterを新たに作成し、Qmonus Value Streamで実行できることを確認しました。 今回作成した CI/CD Adapterでは、curlコマンドを使用してアプリケーションの /health エンドポイントにHTTPリクエストを送信し、ヘルスチェックを実施しました。このように、CI/CD Adapterを使うことで、デプロイ後の動作確認やテストなど、様々なCI/CD処理を自動化できます。

CI/CD Adapterの詳しい仕様については、CI/CD Adapter Specificationを参照してください。

まとめ

本チュートリアルでは、CI/CD Adapterの作成方法を学びました。 これまでのチュートリアルを通じて、以下のことができるようになりました。

これらの知識を活用して、独自のアプリケーションを Qmonus Value Streamでデプロイしてみましょう。

また、Qmonus Value Streamでは伴走支援を実施しています。伴走支援をご希望の方はこちらからご連絡ください。