Spring Cloud Function を Azure Functions v2にServerless Framework でデプロイする

はじめまして、オプティムのインダストリー事業本部 医療チームの高橋(@ukitiyan)です。
所属部門では、産業カットのサービスや案件を展開しており、私はマネージメントやアーキテクト、更には実装も担当しています。 Twitterをそろそろちゃんと始める予定です。

さて、今回は普段サーバーレスアーキテクチャを実現するためのファクタの1つとして活用しているSpring Cloud Functionで 実装したFunctionをAzure FunctionsにServerless Frameworkを利用してデプロイしてみたいと思います。
また、気になるAzure FunctionsでのJavaの取り扱いについても記事中で触れてみます。

この記事について

手順を通して、Azure上でのサーバーレスな画像リサイズシステムの構築・デプロイを体験します。

例えば不定期に取得された画像をリサイズ・最適化して AI画像解析をかけるといったユースケースにおいてEvent-Drivenに処理、つまりはサーバーレスな構成とすることで様々な恩恵を受けられます。 ただ、サーバーレスなシステムを構成した場合、Functionのプロビジョニングは割と容易に行えますが、以下の点にペインを感じることがあります。*1

  • ローカルでの動作確認
  • コード、デプロイメントのマルチクラウド対応
  • コードベースと既存CI/CDとの統合
  • Functionの周辺リソース管理
  • 環境変数の設定*2

上記ソリューションとして、Spring Cloud FunctionやServerless Frameworkを活用することが考えられます。
今回はサーバーレスプラットフォームにAzure Functionsを採用して説明していきたいと思います。
尚、AWS Lambdaや他プラットフォームで当たり前のように対応しているといった方も、Azure Functionsは2018年9月にversion2.0がGAとなったことや、Javaがプレビュー段階にあることから 情報自体が少ない状況にあるので、今回の組み合わせについては今更感の無い内容になると思います。

Spring Cloud Functionとは

Page Redirection

Spring によるサーバーレスなFunctionを実装するためのフレームワークで、以下の特徴があります。

  • Spring Boot上で動作します。
  • 同じコードでHTTPエンドポイントやKafka, RabbitMQ等のPub/Sub ストリームプロセッサ、タスクが実行できます。
  • 分離されたクラスローダーでデプロイすることができます。(JVM Packing)
  • AWS Lambda, Azure Functions, Riff 等に対するServerless Providerが用意されています。

以下の手順では、Serverless PratformへのアダプタとなるServerless Providerを通して、Azure Functionsでの実行を実現しています。

Azure Functionsとは

azure.microsoft.com

  • Azureのサーバーレスプラットフォームです。柔軟で強力なマネージドサーバーレス環境を提供してくれます。
  • AWS で言うところの、AWS Lambdaと訳されることが多いと思います。
  • 料金体系として、従量課金プランとApp Serviceプランの2種類が存在することが特徴的です。
  • また、 Azure Application Insights との統合やDevOps のエクスペリエンス、Function のオーケストレータであるDurable Fucntionsといったサーバーレスアーキテクチャを構成する上での様々なトピックが存在します。これらについては次回以降に深堀りしていければと考えています。

現在サポートしている言語は以下の通りです。

Language version 1.x version 2.x
C# GA (.NET Framework 4.7) GA (.NET Core 2)
JavaScript GA (Node 6) GA (Node 8、10)
F# GA (.NET Framework 4.7) GA (.NET Core 2)
Java 該当なし プレビュー (Java 8)
Python 試験段階 該当なし
TypeScript 試験段階 JavaScript へのトランスパイリングによってサポートされます
PHP 試験段階 該当なし
バッチ
(.cmd、.bat)
試験段階 該当なし
Bash 試験段階 該当なし
PowerShell 試験段階 該当なし

最新情報は、以下をチェックしてください。

Azure Functions でサポートされている言語 | Microsoft Docs

現在Javaについてはプレビュー段階にあり、ランタイムバージョンはJava 8です。
尚、今回のサンプルコードの実行ログからAzure Functions上で利用しているJavaビルドを確認出来るようにしています。

内容を確認すると、vendorが Azul Systems, Inc, と表示されており、Azul SystemsがビルドしたJavaであることが分かります。
当該ビルドはLTS版で2026年までサポートされるロードマップとなっており、直近でJava8のサポートが打ち切られることは無いと思われますが、併せて次バージョンの対応が待ち望まれます。

