Azure Kubernetes Service のコンテナーに Tomcat アプリケーションを移行する

このガイドでは、既存の Tomcat アプリケーションを移行して Azure Kubernetes Service (AKS) で実行する場合に知っておくべきことについて説明します。

移行前

移行を確実に成功させるには、開始する前に、次のセクションで説明する評価とインベントリの手順を完了します。

外部リソースをインベントリする

データ ソース、JMS メッセージ ブローカー、およびその他の外部リソースは、JNDI (Java Naming and Directory Interface) を介して挿入されます。 こうしたリソースの一部では、移行または再構成が必要な場合があります。

アプリケーション内

META-INF/context.xml ファイルを調べます。 <Context> 要素内の <Resource> 要素を探します。

アプリケーション サーバー上

$CATALINA_BASE/conf/context.xml および $CATALINA_BASE/conf/server.xml ファイルと、$CATALINA_BASE/conf/[engine-name]/[host-name] ディレクトリにある .xmlファイルを調べます。

context.xmlファイルでは、JNDI リソースは、最上位の <Context> 要素内の <Resource> 要素で記述されます。

server.xml ファイルでは、JNDI リソースは <GlobalNamingResources> 要素内の <Resource> 要素で記述されます。

データソース

データソースは、type 属性が javax.sql.DataSource に設定されている JNDI リソースです。 データソースごとに、次の情報を文書にまとめます。

  • データソース名
  • 接続プールの構成
  • JDBC ドライバーの JAR ファイルの場所

詳細については、Tomcat のドキュメントの「JNDI Datasource How-To」を参照してください。

その他のすべての外部リソース

このガイドでは、考えられるすべての外部依存関係を記載することはできません。 アプリケーションの外部依存関係がすべて満たされるよう確認するのは、担当チームの責任です。

シークレットをインベントリする

パスワードとセキュリティで保護された文字列

すべてのシークレット文字列とパスワードについて、運用サーバー上のすべてのプロパティと構成ファイルを確認します。 $CATALINA_BASE/conf にある server.xmlcontext.xml を必ず確認してください。 また、アプリケーション内にパスワードや資格情報を含むファイルが存在する場合もあります。 これらには、META-INF/context.xmlや、Spring Boot アプリケーションの場合は application.properties または application.yml ファイルなどがあります。

ファイル システムが使用されているかどうかとその使用方法を判断する

アプリケーション サーバーでファイル システムを使用する場合は、再構成や、まれにアーキテクチャの変更が必要になります。 次のシナリオの一部または全部を確認できます。

読み取り専用の静的コンテンツ

現在、アプリケーションで静的コンテンツを提供している場合は、そのための別の場所が必要になります。 静的コンテンツを Azure Blob Storage に移動し、グローバルな高速ダウンロードのために Azure CDN を追加することを検討できます。 詳細については、「Azure Storage での静的 Web サイト ホスティング」と「クイック スタート:Azure ストレージ アカウントと Azure CDN との統合」を参照してください。 また、Azure Spring Apps Enterprise プランのアプリに静的コンテンツを直接デプロイすることもできます。 詳細については、「 Web 静的ファイルをデプロイする」を参照してください。

動的に公開される静的コンテンツ

アプリケーションによってアップロードまたは生成されるが、作成後に変更できない静的コンテンツをアプリケーションで許可する場合は、前述のように Azure Blob Storage と Azure CDN を使用し、Azure Function でアップロードと CDN の更新を処理します。 「Azure Functions を使用した静的コンテンツのアップロードと CDN の事前読み込み」で、ご利用いただけるサンプルの実装を提供しています。 また、Azure Spring Apps Enterprise プランのアプリに静的コンテンツを直接デプロイすることもできます。 詳細については、「 Web 静的ファイルをデプロイする」を参照してください。

動的または内部のコンテンツ

