Spring Boot WEBサービスを ACA (Azure Container Apps) で起動する (Docker Hub カスタムコンテナイメージ)

# Spring Boot WEBサービスを ACA (Azure Container Apps) で起動する (Docker Hub カスタムコンテナイメージ)

## 目的

[Spring Boot WEBサービス](https://qiita.com/hiroxpepe/items/4a0f5e077155ce41e7ac)を [Azure Container Apps](https://learn.microsoft.com/ja-jp/azure/container-apps/overview) 環境で起動して理解を深めます。  

## 実現すること

Microsoft Azure Container Apps (ACA) に Spring Boot WEBアプリのカスタムコンテナイメージをデプロイします。  

:::note warn
この記事では、Docker Hub にプュシュした**パブリックアクセス**のカスタムコンテナイメージを使用しています。 実際のシステムを構築される際にはご注意ください。
:::

Microsoft Azure Container Registry (ACR) を使用した構成の例として、以下の記事もございます。  

https://qiita.com/hiroxpepe/items/a054d91abfa645d55165

## 技術背景

### Microsoft Azure Container Apps (ACA) とは？  

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

### [Microsoft Azure Container Apps (ACA)](https://learn.microsoft.com/ja-jp/azure/container-apps/overview)

Azure 上で実行されるコンテナアプリケーションのプラットフォームです。  

ACA は、Docker コンテナと Kubernetes クラスタの環境をサポートしており、開発者は Docker コンテナイメージを作成して、Kubernetes リソースとしてデプロイすることができます。また、カスタムドメイン名をサポートしているため、自社のブランドを維持することができます。  

ACA の主なメリットは以下のとおりです。  

#### 簡単なセットアップとデプロイ

ACA は、簡単なユーザーインターフェイスを備えており、数回のクリックでコンテナアプリケーションをセットアップしてデプロイすることができます。

#### スケーラビリティ

ACA は、自動的にスケーリングを行うことができます。アプリケーションに必要なリソースが増加した場合、ACA は自動的に必要なリソースを追加して処理を分散します。

#### セキュリティ

ACA は、コンテナアプリケーションを実行する際に必要なセキュリティ機能を提供しています。例えば Azure Active Directory との統合や、コンテナレベルでのアクセス制御などがあります。

#### コスト効率

ACA は、必要なときに必要なリソースを追加するため、無駄なリソースの使用を最小限に抑えることができます。また、必要なリソースに対してのみ支払いを行うため、コスト効率的に利用することができます。

</details>

## 開発環境

+ Windows 11 Home 22H2 を使用しています。  
+ [WSL](https://learn.microsoft.com/ja-jp/windows/wsl/about) の Ubuntu を操作していきますので macOS の方も参考にして頂けます。  

<details>

WSL (Microsoft Store アプリ版)

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

Ubuntu

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

Java JDK [※ Java JDK の導入と Hello World!](https://qiita.com/hiroxpepe/items/8650664e466906a57996)

```sh
$ java -version
openjdk version "11.0.17" 2022-10-18
OpenJDK Runtime Environment (build 11.0.17+8-post-Ubuntu-1ubuntu222.04)
OpenJDK 64-Bit Server VM (build 11.0.17+8-post-Ubuntu-1ubuntu222.04, mixed mode, sharing)
```

Maven [※ Maven の導入と Hello World!](https://qiita.com/hiroxpepe/items/40f7f199b9be7f9e3b5b)

```sh
$ mvn -version
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 11.0.17, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
```

Docker Desktop

```sh
Version 4.16.3 (96739)
```

```sh
$ docker --version
Docker version 20.10.22, build 3a2c30b
```

```sh
$ docker-compose --version
Docker Compose version v2.15.1
```

</details>

※ この記事では基本的に Ubuntu のターミナルで操作を行います。

## "Hello World" を表示する手順

### Spring Boot WEBサービスの作成

こちらを参照して頂けます。  

https://qiita.com/hiroxpepe/items/4a0f5e077155ce41e7ac

### プロジェクトフォルダに移動  

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

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

### Java アプリをビルド(※参考)  

※ target/app.jar が作成されます。  
※ 下の コンテナイメージを作成する操作でも同時に target/app.jar が作成されるので必須ではありません。  

```sh
$ mvn clean install
```

### コンテナイメージを作成

コンテナイメージをビルド  

※ ローカルの Docker 環境 (Docker Desktop) に app-hello-spring-boot コンテナイメージが作成されます。  
※ コンテナイメージの作成時間が Unix エポックになるのは spring-boot:build-image の仕様です。  

```sh
$ mvn spring-boot:build-image \
    -Dspring-boot.build-image.imageName=app-hello-spring-boot
```

コンテナイメージの確認

```sh
$ docker images | grep app-hello-spring-boot
app-hello-spring-boot   latest   e37cc77f2b36   43 years ago   262MB
```

ローカルでコンテナイメージを起動して確認

```sh
$ docker run --name app-local -p 8080:8080 app-hello-spring-boot
```

別ターミナルから curl コマンドで確認  

※ 8080 ポートでリッスン出来ているか確認します。  

```
$ curl -v http://localhost:8080/api/data
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /api/data HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 27 Feb 2023 07:18:25 GMT
<
* Connection #0 to host localhost left intact
{"message":"Hello World!"}
```

### Docker Hub にコンテナイメージを登録  

※ $USER の部分はご自身のコンテナレジストリに読み替えて下さい。  

タグ付け

```sh
$ docker tag app-hello-spring-boot $USER/app-hello-spring-boot:latest
```

プュシュ

```sh
$ docker push $USER/app-hello-spring-boot:latest
```

Docker Hub に $USER/app-hello-spring-boot:latest としてカスタムコンテナイメージが公開出来ました。  

### Azure のアカウントを取得

公式 [Azure の無料アカウントを使ってクラウドで構築](https://azure.microsoft.com/ja-jp/free/)

### Azure CLI でサインイン

こちらを参照して頂けます。  

https://qiita.com/hiroxpepe/items/20aeb54a676f190d5c96

ログイン

```sh
$ az login
```

※ 最新バージョンに更新する場合

```sh
$ az upgrade
```

### Azure 環境

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

リソースグループを作成

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

リソースグループ一覧表示

```sh
$ az group list
```

※ リソースグループを削除する場合

```sh
$ az group delete -n <group>
```

#### コンテナ アプリ環境

拡張機能をインストール ※初回のみ

<details>
<summary>手順を表示します。</summary>

##### 拡張機能

インストール

```sh
$ az extension add --name containerapp --upgrade
```

Microsoft.App 名前空間を登録

```sh
$ az provider register --namespace Microsoft.App
```

Microsoft.OperationalInsights プロバイダーを登録

```sh
$ az provider register --namespace Microsoft.OperationalInsights
```

</details>

コンテナ アプリ環境の作成  
※ Kubernetes 基盤の環境だと思われるので少し時間がかかります。  

```sh:Microsoft.App/managedEnvironments
$ az containerapp env create \
    --resource-group rg-hello \
    --name cae-hello \
    --location japaneast
```

コンテナ アプリ環境の一覧表示

```sh
$ az containerapp env list
```

※ コンテナ アプリ環境を削除する場合

```sh
$ az containerapp env delete -n <env-name> -g <group>
```

#### コンテナ アプリ

コンテナ アプリの作成とデプロイ  
※ $USER の部分はご自身のコンテナレジストリに読み替えて下さい。  

```sh:Microsoft.App/containerApps
$ az containerapp create \
    --resource-group rg-hello \
    --environment cae-hello \
    --name ca-hello-spring-boot \
    --image $USER/app-hello-spring-boot:latest \
    --target-port 8080 \
    --ingress 'external' \
    --min-replicas 1
```

コンテナ アプリが作成されました。

```sh
Container app created. Access your app at https://ca-hello-spring-boot.agreeablepond-79ba8527.japaneast.azurecontainerapps.io/
```

※ コンテナ アプリを削除する場合

```sh
$ az containerapp delete -n <name> -g <group>
```

WEBブラウザで確認  
※ URLは環境で読み替えて下さい。

```sh
https://ca-hello-spring-boot.agreeablepond-79ba8527.japaneast.azurecontainerapps.io/api/data
```

WEBブラウザに {"message":"Hello World!"} と表示され、JSON データを取得することが出来ました。  

※ 別ターミナルから curl コマンドで確認

```
$ curl -v https://ca-hello-spring-boot.agreeablepond-79ba8527.japaneast.azurecontainerapps.io/api/data
*   Trying 20.210.29.255:443...
* Connected to ca-hello-spring-boot.agreeablepond-79ba8527.japaneast.azurecontainerapps.io (20.210.29.255) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=WA; L=Redmond; O=Microsoft Corporation; CN=agreeablepond-79ba8527.japaneast.azurecontainerapps.io
*  start date: Feb 25 03:06:09 2023 GMT
*  expire date: Feb 20 03:06:09 2024 GMT
*  subjectAltName: host "ca-hello-spring-boot.agreeablepond-79ba8527.japaneast.azurecontainerapps.io" matched cert's "*.agreeablepond-79ba8527.japaneast.azurecontainerapps.io"
*  issuer: C=US; O=Microsoft Corporation; CN=Microsoft Azure TLS Issuing CA 01
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* Using Stream ID: 1 (easy handle 0x55c86464be80)
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> GET /api/data HTTP/2
> Host: ca-hello-spring-boot.agreeablepond-79ba8527.japaneast.azurecontainerapps.io
> user-agent: curl/7.81.0
> accept: */*
>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
< HTTP/2 200
< content-type: application/json
< date: Mon, 27 Feb 2023 07:31:07 GMT
<
* Connection #0 to host ca-hello-spring-boot.agreeablepond-79ba8527.japaneast.azurecontainerapps.io left intact
{"message":"Hello World!"}
```

ターミナルに {"message":"Hello World!"} と表示され、JSON データを取得することが出来ました。  

※ Azure 側で SSL/TLS対応と HTTP/2プロトコルを使用してくれます。  

コンテナ アプリに接続

```sh
$ az containerapp exec \
    --resource-group rg-hello \
    --name ca-hello-spring-boot 
INFO: Connecting to the container 'ca-hello-spring-boot'...
Use ctrl + D to exit.
INFO: Successfully connected to container: 'ca-hello-spring-boot'. 
```

コンテナに接続後

```
$ pwd
/workspace
$ ls -la
total 20
drwxr-xr-x 1 cnb  cnb  4096 Jan  1  1980 .
drwxr-xr-x 1 root root 4096 Feb 27 07:30 ..
drwxr-xr-x 1 cnb  cnb  4096 Jan  1  1980 BOOT-INF
drwxr-xr-x 3 cnb  cnb  4096 Jan  1  1980 META-INF
drwxr-xr-x 3 cnb  cnb  4096 Jan  1  1980 org
```

コンテナの情報

```sh
$ cat /etc/*-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.6 LTS"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
NAME="Ubuntu"
ID=ubuntu
PRETTY_NAME="Paketo Buildpacks Base Bionic"
VERSION_ID="18.04"
HOME_URL="https://github.com/paketo-buildpacks/bionic-base-stack"
UBUNTU_CODENAME=bionic
VERSION="18.04.6 LTS (Bionic Beaver)"
ID_LIKE=debian
SUPPORT_URL="https://github.com/paketo-buildpacks/bionic-base-stack/blob/main/README.md"
BUG_REPORT_URL="https://github.com/paketo-buildpacks/bionic-base-stack/issues/new"
VERSION_CODENAME=bionic
```

## まとめ

+ Ubuntu のシンプルな構成の Java 開発環境で  Spring Boot WEBサービスのカスタムコンテナイメージを ACA (Azure Container Apps) 環境で起動させることが出来ました。

## 個人的見解

+ Azure でコンテナ化されたアプリをデプロイする環境としては一番分かりやすいと思いました。
    + JVM コンテナで Hollo World するだけなら、世の中の PaaS で一番初心者向きではないでしょうか？ 環境依存の特殊な設定などなく、素の JVM コンテナイメージをそのまま実行出来ました。
    + 今後、他の Azure サービスでカスタムコンテナイメージを使用してアプリをデプロイする方式を比較したいと思います。
+ メリット
    + SSH の設定なしてデフォルトでコンテナに接続出来るのは手軽で良いと思います。
+ デメリット
    + 強いて上げれば初回の環境の構築に時間がかかります。

## 参考

##### [Microsoft Learn] Azure Container Apps コマンド リファレンス

https://learn.microsoft.com/ja-jp/cli/azure/service-page/azure%20container%20apps?view=azure-cli-latest