IBM CloudのContinuous Delivery Toolchainを用いて、IBM Cloud Kubernetes Serviceにアプリケーションをデプロイする

# はじめに

昨今の開発では、アプリケーションのビルド、サーバ環境へのデプロイを自動化し、早期にコンパイル・単体テストを実行して問題を把握したり、サーバにおけるアプリケーションの確認・テストのサイクルを迅速に回したりといった継続的インテグレーション・継続的デリバリ（CI/CD）が当たり前になっています。

CI/CDのパイプラインは、CI/CDにおけるビルド、デプロイ、テストなどの処理をステージ化し、それらをつなぎ合わせて自動化を実現するものです。IBM CloudでCI/CDのパイプラインを実装する場合、[Continuous Delivery](https://cloud.ibm.com/docs/ContinuousDelivery?topic=ContinuousDelivery-getting-started)サービス、および、それに含まれるToolchainを利用します。

当記事では、Toolchainを用いて、GitHub上のReactアプリケーションのソースコードからDockerイメージをビルドし、IBM Cloud Kubernetes Service（IKS）に対してデプロイする処理を自動化してみます。

デプロイ対象のアプリケーションとして、[IBMが提供しているReactのサンプルアプリケーション](https://github.com/IBM/deploy-react-kubernetes)を使用します。


# 前提事項

当記事の手順を実施するにあたり、以下を前提とします。

- IKSのクラスターを既に作成済みである（無料クラスターで可）
- IBM Cloud Container Registryのインスタンスを作成し、名前空間を作成済みである
- IBM Cloud CLIが導入済みである

上記が未実施の場合、別記事「[QuarkusアプリをIBM Cloud Kubernetes Service(IKS)で動かしてみる](https://qiita.com/hisato_imanishi/items/94a1a0ddd744423906a8)」の該当箇所を実施ください。


# APIキーの作成

手順の中で、IBM CloudのAPIキーを利用する場面がありますので事前に作成しておきます。

IBM Cloud CLIでログインします。

```
ibmcloud login -a cloud.ibm.com -r us-south
```

ログイン完了後、APIキーを生成します。

```
ibmcloud iam api-key-create api_key -d "this is my API key" --file key_file
```

上記により、「api_key」という名前でAPIキーが作成され、APIキーの値がコマンドを実行したディレクトリの「key_file」ファイルに格納されます。このファイルはJSON形式となっており、その中の「apikey」という項目の値をメモしておいてください。


# Toolchainの作成

[Continuous Deliveryの作成ページ](https://cloud.ibm.com/catalog/services/continuous-delivery)にアクセスします。

「リージョン」で「ダラス」を選択し、「作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/38b52b6d-b25b-1b65-7755-0dbfecc96d8e.png)

Continuous Deliveryのインスタンスが作成され、以下の画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/e2ce39cb-8ca6-71c8-4da4-4e07003d0cc2.png)

次にToolchainを作成します。
[Toolchainの作成ページ](https://cloud.ibm.com/devops/toolchains)にアクセスします。

画面下部にある「Build your ownツールチェーン」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/60920cc2-b001-c855-1c67-258b678088e6.png)

Toolchainの名前を入力し、「リージョン」で「ダラス」を選択し、「作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/c7c5787f-7e34-fb95-8624-5c0308779d56.png)


# ToolchainへのGitHubの組み込み

Toolchainが作成されるので、画面右側の「ツールの作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/bd0682ad-34f3-dedc-9f7f-0d5e50eb8d68.png)

「GitHub」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/011bb5ca-1171-f25b-39e8-72b478b17244.png)

「認証」ボタンをクリックします。
すると、GitHubのサイトに遷移し、GitHubへのアクセス権限付与の承認ダイアログが表示されるので、承認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/effe0d78-d7d2-834b-79cf-98c8caacd34a.png)

Toolchainのページに帰ってくるので、「リポジトリー・タイプ」に「既存」、「リポジトリーURL」に「``https://github.com/IBM/deploy-react-kubernetes.git``」（サンプルのReactアプリケーションのGitリポジトリ）を入力し、「統合の作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/b2675595-ff11-9beb-739b-d4f1bcc49ceb.png)


# ToolchainへのDelivery Pipelineの組み込み

ToolchainにGitHubが組み込まれたので、次にビルド・デプロイ処理を盛り込んでいきます。
「ツールの作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/c598c28d-9d4e-04e8-ad2d-58a7db94d70e.png)

「Delivery Pipeline」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/f237a414-6fa9-0c67-5ce9-b53a361c7ece.png)

「パイプライン・タイプ」として「Classic」を選択し、「統合の作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/bf32a267-ffb6-f776-1727-6e47a87684dd.png)


# ビルド・デプロイステージの定義

「Delivery Pipeline」がToolchainに組み込まれたので、これをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/04cc80ca-7cb4-4720-ee3f-cc2550bf0d21.png)

ここから、Dockerイメージをビルドする「Build Image」、ビルドしたイメージをIKSにデプロイする「Deploy to Cluster」という2つのステージをパイプラインに定義します。

「ステージの追加」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/4e5c30fd-a3cb-1707-1b03-dadf241dddfc.png)