www.azul.com

Serveless Frameworkとは

serverless.com

  • 各種サーバレスプラットフォームに対してFunctionをデプロイすることのできるCLIツールです。
  • AWS, Azure, GCP等のマルチクラウドプロバイダーをサポートしています。
  • Functionのデプロイ以外に周辺リソースやEnviromentの管理も可能です。

実はAzureにはJavaで実装したFunctionをデプロイするためのCLIツールが用意されています。
Java と Maven を使用して関数を発行する - Azure Functions | Microsoft Docs

本記事のまとめにも記載しますが、現時点ではServerless Frameworkよりも純正のCLIの方がデプロイにおける性能が良いです。
しかしながら、マルチクラウドを意識したCI/CD環境を目指したかったことや、デプロイフェーズでのEnviromentを設定することに 都合が良いと判断したことから、今回はServerless Frameworkを採用しています。

サーバーレスな画像リサイズシステムのサンプル

Requirement

ローカル環境に以下がインストールされていることを確認してください。

  • Jdk 1.8
  • Maven 3.6.0
  • Node >= v6.5.0

環境変数に個別IDを設定

いくつかユニークIDを必要とする部分があるので事前に定義しておきます

$ export SLS_ID=$(($RANDOM % 9999))
$ echo $SLS_ID

Azure上に周辺リソースを構築

Azure CLIのインストール

以下を参考にAzure CLIをインストールしてください。
Azure CLI のインストール | Microsoft Docs
インストール後、 az login コマンドによりAzureへのログインを行います。

Azure BLOB Storageへの画像アップロード

以下の手順でAzure BLOB Storageのコンテナ*3を作成し、PNGファイルをアップロードしてください。

リソース グループの作成

$ az group create \
     --name slsResourceGroup \
     --location centralus

ストレージ アカウントの作成

$ az storage account create \
    --name slsstorageaccount$SLS_ID \
    --resource-group slsResourceGroup \
    --location centralus \
    --sku Standard_LRS \
    --encryption blob

Funcstionから接続するための資格情報の取得

$ az storage account keys list \
    --account-name slsstorageaccount$SLS_ID \
    --resource-group slsResourceGroup \
    --output table

資格情報をローカルPCの環境変数に設定

$ export AZURE_STORAGE_ACCOUNT="slsstorageaccount$SLS_ID"
$ export AZURE_STORAGE_ACCESS_KEY="{{上記で取得したkey1の値}}"

コンテナーの作成

$ az storage container create \
    --name slsstoragecontainer

画像のアップロード

  • 250px*250px以上のPNG画像をアップロードしてください。
$ az storage blob upload \
    --container-name slsstoragecontainer \
    --name image \
    --file image.png

ローカルのSpring Boot上での実行

ソースのダウンロード

  • 今回用意したサンプルコードをcloneします。
$ git clone https://github.com/optim-corp/thumbnail-azure-sample.git
$ cd thumbnail-azure-sample

プロパティファイルの確認

  • 必要に応じて適宜修正してください。
  • 尚、 spring.cloud.function.scan.packages の指定によりFunctionScanするパッケージを指定しています。
$ cat src/main/resources/application.yml

ローカルSpringBoot用の資格情報を環境変数に設定

  • azure.storage.account-nameazure.storage.account-key に該当するプロパティ値を環境変数にて指定します。
$ export AZURE_STORAGE_ACCOUNT-NAME=$AZURE_STORAGE_ACCOUNT
$ export AZURE_STORAGE_ACCOUNT-KEY=$AZURE_STORAGE_ACCESS_KEY

Mavenによるビルド・パッケージ

$ ./mvnw clean package

SpringBootの起動

$ java -jar target/thumbnail-0.0.1-SNAPSHOT.jar

ローカルへのテストリクエスト

  • ローカルで起動したSpringBootに対してcurlします。
  • 成功するとリサイズされた画像のBLOB名が返却されます。
$ curl -w '\n' -H 'Content-Type:application/json' -d '{"blobName":"image"}' \
 http://localhost:8080/thumbnail
{
  "blobName": "image-thumbnail"
}

サムネイル画像のダウンロード

  • 上記のBLOB名でAzure BLOB Storageにリサイズされた画像が配置されているので、ダウンロードし、リサイズされているかを確認します。
