【Google Cloud】コンテナアプリをGKEにデプロイしてみた

# はじめに
Google Cloudのドキュメントを参考にしながら実際にコンテナアプリケーションのデプロイを実施してみたので、記事にまとめてみました。

# 1. 構成

## 1-1. 使用したサービス
- Artifact Registry
	パッケージとDockerコンテナイメージを管理するためのレジストリサービス。
	Container Registryの進化系(機能を拡張している)。

- Google Kuberntes Engine
	クラウド上でKubernetesを管理・操作するためのサービス。
	Kubernetesはコンテナ化されたワークフローやサービスを管理するためのプラットフォーム。

## 1-2. イメージ図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/a4b022f1-1d21-4475-cee5-86edddc0c09f.png)

# 2. GKEの構成

## 2-1. 構成要素の説明
- クラスタ
    コンテナアプリを実行するためのノードの集合体。
    Kubernetesにおいて最も大きい概念。
- ノード
    Kubernetes環境を構成する個々のサーバー(物理サーバもしくは仮想マシン)。
    2種類のノードが存在する。
    - マスターノード
        クラスタ全体を監視・管理するためのノード。
        クライアントからの操作は、マスターノードを経由してワーカーノードに割り振られる。
    - ワーカーノード
        コンテナアプリが実際にデプロイされるノード。
- ポッド
    Kubernetesアプリケーションにおける最小単位。
    アプリのコンテナ、ストレージ、ネットワークなどのリソースをカプセル化する。

## 2-1. イメージ図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/f2b272db-aeb2-aa33-1c56-bf8a5f9132f2.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/f78fe855-062c-2572-06bb-99ca5f965c47.png)

# 3. 手順

## 3-1. API の有効化
Google Cloudのプロジェクトと課金設定が有効になっていることを確認した上で、下記2つのAPIを有効にします。

- Artifact Registry API
- Kubernetes Engine API

下記でArtifact RegistryとKubernetes EngineのAPIを一括で有効化できます。
[APIs へのアクセスの有効化](https://console.cloud.google.com/flows/enableapi?apiid=artifactregistry.googleapis.com%2Ccontainer.googleapis.com&hl=ja&_ga=2.190453099.1791385708.1661721700-829733359.1657449856)

GCPコンソールのナビゲーションメニュー→「APIとサービス」→「有効なAPIとサービス」に移動し、有効化したAPIが一覧にあれば大丈夫です。

## 3-2. リポジトリ作成
Cloud Shellで操作を行います。
下記コマンドを実行し、Artifact Registryに `hello-repo` というリポジトリを作成します。
```
gcloud artifacts repositories create hello-repo \
   --repository-format=docker \
   --location=<REGION> \
   --description="Docker repository"
```
「--location」はリポジトリを作成するリージョンを指定します。
自分は「asia-northeast1」を指定してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/2ad68d30-c45c-b5ec-c3f6-ac6695557019.png)

## 3-3. コンテナイメージのビルド
GoogleがGithubで公開している[GKEのサンプル](https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/tree/main/hello-app)を使用します。
下記のコマンドを実行し、サンプルコードをクローンします。
```
git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/f4e2a6d8-05f5-680e-1a61-ab3fb04dde9b.png)

今回は `kubernetes-engine-samples` に同梱されている `hello-app` を使用します。
ポート8080のリクエストに対して「Hello, World!」というメッセージを返すGoのWebアプリケーションになっています。

`kubernetes-engine-samples/hello-app` 内で下記のコマンドを実行し、コンテナイメージをビルドします。
```
docker build -t <REGION>-docker.pkg.dev/<PROJECT_ID>/hello-repo/hello-app:v1 .
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/0683b58d-16a3-dbc3-e10c-3487bb5f8572.png)

