Azure Functions に Spring Cloud Function で関数アプリをデプロイする (Docker Hub カスタムコンテナイメージ)

# Azure Functions に Spring Cloud Function で関数アプリをデプロイする (Docker Hub カスタムコンテナイメージ)

こんにちは、@studio_meowtoon です。今回は、Azure Functions 環境で Spring Cloud Function 関数アプリを、カスタムコンテナイメージから起動する方法を紹介します。
![spring-boot_on_azure-functions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/37675e09-0d43-7f69-28dd-0f7b766a3a69.png)

## 目的

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

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

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

## 実現すること

Microsoft Azure Functions に、カスタムコンテナイメージの Spring Cloud Function 関数アプリをデプロイします。 

## 技術トピック

### Microsoft Azure Functions とは？  

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

### [Microsoft Azure Functions](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-overview)

:::note info
Azure Functions は、クラウド上で実行されるサーバーレスのコンピューティングサービスで、アプリケーションを開発するために使用されます。

|特徴とメリット|
|:--|
|Azure Functions を使用することで、開発者はサーバーの設定や管理などの面倒な作業をせずに、コードを実行することができます。|
|Azure Functions は、イベントに応答して実行されることが一般的で、トリガーと呼ばれる外部イベントが発生すると、自動的にコードを実行します。トリガーには、HTTP リクエスト、メッセージキュー、ファイルの変更などがあります。|
|Azure Functions は、多くのプログラミング言語をサポートしており、C#、Java、JavaScript、Python などが利用可能です。|
|また、Azure Functions は、Azure の他のサービスとの統合が容易で、Azure Event Grid、Azure Cosmos DB、Azure Blob Storage などのサービスと簡単に連携することができます。|

:::

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

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

```sh
$ docker --version
Docker version 23.0.1, build a5ee5b1
```

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

```sh
$ az --version
azure-cli                         2.45.0
core                              2.45.0
telemetry                          1.0.8
```

</details>

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

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

## 作成する Web アプリケーションの仕様

|No|エンドポイント|HTTPメソッド|MIME タイプ|
|:--|:--|:--|:--|
|1|/api/data|GET|application/json|

## Hello World を表示する手順 

### Spring Boot 関数アプリの作成  

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

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

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

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

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

### アプリをビルド

Java アプリをビルドします。
※ target/app.jar が作成されます。

```sh
$ mvn clean package
```

:::note info
ここまでの手順で、ローカル環境の **Ubuntu** に アプリの**JAR ファイル**をビルドすることができました。
:::

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

Dockerfile を作成します。

```sh
$ vim Dockerfile
```

ファイルの内容

:::note info
こちらの Dockerfile は、[**Microsoft** から公開された内容](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-create-function-linux-custom-image?tabs=in-process%2Cbash%2Cazure-cli%2Cv1&pivots=programming-language-java)です。
:::

```sh:Dockerfile
ARG JAVA_VERSION=17
# This image additionally contains function core tools – useful when using custom extensions
#FROM mcr.microsoft.com/azure-functions/java:4-java$JAVA_VERSION-core-tools AS installer-env
FROM mcr.microsoft.com/azure-functions/java:4-java$JAVA_VERSION-build AS installer-env

COPY . /src/java-function-app
RUN cd /src/java-function-app && \
    mkdir -p /home/site/wwwroot && \
    mvn clean package && \
    cd ./target/azure-functions/ && \
    cd $(ls -d */|head -n 1) && \
    cp -a . /home/site/wwwroot

# This image is ssh enabled
FROM mcr.microsoft.com/azure-functions/java:4-java$JAVA_VERSION-appservice
# This image isn't ssh enabled
#FROM mcr.microsoft.com/azure-functions/java:4-java$JAVA_VERSION

ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]
```

Docker デーモンを起動します。

```sh
$ sudo service docker start
 * Starting Docker: docker    [ OK ]
```

:::note warn
Docker 環境をお持ちでない場合は、以下の関連記事から Docker Engine のインストール手順をご確認いただけます。
:::

https://qiita.com/studio_meowtoon/items/3bceee25ac67cc73ebbe