アプリケーションで頻繁に書き込みおよび読み取りされるファイル (一時データ ファイルなど) や、アプリケーションでのみ表示できる静的ファイルには、Azure Storage 共有を永続ボリュームとしてマウントできます。 詳細については、「Azure Kubernetes Service で Azure Files を含む永続ボリュームを動的に作成して使用する」を参照してください。

ID セッションの永続化メカニズム

使用されているセッション永続化マネージャーを特定するには、アプリケーション内の context.xml ファイルおよび Tomcat の構成を調べます。 <Manager> 要素を探して、className 属性の値を確認します。

Tomcat の組み込みの PersistentManager の実装 (StandardManagerFileStore など) は、Kubernetes のような分散型のスケーリングされたプラットフォームで使用するように設計されていません。 AKS は、複数のポッド間で負荷を分散し、任意の時点で任意のポッドを透過的に再起動することがあります。変更可能な状態をファイル システムに保持することは推奨されません。

セッションの永続化が必要な場合は、代替の PersistentManager の実装を使用する必要があります。これは、Redis Cache を使用して VMware Tanzu Session Manager などの外部データ ストアへの書き込みを行います。 詳細については、「Tomcat を使用してセッション キャッシュとして Redis を使用する」を参照してください。

特殊なケース

運用環境のシナリオによっては、追加の変更が必要な場合や、追加の制限が課される場合があります。 そのようなシナリオはめったに発生しませんが、それらがアプリケーションに適用されないこと、または正しく解決されることを確認することが重要です。

スケジュールされたジョブにアプリケーションが依存しているかどうかを判断する

スケジュールされたジョブ (Quartz Scheduler タスクや cron ジョブなど) は、コンテナー化された Tomcat デプロイでは使用できません。 アプリケーションをスケールアウトすると、1 つのスケジュールされたジョブが、スケジュールされた期間に複数回実行されることがあります。 このような場合、意図しない結果になることがあります。

アプリケーション サーバーの内部または外部で、すべてのスケジュールされたジョブをインベントリします。

アプリケーションに OS 固有のコードが含まれているかどうかを判断する

アプリケーションに、アプリケーションが実行されている OS に対応するコードが含まれている場合は、基盤の OS に依存しないようにアプリケーションをリファクターする必要があります。 たとえば、ファイル システム パスで /\ が使用されている場合は、File.Separator または Path.get に置き換える必要があります。

MemoryRealm が使用されているかどうかを判断する

MemoryRealm には、永続化された XML ファイルが必要です。 Kubernetes では、このファイルをコンテナー イメージに追加するか、コンテナーで利用可能になっている共有ストレージにアップロードする必要があります。 pathName パラメーターを適宜変更する必要があります。

MemoryRealm が現在使用されているかどうかを確認するには、server.xml および context.xml ファイルを調べ、className 属性が org.apache.catalina.realm.MemoryRealm に設定されている <Realm> 要素を検索します。

SSL セッションの追跡が使用されているかどうかを判断する

コンテナー化されたデプロイでは、一般に SSL セッションはアプリケーション コンテナーの外部 に (通常はイングレス コント ローラーによって) オフロードされます。 アプリケーションで SSL セッションの追跡を必要とする場合は、SSL トラフィックがアプリケーション コンテナーに直接渡されるようにしてください。

AccessLogValve が使用されているかどうかを判断する

AccessLogValve が使用されている場合、directory パラメーターを、マウントされた Azure Files 共有またはそのサブディレクトリのいずれかに設定する必要があります。

インプレース テスト

コンテナー イメージを作成する前に、AKS で使用する予定のアプリケーションを JDK と Tomcat に移行します。 アプリケーションを十分にテストして、互換性とパフォーマンスを確認します。

構成のパラメーター化

多くの場合、移行前に、server.xml および context.xml ファイル内でシークレットや外部依存関係 (データソースなど) が識別されます。 そのため、識別された各項目について、ユーザー名、パスワード、接続文字列、または URL を環境変数に置き換えます。