コンテナイメージがローカルにビルドされたことを確認します。
```
docker images
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/d1913e33-b1bc-bb84-beba-9deb321a8ee2.png)

ローカルのDockerエンジンを使用し、ビルドしたコンテナイメージが正常に動作することを確認します。
下記のコマンドでアプリを起動します。
```
docker run --rm -p 8080:8080 <REGION>-docker.pkg.dev/<PROJECT_ID>/hello-repo/hello-app:v1
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/8d6a79f2-908a-f38f-bfa1-2e4a60a96e0b.png)

Cloud Shellターミナルをもう1つ立ち上げ、curlを使用してポート8080にリクエストを送信し、「Hello, World!」というメッセージが返されたら成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/3300843c-813a-eb38-4905-bdf975bbb148.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/2729b0ce-0c6c-049a-ac5d-35a7637c78e4.png)

正常に動作することを確認したら、Ctrl+Cキーでアプリを停止します。

## 3-4. ビルドしたコンテナイメージをリポジトリにプッシュ
前述のとおり、コンテナイメージをビルドして正常に動作することを確認できました。
このコンテナイメージを先ほど作成した Artifact Registry のリポジトリにプッシュします。

はじめに、Docker用のArtifact Registryの認証を行います。
```
gcloud auth configure-docker <REGION>-docker.pkg.dev
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/6e411d99-06a5-d1b6-fed1-fb208a6a8114.png)

そして、コンテナイメージをリポジトリにプッシュします。
```
docker push <REGION>-docker.pkg.dev/<PROJECT_ID>/hello-repo/hello-app:v1
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/83758ed4-8f5f-2a08-018d-c719bb110ac8.png)

GCPコンソールからArtifact Registryのリポジトリを確認すると、プッシュしたイメージが存在することを確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/e0e8c547-1172-4596-b40c-c8b12d48bc96.png)

## 3-5. GKEクラスタ作成
GKEクラスタは2種類のモードが存在します。
- スタンダードモード  
	事前にワーカーノードのリソース情報を含んだ設定を用意する。  

- Autopilot モード  
	ワーカーノードが自動で管理されるモード。  
	コンテナが要求するリソース分のワーカーノードを自動で用意するため、ユーザ自身が管理しなくてもオートスケーリングしてくれる。

詳細は公式ドキュメントの[クラスタのタイプ](https://cloud.google.com/kubernetes-engine/docs/concepts/types-of-clusters?hl=ja#modes)を参照してください。

GKEのクラスタは、Kubernetesを実行しているCompute EngineのVMインスタンスプールで構成されています。
そのため、GKEクラスタを作成する際は、Compute Engineのゾーンまたはリージョンを指定する必要があります。

スタンダードモードは **ゾーンクラスタ** が作成されるため、ゾーンを指定します。
Autopilot モードは **リージョンクラスタ** が作成されるため、リージョンを指定します。

Cloud Shellにて下記のコマンドを使用するクラスタモードに応じて実行します。
(Artifact Registryのリポジトリと近いゾーンまたはリージョンを選択してください)

スタンダードクラスタの場合
```
gcloud config set compute/zone <COMPUTE_ZONE>
```
Autopilot クラスタの場合
```
gcloud config set compute/region <COMPUTE_REGION>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/f2fe60bc-428c-9b8a-01d3-ec54d8c8ce63.png)

そして、GKEクラスタを作成します。
今回クラスタの名前は `hello-cluster` とします。

スタンダードクラスタの場合
```
gcloud container clusters create hello-cluster
```
Autopilot クラスタの場合
```
gcloud container clusters create-auto hello-cluster
```

Autopilot モードで実施しましたが、コマンド実行後、クラスタの作成に5分ほどかかりました。
少し時間がかかるそうです。コーヒーでも飲みながら待ちましょう。

クラスタの作成が完了すると、下記のように出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/45f21434-290e-ea48-c044-bba51f290965.png)

GCPコンソールでも確認してみると、クラスタ作成されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/7595da66-bbeb-b4b5-9942-6e4eb53bdb82.png)

下記のコマンドを実行し、クラスタのノードを確認します。
```
kubectl get nodes
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/3b526fa5-98fc-a5d9-4768-911cf90ab3f8.png)

