投稿日
NablarchアプリをAzureで動かした事例の紹介
もくじ
- はじめに
- システム構成
- コンテナを実行するサービス
- ストレージサービス
- メール送信機能は使用しない
- セキュリティについて
- 準備作業・前提条件
- アプリケーションのDockerイメージを作成する
- notifierをビルドする
- backendをビルドする
- frontendをビルドする
- DockerイメージをAzure Container Registryにアップロードする
- 各種ストレージサービスを作成する
- Azure Blob Storage
- Azure Database for PostgreSQL
- Azure Cache for Redis
- App Serviceでコンテナインスタンスを作成する
- App Serviceプランを作成する
- 各Webアプリを作成する
- 環境変数を設定する
- 動作確認
- おわりに
はじめに
Nablarchには、2020年9月29日にリリースされた5u18でクラウドネイティブ対応というものが入っています。 これは、NablarchをAWSやAzureなどのクラウド環境で利用しやすくするためのアップデートです。 詳細は、Nablarch 5u18をリリースしました!を参照してください。
このクラウドネイティブ対応によって、NablarchはAzureでも利用できるようになっています。 実際にAzure上でNablarchを使ったアプリケーションを動かしてみたので、本記事ではその手順について解説します。
なお、Nablarchを使ったアプリケーションには、サービス開発リファレンスで公開されているチャットアプリの ver 1.1(以下example-chat)を使用しました。 サービス開発リファレンスについては、SPA + REST API構成のサービス開発リファレンスを参照してください。
システム構成
システムの構成は、上図のようにしました。
コンテナを実行するサービス
example-chatは、以下3つのコンポーネントで構成されています。
- frontend
- Reactで構築されたSPA画面
- backend
- RESTful Web APIを提供するJavaアプリケーション
- notifier
- WebSocketでチャットのメッセージをサーバーからプッシュするJavaアプリケーション
これらのコンポーネントは、Dockerコンテナ化してApp ServiceのWeb App for Containersで動かします。
AzureにはDockerコンテナを動かすサービスとしてContainer Instancesというサービスもありますが、今回はApp Serviceを選択しました。 サービスの選択には、こちらのチャートを利用しました。 このチャートによると、マイクロサービスでないWebアプリにはApp Serviceが向いているそうです。
DockerコンテナのイメージはContainer Registryにあらかじめプッシュしておき、App Serviceからプルして使用します。
ストレージサービス
アプリケーションが使用するストレージには、以下のサービスを使用しています。
- Azure Database for PostgreSQL
- example-chatが使用する様々なデータを永続化します
- Azure Cache for Redis
- セッションなどの一時情報の保存に使用したり、backendからnotifierにメッセージを送るためのキューとして使用します
- Azure Blob Storage
- アップロードされた画像ファイルなどを保存するオブジェクトストレージとして使用します
メール送信機能は使用しない
example-chatは、サインアップのときにメールを送信して本人確認を行います。
メールの送信には、AWSであればAmazon Simple Email Service (SES)というメール送信のためのサービスがありますが、Azureには独自のメール送信サービスはありません。 代わりに、SendGridのようなサードパーティのメール送信サービスを使用することが推奨されています。 詳しくは、以下のページを参照してください。
本検証の目的は、NablarchのアプリケーションがAzure上で動作するかどうかを確認することです。 SendGridを使用する選択肢もありましたが、検証を簡単に進めるため、メールの送信機能は利用しないことにしました。
メール送信機能を利用しない場合、新規のサインアップはできなくなります。 しかし、テストデータを投入すれば事前定義されたユーザでのログインは可能なので、動作検証はできます。
セキュリティについて
セキュリティを考慮するとWAFやプライベートリンクなどの利用が必要になってきます。
しかし、今回はNablarchのアプリケーションがAzure上で動かせるかどうかを確認することが目的です。 このため、セキュリティに関するサービスは導入せず、簡単に構築できるシンプルなシステム構成にしています。
準備作業・前提条件
今回の検証で必要な準備作業や、前提条件などを説明します。
ローカルでの作業は、Windows 10上で行いました。 CLIの操作は、コマンドプロンプトかGit Bashで行っています。
また、以下のツールが必要になるので、あらかじめローカルにインストールしておいてください(インストール手順については割愛します)。 括弧内は、検証時の各ツールのバージョンです。
- Git for Windows (2.24.1)
- AdoptOpenJDK (11.0.6+10 HotSpotVM)
- Apache Maven (3.6.3)
- Azure CLI (2.20.0)
- Docker Desktop (19.03.5)
- Node.js (14.16.0)
また、Azureの環境を構築するために、サブスクリプションを用意してください。 Azureのアカウントが無い場合は、アカウントを作成してください。
なお、以下の手順では、全てのAzureリソースを「東日本(japaneast)」リージョンに作成しています。
アプリケーションのDockerイメージを作成する
まずは、example-chatの各コンポーネントのDockerイメージを作成します。
Gitのリポジトリをローカルにクローンして、ver 1.1のタグに切り替えます。 Gitコマンドを実行できるCLIを使い、以下のコマンドを実行してください(以下はGit Bashから実行した例です)。
$ git clone git@github.com:Fintan-contents/example-chat.git
$ cd example-chat
$ git checkout 1.1
notifierをビルドする
最初はnotifierをビルドします。
コマンドラインでexample-chatのルートフォルダに移動し、以下のようにコマンドを実行します。
> cd notifier
> mvn package jib:dockerBuild -DskipTests
notifierがビルドされ、ローカルのDockerレジストリにexample-chat-notifier
という名前のイメージが追加されます。
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
...
example-chat-notifier latest 4a59725ccc08 51 years ago 451MB
...
notifierのビルドは以上です。
backendをビルドする
次に、backendをビルドします。 ただし、backendはAzureで動かすために、若干の修正が必要です。
Azure Blob Storageに対応させる
example-chatのver 1.1は、画像などを保存するオブジェクトストレージにAmazon S3を使用する実装が使われています。
AzureではオブジェクトストレージにAzure Blob Storageを使うので、このままではAzure上で画像のアップロード機能などが利用できません。
このAwsS3Client
はPublishableFileStorageというインタフェースを実装しています。 このインタフェースは、オブジェクトストレージの具体的なサービスを隠蔽し、example-chatが要求する抽象的なAPIのみを定義しています。
つまり、Azure Blob Storage用のPublishableFileStorage
実装クラスを作って差し替えれば、Azure上でもオブジェクトストレージが使用できるようになります。
ここでは、実装の修正方法だけを説明します。 Azure Blob StorageのJava SDKの詳しい使い方については、公式のガイドを参照してください。
まず、pom.xml
にAzure Blob StorageのSDKを依存関係として追加します。
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.10.0</version>
</dependency>
次に、PublishableFileStorage
のAzure Blob Storage用実装クラス、AzureBlobStorageClient
を作成します。
AzureBlobStorageClient のソースコードは、こちらを開くと確認できます
package com.example.infrastructure.service;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.azure.storage.blob.specialized.BlobInputStream;
import com.example.application.service.message.PublishableFileStorage;
import com.example.domain.model.channel.ChannelId;
import com.example.domain.model.message.ImageData;
import com.example.domain.model.message.ImageFile;
import com.example.domain.model.message.ImageKey;
import com.example.domain.model.message.MessageHistoryData;
import com.example.domain.model.message.MessageHistoryFile;
import com.example.domain.model.message.MessageHistoryKey;
import nablarch.core.repository.di.config.externalize.annotation.ConfigValue;
import nablarch.core.repository.di.config.externalize.annotation.SystemRepositoryComponent;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.function.BiFunction;
@SystemRepositoryComponent
public class AzureBlobStorageClient implements PublishableFileStorage {
private final String connectionString;
private final String containerName;
public AzureBlobStorageClient(
@ConfigValue("${azure.blobStorage.connectionString}") String connectionString,
@ConfigValue("${azure.blobStorage.containerName}") String containerName) {
this.connectionString = connectionString;
this.containerName = containerName;
}
@Override
public ImageKey save(ChannelId channelId, ImageFile imageFile) {
String key = uploadFile(channelId, imageFile.path(), imageFile.contentType());
return new ImageKey(key);
}
@Override
public MessageHistoryKey save(ChannelId channelId, MessageHistoryFile messageHistoryFile) {
String key = uploadFile(channelId, messageHistoryFile.path(), "text/csv");
return new MessageHistoryKey(key);
}
private String uploadFile(ChannelId channelId, Path path, String contentType) {
BlobContainerClient blobContainerClient = createBlobContainerClient();
if (!blobContainerClient.exists()) {
blobContainerClient.create();
}
String key = UUID.randomUUID().toString();
BlobClient blobClient = blobContainerClient.getBlobClient(key);
blobClient.uploadFromFile(path.toString());
blobClient.setMetadata(Map.of("channelId", channelId.value().toString(), "contentType", contentType));
return key;
}
@Override
public ImageData findImageData(ChannelId channelId, ImageKey imageKey) {
return findFile(channelId, imageKey.value(), ImageData::new);
}
@Override
public MessageHistoryData findMessageHistoryData(ChannelId channelId, MessageHistoryKey messageHistoryKey) {
return findFile(channelId, messageHistoryKey.value(), MessageHistoryData::new);
}
private <T> T findFile(ChannelId channelId, String key, BiFunction<byte[], String, T> resultCreator) {
BlobContainerClient blobContainerClient = createBlobContainerClient();
BlobClient blobClient = blobContainerClient.getBlobClient(key);
try (BlobInputStream blobInputStream = blobClient.openInputStream()) {
Map<String, String> metadata = blobInputStream.getProperties().getMetadata();
if (!Objects.equals(metadata.get("channelId"), channelId.value().toString())) {
throw new RuntimeException();
}
byte[] bytes = blobInputStream.readAllBytes();
String contentType = metadata.get("contentType");
return resultCreator.apply(bytes, contentType);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private BlobContainerClient createBlobContainerClient() {
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(connectionString).buildClient();
return blobServiceClient.getBlobContainerClient(containerName);
}
}
次に、AwsS3Client
をシステムリポジトリの管理対象から外すため、AwsS3Client.java
を削除します。
最後に、AzureBlobStorageClient
に渡す環境設定値の定義をsrc/main/resources/env.config
に追加します。
...
azure.blobStorage.connectionString=changeme
azure.blobStorage.containerName=example-chat
...
以上で、Azureで動かすための修正は完了です。
テストデータが登録されるようにする
アプリケーション起動時に、テストデータがデータベースへ登録されるようにします。
以下の要領で、テストデータのSQLファイルをコピーしてください。
- コピー元のファイル
backend/src/test/resources/db/testdata/V9999__testdata.sql
- コピー先のフォルダ
backend/src/main/resources/db/migration
コピー後のフォルダの中は、以下のようになります。
> dir /b
V1_1__prepare_accounts.sql
V1__create_table.sql
V9999__testdata.sql
これで、アプリケーション起動時にV9999__testdata.sql
が実行されて、テストデータが登録されるようになります。
ビルドする
ビルドのコマンドは、notifierのときと同じです。 コマンドラインでexample-chatのルートフォルダに移動し、以下のコマンドを実行してください。
> cd backend
> mvn package jib:dockerBuild -DskipTests
backendがビルドされ、ローカルのDockerレジストリにexample-chat-backend
という名前のイメージが追加されます。
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
...
example-chat-backend latest d7cfaf840c7e 51 years ago 471MB
...
backendのビルドは以上です。
frontendをビルドする
frontendも、ビルドの前に少しだけ手を入れる必要があります。
backendのURLを設定する
frontendからbackendのAPIを実行するために、frontendはあらかじめbackendのURLを知っておく必要があります。
example-chatでは、backendのURLはCreate React AppのEnvironment Variablesを使って設定します。
frontend
フォルダの直下に.env.local
という名前のファイルを作成します。 ファイルの中身は、次のようにしてください。
REACT_APP_BACKEND_BASE_URL=https://<backendのWebアプリ名>.azurewebsites.net
<backendのWebアプリ名>
には、後で作成するbackendのWebアプリの名前を記述します。 任意の名前を考えて設定してください(例:example-chat-backend
)。 ただし、この名前はAzure全体で一意である必要があります。
.env.local
が作成できたら、コマンドラインでexample-chatのルートフォルダに移動し、以下のコマンドを実行してください。
> cd frontend
> npm install
> npm run build
> docker build -t example-chat-frontend .
frontendがビルドされ、ローカルのDockerレジストリにexample-chat-frontend
という名前のイメージが追加されます。
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
...
example-chat-frontend latest eb98c2d881e0 15 minutes ago 137MB
...
frontendのビルドは以上です。
DockerイメージをAzure Container Registryにアップロードする
frontend, backend, notifierの3つのDockerイメージが作成できたら、これらをAzure Container Registryにアップロード(プッシュ)します。
まず、Azure Container Registryにレジストリを作成します。 作成手順については、クイック スタート:Azure portal を使用して Azure コンテナー レジストリを作成するを参照してください(レジストリの名前は任意です)。
レジストリが作成できたら、Azure CLIを使ってレジストリにログインします。 ただし、その前にAzure CLIのサインインを済ませておく必要があります。 サインインは、az login
コマンドで行います。 詳細はSign in with Azure CLIを参照してください。
サインインが完了したら、以下のコマンドでレジストリにログインします。
> az acr login --name <レジストリ名>
<レジストリ名>
には、作成したレジストリの名前を指定してください。 作成したレジストリの名前がmyregistry
の場合は、以下のようになります。
> az acr login --name myregistry
Login Succeeded
レジストリへのログインが完了したら、ローカルのDockerイメージにタグを設定します。
> docker tag example-chat-frontend:latest <レジストリ名>.azurecr.io/example-chat-frontend:latest
> docker tag example-chat-backend:latest <レジストリ名>.azurecr.io/example-chat-backend:latest
> docker tag example-chat-notifier:latest <レジストリ名>.azurecr.io/example-chat-notifier:latest
最後に、イメージをAzure上のレジストリにプッシュします。
> docker push <レジストリ名>.azurecr.io/example-chat-frontend:latest
> docker push <レジストリ名>.azurecr.io/example-chat-backend:latest
> docker push <レジストリ名>.azurecr.io/example-chat-notifier:latest
Azureポータルでレジストリを開き、メニューの「サービス」→「リポジトリ」でアップロードされたイメージの一覧が確認できます。 3つのイメージがプッシュできていることを確認してください。
以上で、イメージのアップロードは完了です。
各種ストレージサービスを作成する
続いて、各種ストレージサービスを作成していきます。
Azure Blob Storage
Azure Blob Storageを使い始めるためには、先にストレージアカウントを作成する必要があります。 ストレージアカウントの作成方法については、ストレージ アカウントを作成するを参照してください(ストレージアカウントの名前は任意です)。
ストレージアカウントが作成できたら、次にコンテナーを作成します。 コンテナーを作成する手順については、コンテナーを作成するを参照してください。 このとき、作成するコンテナーの名前はexample-chat
としてください。
Azure Blob Storageの作成手順は以上です。
Azure Database for PostgreSQL
Azure Database for PostgreSQLの単一サーバーを作成します。 作成手順については、クイック スタート:Azure portal を使用して Azure Database for PostgreSQL サーバーを作成するを参照してください。
ただし、作成時に指定するパラメータは、以下のように設定してください(特に記述がないパラメータは任意の値を設定してください)。
パラメータ | 値 |
---|---|
バージョン | 11 |
管理者ユーザー名 | postgres |
作成直後のデータベースは外部から接続できません。 Azure内からの接続を許可するために、設定を変更します。 手順はAzure からの接続を参照してください。
Azure Database for PostgreSQLの作成手順は以上です。
Azure Cache for Redis
Azure Cache for Redisを作成します。 作成手順については、クイックスタート: オープンソースの Redis キャッシュを作成するを参照してください。
ただし、作成時に指定するパラメータは、以下のように設定してください(特に記述がないパラメータは任意の値を設定してください)。
パラメータ | 値 |
---|---|
接続方法 | パブリックエンドポイント |
Redisバージョン | 4 |
Azure Cache for Redisの作成手順は以上です。
App Serviceでコンテナインスタンスを作成する
最後に、App Serviceを作成してexample-chatの3つのコンテナを実行します。
App Serviceプランを作成する
App Serviceを使い始めるには、まずApp Serviceプランを作成する必要があります。
Azure Marketplaceで「App Serviceプラン」を検索してリソースを作成します(App Service Plan
で検索すると出てきます)。
「オペレーティングシステム」にLinux
を選択して作成してください。名前やサイズは任意のもので構いません。
各Webアプリを作成する
作成したApp Serviceプランに、3つの「Webアプリ」を追加します。
Azure Marketplaceで「Webアプリ」を検索してリソースを作成します(Web App
で検索すると出てきます)。
作成時に指定するパラメータは、以下のように設定してください(特に記述がないパラメータは任意の値を設定してください)。
パラメータ | 値 |
---|---|
名前 | 任意(ただし、backendの名前はfrontendの.env.local で指定したものと同じ名前にする) |
公開 | Dockerコンテナー |
オペレーティングシステム | Linux |
地域 | 先ほど作成したApp Serivceプランと同じリージョン |
Linuxプラン | 先ほど作成したApp Serviceプラン |
イメージソース | Azure Container Registry |
レジストリ | 先の手順で作成したAzure Container Registry |
イメージ | example-chat-frontend , example-chat-backend , example-chat-notifier |
タグ | latest |
環境変数を設定する
「Webアプリ」が作成できたら、次に環境変数を設定します。
Nablarchには、OS環境変数で設定値を上書きする機能が用意されています。 example-chatは、この機能を利用してデータベースの接続先など環境に依存する設定を環境変数で上書きできるようにしています。
コンテナに渡す環境変数を設定する方法については、アプリケーションの設定の構成を参照してください。
以下に、各「Webアプリ」に設定する環境変数を記載します。
アプリケーションの設定が完了したら変更内容を保存し、コンテナを再起動してください。
backend
名前 | 値 |
---|---|
NABLARCH_DB_URL |
jdbc:postgresql://<DBのサーバー名>:5432/postgres?sslmode=require |
NABLARCH_DB_USER |
postgres@<DBのサーバー名> |
NABLARCH_DB_PASSWORD |
DB作成時に指定したパスワード |
NABLARCH_LETTUCE_SIMPLE_URI |
rediss://<Redisのパスワード>@<Redisのホスト名>:6380 |
WEBSOCKET_URI |
wss://<notifierのWebアプリ名>.azurewebsites.net:443/notification |
CORS_ORIGINS |
https://<frontendのWebアプリ名>.azurewebsites.net |
BACKEND_BASE_URL |
https://<backendのWebアプリ名>.azurewebsites.net |
APPLICATION_EXTERNAL_URL |
https://<frontendのWebアプリ名>.azurewebsites.net |
NABLARCH_SESSIONSTOREHANDLER_COOKIESECURE |
true |
AZURE_BLOBSTORAGE_CONNECTIONSTRING |
作成したストレージアカウントの接続文字列 |
WEBSITES_PORT |
8080 |
値に埋め込む<DBのサーバー名>
などは、いずれもAzureポータルで対象のリソースを開くことで確認できます。 以下で、それぞれの値の参照方法を説明します。
次の2つは、各リソースの「概要」ページで確認できます。
<DBのサーバー名>
<Redisのホスト名>
次の2つは、各リソースの「アクセス キー」ページで確認できます。
<Redisのパスワード>
- 作成したストレージアカウントの接続文字列
notifier
名前 | 値 |
---|---|
NABLARCH_LETTUCE_SIMPLE_URI |
backendと同じ |
WEBSITES_PORT |
8080 |
frontend
名前 | 値 |
---|---|
WEBSITES_PORT |
80 |
すべての「Webアプリ」に共通で設定しているWEBSITES_PORT
は、App Serviceがアプリのポートを特定するための設定です。 詳しくは ポート番号を構成する を参照してください。
動作確認
お疲れ様です、以上でexample-chatがAzure上で動くようになりました。
ブラウザで、https://<frontendのWebアプリ名>.azurewebsites.net にアクセスしてみてください。 以下のような画面が表示されれば成功です。
テスト用のユーザーを利用して、ログインしてみましょう。
画像のアップロードも問題なく動いているようです。
おわりに
クラウドネイティブ対応でコンテナ化や環境変数による設定値の上書きが可能となったことで、Azure上でも問題なくNablarchアプリケーションを動かすことができました。
本記事が、AzureでのNablarchを用いたシステム開発を検討している方の参考になれば幸いです。