コンテナイメージをタグ付きでビルドします。

:::note alert
ここで **$USER** という埋め込み変数は、**Ubuntu** のシェル変数 **USER** のことです。つまり、カスタムコンテナイメージを **Docker Hub** にプッシュする際に指定する**アカウント名**と、**Ubuntu** の**ユーザー名**を同じにすると、イメージの取り扱いが簡単になります。ただし、この方法は検証用途に限定されます。
:::

```sh
$ docker build --no-cache --tag $USER/func-hello-spring-boot:latest .
```

コンテナイメージを確認します。

```sh
$ docker images | grep func-hello-spring-boot
REPOSITORY                     TAG       IMAGE ID       CREATED             SIZE
$USER/func-hello-spring-boot   latest    69f1a501ab0a   About an hour ago   1.33GB
```

### コンテナを起動

ローカルでコンテナを起動します。
※ コンテナを停止するときは ctrl + C を押します。

```sh
$ docker run --rm \
    --name app-local \
    -p 8080:80 \
    -it $USER/func-hello-spring-boot:latest
```

### コンテナの動作確認

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

```sh
$ curl http://localhost:8080/api/data -w "\n"
{
  "message": "Hello World!"
}
```

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

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

Docker Hub にログインします。  

```sh
$ docker login
Login Succeeded
```

Docker Hub にコンテナイメージをプュシュします。

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

### シェル変数の作成

:::note alert
**シェル変数**として、**Ubuntu** に以下の値を作成します。状況により、関数アプリの名前を変える必要があります。
:::

```sh
$ location_name=japaneast
$ resource_group_name=rg-hello
$ appservice_plan_name=asp-hello
$ functionapp_name=func-hello-spring-boot
$ storage_account_name=st`date "+%Y%m%d"`hello
$ container_image_name=func-hello-spring-boot:latest
```

### Azure 環境にサインイン

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

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

Azure CLI でログインします。  

```sh
$ az login
```

### Azure 環境

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

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

```sh:Microsoft.Resources/resourceGroups
$ az group create \
    --name $resource_group_name \
    --location $location_name
```

#### ストレージアカウント

ストレージアカウントを作成します。

```sh:Microsoft.Storage/storageAccounts
$ az storage account create \
    --location $location_name \
    --resource-group $resource_group_name \
    --name $storage_account_name \
    --sku Standard_LRS
```

#### Azure Functions プラン

Azure Functions プランを作成します。

```sh:Microsoft.Web/serverfarms
$ az functionapp plan create \
    --location $location_name \
    --resource-group $resource_group_name \
    --name $appservice_plan_name \
    --number-of-workers 1 \
    --sku EP1 \
    --is-linux
```

#### 関数アプリ

関数アプリを作成します。

```sh:Microsoft.Web/sites
$ az functionapp create \
    --resource-group $resource_group_name \
    --plan $appservice_plan_name \
    --storage-account $storage_account_name \
    --name $functionapp_name \
    --deployment-container-image-name $USER/$container_image_name \
    --functions-version 4 \
    --runtime custom
```

ストレージアカウントの接続文字列をシェル変数 **connection_string** に取得します。

```sh
$ connection_string=$(az storage account show-connection-string \
    --resource-group $resource_group_name \
    --name $storage_account_name \
    --query connectionString \
    --output tsv)
```

ストレージアカウントの接続文字列を確認します。

```sh
$ set | grep connection_string
```

ストレージアカウントの接続文字列設定を関数アプリに追加します。

```sh
$ az functionapp config appsettings set \
    --resource-group $resource_group_name \
    --name $functionapp_name \
    --settings AzureWebJobsStorage=$connection_string
```

#### Azure Portal の確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/0daea85f-b0ae-c997-7883-04f03b3976c7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/ee37963c-b381-7dbe-7b80-12924dc72d73.png)

:::note info
ここまでの手順で、**Azure** に**必要なリソース**と、**関数アプリ**が作成されたことが確認できました。
:::

### アプリの動作確認

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

```sh
$ curl https://$functionapp_name.azurewebsites.net/api/data -w "\n"
{
  "message": "Hello World!"
}
```

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