## 3-6. ビルドしたコンテナイメージをGKEにデプロイ
GKEクラスタを作成できたので、ビルドしたコンテナイメージをGKEクラスタにデプロイしていきますが、
そこで **レプリカセット** と **デプロイメント** というリソースが登場します。

- レプリカセット
    コンテナをデプロイする際に作成するポッド数を定義するためのリソース。
    例えば、特定のポッドについて「クラスタ全体で3つのポッドを常に維持する」とレプリカセットで設定すれば、kubernetesがその設定をもとにポッド数を3個維持してくれる。
    もし障害などが発生してポッドが減少した場合に、Kubernetesが別のノードで自動的にポッドを起動し、レプリカ数を維持するといったことが可能。

- デプロイメント
    レプリカセットを作成するためのリソース。    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/f4c06606-963e-e279-b3f4-c02fc237fc7a.png)

まずは、GKEクラスタの接続を確認します。
```
gcloud container clusters get-credentials hello-cluster --region <REGION>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/eacd2b82-4cd6-54bc-5e6d-e5b795d8e21c.png)

コンテナイメージのデプロイメントを作成します。
```
kubectl create deployment hello-app --image=<REGION>-docker.pkg.dev/<PROJECT_ID>/hello-repo/hello-app:v1
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/3f6cae14-0ddb-f687-9761-f1520e93fabd.png)

Pod数を3個に設定します。
```
kubectl scale deployment hello-app --replicas=3
```
Pod数を確認
```
kubectl get pod
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/ca74da0b-2a5e-86fd-9af3-a01864f14c13.png)

Horizontal Pod Autoscaler を設定します。

- Horizontal Pod Autoscaler
    CPU使用率などの負荷状況に基づき、Pod単位でオートスケーリングする機能のこと。

今回は、Podの平均CPU使用率を80%とし、CPU負荷に応じてPod数を3から1～5にスケーリングするように設定。
```
kubectl autoscale deployment hello-app --cpu-percent=80 --min=1 --max=5
```
現在のAutoscalerの状況を確認
```
kubectl get hpa
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/ecc98f65-1261-1fc3-c041-994eaa9f2e00.png)
※現在CPU使用率が0%なのは、リクエストが送られてきていないから。

## 3-7. インターネットに公開
デプロイしたアプリに外部からアクセスできるようにするため、Kubernetesの **サービス** を使用し、クラスタ内のアプリにアクセスするための静的な外部IPアドレスを設定します。

- サービス
    複数デプロイされた同一ポッドを1つのリソースにグループ化したもの。
    グループ化したポッドに対するアクセス手段をそれぞれ提供する。

デプロイメントを公開するサービスを作成します。
```
kubectl expose deployment hello-app --name=hello-app-service --type=LoadBalancer --port 80 --target-port 8080
```
「LoadBalancer」はサービスタイプの1つであり、アプリをクラスタ外部に公開するしくみです。
※サービスタイプを指定していない場合、デフォルトで「ClusterIP」が設定され、クラスタ内でのみ到達可能な IP アドレスを取得します。

サービスの詳細情報を取得します。
```
kubectl get service
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/c43da5b6-1def-7e6b-076d-1b6862e37a1f.png)

「hello-app-service」のEXTERNAL_IPをコピーします。
新しいブラウザタブを立ち上げ、コピーしたEXTERNAL_IPに移動します。
「Hello, World!」というメッセージが表示されればうまくできたと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646497/ef0d920e-4a23-968e-d390-790e53d05fc7.png)

# おわりに
今回はArtifact RegistryとGoogle Kuberntes Engineを使用してコンテナアプリのデプロイから動作確認までの一連の操作を実施しました。
GKEのローリングアップデートやクリーンアップなどの機能については、次回実践して記事にまとめてみようと思います。

# 参考資料
[コンテナ化されたウェブ アプリケーションのデプロイ](https://cloud.google.com/kubernetes-engine/docs/tutorials/hello-app?hl=ja)
[Kubernetes公式ドキュメント](https://kubernetes.io/ja/docs/home/)