Azure Kubernetes Service (AKS) でブルーグリーンデプロイする
こんにちは、@studio_meowtoon です。今回は、Azure Kubernetes Service (AKS) 環境でコンテナアプリをブルーグリーンデプロイする方法の一例を紹介します。
目的
Windows 11 の Linux でクラウド開発します。
こちらから記事の一覧がご覧いただけます。
実現すること
Microsoft Azure Kubernetes Service (AKS) にて、コンテナアプリのブルーグリーンデプロイを実践してみます。
この記事では、Kubernetes におけるブルーグリーンデプロイの手順を説明していますが、これが唯一の正しい方法ではありません。実際には、他にも多くの手順が存在します。したがって、この記事を参考にしながら、他の情報も収集することをお勧めします。
また、この記事では、Docker Hub に公開するパブリックアクセス可能なカスタムコンテナイメージを使用しています。実際のシステム開発では、どのレジストリからコンテナイメージを取得するかは異なる場合がありますので、適宜ご確認ください。
技術トピック
ブルーグリーンデプロイとは?
こちらを展開してご覧いただけます。
ブルーグリーンデプロイメント
ブルーグリーンデプロイは、アプリケーションの新しいバージョンを本番環境にデプロイする際に、従来の運用方法とは異なり、新しいバージョンを本番環境に構築し、そこでアプリケーションをテストした後、従来の本番環境と切り替える方法です。
内容 |
---|
通常、ブルーの本番環境とグリーンの新しい本番環境を用意します。最初はブルーがアクティブな本番環境として稼働し、グリーンはアクティブではありません。 |
新しいバージョンのアプリケーションをグリーン環境にデプロイしてテストし、問題がなければトラフィックをグリーン環境に切り替えます。 |
これにより、ダウンタイムを最小限に抑え、システムの信頼性を高めることができます。 |
開発環境
- 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
kubectl ※ こちらの関連記事からインストール方法をご確認いただけます
$ kubectl version --short
Client Version: v1.26.1
Kustomize Version: v4.5.7
Server Version: v1.26.1
Azure CLI ※ こちらの関連記事からインストール方法をご確認いただけます
$ az --version
azure-cli 2.45.0
core 2.45.0
telemetry 1.0.8
この記事では基本的に 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
ファイルの内容
package com.example.springboot.controller;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@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
コンテナイメージを Docker Hub にプッシュ
こちらの関連記事で手順がご確認いただけます。
https://qiita.com/studio_meowtoon/items/cbcfe1433885bae514db
Docker Hub にログインします。
$ docker login
Login Succeeded
v1 アプリのコンテナイメージにタグを付けます。
※ $USER の部分はご自身のコンテナリポジトリに読み替えて下さい。
$ docker tag app-hello-spring-boot:v1 $USER/app-hello-spring-boot:v1
Docker Hub に v1 アプリのコンテナイメージをプュシュします。
$ docker push $USER/app-hello-spring-boot:v1
ローカルでコンテナを起動させアプリの動作を確認します。
※ $USER の部分はご自身のコンテナリポジトリに読み替えて下さい。
$ docker run --rm --name app-local -p 8080:8080 $USER/app-hello-spring-boot:v1
$ curl http://localhost:8080/api/data/
{"message":"Hello Blue!"}
コンテナイメージをビルドし、Docker Hub にプッシュする際、キャッシュが不適切に使用され、ソースコードの変更が反映されないことがあります。この問題を解決するために、事前に Docker Hub にプッシュしたコンテナイメージをローカルで起動し、正しい動作を確認することをお勧めします。
ここまでの作業で v1 アプリのカスタムコンテナイメージが Docker Hub に公開できました。
Azure 環境にサインイン
こちらの関連記事から手順をご確認いただけます。
Azure CLI でログインします。
$ az login
Azure 環境
リソースグループ
リソースグループを作成します。
$ az group create \
--name rg-hello \
--location japaneast
AKS 環境
こちらの関連記事から手順をご確認いただけます。
AKS クラスターを作成します。
※ Kubernetes 基盤の環境作成に少し時間がかかります。
$ az aks create \
--resource-group rg-hello \
--name aks-hello \
--node-count 1 \
--generate-ssh-keys
設定ファイルの権限を修正します。
※ 初回のみ必要です。
$ chmod 600 /home/$USER/.kube/config
AKS クラスターと接続します。
※ 既存設定の上書きを許可する為、yes を選択します。
$ az aks get-credentials \
--resource-group rg-hello \
--name aks-hello
接続設定が成功した場合の出力は、以下のようになります。
Merged "aks-hello" as current context in /home/$USER/.kube/config
ここまでで Azure 上に構築した Kubernetes クラスターに接続できました。ここからはその Kubernetes クラスターをリモートでコマンド操作していきます。
AKS クラスターの操作
現在の接続先を確認します。
$ kubectl config current-context
aks-hello
v1 アプリを AKS クラスターにデプロイ
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
ファイルの内容
$USER の部分はご自身のコンテナリポジトリに書き換えて下さい。
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: $USER/app-hello-spring-boot:v1
ports:
- containerPort: 8080
v1 アプリを AKS クラスターにデプロイします。
$ kubectl apply -f deployment-v1.yaml
deployment.apps/app-v1 created
$ kubectl apply -f service-prod.yaml
service/app-service-prod created
AKS クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v1-777d85d649-r6tmk 1/1 Running 0 14m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-prod LoadBalancer 10.0.98.40 xx.xx.xx.xx 80:30472/TCP 14m
service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 27m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v1 1/1 1 1 14m
app-service-prod サービスの EXTERNAL-IP を取得します。
$ kubectl get svc app-service-prod -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
xx.xx.xx.xx
別ターミナルから curl コマンドで app-service-prod サービスの URL を確認します。
$ curl http://xx.xx.xx.xx/api/data
{"message":"Hello Blue!"}
ここまでの作業で v1 アプリの Kubernetes クラスター へのデプロイが完了しました。
v2 アプリのコンテナイメージの作成
こちらを展開して手順をご覧いただけます。
Java アプリ作成
v2 アプリのソースコードを作成します。
$ vim src/main/java/com/example/springboot/controller/HelloController.java
ファイルの内容
package com.example.springboot.controller;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@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 .
v2 アプリのコンテナイメージを確認します。
$ docker images | grep app-hello-spring-boot
app-hello-spring-boot v1 c61d32929a90 58 seconds ago 390MB
app-hello-spring-boot v2 9d54d1056fb2 9 seconds ago 390MB
コンテナイメージを Docker Hub にプッシュ
v2 アプリのコンテナイメージにタグを付けます。
※ $USER の部分はご自身のコンテナリポジトリに読み替えて下さい。
$ docker tag app-hello-spring-boot:v2 $USER/app-hello-spring-boot:v2
Docker Hub に v2 アプリのコンテナイメージをプュシュします。
$ docker push $USER/app-hello-spring-boot:v2
ローカルでコンテナを起動させアプリの動作を確認します。
※ $USER の部分はご自身のコンテナリポジトリに読み替えて下さい。
$ docker run --rm --name app-local -p 8080:8080 $USER/app-hello-spring-boot:v2
$ curl http://localhost:8080/api/data/ -w "\n"
{"message":"Hello Green!"}
ここまでの作業で v2 アプリのカスタムコンテナイメージが Docker Hub に公開できました。
v2 アプリを AKS クラスターにデプロイ
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
ファイルの内容
$USER の部分はご自身のコンテナリポジトリに書き換えて下さい。
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: $USER/app-hello-spring-boot:v2
ports:
- containerPort: 8080
v2 アプリを AKS クラスターにデプロイします。
$ kubectl apply -f deployment-v2.yaml
deployment.apps/app-v2 created
$ kubectl apply -f service-dev.yaml
service/app-service-dev created
AKS クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v1-777d85d649-r6tmk 1/1 Running 0 30m
pod/app-v2-555bf9fc8f-pj8c9 1/1 Running 0 9s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-dev LoadBalancer 10.0.196.119 zz.zz.zz.zz 80:32645/TCP 67s
service/app-service-prod LoadBalancer 10.0.98.40 xx.xx.xx.xx 80:30472/TCP 30m
service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 43m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v1 1/1 1 1 30m
deployment.apps/app-v2 1/1 1 1 2m32s
別ターミナルから curl コマンドで app-service-prod サービスの URL を確認します。
$ curl http://xx.xx.xx.xx/api/data -w "\n"
{"message":"Hello Blue!"}
app-service-prod サービスの URLをリクエストすると、更新前の v1 アプリの出力が表示されます。
app-service-dev サービスの EXTERNAL-IP を取得します。
$ kubectl get svc app-service-dev -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
zz.zz.zz.zz
別ターミナルから curl コマンドで app-service-dev サービスの URL を確認します。
$ curl http://zz.zz.zz.zz/api/data -w "\n"
{"message":"Hello Green!"}
AKS クラスターに 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 の更新を AKS クラスターにデプロイします。
$ kubectl apply -f service-prod.yaml
service/app-service-prod configured
AKS クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v1-777d85d649-r6tmk 1/1 Running 0 36m
pod/app-v2-555bf9fc8f-pj8c9 1/1 Running 0 5m55s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-dev LoadBalancer 10.0.196.119 zz.zz.zz.zz 80:32645/TCP 6m53s
service/app-service-prod LoadBalancer 10.0.98.40 xx.xx.xx.xx 80:30472/TCP 36m
service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 49m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v1 1/1 1 1 36m
deployment.apps/app-v2 1/1 1 1 8m18s
別ターミナルから curl コマンドで app-service-prod サービスの URL を確認します。
$ curl http://xx.xx.xx.xx/api/data -w "\n"
{"message":"Hello Green!"}
AKS 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 の更新を AKS クラスターにデプロイします。
$ kubectl apply -f service-prod.yaml
service/app-service-prod configured
AKS クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v1-777d85d649-r6tmk 1/1 Running 0 38m
pod/app-v2-555bf9fc8f-pj8c9 1/1 Running 0 7m46s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-dev LoadBalancer 10.0.196.119 zz.zz.zz.zz 80:32645/TCP 8m44s
service/app-service-prod LoadBalancer 10.0.98.40 xx.xx.xx.xx 80:30472/TCP 37m
service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 51m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v1 1/1 1 1 38m
deployment.apps/app-v2 1/1 1 1 10m
別ターミナルから curl コマンドで app-service-prod サービスの URL を確認します。
$ curl http://xx.xx.xx.xx/api/data -w "\n"
{"message":"Hello Blue!"}
AKS app-service-prod サービスの URLをリクエストして、更新前の v1 アプリの出力が表示されました。デプロイの切り戻しが完了しました。
デプロイ後の注意点
従量課金モデルでは、リソースの利用に応じて課金されます。新しいデプロイが正常に動作し、安定した状態になった後に、不要となったデプロイメントを削除することができます。これにより、必要以上に課金されることを防ぐことができます。
v2 アプリのデプロイに成功したので v1 アプリのデプロイメントを AKS クラスターから削除します。
$ kubectl delete deployment app-v1
deployment.apps "app-v1" deleted
AKS クラスターの状態を確認します。
$ kubectl get pods,services,deployments
NAME READY STATUS RESTARTS AGE
pod/app-v2-555bf9fc8f-pj8c9 1/1 Running 0 9m23s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-service-dev LoadBalancer 10.0.196.119 zz.zz.zz.zz 80:32645/TCP 10m
service/app-service-prod LoadBalancer 10.0.98.40 xx.xx.xx.xx 80:30472/TCP 39m
service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 52m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-v2 1/1 1 1 11m
v1 アプリのデプロイメントが削除されたことが確認できました。
まとめ
Azure Kubernetes Service (AKS) 環境で、ブルーグリーンデプロイを実践することができました。
Maven、Docker、kubectl、Azure CLI を使って、Spring Boot アプリの開発からコンテナイメージの作成、Azure 環境へのデプロイまで、すべてをターミナルから行うことができます。このように、クラウドでのシステム開発に必要なスキルや理解を深めることができます。初めての人でも簡単に手順を追うことができるので、ぜひ挑戦してみてください。
どうでしたか? 検証目的の Azure Kubernetes Service (AKS) 環境で、コンテナアプリのブルーグリーンデプロイを手軽に体験することができます。ぜひお試しください。今後も Azure の開発環境などを紹介していきますので、ぜひお楽しみにしてください。