$ az storage blob download \
    --container-name slsstoragecontainer \
    --name image-thumbnail \
    --file image-thumbnail.png

無事リサイズされていればOKです 👍

Serverless FrameworkによるAzure Functionへのデプロイ

Serverless Framework、Azure Pluginのインストール

Credentialsの設定

Application settingsの設定

  • 直下の serverless.yml を確認しておきます。
  • provider.environmentセクションがApplication settingsに該当します。
  • yamlに記載すべき内容でない場合、外部から値を指定することが可能です。
provider:
  name: azure
  location: centralus
  environment:
    AZURE_STORAGE_ACCOUNT_NAME: ${self:custom.storage-account-name}
    AZURE_STORAGE_ACCOUNT_KEY: ${self:custom.storage-account-key}
    FUNCTIONS_WORKER_RUNTIME: java
    MAIN_CLASS: example.Application

デプロイ

  • コマンド実行後 resource group, function app, upload function, Running Kudu commnad に続いて Successfully created Function App と表示されれば成功です。
$ serverless deploy \
    --id $SLS_ID \ 
    --storage-account-name "${AZURE_STORAGE_ACCOUNT-NAME}" \ 
    --storage-account-key "${AZURE_STORAGE_ACCOUNT-KEY}" 

Function Keyの確認

  • Azure Functionsの Functionに対する設定である src/main/azure/thumbnail-function.json *4 を確認すると、当該FunctionのauthLevelが function となっていることがわかります。
  • authLevel=functionのEndpointにアクセスする場合には、Function Keyが必要となるため、AzureのマネージドコンソールからFunction Keyを取得します。

Azureへのテストリクエスト

  • Azure Functionsで起動しているFunctionに対してcurlします。
  • 成功するとリサイズされた画像のBLOB名が返却されます。
$ curl -w '\n' -H 'Content-Type:application/json' -d '{"blobName":"image"}' \
 https://thumbnail-azure-$SLS_ID.azurewebsites.net/api/thumbnail?code={function key}
{
  "blobName": "image-thumbnail"
}
$ az storage blob download \
    --container-name slsstoragecontainer \
    --name image-thumbnail \
    --file image-thumbnail.png

サムネイル画像のダウンロード

  • 上記のBLOB名でAzure BLOB Storageにリサイズされた画像が配置されているので、ダウンロードし、リサイズされているかを確認します。
$ az storage blob download \
    --container-name slsstoragecontainer \
    --name image-thumbnail \
    --file image-thumbnail.png

無事リサイズされていればOKです 👍 👍

リソースの削除

  • 以下のコマンドで今回作成したリソースを削除します。
$ serverless remove \
    --id $SLS_ID \ 
    --storage-account-name "${AZURE_STORAGE_ACCOUNT-NAME}" \ 
    --storage-account-key "${AZURE_STORAGE_ACCOUNT-KEY}" 
$ az storage container delete \
    --name slsstoragecontainer
$ az storage account delete \
    --name slsstorageaccount$SLS_ID \
    --resource-group slsResourceGroup
$ az group delete \
    --name slsResourceGroup

おわりに

今回は、Spring Cloud Function、Azure Functions、Serveless Frameworkの利用を前提とした構成のサンプルについて説明しました。
慣れ親しんだSpringベースのコードでサーバーレスが出来る事、SpringBootによるローカル実行が可能であることは大きなメリットです。
本記事のアウトプットをベースにEventの変更や既存 CI/CDの統合等についても、チャンレンジしてもらえればと考えています。
今回の構成では、文中にも記載した通り現時点では設定が一部冗長である等の課題はありますので、アップデートがあれば本記事を更新していきたいと思います。
オプティムでは、このようなアーキテクチャの設計・構築やOSSを活用した開発・実装に興味がある・作ってみたい人を募集しています。興味のある方は、是非こちらをご覧ください。

www.optim.co.jp

*1:監視等他にも考慮するべき点はありますが、今回の記事から外れるので内容を絞っています。

*2:Azure FunctionsのApplication settingsの設定

*3:AWSで言うところのS3 Bucketです。

*4:azure-functions-maven-pluginにより生成されるfunction.jsonが存在しますが、Serverless Frameworkで利用できない状況にあるため冗長に作成しています