たとえば、context.xml ファイルに次の要素が含まれているとします。

<Resource
    name="jdbc/dbconnection"
    type="javax.sql.DataSource"
    url="jdbc:postgresql://postgresdb.contoso.com/wickedsecret?ssl=true"
    driverClassName="org.postgresql.Driver"
    username="postgres"
    password="t00secure2gue$$"
/>

この場合は、次の例に示されているように変更できます。

<Resource
    name="jdbc/dbconnection"
    type="javax.sql.DataSource"
    url="${postgresdb.connectionString}"
    driverClassName="org.postgresql.Driver"
    username="${postgresdb.username}"
    password="${postgresdb.password}"
/>

移行

最初の手順 (「コンテナー レジストリと AKS をプロビジョニングする」) を除いて、以下の手順を移行対象の各アプリケーション (WAR ファイル) に個別に実行することをお勧めします。

注意

一部の Tomcat デプロイでは、1 つの Tomcat サーバーで複数のアプリケーションが実行されている場合があります。 デプロイがこれに該当する場合は、各アプリケーションを個別のポッドで実行することを強くお勧めします。 これにより、各アプリケーションのリソース使用率を最適化しながら、複雑さと結合を最小限に抑えることができます。

コンテナー レジストリと AKS をプロビジョニングする

コンテナーレジストリと、サービス プリンシパルがレジストリの閲覧者ロールを持つ Azure Kubernetes クラスターを作成します。 クラスターのネットワーク要件に応じて、適切なネットワーク モデルを選択してください。

az group create \
    --resource-group $resourceGroup \
    --location eastus
az acr create \
    --resource-group $resourceGroup \
    --name $acrName \
    --sku Standard
az aks create \
    --resource-group $resourceGroup \
    --name $aksName \
    --attach-acr $acrName \
    --network-plugin azure

デプロイの成果物を準備する

コンテナー上の Tomcat のクイックスタート GitHub リポジトリを複製します。 これには、いくつかの推奨される最適化が適用された Dockerfile と Tomcat 構成ファイルが含まれています。 次の手順では、コンテナー イメージをビルドして AKS にデプロイする前に、これらのファイルに対して行う必要があると思われる変更について説明します。

クラスタリング用のポートを開く (必要な場合)

AKS で Tomcat クラスタリング を使用する場合は、必要なポート範囲が Dockerfile で公開されていることを確認してください。 server.xml でサーバーの IP アドレスを指定するには、コンテナーの起動時にポッドの IP アドレスに初期化された変数の値を使用してください。

または、セッション状態を別の場所に保持して、レプリカ間で利用できるようにすることもできます。

アプリケーションでクラスタリングが使用されているかどうかを判断するには、server.xml ファイルの <Host> または <Engine> 要素内で <Cluster> 要素を探します。

JNDI リソースを追加する

server.xml を編集して、移行前の手順で準備したリソース (データ ソースなど) を追加します。

次に例を示します。

<!-- Global JNDI resources
      Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml"
               />

    <!-- Migrated datasources here: -->
    <Resource
        name="jdbc/dbconnection"
        type="javax.sql.DataSource"
        url="${postgresdb.connectionString}"
        driverClassName="org.postgresql.Driver"
        username="${postgresdb.username}"
        password="${postgresdb.password}"
    />
    <!-- End of migrated datasources -->
</GlobalNamingResources>

その他のデータ ソースの手順については、Tomcat のドキュメントで「JNDI Datasource How-To」の次のセクションを参照してください。

イメージをビルドしてプッシュする

AKS で使用するためにイメージをビルドして Azure Container Registry (ACR) にアップロードする最も簡単な方法は、az acr build コマンドを使用することです。 このコマンドでは、コンピューターに Docker をインストールする必要はありません。 たとえば、上記の Dockerfile とアプリケーション パッケージ petclinic.war が現在のディレクトリにある場合は、次の 1 つの手順で ACR でコンテナー イメージをビルドできます。

