CI/CDを使ってみよう
はじめに
前回は、作成したアプリケーションをマイクロサービスとして連携する方法を紹介しました。
今回は、作成したサービスをOpenShiftが提供するCI/CDツールを用いて、外部のイベントを契機にビルド・デプロイを動的に実行する方法を解説します。
CI/CDとは
昨今では、ビジネスの変化に対応するため、アプリケーションの改善およびリリースサイクルを継続的かつ迅速に回していくことが重要となっています。
それを実現していくために必要な手法としてCI/CDがあります。CI/CDは「継続的インテグレーション/継続的デリバリ(Continuous Integration/Continuous Delivery)」の略で、ソフトウェア開発におけるビルド、テスト、デプロイ等の作業を自動化し、アプリケーションのリリース頻度を高める手法です。これらの作業を自動化することで人に起因するミスを防げるため、品質担保しつつ、アプリケーションのライフサイクルを迅速に回し、ビジネスの変化への柔軟な対応が見込めます。
これらアプリケーションのライフサイクルに必要な作業を一連の定型作業としてまとめ、定義したものを「CI/CDパイプライン」と呼びます。
CI/CDパイプラインを作成してみよう
前回は、2つのサービスを手動でビルド・デプロイしましたが、今回はCI/CDパイプラインを用いて自動でビルド・デプロイしてみます。CI/CDパイプラインの作成および実行方法と、サービス(sample)のソースコードの改変をトリガにサービス(sample)のビルド・デプロイを動的に実施する仕組みを説明します。
なお、一般的にはテスト・検証のフェーズを含めてパイプラインを構築しますが、内容簡易化のため割愛しています。また、今回はテスト・検証環境を想定した内容としています。
今回のCI/CDパイプラインの全体概要は下図の通りです。
コンテナ管理ツールが提供する
CI/CDツール「Tekton」
コンテナ管理基盤のCI/CDツールとして注目を集めているのが「Tekton」です。
Tektonは、元々Knative(Serverless)のプロジェクトの一部として始まりました。現在は「Tekton Piplines」としてThe Linux Foundationの傘下のCDF(Continous Delivery Foundation)というCI/CDを標準化する組織で開発が行われているツールの1つです。
Tektonの特徴として、下記のような点がが挙げられます。
- Kubernetes上でのCI/CDパイプラインを構築するためのフレームワークを提供
- Kubernetes環境であればCI/CDが可能なため、他Kubernetes上への移植や再現性が高い
- Tektonで利用するCRDはビルディングブロックとして組み合わせ可能なため、再利用性が高い
- パイプライン起動時のみコンテナ生成するため、効率的にリソース活用が可能
今回は、コンテナ管理基盤としてRed Hat OpenShift Container Platform(以下、OpenShift v4.5)に同梱されているTektonベースのOpenShift Pipelineを使用します。
環境の準備
今回は、下記の環境でCI/CDパイプラインを構築していきます。
- Red Hat OpenShift Container Platform v4.5(k8s 4.18.0.-193.13.2)
⇒ 作成したサービスおよびTekton(OpenShift Pipeline)が稼働するコンテナ管理基盤 - GitHub Enterprise Server(v2.20.5)(以下GHES)
⇒ 作成したサービスのソースコードおよびTektonのCRDなどを格納するSCM(Source Code Management)として利用
※ GitHub.com、Gitlab等でも代替可能 - 作業サーバ
⇒ oc(OpenShiftのCLI)やtkn(TektonのCLI)コマンドなどを実施するサーバ
※OpenShift及びGHESインストールについては割愛
Tekton(OpenShift Pipeline)のインストール
まず、Tekton(OpenShift Pipeline)をインストールします。Tekton(OpenShift Pipeline)はOpenShift Container Platform Operator Hubから簡単にインストールできます。
作業サーバからOpenShiftのWebコンソールにログインし、Administratorのパースペクティブから「Operators」→>「OperatorHub」を選択し、検索ボックスに「OpenShift Pipelines Operator」と入力すると、OpenShift Pipelines Operatorが表示されるのでクリックします(※OpenShiftドキュメントにも記載がありますが、Communnity版は選択しないでください)。
OpenShift Pipelines Operatorの画面が表示されるので、「Install」をクリックします。続いて表示される「Install Operator」画面で「Install」をクリックします。
インストール後、「Operators」→「Installed Operators」からインストールされた「OpenShift Pipelines Operator」を選択し、「CLI(tkn)」からTektonのCLIをダウンロードしてください。本環境ではLinux版をダウンロードして作業サーバでアーカイブを展開し、PATHのあるディレクトリに配置します。
$ tar zxvf$ sudo mv tkn /usr/bin/
以上で、Tekton(OpenShift Pipeline)のインストールは完了です。なお、インストール方法はOpenShiftドキュメントにも記載されているので活用ください。
Tekton概要
ここで、これから操作していくTektonについて説明します。Tektonでは、パイプラインの構築に必要なリソースをCRD(Custom Resource Definition)として作成し、これを元にパイプラインを構築および実行していきます。
作成するCRDの種別は下記の通りです。
CRD | 説明 |
---|---|
Task | コードのコンパイル、イメージのビルドやデプロイ等を定義 |
Pipeline | 1つまたは複数のTaskをまとめて定義 |
PipelineResource | Task、Pipelineで利用する入力(ex: Gitリポジトリ)および出力(ex: ビルドされるコンテナイメージ)として利用するリソースを定義 |
PipelineRun | 定義したPipelineを実行する際に利用 |
EventListener | 動的にPipelineを実行するための、外部イベントを受け取るPodを定義 |
TriggerBinding | TriggerTemplateで利用するパラメータを定義 |
TriggerTemplate | EventListenerが受け取った外部イベントを契機に、実行するパイプラインを定義 |
Tektonおよび各CRDの詳細設定についてはTekton Documentationなどを参考にしてください。
それでは、実際にTask、Pipeline、PipelineResourceの3つを作成し、パイプラインを構築および実行(ビルド・デプロイ)していきます。Task、Pipeline、PipelineResourceの関係性は下記図の通りです。
今回利用するソースコードおよび内容については、第3回を参照ください。
Step1: Taskの作成
続いて、サービス(sample)とサービスを呼び出すための呼び出し元サービス(caller)をビルド・デプロイするTaskを作成していきます。今回は、これらサービスをmvnでビルドおよびデプロイするパイプラインを作成します。
サービス(sample)のTask
mvnコマンドを実行するためのPodの情報とそのPod内で実行する処理内容をspec.stepsで定義します。本Taskではビルドで利用するPodとして"maven:3.6.0-jdk-8-slim"を用いて、そのPod内の処理内容(コマンド)の"/usr/bin/mvn -f /workspace/source/pom.xml clean fabric8:deploy -Popenshift"を実行しています。Tektonでは、spec.inputs.resourcesで定義したリソース(ここではsampleのソースコードがあるgitレポジトリ)をmaven Pod内の/workspace/sourceというディレクトリにcloneし、pom.xmlを元にビルド・デプロイします。サービス(sample)のTaskのファイル名は「01_sample_task.yaml」として作成します。
apiVersion: tekton.dev/v1alpha1 kind: Task metadata: name: sample-build spec: inputs: resources: - name: source type: git steps: - name: build image: maven:3.6.0-jdk-8-slim command: - /usr/bin/mvn args: - "-f" - "/workspace/source/pom.xml" - "clean" - "fabric8:deploy" - "-Popenshift"
呼び出し元サービス(caller)のTask
呼び出し元サービス(caller)には、サービス(sample)を呼び出す際に使用するライブラリ(sample-api-client)も必要です。本Taskではライブラリ(sample-api-client)をビルドし、ビルドしたライブラリを用いて呼び出し元サービス(caller)を同一のmaven Podでビルドしています。呼び出し元サービス(caller)のTaskのファイル名は「01_api_caller_task.yaml」として作成します。
apiVersion: tekton.dev/v1alpha1 kind: Task metadata: name: sample-api-caller spec: inputs: resources: - name: source type: git steps: - name: registry image: maven:3.6.0-jdk-8-slim command: - /usr/bin/mvn args: - "-f" - "/workspace/source/sample-api-client/pom.xml" - "install" - name: build image: maven:3.6.0-jdk-8-slim command: - /usr/bin/mvn args: - "-f" - "/workspace/source/caller/pom.xml" - "clean" - "fabric8:deploy" - "-Popenshift"
以上で、Taskの作成は終了です。
Step2: Pipelineの作成
次に、作成したTaskを実行していくためのPipelineを定義します。本記事では、サービス(sample)と呼び出し元サービス(caller)の2つのPipelineを定義します。
サービス(sample)のPipeline定義
Pipelineでは、spec.resourcesで利用するリソース(git、image)を定義し、関連するTask名(サービス(sample)のTask名)をspec.tasksで定義します。サービス(sample)のPipelineのファイル名は「02_sample_pipeline.yaml」として作成します。
apiVersion: tekton.dev/v1alpha1 kind: Pipeline metadata: name: maven-build-sample spec: resources: - name: git-repo type: git - name: image type: image tasks: - name: sample-build taskRef: name: sample-build resources: inputs: - name: source resource: git-repo
呼び出し元サービス(caller)のPipeline定義
呼び出し元サービス(caller)の場合は、呼び出し元サービス(caller)のTask名をspec.taskで定義します。呼び出し元サービス(caller)のPipelineのファイル名は「02_api_caller_pipeline.yaml」として作成します。
apiVersion: tekton.dev/v1alpha1 kind: Pipeline metadata: name: maven-build-api-and-caller spec: resources: - name: git-repo type: git - name: image type: image tasks: - name: sample-api-caller taskRef: name: sample-api-caller resources: inputs: - name: source resource: git-repo
以上で、Pipelineの作成は終了です。
Step3: PipelineResourceの作成
次に、利用するリソースを定義していきます。本記事では、すでにGHES上にサービス(sample)と呼び出し元サービス(caller)およびライブラリ(sapmle-api-client)のソースコードをpushしているので、それらのURLを指定しています。そして、imageとしてs2iで作成されるイメージをそれぞれ指定します。
今回は「sample-cicd」というプロジェクトに作成します(※今回はSSLを無効にしてGHESのリソースをcloneしているため、sslVerifyをfalseとして設定しています)。PipelineResourceのファイル名は「03_resources.yaml 」として作成します。
--- apiVersion: tekton.dev/v1alpha1 kind: PipelineResource metadata: name: sample-repo spec: type: git params: - name: url value: - name: sslVerify value: "false" --- apiVersion: tekton.dev/v1alpha1 kind: PipelineResource metadata: name: sample-image spec: type: image params: - name: url value: image-registry.openshift-image-registry.svc:5000/sample-cicd/sample:1.0.0 --- apiVersion: tekton.dev/v1alpha1 kind: PipelineResource metadata: name: caller-repo spec: type: git params: - name: url value: - name: sslVerify value: "false" --- kind: PipelineResource metadata: name: caller-image spec: type: image params: - name: url value: image-registry.openshift-image-registry.svc:5000/sample-cicd/caller:1.0.0
以上で、PipelineResourceの作成は終了です。
手動によるPipelineの実行
作成したCRDを組み合わせて、パイプラインが実行可能かを手動で確認していきます。作成したTask、Pipeline、PipelineResourceをocコマンド用いて作成していきます。
$ oc create -f 01_sample_task.yaml -n sample-cicd $ oc create -f 01_api_caller_task.yaml -n sample-cicd $ oc create -f 02_sample_pipeline.yaml -n sample-cicd $ oc create -f 02_api_caller_pipeline.yaml -n sample-cicd $ oc create -f 03_resources.yaml -n sample-cicd
作成した各CRDはtknコマンドで確認できます。
$ tkn task ls -n sample-cicd NAME DESCRIPTION AGE sample-api-caller 36 seconds ago sample-build 36 seconds ago
$ tkn pipeline ls -n sample-cicd NAME AGE LAST RUN STARTED DURATION STATUS maven-build-api-and-caller 36 seconds ago --- --- --- --- maven-build-sample 36 seconds ago --- --- --- ---
$ tkn resource ls -n sample-cicd NAME TYPE DETAILS caller-repo git url: sample-repo git url: caller-image image url: image-registry.openshift-image-registry.svc:5000/sample-cicd/caller:1.0.0 sample-image image url: image-registry.openshift-image-registry.svc:5000/sample-cicd/sample:1.0.0
パイプラインを実行していくには、Pipeline名とPipelineResourceで定義したgitの名前およびimage名を用いる必要があります。
$ tkn pipeline start < Pipeline名 > -r git-repo=< PipelineResourceで定義したgit名 > -r image=< PipelineResourceで定義したimage名 > -n <プロジェクト名>
実際の実行内容は、下記の通りです。
$ tkn pipeline start maven-build -r git-repo=sample-repo -r image=sample-image -n sample-cicd $ tkn pipeline start maven-build-api-and-caller -r git-repo=caller-repo -r image=caller-iamge -n sample-cicd
Pipelineを実行すると下記のメッセージが出力され、パイプラインの実行ログを確認できます。
Pipelinerun started: maven-build-run-xxx In order to track the pipelinerun progress run: tkn pipelinerun logs maven-build-run-xxx -f -n sample-cicd $ tkn pipelinerun logs maven-build-run-xxx -f -n sample-cicd
処理が成功していると、下記のようにPod等のリソースが作成されます。
$ oc get all -n sample-cicd NAME READY STATUS RESTARTS AGE pod/caller-1-deploy 0/1 Completed 0 103s pod/caller-1-qkvsq 1/1 Running 0 101s pod/caller-s2i-1-build 0/1 Completed 0 2m44s pod/maven-build-api-and-caller-run-wnxfx-sample-api-caller-l6-2bkgx 0/3 Completed 0 6m5s pod/maven-build-run-8lmtn-sample-build-deploy-l2bdv-pod-6xtgg 0/2 Completed 0 6m8s pod/sample-1-deploy 0/1 Completed 0 2m15s pod/sample-1-v52fx 1/1 Running 0 2m pod/sample-s2i-1-build 0/1 Completed 0 3m12s NAME DESIRED CURRENT READY AGE replicationcontroller/caller-1 1 1 1 103s replicationcontroller/sample-1 1 1 1 2m15s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/caller ClusterIP 172.30.93.368080/TCP 104s service/sample ClusterIP 172.30.194.53 8080/TCP 2m16s NAME REVISION DESIRED CURRENT TRIGGERED BY deploymentconfig.apps.openshift.io/caller 1 1 1 config,image(caller:1.0.0) deploymentconfig.apps.openshift.io/sample 1 1 1 config,image(sample:1.0.0) NAME TYPE FROM LATEST buildconfig.build.openshift.io/caller-s2i Source Binary 1 buildconfig.build.openshift.io/sample-s2i Source Binary 1 NAME TYPE FROM STATUS STARTED DURATION build.build.openshift.io/sample-s2i-1 Source Binary Complete 3 minutes ago 54s build.build.openshift.io/caller-s2i-1 Source Binary Complete 2 minutes ago 59s NAME IMAGE REPOSITORY TAGS UPDATED imagestream.image.openshift.io/caller image-registry.openshift-image-registry.svc:5000/sample-cicd/caller 1.0.0 About a minute ago imagestream.image.openshift.io/sample image-registry.openshift-image-registry.svc:5000/sample-cicd/sample 1.0.0 2 minutes ago NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD route.route.openshift.io/caller caller-xxx caller 8080 None route.route.openshift.io/sample sample-xxx sample 8080 None
呼び出し元サービス(caller)のrouteに、curlコマンドでサービスを実行します。
$ curl http://caller-xxx/api/caller/apply -v -X POST -H "Content- Type:application/json" -d "{\"user_name\":\"Taro\", \"store_id\":1111, \"store_name\":\"shop_A\"}" … {"user_id":1}
第3回と同様の結果が得られているので、パイプラインの作成は成功です。
ここまで、パイプラインの構築方法およびその実行方法を解説しました。パイプラインの実行により定型作業をまとめて実行できるようになりましたが、アプリケーションを更新する度にPipelineを手作業で実施するには手間が掛かります。
そこで、次はソースコード変更のpushを契機に、自動でサービス(sample)をGHESにローリングアップデートするEvent Triggerについて解説します。
Event Triggerの作成
外部イベントの発生と共にパイプラインを自動で実行するEvent Triggerを設定します。今回は、GHES上のサービス(sample)のレポジトリへソースコードをpushした場合に、自動でパイプラインが実行されるように設定します。
Event TriggerはTriggerTemplate、TriggerBinding、EventListenerの3つから構成されています。これら3つの関係性は下図の通りです。
Step4: TriggerTemplateの作成
TriggerTemplateは、外部イベントの取得時にどのようにPipeline実行するかを定義します。今回は受け取るパラメータを定義し、定義したパラメータを使用したPipelineResourceとPipelineRunを定義します。TriggerTemplateのファイル名は「04_template.yaml」として作成します。
apiVersion: triggers.tekton.dev/v1alpha1 kind: TriggerTemplate metadata: name: sample spec: params: - name: git-repo-url description: The git repository url - name: git-revision description: The git revision default: master - name: git-repo-name description: The name of the deployment to be created/patched resourcetemplates: - apiVersion: tekton.dev/v1alpha1 kind: PipelineResource metadata: name: $(params.git-repo-name)-git-repo-$(uid) spec: type: git params: - name: revision value: $(params.git-revision) - name: url value: $(params.git-repo-url) - name: sslVerify value: "false" - apiVersion: tekton.dev/v1alpha1 kind: PipelineResource metadata: name: $(params.git-repo-name)-image-$(uid) spec: type: image params: - name: url value: image-registry.openshift-image-registry.svc:5000/sample-cicd/$(params.git-repo-name):latest - apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: name: maven-build-sample-$(params.git-repo-name)-$(uid) spec: serviceAccountName: pipeline pipelineRef: name: maven-build-sample resources: - name: git-repo resourceRef: name: $(params.git-repo-name)-git-repo-$(uid) - name: image resourceRef: name: $(params.git-repo-name)-image-$(uid) params: - name: deployment-name value: $(params.git-repo-name)
Step5: TriggerBindingの作成
TriggerBindingは、TriggerTemplateのパラメータで利用する値を定義しています。今回のTriggerBindingでは、トリガーとなるイベントの値(GHESからのWebhook)と、TriggerTemplateで利用する名前を定義しています。TriggerBindingのファイル名は「05_binding.yaml」として作成します。
apiVersion: triggers.tekton.dev/v1alpha1 kind: TriggerBinding metadata: name: sample spec: params: - name: git-repo-url value: $(body.repository.url) - name: git-repo-name value: $(body.repository.name) - name: git-revision value: $(body.head_commit.id)
Step6: EventListenerの作成
EventListenerは、外部からのWebhookリクエストを受け取るPodとして稼働します。TriggerBindingとTriggerTemplateとを紐づけるように定義します。EventListenerのファイル名は「06_event_listener.yaml」として作成します。
apiVersion: triggers.tekton.dev/v1alpha1 kind: EventListener metadata: name: sample spec: serviceAccountName: pipeline triggers: - bindings: - name: sample template: name: sample
それでは、TriggerBinding、TriggerTemplateおよびEventListenerを作成していきます。EventListener作成後GHESから疎通可能にするため、作成されたServiceをRouteに紐づけします。
$ oc create -f 04_template.yaml -n sample-cicd $ oc create -f 05_binding.yaml -n sample-cicd $ oc create -f 06_event_listener.yaml -n sample-cicd $ oc expose svc el-sample -n sample-cicd $ oc get route -n sample-cicd NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD route.route.openshift.io/caller caller-xxx caller 8080 None route.route.openshift.io/el-sample el-sample-xxx el-sample http-listener None route.route.openshift.io/sample sample-xxx sample 8080 None
作成したRouteのURLをGHESのWebhookに設定します。今回は、サービス(sample)リポジトリがソースコードの変更(git push相当)を検知したら、サービス(sample)のパイプラインが動的に実行されるように設定します。
Webhookの設定は、GHES上のサービス(sample)のレポジトリで「Settings」→「Hooks」の「Add webhook」で作成します。「Payload URL」にEventListener作成時に構築したRouteを指定します。
以上で、Event Triggerの設定は完了です。
外部イベントを契機にした
自動Pipelineの実行
それでは、外部イベント契機で動的にビルド・デプロイするか確認していきます。今回のサービス(sample)は{user_id:1}を返すようにsrc\main\java\com\example\samplesystem\sample\service\SampleService.javaにてハードコードしています。そのため、返す値を下記の通り適当な数値に変更し「Commit changes」します。
Commit changesされたことを契機に、自動でパイプラインが実施されます。
$ tkn pipelinerun ls -n sample-cicd NAME STARTED DURATION STATUS maven-build-sample-sample-4x88r 9 seconds ago --- Running maven-build-api-and-caller-run-djr78 7 minutes ago 4 minutes Succeeded maven-build-sample-run-vbp45 8 minutes ago 4 minutes Succeeded
ビルド・デプロイが完了すると、これまで"sample-1-v52fx"で稼働していたPodが"sample-3-wscsn"として稼働(ローリングアップデート)していることが分かります。
$ oc get pod -n sample-cicd NAME READY STATUS RESTARTS AGE pod/caller-1-deploy 0/1 Completed 0 7m6s pod/caller-1-qqx98 1/1 Running 0 7m4s pod/caller-s2i-1-build 0/1 Completed 0 8m pod/el-sample-684bb5db4-vb654 1/1 Running 0 6m4s pod/maven-build-api-and-caller-run-djr78-sample-api-caller-bu-s9hfv 0/3 Completed 0 11m pod/maven-build-sample-run-vbp45-sample-build-k8tjq-pod-hp6j8 0/2 Completed 0 13m pod/maven-build-sample-sample-4x88r-sample-build-pbwkp-pod-mv64m 0/2 Completed 0 4m30s pod/sample-1-deploy 0/1 Completed 0 8m58s pod/sample-3-deploy 0/1 Completed 0 32s pod/sample-3-wscsn 1/1 Running 0 28s pod/sample-s2i-1-build 0/1 Completed 0 9m53s pod/sample-s2i-2-build 0/1 Completed 0 93s
最後に、呼び出し元サービス(caller)のrouteに対して同様にcurlを実施します。
$ curl http://caller-xxx/api/caller/apply -v -X POST -H "Content- Type:application/json" -d "{\"user_name\":\"Taro\", \"store_id\":1111, \"store_name\":\"shop_A\"}" … {"user_id":39}
{"user_id":39}と最新のソースコードで稼働していることがわかります。
おわりに
今回は、Tekton(OpenShift Pipeline)を用いて、第3回に紹介したサービスをビルド・デプロイするCI/CDパイプラインの作成およびソースコードpush契機でパイプラインを自動で実施する一例を紹介しました。
今回はマイクロサービスの開発部分を中心に説明してきましたが、サービスが増えていくにつれてサービス全体の処理の流れの把握やサービス間の認証・認可の実装等が難しくなります。これらを解決するためのアプローチの1つとしてサービスメッシュがあります。
次回は、サービスメッシュを使ったマイクロサービスの運用について解説します。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- サービスメッシュを使ってみよう
- TFXを使った機械学習パイプラインの構築(デプロイ編)
- CNDT2020シリーズ:CAのインフラエンジニアが解説するKubernetesネイティブなCI/CD
- Kubernetesアプリケーションの快適リリースとGitOps
- OpenShift Commons Gatheringで語られたOpenShiftに最適なCI/CDとは
- 3scaleをインストールしてみよう!
- マイクロサービスを作ってみよう!
- KubernetesのWorkloadsリソース(その1)
- Rancherのカスタムカタログの作成
- KubernetesのConfig&Storageリソース(その1)