Kubernetes でブルーグリーンデプロイする
こんにちは、@studio_meowtoon です。今回は、ローカル Ubuntu の Kubernetes クラスターでブルーグリーンデプロイする方法の一例を紹介します。
目的
Windows 11 の Linux でクラウド開発します。
こちらから記事の一覧がご覧いただけます。
実現すること
Windows 11 の WSL で稼働する Ubuntu の Kubernetes クラスターにて、コンテナアプリのブルーグリーンデプロイを実践してみます。
この記事では、Kubernetes におけるブルーグリーンデプロイの手順を説明していますが、これが唯一の正しい方法ではありません。実際には、他にも多くの手順が存在します。したがって、この記事を参考にしながら、他の情報も収集することをお勧めします。
技術トピック
ブルーグリーンデプロイとは?
こちらを展開してご覧いただけます。
ブルーグリーンデプロイメント
ブルーグリーンデプロイは、アプリケーションの新しいバージョンを本番環境にデプロイする際に、従来の運用方法とは異なり、新しいバージョンを本番環境に構築し、そこでアプリケーションをテストした後、従来の本番環境と切り替える方法です。
内容 |
---|
通常、ブルーの本番環境とグリーンの新しい本番環境を用意します。最初はブルーがアクティブな本番環境として稼働し、グリーンはアクティブではありません。 |
新しいバージョンのアプリケーションをグリーン環境にデプロイしてテストし、問題がなければトラフィックをグリーン環境に切り替えます。 |
これにより、ダウンタイムを最小限に抑え、システムの信頼性を高めることができます。 |
開発環境
- Windows 11 Home 22H2 を使用しています。
WSL の Ubuntu を操作していきますので macOS の方も参考にして頂けます。
WSL (Microsoft Store アプリ版) ※ こちらの関連記事からインストール方法をご確認いただけます
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
Ubuntu ※ こちらの関連記事からインストール方法をご確認いただけます
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.1 LTS
Release: 22.04
Java JDK ※ こちらの関連記事からインストール方法をご確認いただけます
$ java -version
openjdk version "11.0.18" 2023-01-17
OpenJDK Runtime Environment (build 11.0.18+10-post-Ubuntu-0ubuntu122.04)
OpenJDK 64-Bit Server VM (build 11.0.18+10-post-Ubuntu-0ubuntu122.04, mixed mode, sharing)
Maven ※ こちらの関連記事からインストール方法をご確認いただけます
$ mvn -version
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 11.0.18, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Docker ※ こちらの関連記事からインストール方法をご確認いただけます
$ docker --version
Docker version 23.0.1, build a5ee5b1
Kubernetes ※ こちらの関連記事からインストール方法をご確認いただけます
$ minikube version
minikube version: v1.29.0
commit: ddac20b4b34a9c8c857fc602203b6ba2679794d3
kubectl ※ こちらの関連記事からインストール方法をご確認いただけます
$ kubectl version --short
Client Version: v1.26.1
Kustomize Version: v4.5.7
Server Version: v1.26.1
この記事では基本的に Ubuntu のターミナルで操作を行います。Vim を使用してコピペする方法を初めて学ぶ人のために、以下の記事で手順を紹介しています。ぜひ挑戦してみてください。
ブルーグリーンデプロイする仕様
※ この記事での仕様となります。
識別するタグ | 内容 | エンドポイント | JSON レスポンス |
---|---|---|---|
v1 | 現用のサービス | /api/data | {"message":"Hello Blue!"} |
v2 | 更新するサービス | /api/data | {"message":"Hello Green!"} |
Kubernetes マニュフェスト
No | kind | ファイル | 内容 |
---|---|---|---|
1 | Service | service-prod.yaml | 本番環境 エンドポイント |
2 | Service | service-dev.yaml | 検証環境 エンドポイント |
3 | Deployment | deployment-v1.yaml | 現用のアプリケーション |
4 | Deployment | deployment-v2.yaml | 更新するアプリケーション |
ブルーグリーンデプロイする手順
v1 アプリのコンテナイメージの作成
こちらを展開して手順をご覧いただけます。
Java アプリ作成
こちらの関連記事で手順がご確認いただけます。
https://qiita.com/studio_meowtoon/items/37ac0082a3228e4de570
プロジェクトフォルダに移動します。
※ ~/tmp/hello-spring-boot をプロジェクトフォルダとします。
$ cd ~/tmp/hello-spring-boot
v1 アプリのソースコードを作成します。
$ vim src/main/java/com/example/springboot/controller/HelloController.java
ファイルの内容
※ コードの内容を一部省略しています。
@RestController
@RequestMapping("/api")
public class HelloController {
@GetMapping("/data")
public Map<String, String> getData() {
Map<String, String> map = Map.of("message", "Hello Blue!");
return map;
}
}
v1 アプリをビルドします。
$ mvn clean package
コンテナイメージ作成
こちらの関連記事で手順がご確認いただけます。
https://qiita.com/studio_meowtoon/items/9c07e20b4124d8c5f972
Docker デーモンを起動します。
$ sudo service docker start
* Starting Docker: docker [ OK ]
v1 アプリをコンテナイメージにビルドします。
$ docker build --no-cache -t app-hello-spring-boot:v1 .
v1 アプリのコンテナイメージを確認します。
$ docker images | grep app-hello-spring-boot
app-hello-spring-boot v1 c61d32929a90 58 seconds ago 390MB
ここまでの作業で v1 アプリのカスタムコンテナイメージが作成できました。
v1 アプリを Kubernetes クラスターにデプロイ
コンテナイメージの取り込み
こちらの関連記事で手順がご確認いただけます。
Kubernetes クラスターを開始します。
$ minikube start
v1 アプリのコンテナイメージを Minikube に取り込みます。
$ minikube image load app-hello-spring-boot:v1
Minikube 上のコンテナイメージを確認します。
$ minikube image ls | grep app-hello-spring-boot
docker.io/library/app-hello-spring-boot:v1
Kubernetes マニュフェスト作成
本番環境 service マニュフェストファイルを作成します。
$ vim service-prod.yaml
ファイルの内容
apiVersion: v1
kind: Service
metadata:
name: app-service-prod
spec:
type: LoadBalancer
selector:
app: app
version: v1
ports:
- port: 80
targetPort: 8080
v1 アプリ deployment マニュフェストファイルを作成します。
$ vim deployment-v1.yaml
ファイルの内容
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v1
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
version: v1
spec:
containers:
- name: app
image: app-hello-spring-boot:v1
ports:
- containerPort: 8080
Kubernetes クラスター接続先を確認します。
$ kubectl config current-context
minikube
v1 アプリを Kubernetes クラスターにデプロイします。
$ kubectl apply -f deployment-v1.yaml
deployment.apps/app-v1 created
$ kubectl apply -f service-prod.yaml
service/app-service-prod created
Kubernetes クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v1-56dddcc497-s9gd6 1/1 Running 0 19m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-prod LoadBalancer 10.101.34.176 <pending> 80:31015/TCP 19m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 21m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v1 1/1 1 1 19m
Minikube はローカルで稼働する Kubernetes クラスターであり、LoadBalancer タイプのサービスに外部 IP を割り当てる機能が備わっていないため、EXTERNAL-IP が <pending> になります。しかし、minikube service コマンドを使用することで、Minikube の仮想マシン上で、Service リソースに割り当てられた LoadBalancer にアクセスすることができます。
別ターミナルから app-service-prod に接続する URL を取得します。
$ minikube service app-service-prod
🎉 デフォルトブラウザーで default/app-service-prod サービスを開いています...
👉 http://127.0.0.1:41087
❗ Docker ドライバーを linux 上で使用しているため、実行するにはターミナルを開く必要があります。
別ターミナルから curl コマンドで app-service-prod サービスの URL を確認します。
$ curl http://127.0.0.1:41087/api/data
{"message":"Hello Blue!"}
ここまでの作業で v1 アプリの Kubernetes クラスター へのデプロイが完了しました。
v2 アプリのコンテナイメージの作成
こちらを展開して手順をご覧いただけます。
Java アプリ作成
v2 アプリのソースコードを作成します。
$ vim src/main/java/com/example/springboot/controller/HelloController.java
ファイルの内容
※ コードの内容を一部省略しています。
@RestController
@RequestMapping("/api")
public class HelloController {
@GetMapping("/data")
public Map<String, String> getData() {
Map<String, String> map = Map.of("message", "Hello Green!");
return map;
}
}
v2 アプリをビルドします。
$ mvn clean package
コンテナイメージ作成
v2 アプリをコンテナイメージにビルドします。
$ docker build --no-cache -t app-hello-spring-boot:v2 .
v1 アプリのコンテナイメージを確認します。
$ docker images | grep app-hello-spring-boot
app-hello-spring-boot v2 c61d32929a90 58 seconds ago 390MB
ここまでの作業で v2 アプリのカスタムコンテナイメージが作成できました。
v2 アプリを Kubernetes クラスターにデプロイ
コンテナイメージの取り込み
v2 アプリのコンテナイメージを Minikube に取り込みます。
$ minikube image load app-hello-spring-boot:v2
Minikube 上のコンテナイメージを確認します。
$ minikube image ls | grep app-hello-spring-boot
docker.io/library/app-hello-spring-boot:v2
docker.io/library/app-hello-spring-boot:v1
Kubernetes マニュフェスト作成
検証環境 service マニュフェストファイルを作成します。
$ vim service-dev.yaml
ファイルの内容
apiVersion: v1
kind: Service
metadata:
name: app-service-dev
spec:
type: LoadBalancer
selector:
app: app
version: v2
ports:
- port: 80
targetPort: 8080
v2 アプリ deployment マニュフェストファイルを作成します。
$ vim deployment-v2.yaml
ファイルの内容
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v2
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
version: v2
spec:
containers:
- name: app
image: app-hello-spring-boot:v2
ports:
- containerPort: 8080
v2 アプリを Kubernetes クラスターにデプロイします。
$ kubectl apply -f deployment-v2.yaml
deployment.apps/app-v2 created
$ kubectl apply -f service-dev.yaml
service/app-service-dev created
Kubernetes クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v1-56dddcc497-s9gd6 1/1 Running 0 34m
pod/app-v2-6b587b579-zpxlp 1/1 Running 0 26s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-dev LoadBalancer 10.106.214.150 <pending> 80:32513/TCP 13s
service/app-service-prod LoadBalancer 10.101.34.176 <pending> 80:31015/TCP 34m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v1 1/1 1 1 34m
deployment.apps/app-v2 1/1 1 1 26s
別ターミナルから app-service-dev に接続する URL を取得します。
$ minikube service app-service-dev
🎉 デフォルトブラウザーで default/app-service-dev サービスを開いています...
👉 http://127.0.0.1:34167
❗ Docker ドライバーを linux 上で使用しているため、実行するにはターミナルを開く必要があります。
別ターミナルから curl コマンドで app-service-prod サービスの URL を確認します。
$ curl http://127.0.0.1:41087/api/data
{"message":"Hello Blue!"}
app-service-prod サービスの URLをリクエストすると、更新前の v1 アプリの出力が表示されます。
別ターミナルから curl コマンドで app-service-dev サービスの URL を確認します。
$ curl http://127.0.0.1:34167/api/data
{"message":"Hello Green!"}
Kubernetes クラスターに v2 アプリを、app-service-dev サービスの URL からアクセス可能なアプリとしてデプロイすることができました。また、実際にリリースする前に app-service-dev サービスの URL を利用して動作確認を行うことができます。これは、ブルーグリーンデプロイの目的の1つです。
新しいデプロイメントへの切り替え
app-service-prod サービスの URL の向き先を deployment-v2 デプロイメントに切り替えます。
$ vim service-prod.yaml
ファイルの内容
apiVersion: v1
kind: Service
metadata:
name: app-service-prod
spec:
type: LoadBalancer
selector:
app: app
version: v2
ports:
- port: 80
targetPort: 8080
app-service-prod の更新を Kubernetes クラスターにデプロイします。
$ kubectl apply -f service-prod.yaml
service/app-service-prod configured
Kubernetes クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v1-56dddcc497-s9gd6 1/1 Running 0 44m
pod/app-v2-6b587b579-zpxlp 1/1 Running 0 10m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-dev LoadBalancer 10.106.214.150 <pending> 80:32513/TCP 10m
service/app-service-prod LoadBalancer 10.101.34.176 <pending> 80:31015/TCP 44m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 46m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v1 1/1 1 1 44m
deployment.apps/app-v2 1/1 1 1 10m
別ターミナルから curl コマンドで app-service-prod サービスの URL を確認します。
$ curl http://127.0.0.1:41087/api/data
{"message":"Hello Green!"}
app-service-prod サービスの URLをリクエストして、更新後の v2 アプリの出力が表示されました。本番環境へのデプロイ作業が完了しました。
トラブル発生時の切り戻し
サービスを最新バージョンにデプロイした後にトラブルが発生した場合、以前に正常に動作していたデプロイに戻す必要があります。これは、ブルーグリーンデプロイの目的の1つです。
app-service-prod サービスの URL の向き先を deployment-v1 デプロイメントに切り戻します。
$ vim service-prod.yaml
ファイルの内容
apiVersion: v1
kind: Service
metadata:
name: app-service-prod
spec:
type: LoadBalancer
selector:
app: app
version: v1
ports:
- port: 80
targetPort: 8080
app-service-prod の更新を Kubernetes クラスターにデプロイします。
$ kubectl apply -f service-prod.yaml
service/app-service-prod configured
Kubernetes クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v1-56dddcc497-s9gd6 1/1 Running 0 49m
pod/app-v2-6b587b579-zpxlp 1/1 Running 0 15m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-dev LoadBalancer 10.106.214.150 <pending> 80:32513/TCP 15m
service/app-service-prod LoadBalancer 10.101.34.176 <pending> 80:31015/TCP 49m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 51m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v1 1/1 1 1 49m
deployment.apps/app-v2 1/1 1 1 15m
別ターミナルから curl コマンドで app-service-prod サービスの URL を確認します。
$ curl http://127.0.0.1:41087/api/data
{"message":"Hello Blue!"}
app-service-prod サービスの URLをリクエストして、更新前の v1 アプリの出力が表示されました。デプロイの切り戻しが完了しました。
デプロイ後の注意点
v2 アプリのデプロイに成功したので v1 アプリのデプロイメントを Kubernetes クラスターから削除します。
$ kubectl delete deployment app-v1
deployment.apps "app-v1" deleted
Kubernetes クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v2-6b587b579-zpxlp 1/1 Running 0 27m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-dev LoadBalancer 10.106.214.150 <pending> 80:32513/TCP 26m
service/app-service-prod LoadBalancer 10.101.34.176 <pending> 80:31015/TCP 60m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 63m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v2 1/1 1 1 27m
v1 アプリのデプロイメントが削除されたことが確認できました。
まとめ
WSL Ubuntu の Kubernetes クラスターでブルーグリーンデプロイを実践することができました。
クラウド開発においては、Kubernetes の基本的な理解は重要です。また、Ubuntu を使用することで Linux の知識も身につけることができます。最初は難しく感じるかもしれませんが、少しずつ進めていくことで自信を持って取り組むことができるようになります。
どうでしたか? WSL Ubuntu で、Kubernetes クラスターでのブルーグリーンデプロイを手軽に体験することができます。ぜひお試しください。今後もクラウド開発手法などを紹介していきますので、ぜひお楽しみにしてください。
推奨コンテンツ