### コンテナに接続

関数アプリに SSH 接続します。

```sh:Microsoft.Web/sites
$ az webapp ssh \
    --resource-group $resource_group_name \
    --name $functionapp_name
```

コンテナに接続後にディレクトリを確認します。 
※ コンテナから出るときは ctrl + D を押します。  

```
# pwd
/root
# cd /home/site/wwwroot
# ls -lah
total 40K
drwxr-xr-x 1 root root 4.0K Mar 22 08:28 .
drwxr-xr-x 1 root root 4.0K Mar 22 08:05 ..
-rw-r--r-- 1 root root 7.2K Mar 22 08:04 app.jar
drwxr-xr-x 2 root root 4.0K Mar 22 08:05 data
-rw-r--r-- 1 root root  169 Mar 21 23:09 host.json
drwxr-xr-x 2 root root 4.0K Mar 22 08:05 lib
-rw-r--r-- 1 root root  190 Mar 21 23:09 local.settings.json
drwxr-xr-x 2 root root 4.0K Mar 22 08:28 log
```

top コマンドで状況を確認します。

```sh
top - 10:08:20 up 3 days,  9:43,  1 user,  load average: 0.19, 0.54, 0.50
Tasks:   8 total,   1 running,   7 sleeping,   0 stopped,   0 zombie
%Cpu0  :  4.7 us,  3.4 sy,  2.3 ni, 80.2 id,  9.4 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   3299.6 total,     98.6 free,   1542.1 used,   1658.9 buff/cache
MiB Swap:   2048.0 total,   1986.6 free,     61.4 used.   1566.1 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   24 root      30  10 3190108 180268 108476 S   4.0   5.3   0:23.58 Microsoft.Azure
    1 root      30  10    3904   2948   2648 S   0.0   0.1   0:00.27 bash
   23 root      30  10   13356   3956   3408 S   0.0   0.1   0:00.00 sshd
   44 root      30  10 2566148 109632  24640 S   0.0   3.2   0:03.02 java
   76 root      30  10 2549776  77032  24384 S   0.0   2.3   0:01.53 java
  123 root      30  10   13468   7944   6888 S   0.0   0.2   0:00.04 sshd
  126 root      30  10    4168   3412   2920 S   0.0   0.1   0:00.00 bash
  129 root      30  10    7000   3228   2756 R   0.0   0.1   0:00.00 top
```

<details>
<summary>詳細を表示します。</summary>

lsof コマンドをインストールしてプロセスに紐づいた JAR ファイルを確認してみます。

```
# apt install lsof
```

```sh:※一部を表示
# lsof -p 44 | grep "\.jar$"
java    44 root    5r     REG    0,68  19014435 2407036 /azure-functions-host/workers/java/azure-functions-java-worker.jar
java    44 root    28r    REG    0,68      7288 2410226 /home/site/wwwroot/app.jar
```

```sh:※一部を表示
# lsof -p 76 | grep "\.jar$"
java    76 root     4r     REG    0,68  19014435 2407036 /azure-functions-host/workers/java/azure-functions-java-worker.jar
java    76 root    28r     REG    0,68      7288 2410226 /home/site/wwwroot/app.jar
```

:::note warn
なぜ2つの **Java** のプロセスがそれぞれ動作していて、また、それが Azure Functions for Java の仕様なのかどうかは、現時点ではわかりませんでした。
:::

</details>

コンテナの情報を表示してみます。

```
# cat /etc/*-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```

:::note warn
このコンテナは Debian Linux をベースに作成されています。つまり、Debian Linux と同じように扱うことができます。
:::

## まとめ

Azure Functions 環境で、カスタムコンテナイメージから Spring Boot Web サービスを起動することができました。

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

どうでしたか？ 検証目的として、WSL Ubuntu で、Spring Boot Web アプリケーションを Azure Functions 環境で手軽に起動することができます。ぜひお試しください。今後も Azure の開発環境などを紹介していきますので、ぜひお楽しみにしてください。

## 参考資料

https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-create-function-linux-custom-image?tabs=in-process%2Cbash%2Cazure-cli%2Cv1&pivots=programming-language-java