ステージの名前を「Build Image」に変更します。
ビルド対象の「ブランチ」を「master」に設定します。
「ステージ・トリガー」として、「このステージが手動で実行されたときにのみジョブを実行」を選択します。
（※masterにマージされたタイミングなどで自動的に実行するように構成するためには、対象のGitHubリポジトリに対する権限が必要となります。これを実現するためには、今回の対象のGitHubリポジトリをforkする必要があります）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/1b227887-a668-bb50-96e5-13de5482a1e8.png)

「ジョブ」タブを選択し、「ジョブの追加」→「ビルド」を選択します。
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/6d793692-5aa0-acd3-a5c9-0422de001a56.png">

「ビルダー・タイプ」として「Container Registry」を選択します。これにより、ビルドしたDockerイメージをContainer Registryに格納することになります。
また、「APIキー」のフィールドをクリックします。
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/6ede182a-4599-85af-6f0f-d03367ab8352.png">

APIキーの入力ダイアログが表示されるので、上記でメモしておいたAPIキーを入力し、「認証」ボタンをクリックします。
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/1062982c-680c-c3c9-7623-e6ed872eb060.png">

APIキーを入力すると、自動的にContainer Registryの情報が取得され、表示されます。
リージョンが正しいことを確認し、「Container Registry名前空間」に自身で作成した名前空間を指定し、「Dockerイメージ名」に任意のイメージ名を入力し、画面下部の「保存」ボタンをクリックします。
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/39673aed-5a72-3b24-6bb2-0357f1b3fe9a.png">

イメージをビルドするステージが追加されました。
赤枠部分のアイコンをクリックして（テスト目的の）ビルドを開始します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/e099bdd3-b4c2-20ec-71c5-b04a5372cc35.png)

ビルドが開始され、数分間待つと、以下のようにビルドが終了し、「ステージは成功」と表示されます。
これにより、指定したGitHubリポジトリからソースコードが取得され、ビルドされたイメージがContainer Registryに格納されます。
さらに、IKSへのデプロイを実行するステージを追加するため、「ステージの追加」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/7554b89b-8aca-fa8d-ee05-f40b69b20d90.png)

ステージの名前を「Deploy to Cluster」に変更します。
「Build Image」ステージでビルドされたDockerイメージを入力とするため、下記赤枠部分の通りに設定します（自動で設定されます）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/2f014dbd-a14d-b8d4-fc34-b245614ed049.png)

「ジョブ」タブを選択し、「ジョブの追加」→「デプロイ」を選択します。
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/0ed39db3-0b0d-7c61-b721-6575b39667f2.png">

「デプロイヤー・タイプ」として「Kubernetes」を選択します。
「APIキー」をクリックすると、「Build Image」ステージと同様にAPIキーの入力ダイアログが表示されるので、先ほどと同じAPIキーを入力します。
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/c80cd854-e342-b12b-ac44-1a4995b577b0.png">

APIキーの入力後、デプロイ対象のIKSクラスターを選択します。
「IBM Cloud リージョン」でIKSのリージョンを指定します（無料クラスターの場合は「US South」）。
「クラスター名」でクラスターの名前を指定します。
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/06934e43-7ebc-4f79-8951-86c9d6703a1a.png">

当画面では「デプロイ・スクリプト」というフィールドに、このステージで実行されるデプロイ処理のスクリプトが表示されています。
通常であれば、スクリプト中でdocker inspectによりアプリケーションのポート番号を取得し、それをKubernetesのマニフェストファイル生成で使用するのですが、対象のサンプルアプリケーションではそれが正常に動作しないため、以下のように``PORT=3000``と明示的に設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/1f85e98f-811b-fb2e-2d53-e1393a6c8403.png)

その後、画面下部の「保存」ボタンをクリックします。

以下のように、「Build Image」に続けて、「Deploy to Cluster」というステージを構成することができました。
ビルドしたイメージをIKSにデプロイするため、「Deploy to Cluster」の実行ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/e9cfc07a-4c02-ad58-6c89-2f21730740ed.png)

しばらく待つと、デプロイステージが完了し、以下のように「ステージは成功」と表示されます。
アプリケーションは自動的にNodePortサービスで公開されています。
デプロイのログにURLが出力されていますので、「ログおよび履歴の表示」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/6353b349-ed26-1022-4357-a99a1705e4fe.png)

ログの最下部にポート番号付きのURLが出力されていますので、そのURLにアクセスしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/f5024c6a-9c89-9440-3d85-b23dde200435.png)

以下のような画面が表示されればデプロイ成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/86f3c803-9a0d-4a4b-35f8-90bdd2043275.png)

IKSのPod、Servieを見てみると、Toolchainでビルドしたコンテナがデプロイされていることが分かります。

```
❯ kubectl get pods --all-namespaces | grep toolchain
default       kubernetes-toolchain-01-6fd8df79c9-lfpj6        1/1     Running   0          2m58s


❯ kubectl get svc
NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes                ClusterIP   xxx.xxx.xxx.xxx  <none>        443/TCP          17d
kubernetes-toolchain-01   NodePort    xxx.xxx.xxx.xxx  <none>        3000:31745/TCP   4m58s
quarkus-getting-started   NodePort    xxx.xxx.xxx.xxx  <none>        8080:32423/TCP   17d
```

※なお、このアプリケーションは、外部サイトへのAPI呼び出しにより検索処理を実現しています。
本来であれば、GitHubリポジトリをforkしたうえでgit cloneし、外部サイトへのAPIのキーを設定する必要があり、それを実施していない場合は検索処理がエラーとなります。