Spring Boot WEBサービスを ACA (Azure Container Apps) + Dapr 環境で起動する

# Spring Boot WEBサービスを ACA (Azure Container Apps) + Dapr 環境で起動する

## 目的

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

## 実現すること

Microsoft Azure Container Apps (ACA) に Spring Boot WEBアプリの Docker イメージをデプロイ、[**Dapr 拡張機能**](https://learn.microsoft.com/ja-jp/azure/container-apps/dapr-overview?tabs=bicep1%2Cyaml)を有効にして起動します。

## 補足

Spring Boot + Dapr 連携の初歩的な動作について以前の記事を参考にして頂けます。  

+ [Spring Boot REST API に Dapr API からアクセスする手順 (基本編)](https://qiita.com/hiroxpepe/items/428b5d1b45ccb397abe7)
+ [Spring Boot REST API に Dapr API からアクセスする手順 (コンテナ編)](https://qiita.com/hiroxpepe/items/22b34bef2cf181f1a48c)

## 開発環境

+ 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
```

Dapr [※ Dapr CLI をインストールする手順](https://qiita.com/hiroxpepe/items/070c6efe6e84c683e336)

```sh
$ dapr --version
CLI version: 1.10.0
Runtime version: 1.10.0
```

</details>

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

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

### Spring Boot WEBアプリの作成

#### プロジェクトフォルダの作成

```sh
$ cd ~
$ mkdir -p tmp/hello-spring-dapr
$ cd ~/tmp/hello-spring-dapr
```

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

#### アプリケーションクラスの作成

※ 構成を単純にする為に全ての要素を記述しています。  

```sh
$ mkdir -p src/main/java/com/example/springdapr
$ vim src/main/java/com/example/springdapr/SpringbootApplication.java
```

ファイルの内容

```java:SpringbootApplication.java
package com.example.springdapr;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }

    @GetMapping("/hello")
    public String hello() {
        return "Hello World!";
    }
}
```

#### pom.xml の作成

```sh
$ vim pom.xml
```

ファイルの内容

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.8</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>hello-spring-dapr</artifactId>
    <version>1.0</version>
    <name>hello-spring-dapr</name>

    <properties>
        <java.version>11</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
```

### Java アプリビルド

ビルド  
※ target/app.jar が作成されます。

```sh
$ mvn clean install
```

### Docker イメージビルド

Dockerfile 作成

```sh
$ vim Dockerfile
```

ファイルの内容

```sh:Dockerfile
FROM adoptopenjdk/openjdk11:jdk-11.0.11_9-alpine-slim

WORKDIR /app

COPY target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java","-jar","app.jar"]
```

ビルド

```sh
$ docker build -t app-hello-spring-boot .
```

確認

```sh
$ docker images | grep app-hello-spring-boot
app-hello-spring-boot   latest   10c6cdc26e44   52 seconds ago   278MB
```

app-hello-spring-boot イメージが作成されました。  

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

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

### Azure CLI でサインイン

[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>
```

#### コンテナ レジストリ

コンテナ レジストリを作成  
※ --sku Free では作成できません。  
※ --name <name> はパブリックで一意の値が求められます。

```sh:Microsoft.ContainerRegistry/registries
$ az acr create \
    --resource-group rg-hello \
    --name cr20230212 \
    --sku Basic
```

コンテナ レジストリ一覧表示

```sh
$ az acr list
```

コンテナ レジストリログイン資格情報を表示

```sh
$ az acr update -n cr20230212 --admin-enabled true
$ az acr credential show \
    --resource-group rg-hello \
    --name cr20230212
```

```json
{
  "passwords": [
    {
      "name": "password",
      "value": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    },
    {
      "name": "password2",
      "value": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    }
  ],
  "username": "cr20230212"
}
```

※ コンテナ レジストリを削除する場合

```sh
$ az acr delete --n <acr-name>
```

コンテナ レジストリにログイン

```sh
$ az acr login --name cr20230212
```

Docker イメージをコンテナ レジストリにプュシュ

```sh
$ docker tag app-hello-spring-boot cr20230212.azurecr.io/app-hello-spring-boot:latest
$ docker push cr20230212.azurecr.io/app-hello-spring-boot:latest
```

コンテナ レジストリのイメージを確認

```sh
$ az acr repository list --name cr20230212 --output table
Result
---------------------
app-hello-spring-boot
```

ローカルの Docker イメージを Azure コンテナ レジストリにプュシュすることが出来ました。

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

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

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

コンテナ アプリ環境の作成  
※ 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>
```

#### コンテナ アプリ

コンテナ アプリの作成とデプロイ

```sh:Microsoft.App/containerApps
$ az containerapp create \
    --resource-group rg-hello \
    --environment cae-hello \
    --name ca-hello-spring-boot \
    --image cr20230212.azurecr.io/app-hello-spring-boot:latest \
    --target-port 8080 \
    --ingress 'external' \
    --registry-server cr20230212.azurecr.io \
    --registry-username cr20230212 \
    --registry-password XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
    --min-replicas 1 \
    --enable-dapr true \
    --dapr-app-id app-hello-spring \
    --dapr-app-port 8080 \
    --dapr-app-protocol http \
    --dapr-enable-api-logging
```

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

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

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

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

Azure ポータルからコンテナ アプリのコンソールを操作します。  
※ Azure の構成ではデフォルトでは Dapar API はクラウド環境の外部に公開されていません。  

コンテナ アプリに curl をインストール

```m
# apk update
# apk apk add curl
# curl --version
curl 7.79.1 (x86_64-alpine-linux-musl) libcurl/7.79.1 OpenSSL/1.1.1k zlib/1.2.11 brotli/1.0.9 nghttp2/1.43.0
```

コンテナ アプリから Dapr API をリクエストして確認

```sh
$ curl -X GET http://localhost:3500/v1.0/invoke/app-hello-spring/method/hello
Hello World!
```

Azure Container Apps にデプロイしたコンテナ アプリから Dapr API 経由で "Hello World!" が取得出来ました。  

## まとめ

+ Azure Container Apps にデプロイした Spring Boot アプリの REST API を Dapr API 経由で呼び出すことが出来ました。
    + Azure Container Apps では Dapr の **Service invocation API** は**コンテナ アプリ側の設定だけ**で使用出来るようです。
+ この記事の例ではまだ Spring Boot 側のアプリに Dapr への依存関係はありません。
+ 今後さらに Spring Boot から Dapr クライアントオブジェクトを操作する方法を学ぶ必要があります。

## 参考

公式 [Azure Container Apps コマンド リファレンス](https://learn.microsoft.com/ja-jp/cli/azure/service-page/azure%20container%20apps?view=azure-cli-latest)