0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Azure Functions に Spring Cloud Function で関数アプリをデプロイする

Last updated at Posted at 2023-03-21

Azure Functions に Spring Cloud Function で関数アプリをデプロイする

こんにちは、@studio_meowtoon です。今回は、Azure Functions 環境で Spring Cloud Function 関数アプリを起動する方法を紹介します。
spring-boot_on_azure-functions.png

目的

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

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

実現すること

Microsoft Azure Functions に、JAR ファイル形式の Spring Cloud Function 関数アプリをデプロイします。

技術トピック

Microsoft Azure Functions とは?

こちらを展開してご覧いただけます。

Microsoft Azure Functions

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 などのサービスと簡単に連携することができます。

開発環境

  • 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

Azure CLI ※ こちらの関連記事からインストール方法をご確認いただけます

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

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

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

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

Hello World を表示する手順

Azure Functions Core Tools のインストール

※ 初回のみ必要です。

$ curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg

Functions Core Tool を含むリポジトリを登録します。

$ sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list'

Functions Core Tool をインストールします。

$ sudo apt update
$ sudo apt install azure-functions-core-tools-4

Functions Core Tool のバーションを確認します。

$ func --version
4.0.4895

ここまでの作業で、UbuntuAzure Functions Core Tool をインストールすることができました。

プロジェクトの作成

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

$ mkdir -p ~/tmp/hello-spring-func
$ cd ~/tmp/hello-spring-func

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

アプリケーションクラスを作成します。

$ mkdir -p src/main/java/com/example/springboot
$ vim src/main/java/com/example/springboot/Application.java

ファイルの内容

Application.java
package com.example.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

関数クラスの作成

関数クラスを作成します。

$ mkdir -p src/main/java/com/example/springboot/function
$ vim src/main/java/com/example/springboot/function/Hello.java

ファイルの内容

Data.java
package com.example.springboot.function;

import java.util.Map;
import java.util.function.Function;

import reactor.core.publisher.Mono;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component("data")
public class Hello implements Function<Mono<Map<String, String>>, Mono<Map<String, String>>> {

    private static final Logger log = LoggerFactory.getLogger(Hello.class);

    public Mono<Map<String, String>> apply(Mono<Map<String, String>> mono) {
        return mono.map(map -> Map.of("message", "Hello World!"))
            .doOnNext(map -> log.info("Sending message: {}", map));
    }
}

Azure 関数の作成

Azure 関数を作成します。

$ vim src/main/java/com/example/springboot/function/HelloHandler.java

ファイルの内容

HelloHandler.java
package com.example.springboot.function;

import java.util.Map;
import java.util.Optional;

import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;

import org.springframework.cloud.function.adapter.azure.FunctionInvoker;

public class HelloHandler extends FunctionInvoker<Map<String, String>, Map<String, String>> {

    @FunctionName("data")
    public HttpResponseMessage execute(
        @HttpTrigger(
            name = "request", 
            methods = { HttpMethod.GET, HttpMethod.POST }, 
            authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<Map<String, String>>> request, ExecutionContext context) {
        context.getLogger().info(HelloHandler.class + " is called.");
        return request
                .createResponseBuilder(HttpStatus.OK)
                .body(handleRequest(Map.of("", ""), context))
                .header("Content-Type", "application/json")
                .build();
    }
}

Azure 構成ファイルの作成

host.json ファイルを作成します。

$ mkdir -p src/main/azure
$ vim src/main/azure/host.json

ファイルの内容

host.json
{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  },
  "functionTimeout": "00:10:00"
}

local.settings.json ファイルを作成します。

$ vim src/main/azure/local.settings.json
local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "java",
    "FUNCTIONS_EXTENSION_VERSION": "~4",
    "AzureWebJobsDashboard": ""
  }
}

ログの出力設定

  • application.properties ファイルを作成します。
  • logback-spring.xml ファイルを作成します。

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

検証時点では、logback-spring.xml 設定で springProfile 要素の削除が必要でした。こちらについては、運用時の設定切り替えが不可能となるため調査を継続します。

pom.xml の作成

pom.xml ファイルを作成します。

$ vim pom.xml

検証時点では、logback-corelogback-classicslf4j-api 依存の明示的な設定が必要でした。