az acr build \
    --image "${acrName}.azurecr.io/petclinic:{{.Run.ID}}" \
    --registry $acrName \
    --build-arg APP_FILE=petclinic.war \
    --build-arg=prod.server.xml .

WAR ファイルの名前が ROOT.war の場合は、--build-arg APP_FILE... パラメーターを省略できます。 サーバー XML ファイルの名前が server.xml の場合は、--build-arg SERVER_XML... パラメーターを省略できます。 どちらのファイルも Dockerfile と同じディレクトリに存在する必要があります。

または、Docker CLI を使用してイメージをローカルにビルドすることもできます。 この方法を使用すると、ACR への初期デプロイの前に、イメージのテストと調整を簡単に行えます。 ただし、Docker CLI がインストールされ、Docker デーモンが実行されている必要があります。

# Build the image locally
sudo docker build . --build-arg APP_FILE=petclinic.war -t "${acrName}.azurecr.io/petclinic:1"

# Run the image locally
sudo docker run -d -p 8080:8080 "${acrName}.azurecr.io/petclinic:1"

# Your application can now be accessed with a browser at http://localhost:8080.

# Log into ACR
sudo az acr login --name $acrName

# Push the image to ACR
sudo docker push "${acrName}.azurecr.io/petclinic:1"

詳細については、 Azure でのコンテナー イメージのビルドと格納に関する Learn モジュールを参照してください。

パブリック IP アドレスをプロビジョニングする

内部ネットワークまたは仮想ネットワークの外部からアプリケーションにアクセスできるようにする場合は、パブリック静的 IP アドレスが必要になります。 この IP アドレスは、クラスターのノード リソース グループ内にプロビジョニングする必要があります。

export nodeResourceGroup=$(az aks show \
    --resource-group $resourceGroup \
    --name $aksName \
    --query 'nodeResourceGroup' \
    --output tsv)
export publicIp=$(az network public-ip create \
    --resource-group $nodeResourceGroup \
    --name applicationIp \
    --sku Standard \
    --allocation-method Static \
    --query 'publicIp.ipAddress' \
    --output tsv)
echo "Your public IP address is ${publicIp}."

AKS にデプロイする

Kubernetes YAML ファイルを作成して適用します。 外部ロード バランサーを (アプリケーションまたはイングレス コントローラーに) 作成する場合は、前のセクションで LoadBalancerIP としてプロビジョニングした IP アドレスを必ず指定してください。

外部化したパラメーターを環境変数として含めます。 シークレット (パスワード、API キー、JDBC 接続文字列など) は含めないでください。 シークレットについては、「KeyVault FlexVolume を構成する」セクションを参照してください。

永続ストレージを構成する

アプリケーションで非揮発性ストレージが必要な場合は、永続ボリューム を 1 つ以上構成します。

Tomcat ログ ディレクトリ ( /tomcat_logs) にマウントされた Azure Files を含む永続ボリュームを作成し、ログを一元的に保持することができます。 詳細については、「Azure Kubernetes Service (AKS) で Azure Files を含む永続ボリュームを動的に作成して使用する」を参照してください。

KeyVault FlexVolume を構成する

Azure KeyVault を作成し、必要なすべてのシークレットを設定します。 次に、KeyVault FlexVolume を構成して、これらのシークレットにポッドがアクセスできるようにします。

コンテナーのローカル キーストアに証明書をインポートするために、スタートアップ スクリプト (コンテナー上の Tomcat GitHub リポジトリにある startup.sh) を変更する必要があります。

スケジュールされたジョブを移行する

AKS クラスターでスケジュールされたジョブを実行するには、必要に応じて Cron ジョブを定義します。

移行後

アプリケーションを AKS に移行したので、期待どおりに動作することを確認する必要があります。 これを完了したら、アプリケーションをよりクラウド ネイティブにするための推奨事項がいくつかあります。