Azure App Service でブルーグリーンデプロイする

# Azure App Service でブルーグリーンデプロイする

こんにちは、@studio_meowtoon です。今回は、Azure App Service 環境で JAR ファイル形式のアプリをブルーグリーンデプロイする方法を紹介します。
![spring-boot_on_azure-app-service.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/0f7c3e5c-effe-caae-9adf-3589c544ede1.png)

## 目的

Windows 11 の Linux でクラウド開発します。

こちらから記事の一覧がご覧いただけます。

https://qiita.com/studio_meowtoon/items/6119ab05d8b13527efd8

## 実現すること

[Microsoft Azure App Service](https://learn.microsoft.com/ja-jp/azure/app-service/overview) にて、JAR ファイル形式のアプリのブルーグリーンデプロイを実践してみます。

## 技術トピック

### ブルーグリーンデプロイとは？  

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### [ブルーグリーンデプロイメント](https://martinfowler.com/bliki/BlueGreenDeployment.html)

:::note info
ブルーグリーンデプロイは、アプリケーションの新しいバージョンを本番環境にデプロイする際に、従来の運用方法とは異なり、新しいバージョンを本番環境に構築し、そこでアプリケーションをテストした後、従来の本番環境と切り替える方法です。

|内容|
|:--|
通常、ブルーの本番環境とグリーンの新しい本番環境を用意します。最初はブルーがアクティブな本番環境として稼働し、グリーンはアクティブではありません。|
|新しいバージョンのアプリケーションをグリーン環境にデプロイしてテストし、問題がなければトラフィックをグリーン環境に切り替えます。|
|これにより、ダウンタイムを最小限に抑え、システムの信頼性を高めることができます。|

:::

</details>

## 開発環境

+ Windows 11 Home 22H2 を使用しています。  

:::note info
[WSL](https://learn.microsoft.com/ja-jp/windows/wsl/about) の Ubuntu を操作していきますので macOS の方も参考にして頂けます。  
:::

<details>

WSL (Microsoft Store アプリ版) [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
```

Ubuntu [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
```

Java JDK [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/4d11e94a2389758759cd)

```sh
$ 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 [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/f4cdc4039158153365fb)

```sh
$ 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
```

</details>

:::note warn
この記事では基本的に **Ubuntu のターミナルで操作**を行います。**Vim を使用してコピペする方法**を初めて学ぶ人のために、以下の記事で手順を紹介しています。ぜひ挑戦してみてください。
:::

https://qiita.com/studio_meowtoon/items/7771ad48676926e41463

## ブルーグリーンデプロイする仕様
※ この記事での仕様となります。

|識別するタグ|内容|エンドポイント|JSON レスポンス|
|:--|:--|:--|:--|
|v1|現用のサービス|/api/data|{"message":"Hello **Blue**!"}|
|v2|更新するサービス|/api/data|{"message":"Hello **Green**!"}|

**JAR ファイル形式のアプリ**

|No|ファイル|内容|
|:--|:--|:--|
|1|app.jar|現用のアプリケーション|
|2|app.jar|更新するアプリケーション|

## ブルーグリーンデプロイする手順

### v1 アプリの JAR ファイル作成

<details>
<summary>こちらを展開して手順をご覧いただけます。</summary>

#### Java アプリ作成

こちらの関連記事で手順がご確認いただけます。 

https://qiita.com/studio_meowtoon/items/37ac0082a3228e4de570

プロジェクトフォルダに移動します。  
※ ~/tmp/hello-spring-boot をプロジェクトフォルダとします。    

```sh
$ cd ~/tmp/hello-spring-boot
```

v1 アプリのソースコードを作成します。

```sh
$ vim src/main/java/com/example/springboot/controller/HelloController.java
```

ファイルの内容

```java: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 アプリをビルドします。

```sh
$ mvn clean package
```

</details>

:::note info
ここまでの作業で **v1 アプリとして、JAR ファイル**が作成できました。
:::

### v1 アプリを Azure Spring Apps にデプロイ

#### ログイン

Azure CLI で Azure にログインします。  

```sh
$ az login
```

こちらの関連記事で手順がご確認いただけます。 

https://qiita.com/studio_meowtoon/items/9721041f9e6e3e987cf0

#### リソースグループ

リソースグループを作成します。

```sh:Microsoft.Resources/resourceGroups
$ az group create \
    --name rg-hello \
    --location japaneast
```

#### App Service プラン

こちらの関連記事で手順がご確認いただけます。 

https://qiita.com/studio_meowtoon/items/c3f70597291ef1b3679a

App Service プランを作成します。 

:::note alert
--sku **Standard** からブルーグリーンデプロイ(スロット)に対応しています。 
:::

```sh:Microsoft.Web/serverfarms
$ az appservice plan create \
    --resource-group rg-hello \
    --name asp-hello \
    --sku S1 \
    --is-linux
```

#### App Service Web アプリ

Web アプリの作成を行います。 

```sh:Microsoft.Web/sites
$ az webapp create \
    --resource-group rg-hello \
    --plan asp-hello \
    --name app-hello-spring-boot \
    --runtime "JAVA:11-java11"
```

Web アプリのスタートアップコマンドを設定します。  

```sh
$ az webapp config set \
    --resource-group rg-hello \
    --name app-hello-spring-boot \
    --startup-file "java -jar /home/site/wwwroot/app.jar --server.port=80"
```

v1 Web アプリのデプロイを行います。
※ デフォルトで運用スロットにデプロイされます。  

```sh
$ az webapp deploy \
    --resource-group rg-hello \
    --name app-hello-spring-boot \
    --src-path target/app.jar \
    --type jar
```

別ターミナルから curl コマンドで **URL** を確認します。

```sh
$ curl http://app-hello-spring-boot.awesomewebsites.net/api/data
{"message":"Hello Blue!"}
```
#### Azure Portal の確認

[アプリ] → [デプロイ スロット] を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/80855b53-ed11-511c-45b1-80e5dec27d47.png)
v1 アプリが、**運用**スロットに設定されていることが確認できます。

:::note info
ここまでの作業で **v1 アプリ**の Azure App Service への**デプロイが完了**しました。
:::

### v2 アプリの JAR ファイル作成

<details>
<summary>こちらを展開して手順をご覧いただけます。</summary>

#### Java アプリ作成

v2 アプリのソースコードを作成します。

```sh
$ vim src/main/java/com/example/springboot/controller/HelloController.java
```

ファイルの内容

```java: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 アプリをビルドします。

```sh
$ mvn clean package
```

</details>

:::note info
ここまでの作業で **v2 アプリとして、JAR ファイル**が作成できました。
:::

### v2 アプリを Azure App Service にデプロイ

#### App Service Web アプリ

**staging** スロットを作成します。

```sh
$ az webapp deployment slot create \
    --resource-group rg-hello \
    --name app-hello-spring-boot \
    --slot staging
```

<details>
<summary>説明を開きます。</summary>

:::note info
このコマンドは、App Service Web アプリのデプロイ スロットを作成しています。

|コマンド|内容|
|:--|:--|
|az webapp deployment slot create|App Service Web アプリのデプロイ スロットを作成するためのコマンドです。|

|オプション|値|内容|
|:--|:--|:--|
|--resource-group|rg-hello|App Service Web アプリを作成するリソースグループの名前を指定します。|
|--name|app-hello-spring-boot|App Service Web アプリの名前を指定します。|
|--slot|staging|App Service Web アプリに設定するスロットの名前を指定します。|
:::
</details>

v2 Web アプリを **staging** スロットにデプロイします。

```sh
$ az webapp deploy \
    --resource-group rg-hello \
    --name app-hello-spring-boot \
    --src-path target/app.jar \
    --type jar \
    --slot staging
```

<details>
<summary>説明を開きます。</summary>

:::note info
このコマンドは、App Service Web アプリに新しいコードをデプロイします。

|コマンド|内容|
|:--|:--|
|az webapp deploy|App Service Web アプリに新しいコードをデプロイするためのコマンドです。|

|オプション|値|内容|
|:--|:--|:--|
|--resource-group|rg-hello|App Service Web アプリを含むリソースグループの名前を指定します。|
|--name|app-hello-spring-boot|App Service Web アプリの名前を指定します。|
|--src-path|target/app.jar|App Service Web アプリにデプロイするコードの場所を指定します。|
|--type|jar|App Service Web アプリにデプロイするコードが JAR ファイルであることを指定します。|
|--slot|staging|App Service Web アプリをデプロイするスロットの名前を指定します。|
:::

</details>

別ターミナルから curl コマンドで **URL** を確認します。

```sh
$ curl http://app-hello-spring-boot.awesomewebsites.net/api/data
{"message":"Hello Blue!"}
```

:::note warn
アプリの**URL**をリクエストすると、更新前の **v1 アプリの出力が表示**されます。
:::

#### Azure Portal の確認

デプロイ スロットは、App Service (スロット) というリソースとして作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/2d6b707b-61b2-8bd0-7a3b-f7fff49e4713.png)
[アプリ] → [デプロイ スロット] を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/2d0202b3-e92a-8f9c-94e0-6779e3a21f14.png)
ここでは、v2 アプリは app-hello-spring-boot-**staging** スロットに設定されています。

ターミナルから curl コマンドで **staging URL** を確認します。

```sh
$ curl http://app-hello-spring-boot-staging.awesomewebsites.net/api/data
{"message":"Hello Green!"}
```

:::note info
アプリを更新するために、v2 アプリを本番環境の **staging** スロットにデプロイすることができました。また、実際にリリースする前に **staging URL** を利用して**動作確認**を行うことができます。これは、**ブルーグリーンデプロイの目的の1つ**です。
:::

### 新しいアプリへの切り替え

**staging** スロットの中身と**運用** スロットの中身を入れ替えます。

```sh
$ az webapp deployment slot swap \
    --resource-group rg-hello \
    --name app-hello-spring-boot \
    --slot staging
```

<details>
<summary>説明を開きます。</summary>

:::note info
このコマンドは、App Service Web アプリのデプロイ スロットをスワップしています。

|コマンド|内容|
|:--|:--|
|az webapp deployment slot swap|App Service Web アプリのデプロイ スロットをスワップするためのコマンドです。|

|オプション|値|内容|
|:--|:--|:--|
|--resource-group|rg-hello|App Service Web アプリを作成するリソースグループの名前を指定します。|
|--name|app-hello-spring-boot|App Service Web アプリの名前を指定します。|
|--slot|staging|**運用**スロットとスワップするデプロイ スロットの名前を指定します。|
:::
</details>

スロットの中身の入れ替えができました。

別ターミナルから curl コマンドで **URL** を確認します。

```sh
$ curl http://app-hello-spring-boot.awesomewebsites.net/api/data
{"message":"Hello Green!"}
```

:::note info
アプリの**URL**をリクエストすると、更新後の **v2 アプリの出力が表示**されます。**本番環境へのデプロイ作業が完了**しました。※ この記事での本番環境となります。
:::

#### Azure Portal の確認

[アプリ] → [デプロイ スロット] を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/326641d3-5953-946c-9fab-8ff1d05c0b42.png)
**運用**と **staging** の表示はコマンド実行前と変化ありません。

:::note warn
Azure App Service でデプロイ スロットをスワップする場合、**スワップ後**に**アプリの内容が入れ替わります**。ただし、スワップ後にどちらが v1 のアプリであり、どちらが v2 のアプリであったかを示す情報はありません。このため、スワップ後の状況を確認するためには、ログやモニタリングツールなどが必要となる場合があります。
:::

### トラブル発生時の切り戻し

:::note alert
サービスを最新バージョンにデプロイした後に**トラブルが発生**した場合、以前に**正常に動作**していた**デプロイに戻す**必要があります。これは、**ブルーグリーンデプロイの目的の1つ**です。
:::

再度デプロイ スロットの中身をスワップして切り戻します。

```sh
$ az webapp deployment slot swap \
    --resource-group rg-hello \
    --name app-hello-spring-boot \
    --slot staging
```

別ターミナルから curl コマンドで **URL** を確認します。

```sh
$ curl http://app-hello-spring-boot.awesomewebsites.net/api/data
{"message":"Hello Blue!"}
```

アプリの **URL** をリクエストして、更新前の **v1 アプリの出力が表示**されました。

#### Azure Portal の確認

[アプリ] → [デプロイ スロット] を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/326641d3-5953-946c-9fab-8ff1d05c0b42.png)
**運用**と **staging** の表示はコマンド実行前と変化ありませんが、**切り戻し**された状況です。

### デプロイ後の注意点

:::note alert
App Service の従量課金モデルでは、リソースの利用に応じて**課金**されます。新しいデプロイが正常に動作し、安定した状態になった後に、不要となったデプロイ スロットを削除することができます。これにより、必要以上に課金されることを防ぐことができます。
:::

必要のないデプロイ スロットを削除します。

```sh
$ az webapp deployment slot delete \
    --resource-group rg-hello \
    --name app-hello-spring-boot \
    --slot staging
```

<details>
<summary>説明を開きます。</summary>

:::note info
このコマンドは、App Service Web アプリのデプロイ スロットを削除しています。

|コマンド|内容|
|:--|:--|
|az webapp deployment slot delete|App Service Web アプリのデプロイ スロットを削除するためのコマンドです。|

|オプション|値|内容|
|:--|:--|:--|
|--resource-group|rg-hello|App Service Web アプリを作成するリソースグループの名前を指定します。|
|--name|app-hello-spring-boot|App Service Web アプリの名前を指定します。|
|--slot|staging|App Service Web アプリの削除するスロットの名前を指定します。|
:::
</details>

#### Azure Portal の確認

[アプリ] → [デプロイ スロット] を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/5f7feace-d193-ef33-a471-f9370cf80821.png)
**staging** スロットが削除されたことを確認できました。

## まとめ

Azure App Service 環境で、ブルーグリーンデプロイを実践することができました。

:::note warn
**Maven**、**Azure CLI** を使って、Spring Boot の JAR ファイル形式アプリの作成、Azure 環境へのデプロイまで、**すべてをターミナルから行うことができます**。このように、クラウドでのシステム開発に必要なスキルや理解を深めることができます。初めての人でも簡単に手順を追うことができるので、ぜひ挑戦してみてください。
:::

どうでしたか？ 検証目的の Azure App Service 環境で、JAR ファイル形式アプリのブルーグリーンデプロイを手軽に体験することができます。ぜひお試しください。今後も Azure の開発環境などを紹介していきますので、ぜひお楽しみにしてください。

## 推奨コンテンツ

https://qiita.com/studio_meowtoon/items/12bd250b02f8e37e3031