ファイルの内容

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>3.0.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>func-hello-spring-boot</artifactId>
    <version>1.0</version>
    <name>func-hello-spring-boot</name>

    <properties>
        <azure.functions.maven.plugin.version>1.22.0</azure.functions.maven.plugin.version>
        <azure.functions.java.library.version>3.0.0</azure.functions.java.library.version>
        <spring.cloud.function.dependencies>4.0.0</spring.cloud.function.dependencies>
        <start-class>com.example.springboot.Application</start-class>

        <!-- customize those two properties. The functionAppName should be unique across Azure -->
        <functionAppRegion>japaneast</functionAppRegion>
        <functionResourceGroup>rg-example</functionResourceGroup>
        <functionAppServicePlanName>asp-example</functionAppServicePlanName>
        <functionAppName>func-example</functionAppName>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-function-adapter-azure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-function-webflux</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- Log -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-function-dependencies</artifactId>
                <version>${spring.cloud.function.dependencies}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.microsoft.azure.functions</groupId>
                <artifactId>azure-functions-java-library</artifactId>
                <version>${azure.functions.java.library.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <finalName>app</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>com.microsoft.azure</groupId>
                    <artifactId>azure-functions-maven-plugin</artifactId>
                    <version>${azure.functions.maven.plugin.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.3.0</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>3.4.0</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-clean-plugin</artifactId>
                </plugin>
                <plugin>
                    <groupId>io.gatling</groupId>
                    <artifactId>gatling-maven-plugin</artifactId>
                    <version>4.1.5</version>
                    <configuration>
                        <includes>
                            <include>com.example.loadtest.*</include>
                        </includes>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>com.microsoft.azure</groupId>
                <artifactId>azure-functions-maven-plugin</artifactId>
                <configuration>
                    <region>${functionAppRegion}</region>
                    <resourceGroup>${functionResourceGroup}</resourceGroup>
                    <appName>${functionAppName}</appName>
                    <appServicePlanName>${functionAppServicePlanName}</appServicePlanName>
                    <pricingTier>${functionPricingTier}</pricingTier>
                    <hostJson>${project.basedir}/src/main/azure/host.json</hostJson>
                    <localSettingsJson>${project.basedir}/src/main/azure/local.settings.json</localSettingsJson>
                    <runtime>
                        <os>linux</os>
                        <javaVersion>${java.version}</javaVersion>
                    </runtime>
                    <appSettings>
                        <!-- Run Azure Function from package file by default -->
                        <property>
                            <name>FUNCTIONS_EXTENSION_VERSION</name>
                            <value>~4</value>
                        </property>
                    </appSettings>
                </configuration>
                <executions>
                    <execution>
                        <id>package-functions</id>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

アプリをビルド

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

$ mvn clean package

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

アプリを起動

ローカル環境の Ubuntu でアプリを起動して確認します。

$ mvn azure-functions:run

アプリの動作確認 (※ローカル)

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

$ curl -v http://localhost:7071/api/data -w "\n" 
*   Trying 127.0.0.1:7071...
* Connected to localhost (127.0.0.1) port 7071 (#0)
> GET /api/data HTTP/1.1
> Host: localhost:7071
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Tue, 21 Mar 2023 09:07:35 GMT
< Server: Kestrel
< Transfer-Encoding: chunked
<
{
  "message": "Hello World!"
* Connection #0 to host localhost left intact
}

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

pom.xml の修正

pom.xml ファイルを修正します。

仮の設定値を、実際に関数アプリを Azure にデプロイする際の設定値に修正します。状況により、関数アプリの名前を変更する必要があります。

$ vim pom.xml

ファイルの内容

pom.xml ※一部抜粋
<properties>
    ※省略
    <!-- customize those two properties. The functionAppName should be unique across Azure -->
    <functionAppRegion>japaneast</functionAppRegion>
    <functionResourceGroup>rg-hello</functionResourceGroup>
    <functionAppServicePlanName>asp-hello</functionAppServicePlanName>
    <functionAppName>func-hello-spring-boot</functionAppName>
    ※省略
</properties>

Azure にサインイン

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

Azure CLI でログインします。

$ az login

アプリのデプロイ

Azure に関数アプリをデプロイします。

ここでは、Mavenプラグインから Azure へ関数アプリのデプロイを実行しています。

$ mvn clean package
$ mvn azure-functions:deploy

Azure Portal の確認

image.png
image.png

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

アプリの動作確認

Ubuntu にシェル変数を作成します。

$ resource_group_name=rg-hello
$ functionapp_name=func-hello-spring-boot

関数アプリの FQDN をシェル変数 functionapp_fqdn として取得します。

$ functionapp_fqdn=$(az functionapp show \
    --resource-group $resource_group_name \
    --name $functionapp_name \
    --query 'hostNames' \
    --output tsv)

関数アプリの FQDN を確認します。

$ set | grep functionapp_fqdn
functionapp_fqdn=func-hello-spring-boot.awesomewebsites.net

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

$ curl https://$functionapp_fqdn/api/data -w '\n'
{
  "message": "Hello World!"
}

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

まとめ

Azure Functions 環境で、JAR ファイル形式の Spring Boot Web サービスを起動することができました。

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

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?