Warning: The magic method InvisibleReCaptcha\MchLib\Plugin\MchBasePublicPlugin::__wakeup() must have public visibility in /home/c9138697/public_html/juncleit.com/wp-content/plugins/invisible-recaptcha/includes/plugin/MchBasePublicPlugin.php on line 37
Spring Boot アプリをAzure Spring Appsにデプロイ | マサトッシュブログ

Azure tips : SpringマイクロサービスとしてTODOアプリを作成しAzureにデプロイする

Azure Spring Apps

今回利用するAzureリソースは、Azure Spring AppとAzure Database for MySQLの2つ。ただ、マイクロサービスの各インスタンスで共通の構成情報を参照するためにGithubリポジトリも利用し、構成情報の一元管理を行う。

Spring マイクロサービスを Azure にデプロイする - Training
Spring Boot マイクロサービスを Azure Spring Apps (ASA) にデプロイする方法を説明します。

Microsoft LearnのJava on Azureを進めていくと結構最初の方でSpring WebアプリをマイクロサービスとしてAzure Spring Appにデプロイする手順が登場する。一通りやってみた感想として、知らないことが多すぎて1つ1つしっかり理解することにかなり時間を使った。私のような初心者にはかなりハードルの高いトレーニングモジュール。とにかく早くJava on Azureを消化したい。

  1. Azureの事前知識
    1. Azure Spring Appにもあった無料枠
    2. Azure Database for MySQLの単一サーバーの廃止
    3. Azure Spring Appと互換性のあるSpring Bootバージョン
  2. 下準備
    1. Azureセッションのアカウント情報とテナント情報を確認
    2. Azure CLIのSpring拡張機能のインストール
    3. 環境変数の設定
    4. Azure CLIが参照する端末のデフォルト値の設定
  3. Azureリソースの作成
    1. Azure Spring Appサービスインスタンスの作成(CLI)
    2. Spring Bootアプリケーションインスタンスの作成(CLI)
    3. Azure Database for MySQLの作成(CLI)
  4. Spring Cloud Config Serverの構成
    1. Config Serverとは
    2. マイクロサービスにおけるConfig Serverのメリット
    3. Config Server用のGithubリポジトリの作成
    4. 構成ファイルの新規作成
    5. Config ServerからGithubにアクセスするための個人用トークンの作成
    6. Config ServerからGithubへのアクセス情報の登録
    7. MySQL接続文字列の設定
  5. Spring Bootマイクロサービスの作成
    1. Spring Initializrによるプロジェクトファイルの生成とダウンロード
  6. Spring Bootマイクロサービスのビルド
    1. javax.persistenceパッケージが見つからない
    2. javax.annotationパッケージが見つからない
  7. Spring Bootマイクロサービスをデプロイ
    1. Spring Boot Actuatorによるメトリクス情報の収集の無効化
    2. Artifactのアップロード先
    3. ローリングアップグレードによるダウンタイムなしのデプロイの実行
    4. Azure Spring Appsと互換性のあるSpring Bootバージョンへの変更
  8. 動作確認
  9. まとめ

Azureの事前知識

Azure Spring Appにもあった無料枠

Azure Spring Appには、月間50時間分のvCPU消費、100時間分のメモリ消費(GB)の無料枠が含まれる。

例えば、Azure Spring AppsのBASIC Tier(2vCPUと4GBメモリ)のインスタンスを1つ利用した場合、25時間分/月はチャージされない。

Price Reduction - Azure Spring Apps does more, costs less!
Effective September 1st, we are reducing the base unit of Azure Spring Apps Standard and Enterprise to 6 vCPUs and 12 GB...

Azure Database for MySQLの単一サーバーの廃止

Microsoft Learnの内容はAzure Database for MySQLの単一サーバーを使った内容になっているが、2024/9/16に廃止予定とのことなので、ここでは自分なりに調べつつ、Azure Database for MySQLのフレキシブルサーバーを使った内容にする。

Azure Database for MySQL 単一サーバーの現状
Azure Database for MySQL 単一サーバー サービスは非推奨になっています。

Azure Spring Appと互換性のあるSpring Bootバージョン

執筆時点のSpring Bootの最新バージョンは [3.2.2] だが、Azure Spring Appsが対応しているバージョンは [3.1.x] もしくは [3.0.x] だったので、ここでは執筆時点で3.1.xの最新バージョンである [3.1.8] を使用する。この情報は、私の確認した限りではMSのオンラインドキュメントでは明示されておらず、azコマンド実行時に対応しているバージョン番号が表示された。

Version support for Java, Spring Boot, and more - Azure Spring Apps
This article describes the version support for Java, Spring Boot, and Spring Cloud, and the customer responsibilities wh...

以下、az コマンドで Spring Boot 3.2.2でビルドしたjarファイルをデプロイしようとした時に表示されたメッセージ。

Action:

Consider applying the following actions:

- Change Spring Boot version to one of the following versions [3.0.x, 3.1.x] .
You can find the latest Spring Boot versions here [<https://spring.io/projects/spring-boot#learn>].

下準備

Azureセッションのアカウント情報とテナント情報を確認

ログイン中のセッション情報を確認して意図した作業環境であることを確認。もしくは意図した作業環境でログインする。

% az account show
{
  "environmentName": "AzureCloud",
  "homeTenantId": "xxx",
  "id": "xxx",
  "isDefault": true,
  "managedByTenants": [],
  "name": "サブスクリプション名",
  "state": "Enabled",
  "tenantId": "xxx",
  "user": {
    "name": "xxx@xxx.onmicrosoft.com",
    "type": "user"
  }
}

Azure CLIのSpring拡張機能のインストール

Azure CLIからAzure Spring AppにWEBアプリをデプロイする場合、Azコマンドにspring拡張機能をインストールする。

az spring
% az extension add -n spring -y
Default enabled including preview versions for extension installation now. Disabled in May 2024. Use '--allow-preview true' to enable it specifically if needed. Use '--allow-preview false' to install stable version only.

環境変数の設定

Azure CLIでコマンドを発行する場合、いくつかの環境変数を事前設定しておくと後々楽。

% AZ_RESOURCE_GROUP_NAME=azure-spring-workshop
% AZ_SPRING_CLOUD_NAME=azure-spring-workshop-20240206

Azure CLIが参照する端末のデフォルト値の設定

もう一つ、Azure CLIではコマンド対象となるリソースグループやリソースを端末のデフォルトとして設定しておく事ができる。今回のAzure Spring Appであれば、以下のデフォルト値を設定しておくとこれまた後々楽。

% az configure --defaults location=japaneast group=${AZ_RESOURCE_GROUP_NAME}

端末に設定されている各種変数は「az config get」で一覧を取得できる。

% az config get
Command group 'config' is experimental and under development. Reference and support levels: <https://aka.ms/CLI_refstatus>
{
  "cloud": [
    {
      "name": "name",
      "source": "/Users/sato/.azure/config",
      "value": "AzureCloud"
    }
  ],
  "core": [
    {
      "name": "first_run",
      "source": "/Users/sato/.azure/config",
      "value": "yes"
    }
  ],
  "defaults": [
    {
      "name": "group",
      "source": "/Users/sato/.azure/config",
      "value": "azure-spring-workshop"
    },
    {
      "name": "spring",
      "source": "/Users/sato/.azure/config",
      "value": "azure-spring-workshop-20240206"
    },
    {
      "name": "location",
      "source": "/Users/sato/.azure/config",
      "value": "japaneast"
    }
  ]
}

環境情報として設定できる項目のリストは以下に定義されている。

Azure CLI configuration options
The Azure CLI allows user configuration for various settings. Manage values with the az configure command, environment v...

Azureリソースの作成

Azure Spring Appサービスインスタンスの作成(CLI)

Azure Spring Appを作成する場合、”Microsoft.AppPlatform”というプロバイダーが必要らしいが、Azure CLIで実行すると自動的に検出して有効にしてくれた。

以下のコマンドではskuしか指定していないが、自動的にLog Analytics とApplication Insights を有効にしてくれた。この場合、Log AnalyticsとApplication Insightsのリソース名はAzure Spring Appの名称と同じ名称になる。

% az spring create -n $AZ_SPRING_CLOUD_NAME --sku standard
Resource provider 'Microsoft.AppPlatform' used by this operation is not registered. We are registering for you.
Registration succeeded.
 - Creating Service ..
Start configure Application Insights
Log Analytics workspace "azure-spring-workshop-20240206" was created for this Azure Spring Apps. You can visit <https://portal.azure.com/#resource/subscriptions/xxx/resourceGroups/azure-spring-workshop/providers/Microsoft.OperationalInsights/workspaces/azure-spring-workshop-20240206/overview> to view your Log Analytics workspace
Application Insights "azure-spring-workshop-20240206" was created for this Azure Spring Apps. You can visit <https://portal.azure.com/#resource/subscriptions/xxx/resourceGroups/azure-spring-workshop/providers/microsoft.insights/components/azure-spring-workshop-20240206/overview> to view your Application Insights component
{
  "id": "/subscriptions/xxx/resourceGroups/azure-spring-workshop/providers/Microsoft.AppPlatform/Spring/azure-spring-workshop-20240206",
  "location": "japaneast",
  "name": "azure-spring-workshop-20240206",
  "properties": {
    "fqdn": "azure-spring-workshop-20240206.azuremicroservices.io",
    "infraResourceGroup": null,
    "maintenanceScheduleConfiguration": null,
    "managedEnvironmentId": null,
    "marketplaceResource": null,
    "networkProfile": {
      "appNetworkResourceGroup": null,
      "appSubnetId": null,
      "ingressConfig": null,
      "outboundIPs": {
        "publicIPs": [
          "xxx.xxx.xxx.xxx",
          "xxx.xxx.xxx.xxx"
        ]
      },
      "outboundType": "loadBalancer",
      "requiredTraffics": null,
      "serviceCidr": null,
      "serviceRuntimeNetworkResourceGroup": null,
      "serviceRuntimeSubnetId": null
    },
    "powerState": "Running",
    "provisioningState": "Succeeded",
    "serviceId": "xxx",
    "version": 3,
    "vnetAddons": null,
    "zoneRedundant": false
  },
  "resourceGroup": "azure-spring-workshop",
  "sku": {
    "capacity": null,
    "name": "S0",
    "tier": "Standard"
  },
  "systemData": {
    "createdAt": "2024-02-06T08:48:16.457609+00:00",
    "createdBy": "xxx@xxx.onmicrosoft.com",
    "createdByType": "User",
    "lastModifiedAt": "2024-02-06T08:48:16.457609+00:00",
    "lastModifiedBy": "xxx@xxx.onmicrosoft.com",
    "lastModifiedByType": "User"
  },
  "tags": null,
  "type": "Microsoft.AppPlatform/Spring"
}

Spring Bootアプリケーションインスタンスの作成(CLI)

作成したAzure Spring Appsに対して、Spring Boot版のTODOアプリをデプロイするためのアプリケーションインスタンスを作成する。

% az spring app create --name todo-service --service $AZ_SPRING_CLOUD_NAME --runtime-version java_17
This command usually takes minutes to run. Add '--verbose' parameter if needed.
[1/2] Creating app todo-service
[2/2] Creating default deployment with name "default"
App create succeeded
{
  "id": "/subscriptions/xxx/resourceGroups/azure-spring-workshop/providers/Microsoft.AppPlatform/Spring/azure-spring-workshop-20240206/apps/todo-service",
  "identity": null,
  "location": "japaneast",
  "name": "todo-service",
  "properties": {
    "activeDeployment": {
      "id": "/subscriptions/xxx/resourceGroups/azure-spring-workshop/providers/Microsoft.AppPlatform/Spring/azure-spring-workshop-20240206/apps/todo-service/deployments/default",
      "name": "default",
      "properties": {
        "active": true,
        "deploymentSettings": {
          "addonConfigs": null,
          "apms": null,
          "containerProbeSettings": null,
          "environmentVariables": null,
          "livenessProbe": {
            "disableProbe": false,
            "failureThreshold": 3,
            "initialDelaySeconds": 300,
            "periodSeconds": 10,
            "probeAction": {
              "type": "TCPSocketAction"
            },
            "successThreshold": 1,
            "timeoutSeconds": 3
          },
          "readinessProbe": {
            "disableProbe": false,
            "failureThreshold": 3,
            "initialDelaySeconds": 0,
            "periodSeconds": 5,
            "probeAction": {
              "type": "TCPSocketAction"
            },
            "successThreshold": 1,
            "timeoutSeconds": 3
          },
          "resourceRequests": {
            "cpu": "1",
            "memory": "1Gi"
          },
          "scale": null,
          "startupProbe": null,
          "terminationGracePeriodSeconds": 90
        },
        "instances": [
          {
            "discoveryStatus": "UNREGISTERED",
            "name": "todo-service-default-12-55b858c54-vvfm5",
            "reason": null,
            "startTime": "2024-02-08T15:00:11Z",
            "status": "Running",
            "zone": null
          }
        ],
        "provisioningState": "Succeeded",
        "source": {
          "jvmOptions": null,
          "relativePath": "<default>",
          "runtimeVersion": "Java_17",
          "type": "Jar",
          "version": null
        },
        "status": "Running"
      },
      "resourceGroup": "azure-spring-workshop",
      "sku": {
        "capacity": 1,
        "name": "S0",
        "tier": "Standard"
      },
      "systemData": {
        "createdAt": "2024-02-08T15:00:07.687094+00:00",
        "createdBy": "xxx@xxx.onmicrosoft.com",
        "createdByType": "User",
        "lastModifiedAt": "2024-02-08T15:00:07.687094+00:00",
        "lastModifiedBy": "xxx@xxx.onmicrosoft.com",
        "lastModifiedByType": "User"
      },
      "type": "Microsoft.AppPlatform/Spring/apps/deployments"
    },
    "addonConfigs": {
      "applicationConfigurationService": {},
      "serviceRegistry": {}
    },
    "customPersistentDisks": null,
    "enableEndToEndTls": false,
    "fqdn": "azure-spring-workshop-20240206.azuremicroservices.io",
    "httpsOnly": false,
    "ingressSettings": {
      "backendProtocol": "Default",
      "clientAuth": null,
      "readTimeoutInSeconds": 300,
      "sendTimeoutInSeconds": 60,
      "sessionAffinity": "None",
      "sessionCookieMaxAge": 0
    },
    "loadedCertificates": null,
    "persistentDisk": {
      "mountPath": "/persistent",
      "sizeInGb": 0,
      "usedInGb": null
    },
    "provisioningState": "Succeeded",
    "public": false,
    "secrets": null,
    "temporaryDisk": {
      "mountPath": "/tmp",
      "sizeInGb": 5
    },
    "testEndpointAuthState": null,
    "testEndpointAuthStatus": "Enabled",
    "url": null,
    "vnetAddons": null,
    "workloadProfileName": null
  },
  "resourceGroup": "azure-spring-workshop",
  "systemData": {
    "createdAt": "2024-02-08T15:00:01.849831+00:00",
    "createdBy": "xxx@xxx.onmicrosoft.com",
    "createdByType": "User",
    "lastModifiedAt": "2024-02-08T15:00:01.849831+00:00",
    "lastModifiedBy": "xxx@xxx.onmicrosoft.com",
    "lastModifiedByType": "User"
  },
  "type": "Microsoft.AppPlatform/Spring/apps"
}

Azure Database for MySQLの作成(CLI)

Springマイクロサービスからアクセスするデータベースサーバーとして、Azure Database for MySQLを作成する。

(Microsoft Learnでは単一サーバーを作成しているが、冒頭で触れた通り、単一サーバーは今後廃止予定なので、ここではフレキシブルサーバーを作成する)

デフォルトとして全AzureリソースからアクセスできるようにFirewallルールを設定されるが、途中で今使っている端末のIPも許可するかどうか聞かれる。なんて親切設計。MySQL単一サーバーの時はAzureリソース作成した後にデータベースインスタンスを作成していたが、フレキシブルサーバーの場合はデフォルトで1つのコマンドで作成してくれる。これも場面によっては親切設計。

% az mysql flexible-server create --name ${AZ_SPRING_CLOUD_NAME}-mysql-flex --database-name todos --admin-user todosadmin --admin-password xxx
Checking the existence of the resource group 'azure-spring-workshop'...
Resource group 'azure-spring-workshop' exists ? : True 
Detected current client IP : xxx.xxx.xxx.xxx
Do you want to enable access to client xxx.xxx.xxx.xxx (y/n) (y/n): y
IOPS is 396 which is either your input or free(maximum) IOPS supported for your storage size and SKU.
Creating MySQL Server 'azure-spring-workshop-mysql-flex' in group 'azure-spring-workshop'...
Your server 'azure-spring-workshop-mysql-flex' is using sku 'Standard_B1ms' (Paid Tier). Please refer to <https://aka.ms/mysql-pricing> for pricing details
Configuring server firewall rule to accept connections from 'xxx.xxx.xxx.xxx'...
Creating MySQL database 'todos'...
Make a note of your password. If you forget, you would have to reset your password with'az mysql flexible-server update -n azure-spring-workshop-mysql-flex -g azure-spring-workshop -p <new-password>'.
Try using az 'mysql flexible-server connect' command to test out connection.
{
  "connectionString": "mysql todos --host azure-spring-workshop-mysql-flex.mysql.database.azure.com --user todosadmin --password=xxx",
  "databaseName": "todos",
  "firewallName": "FirewallIPAddress_2024-2-12_19-3-46",
  "host": "azure-spring-workshop-mysql-flex.mysql.database.azure.com",
  "id": "/subscriptions/xxx/resourceGroups/azure-spring-workshop/providers/Microsoft.DBforMySQL/flexibleServers/azure-spring-workshop-mysql-flex",
  "location": "Japan East",
  "password": "xxx",
  "resourceGroup": "azure-spring-workshop",
  "skuname": "Standard_B1ms",
  "username": "todosadmin",
  "version": "5.7"
}

az mysql flexible-serverのCLIコマンド詳細は以下を参照。

az mysql flexible-server

MySQLのFirewallパラメータの詳細は以下を参照。

https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/mysql/flexible-server/how-to-manage-firewall-cli.md

デフォルト値が記載されていないパラメータもあるため、実際に指定せずに確認してみた。最初の3つは明示的に指定した方が良いと思う。。。

  • —database-Name : ”flexibleserverdb”固定
  • —admin-user : ランダム値(例:”moraltacos7”)
  • —admin-password : ランダム値(例 : “oKFstnGJ4ZkkLvv0RgwrXw”)
  • —public-access : None(コマンド実行時の端末で開発するのであればコマンド実行途中でIPを登録するか聞かれた時に登録しておけばよく、必要になった時に適切なIPを後で登録すればよい)

Spring Cloud Config Serverの構成

Config Serverとは

実際にサーバーをデプロイするわけではなく、Azure Spring Appsの「Config Server」という項目に対する設定作業のこと。

SpringBootアプリケーションの構成ファイルをGithubリポジトリに保存し、そのリポジトリをAzure Spring Appsの構成ファイルとしてチェックアウトし、SpringBootアプリの各クラスターから構成ファイルを参照する設定を行う。

Javaという言語以外に、Spring Boot、Maven、Azure Spring Apps、MySQLに加えて、ここではGithubも出てくる。。 このように複数の技術要素が絡んだチュートリアルは「初学者泣かせ」だ。 手順の中で色々な要素が出てくると、おそらく自分が今何をやっているのか想像できなくなってくると思う。 全体を俯瞰した図が非常に重要で、どの要素に対して何を実行したかという証跡を書いていくと全部終わった後に見返す事で一気に理解が深まる事がある。 私も一回全体を通して実施してみて全体像を整理してから自分が理解できていなかった部分を深掘りしつつ作業ログを書くようにしている。

マイクロサービスにおけるConfig Serverのメリット

マイクロサービスを構成する際の重要な要素として、構成情報を提供する機能のサービス化がある。複数のプログラムから1つの構成情報を参照できるようになるため、サービスを構成するインスタンスがスケールアウトした時にも複数のインスタンスで簡単に同じ構成情報にアクセスする事ができるようになる。

また、構成情報をGithubなどのバージョン管理できるリポジトリで管理することで、構成情報を一元管理できたり、どのバージョンの構成情報がどの環境で利用されているのかなどをタグで管理したり、必要があればロールバックすることで全てのインスタンスで一斉に同じ構成情報を更新する事ができるなどのメリットがある。

Config Server用のGithubリポジトリの作成

自分のGithubに対して、新規にプライベートなリポジトリを作成し、そこに構成ファイルを作成する。

簡単に作成した時のスクリーンショットを掲載する。

今回は「Repository name」に「SpringCloudConfigServer」を指定し、「Private」として作成。

構成ファイルの新規作成

ここでは簡単にGithub上で「application.yml」というファイルを作成する。

ファイルを作成したら「Commit changes」をクリックしてファイルを保存する。

Config ServerからGithubにアクセスするための個人用トークンの作成

「個人用トークン」とは、有効期限かつアクセススコープを限定した代理パスワード、Azureで言うところのアプリケーションキーのようなもの。Config ServerからGithubにアクセスする場合、クレデンシャル情報(ユーザーIDとパスワード)を入力する必要があるが、通常利用しているパスワードをそのまま利用すると、自分のGithubアカウントで管理している全リポジトリに対してアカウントと同じ権限で恒久的なアクセスを許可するようなものなので、それを目的に応じて制限できる。

今回はConfig Serverに対してapplication.ymlを保存しているリポジトリだけにアクセス権限を付与した個人用トークンを無期限で作成する。

Managing your personal access tokens - GitHub Docs
You can use a personal access token in place of a password when authenticating to GitHub in the command line or with the...

Micorosft Learnでは「repoチェックをオフ」と記載されているが、repoはオンにしないと何もできない。

作成後にトークンが表示されるのでコピーボタンでコピーしてどこかに保持しておく。

Config ServerからGithubへのアクセス情報の登録

先ほど作成したAzure Spring Appsに対してConfig Serverを設定する。

  1. 作成したGithubリポジトリのURLをConfig Serverの「規定のリポジトリ」にコピーする。
  2. Config Serverの「認証」を「基本HTTP」に変更する。
  3. Githubのユーザー名と個人用トークンをコピーし、Config Serverに登録する。

「検証」をクリックして、エラーが発生しなければ「適用」をクリックする。少し時間がかかるがConfig Serverの更新が完了する。

MySQL接続文字列の設定

Config Server はAzure Spring Appsリソース単位に設定するが、データベースへの接続設定はリソース内の各Spring Bootアプリケーション単位に設定する。

また、以前は「サービスバインド」という機能で接続文字列を設定していたが、この機能が将来廃止され、今後は「サービスコネクタ」という新しい機能を用いて接続文字列を設定する。(サービスバインドとサービスコネクタの違いについては別途調査する)

Azure PortalからAzure Spring Appリソースにアクセスし、アプリケーションを選択、「サービスコネクタ」を選択して新規に作成する。

「基本情報」タブの「サービスの種類」で「MySQLフレキシブルサーバー用のデータベース」を選択する。

「認証」タブで「接続文字列」を選択し、データベースユーザー情報を登録する。

「ネットワーク」タブで「ファイアウォールルールを構成し・・・」になっていればOK。

最後に「作成」をクリックすればサービスコネクタが作成される。

以下のようにサービスコネクタが作成され、接続情報(URL, username, password)が確認できる。

Microsoft Learnの手順としてはここまでだが、「接続の状態」が「未検証」だったのが気になったので「検証」を実行。

接続先のデータベースの存在確認、Firewall確認(おそらくネットワーク的に到達可能である事の確認)、設定したユーザー情報を用いた接続確認の3つを検証し、正常に接続できた事が確認できたという事だろう。

長かったが、これでSpring Bootマイクロサービスを構築する準備が整った。

Spring Bootマイクロサービスの作成

Spring Initializrによるプロジェクトファイルの生成とダウンロード

Spring Bootのバージョンが「3.1.5-RELEASE」となっているが、mavenのparentプロジェクト参照時に失敗するため、最新安定版(記事作成時点では「3.2.2」)を指定した。

MS Learnの記載(本記事執筆時点)。

% curl https://start.spring.io/starter.tgz /
    -d type=maven-project /
    -d dependencies=web,mysql,data-jpa,cloud-eureka,cloud-config-client /
    -d baseDir=todo-service /
    -d bootVersion=3.1.5.RELEASE /
    -d javaVersion=17 | tar -xzvf -

最新安定版(本記事執筆時点)に変更。

% curl https://start.spring.io/starter.tgz /
    -d type=maven-project /
    -d dependencies=web,mysql,data-jpa,cloud-eureka,cloud-config-client /
    -d baseDir=todo-service /
    -d bootVersion=3.2.2 /
    -d javaVersion=17 | tar -xzvf -

ソースコードはMS Learnのものを利用する。

Spring Boot マイクロサービスをビルドする - Training
Spring Boot マイクロサービスをビルドする方法について説明します。

Spring Bootマイクロサービスのビルド

MS Learnに記載のコードはいくつか古い状態なのでそのままビルドしようとするとエラーになるため、以下対応を実施。

javax.persistenceパッケージが見つからない

javax.persistenceパッケージ参照エラーとなりjavaコンパイルエラーとなる。

(エラー情報がフェーズ毎に出力された後、再度全体で出力される(重複する)のをなんとかしてほしい。。)

% ./mvnw clean package -DskipTests
[INFO] Scanning for projects...
[INFO] 
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- clean:3.3.2:clean (default-clean) @ demo ---
[INFO] Deleting /Users/sato/proj/learn/java/SpringTips/todo-service/target
[INFO] 
[INFO] --- resources:3.3.1:resources (default-resources) @ demo ---
[INFO] Copying 1 resource from src/main/resources to target/classes
[INFO] Copying 0 resource from src/main/resources to target/classes
[INFO] 
[INFO] --- compiler:3.11.0:compile (default-compile) @ demo ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 4 source files with javac [debug release 17] to target/classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[3,25] パッケージjavax.persistenceは存在しません
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[4,25] パッケージjavax.persistenceは存在しません
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[5,25] パッケージjavax.persistenceは存在しません
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[7,2] シンボルを見つけられません
  シンボル: クラス Entity
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoController.java:[5,24] パッケージjavax.annotationは存在しません
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoRepository.java:[3,47] シンボルを見つけられません
  シンボル:   クラス JpaRespository
  場所: パッケージ org.springframework.data.jpa.repository
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoRepository.java:[5,41] シンボルを見つけられません
  シンボル: クラス JpaRespository
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[17,6] シンボルを見つけられません
  シンボル:   クラス Id
  場所: クラス com.example.demo.Todo
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[18,6] シンボルを見つけられません
  シンボル:   クラス GeneratedValue
  場所: クラス com.example.demo.Todo
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoController.java:[16,6] シンボルを見つけられません
  シンボル:   クラス PostConstruct
  場所: クラス com.example.demo.TodoController
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoController.java:[18,23] シンボルを見つけられません
  シンボル:   メソッド saveAll(java.util.List<com.example.demo.Todo>)
  場所: タイプcom.example.demo.TodoRepositoryの変数 todoRepository
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoController.java:[29,30] シンボルを見つけられません
  シンボル:   メソッド findAll()
  場所: タイプcom.example.demo.TodoRepositoryの変数 todoRepository
[INFO] 12 errors 
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.168 s
[INFO] Finished at: 2024-02-18T11:36:15+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project demo: Compilation failure: Compilation failure: 
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[3,25] パッケージjavax.persistenceは存在しません
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[4,25] パッケージjavax.persistenceは存在しません
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[5,25] パッケージjavax.persistenceは存在しません
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[7,2] シンボルを見つけられません
[ERROR]   シンボル: クラス Entity
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoController.java:[5,24] パッケージjavax.annotationは存在しません
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoRepository.java:[3,47] シンボルを見つけられません
[ERROR]   シンボル:   クラス JpaRespository
[ERROR]   場所: パッケージ org.springframework.data.jpa.repository
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoRepository.java:[5,41] シンボルを見つけられません
[ERROR]   シンボル: クラス JpaRespository
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[17,6] シンボルを見つけられません
[ERROR]   シンボル:   クラス Id
[ERROR]   場所: クラス com.example.demo.Todo
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/Todo.java:[18,6] シンボルを見つけられません
[ERROR]   シンボル:   クラス GeneratedValue
[ERROR]   場所: クラス com.example.demo.Todo
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoController.java:[16,6] シンボルを見つけられません
[ERROR]   シンボル:   クラス PostConstruct
[ERROR]   場所: クラス com.example.demo.TodoController
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoController.java:[18,23] シンボルを見つけられません
[ERROR]   シンボル:   メソッド saveAll(java.util.List<com.example.demo.Todo>)
[ERROR]   場所: タイプcom.example.demo.TodoRepositoryの変数 todoRepository
[ERROR] /Users/sato/proj/learn/java/SpringTips/todo-service/src/main/java/com/example/demo/TodoController.java:[29,30] シンボルを見つけられません
[ERROR]   シンボル:   メソッド findAll()
[ERROR]   場所: タイプcom.example.demo.TodoRepositoryの変数 todoRepository
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] <http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException>

以下の記事を参照すると、Java EE (javax) は jakarta EE (jakarta) に移行したらしい。

https://blogs.oracle.com/javamagazine/post/transition-from-java-ee-to-jakarta-ee

よって、パッケージ名に「javax」が含まれている部分を「jakarta」に置き換えると良いようだ。

package com.example.demo;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

@Entity
public class Todo {
    ・・・
}

javax.annotationパッケージが見つからない

これも「Java EE」が「Jakarta EE」に移行された影響。

package com.example.demo;

import org.springframework.web.bind.annotation.*;

import jakarta.annotation.PostConstruct;
import java.util.Arrays;

@RestController
public class TodoController {
    ・・・
}

これでプロジェクトのビルドに成功

% ./mvnw clean package -DskipTests
[INFO] Scanning for projects...
[INFO] 
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- clean:3.3.2:clean (default-clean) @ demo ---
[INFO] Deleting /Users/sato/proj/learn/java/SpringTips/todo-service/target
[INFO] 
[INFO] --- resources:3.3.1:resources (default-resources) @ demo ---
[INFO] Copying 1 resource from src/main/resources to target/classes
[INFO] Copying 0 resource from src/main/resources to target/classes
[INFO] 
[INFO] --- compiler:3.11.0:compile (default-compile) @ demo ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 4 source files with javac [debug release 17] to target/classes
[INFO] 
[INFO] --- resources:3.3.1:testResources (default-testResources) @ demo ---
[INFO] skip non existing resourceDirectory /Users/sato/proj/learn/java/SpringTips/todo-service/src/test/resources
[INFO] 
[INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ demo ---
[INFO] Changes detected - recompiling the module! :dependency
[INFO] Compiling 1 source file with javac [debug release 17] to target/test-classes
[INFO] 
[INFO] --- surefire:3.1.2:test (default-test) @ demo ---
[INFO] Tests are skipped.
[INFO] 
[INFO] --- jar:3.3.0:jar (default-jar) @ demo ---
[INFO] Building jar: /Users/sato/proj/learn/java/SpringTips/todo-service/target/demo-0.0.1-SNAPSHOT.jar
[INFO] 
[INFO] --- spring-boot:3.2.2:repackage (repackage) @ demo ---
[INFO] Replacing main artifact /Users/sato/proj/learn/java/SpringTips/todo-service/target/demo-0.0.1-SNAPSHOT.jar with repackaged archive, adding nested dependencies in BOOT-INF/.
[INFO] The original artifact has been renamed to /Users/sato/proj/learn/java/SpringTips/todo-service/target/demo-0.0.1-SNAPSHOT.jar.original
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.395 s
[INFO] Finished at: 2024-02-18T12:04:51+09:00
[INFO] ------------------------------------------------------------------------

Spring Bootマイクロサービスをデプロイ

以下のCLIコマンドでAzure Spring Appsのtodo-serviceアプリにデプロイする。

デプロイのログからいくつか知っておくべきAzure Spring Appsの機能があるので確認しておく。

また、このログから、今回使った最新版のSpring Boot 3.2.2は、Azure Spring Appsの対応しているバージョンとは互換性がないというエラーが発生しているのでこれに対処する。

% az spring app deploy --name todo-service --service "$AZ_SPRING_CLOUD_NAME" --resource-group "$AZ_RESOURCE_GROUP_NAME" --artifact-path target/demo-0.0.1-SNAPSHOT.jar
Seems you do not import spring actuator, thus metrics are not enabled, please refer to <https://aka.ms/ascdependencies> for more details
This command usually takes minutes to run. Add '--verbose' parameter if needed.
[1/3] Requesting for upload URL.
[2/3] Uploading package to blob.
[3/3] Updating deployment in app "todo-service" (this operation can take a while to complete)
Azure Spring Apps will use rolling upgrade to update your deployment, you have 1 instance, Azure Spring Apps will update the deployment in 1 round.
The deployment is in round 1, 1 old instance is deleted/deleting and 1 new instance is started/starting
Your application is successfully deployed.
Application logs:
BUILD_IN_EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=https://azure-spring-workshop-20240206.svc.azuremicroservices.io/eureka/eureka
BUILD_IN_SPRING_CLOUD_CONFIG_URI=https://azure-spring-workshop-20240206.svc.azuremicroservices.io/config
BUILD_IN_SPRING_CLOUD_CONFIG_FAILFAST=true
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2024-02-18 03:19:43.790Z INFO  c.m.applicationinsights.agent - Application Insights Java Agent 3.4.18 started successfully (PID 1, JVM running for 5.806 s)
2024-02-18 03:19:43.794Z INFO  c.m.applicationinsights.agent - Java version: 17.0.9, vendor: Microsoft, home: /usr/lib/jvm/msopenjdk-17

  .   ____          _            __ _ _
 /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\
( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\
 \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.2)

2024-02-18T03:19:50.779Z  INFO 1 --- [todo-service] [           main] com.example.demo.DemoApplication         : Starting DemoApplication v0.0.1-SNAPSHOT using Java 17.0.9 with PID 1 (/tmp/3b379036-ed94-437b-8af0-9e64def009e7.jar started by cnb in /home/cnb)
2024-02-18T03:19:50.793Z  INFO 1 --- [todo-service] [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2024-02-18T03:19:50.904Z  INFO 1 --- [todo-service] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : <https://azure-spring-workshop-20240206.svc.azuremicroservices.io/config/>
2024-02-18T03:19:50.904Z  INFO 1 --- [todo-service] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Located environment: name=todo-service, profiles=[default], label=null, version=b2dd9969510baead7d0c186f78e2809f26a4be8f, state=null
2024-02-18T03:19:53.212Z  INFO 1 --- [todo-service] [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2024-02-18T03:19:53.879Z  INFO 1 --- [todo-service] [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 601 ms. Found 1 JPA repository interface.
2024-02-18T03:19:54.578Z  INFO 1 --- [todo-service] [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=96275517-ebea-3a33-94cb-6b989e87d1ff
2024-02-18T03:19:55.577Z  INFO 1 --- [todo-service] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 1025 (http)
2024-02-18T03:19:56.285Z  INFO 1 --- [todo-service] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-02-18T03:19:56.286Z  INFO 1 --- [todo-service] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.18]
2024-02-18T03:19:56.579Z  INFO 1 --- [todo-service] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-02-18T03:19:56.580Z  INFO 1 --- [todo-service] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 5672 ms
2024-02-18T03:19:57.102Z  INFO 1 --- [todo-service] [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-02-18T03:19:57.290Z  INFO 1 --- [todo-service] [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.4.1.Final
2024-02-18T03:19:57.394Z  INFO 1 --- [todo-service] [           main] o.h.c.internal.RegionFactoryInitiator    : HHH000026: Second-level cache disabled
2024-02-18T03:19:58.025Z  INFO 1 --- [todo-service] [           main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2024-02-18T03:19:58.107Z  INFO 1 --- [todo-service] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-02-18T03:19:58.712Z  INFO 1 --- [todo-service] [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@452888c2
2024-02-18T03:19:58.716Z  INFO 1 --- [todo-service] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-02-18T03:19:59.082Z  WARN 1 --- [todo-service] [           main] org.hibernate.dialect.Dialect            : HHH000511: The 5.7.0 version for [org.hibernate.dialect.MySQLDialect] is no longer supported, hence certain features may not work properly. The minimum supported version is 8.0.0. Check the community dialects project for available legacy versions.
2024-02-18T03:20:01.576Z  INFO 1 --- [todo-service] [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2024-02-18T03:20:01.962Z  INFO 1 --- [todo-service] [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2024-02-18T03:20:02.921Z  WARN 1 --- [todo-service] [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2024-02-18T03:20:04.100Z  INFO 1 --- [todo-service] [           main] DiscoveryClientOptionalArgsConfiguration : Eureka HTTP Client uses RestTemplate.
2024-02-18T03:20:04.309Z  WARN 1 --- [todo-service] [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'compositeCompatibilityVerifier' defined in class path resource [org/springframework/cloud/configuration/CompatibilityVerifierAutoConfiguration.class]: Failed to instantiate [org.springframework.cloud.configuration.CompositeCompatibilityVerifier]: Factory method 'compositeCompatibilityVerifier' threw exception with message: Spring Cloud/ Spring Boot version compatibility checks have failed: [[VerificationResult@56dce7f7 description = 'Spring Boot [3.2.2] is not compatible with this Spring Cloud release train', action = 'Change Spring Boot version to one of the following versions [3.0.x, 3.1.x] .
You can find the latest Spring Boot versions here [<https://spring.io/projects/spring-boot#learn>]. 
If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [<https://spring.io/projects/spring-cloud#overview>] and check the [Release Trains] section.
If you want to disable this check, just set the property [spring.cloud.compatibility-verifier.enabled=false]']]
2024-02-18T03:20:04.311Z  INFO 1 --- [todo-service] [           main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-02-18T03:20:04.457Z  INFO 1 --- [todo-service] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2024-02-18T03:20:04.479Z  INFO 1 --- [todo-service] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2024-02-18T03:20:04.482Z  INFO 1 --- [todo-service] [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2024-02-18T03:20:04.503Z  INFO 1 --- [todo-service] [           main] .s.b.a.l.ConditionEvaluationReportLogger : 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-02-18T03:20:04.582Z ERROR 1 --- [todo-service] [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Your project setup is incompatible with our requirements due to following reasons:

- Spring Boot [3.2.2] is not compatible with this Spring Cloud release train

Action:

Consider applying the following actions:

- Change Spring Boot version to one of the following versions [3.0.x, 3.1.x] .
You can find the latest Spring Boot versions here [<https://spring.io/projects/spring-boot#learn>]. 
If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [<https://spring.io/projects/spring-cloud#overview>] and check the [Release Trains] section.
If you want to disable this check, just set the property [spring.cloud.compatibility-verifier.enabled=false]

112404: Exit code 1: application error, please refer to <https://aka.ms/exitcode>

Spring Boot Actuatorによるメトリクス情報の収集の無効化

CLIのデプロイコマンドのログの最初に「Spring Actuatorがインポートされていないため、メトリクスの収集が無効化される」というログが出力される。

本番環境では有効化することが望ましく、実行時の様々な状態監視に有効なパッケージらしい。

おそらく後々知っておくべき機能だと思うので情報源だけは残し、今は一旦このまま進める。

Production-ready Features
Seems you do not import spring actuator, thus metrics are not enabled, please refer to https://aka.ms/ascdependencies for more details

Artifactのアップロード先

Azure Spring Appsにjarファイルをアップロードすると、Azure Spring Apps内のblobストレージに一旦アップロードされるようだ。

[1/3] Requesting for upload URL.
[2/3] Uploading package to blob.

ローリングアップグレードによるダウンタイムなしのデプロイの実行

jarファイルのアップロードが完了すると、最後にjarファイルを展開してプログラムファイルとリソースファイルのデプロイが実行される。

この時、Azure Spring Appsでは、複数のレプリカが存在する場合、ダウンタイムを発生させないようkubernetesの「ローリングアップグレード」という処理が実行されるようだ。

[3/3] Updating deployment in app "todo-service" (this operation can take a while to complete)
Azure Spring Apps will use rolling upgrade to update your deployment, you have 1 instance, Azure Spring Apps will update the deployment in 1 round.
The deployment is in round 1, 1 old instance is deleted/deleting and 1 new instance is started/starting
Your application is successfully deployed.

ローリングアップグレードは、新しいデプロイを含んだレプリカを作成し、古いレプリカを順次停止/削除する処理が実行されるが、実行ログからもわかる通り、今回実行中のレプリカは1つのみなので、入れ替えのタイミングでダウンタイムが発生する可能性がある。

ローリングアップグレード自体はKubernetesの仕組みのようだ。以下のオンラインドキュメントに詳細なKubernetesの動作内容が説明されている。

この仕組みは後で詳細に調査する。

Performing a Rolling Update
Perform a rolling update using kubectl.

Azure Spring Appsでこれが実行されるのは、pom.xmlファイルにNetflix Eurekaというコンポーネントを含めている場合。Netflix Eurekaは、Spring Bootアプリケーションの各レプリカの正常性監視や今回のようなデプロイ時のゼロダウンタイムを実現するための仕組みを提供するコンポーネント。

別途詳細を調査する。

Spring Cloud Netflix

Azure Spring Appsではデプロイ時にゼロダウンタイムを実現するための仕組みとして「ブルーグリーンデプロイ」にも対応しており、ブルーグリーンデプロイの場合は単一レプリカであってもダウンタイムは発生しない。

これも別途詳細に調査する。

Azure Spring Apps でのダウンタイムなしのデプロイ
Azure Spring Apps におけるブルーグリーン デプロイ戦略を使用したダウンタイムなしのデプロイについて説明します。

Azure Spring Appsと互換性のあるSpring Bootバージョンへの変更

上記で言及した処理以外にも、Javaランタイムバージョン、Config Serverから取得した構成情報の適用、JPAやHibernateのセットアップ、Tomcatサーバーの実行と待ち受けポートの初期化など、様々な処理内容が実行ログから読み取れる。

ただ、デプロイログの最後でデプロイの失敗に関する情報が出力された。

--- 失敗の検出は以下の部分で発生し、以降の処理はサービスのシャットダウン処理とエラーに関する情報出力で処理が終わっている ---
2024-02-18T03:20:04.309Z  WARN 1 --- [todo-service] [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'compositeCompatibilityVerifier' defined in class path resource [org/springframework/cloud/configuration/CompatibilityVerifierAutoConfiguration.class]: Failed to instantiate [org.springframework.cloud.configuration.CompositeCompatibilityVerifier]: Factory method 'compositeCompatibilityVerifier' threw exception with message: Spring Cloud/ Spring Boot version compatibility checks have failed: [[VerificationResult@56dce7f7 description = 'Spring Boot [3.2.2] is not compatible with this Spring Cloud release train', action = 'Change Spring Boot version to one of the following versions [3.0.x, 3.1.x] .
You can find the latest Spring Boot versions here [<https://spring.io/projects/spring-boot#learn>]. 
If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [<https://spring.io/projects/spring-cloud#overview>] and check the [Release Trains] section.
If you want to disable this check, just set the property [spring.cloud.compatibility-verifier.enabled=false]']]
2024-02-18T03:20:04.311Z  INFO 1 --- [todo-service] [           main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-02-18T03:20:04.457Z  INFO 1 --- [todo-service] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2024-02-18T03:20:04.479Z  INFO 1 --- [todo-service] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2024-02-18T03:20:04.482Z  INFO 1 --- [todo-service] [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2024-02-18T03:20:04.503Z  INFO 1 --- [todo-service] [           main] .s.b.a.l.ConditionEvaluationReportLogger : 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-02-18T03:20:04.582Z ERROR 1 --- [todo-service] [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Your project setup is incompatible with our requirements due to following reasons:

- Spring Boot [3.2.2] is not compatible with this Spring Cloud release train

Action:

Consider applying the following actions:

- Change Spring Boot version to one of the following versions [3.0.x, 3.1.x] .

Azure Spring Appsでは、Spring Boot 3.2.2にはまだ対応していないようで、3.0.x系、もしくは3.1.x系に変更せよということだった。

pom.xmlを以下のように変更し、Spring Boot 3.1.x系の最新安定版である3.1.8に変更して再度デプロイ。

<?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.1.8</version>
	...
	</parent>
	...
</project>

実行結果として、「provisioningState」が「Succeeded」となり、「status」が「Running」となっていることが確認できる。

% az spring app deploy --name todo-service --service "$AZ_SPRING_CLOUD_NAME" --resource-group "$AZ_RESOURCE_GROUP_NAME" --artifact-path target/demo-0.0.1-SNAPSHOT.jar
Seems you do not import spring actuator, thus metrics are not enabled, please refer to <https://aka.ms/ascdependencies> for more details
This command usually takes minutes to run. Add '--verbose' parameter if needed.
[1/3] Requesting for upload URL.
[2/3] Uploading package to blob.
[3/3] Updating deployment in app "todo-service" (this operation can take a while to complete)
Azure Spring Apps will use rolling upgrade to update your deployment, you have 1 instance, Azure Spring Apps will update the deployment in 1 round.
The deployment is in round 1, 1 old instance is deleted/deleting and 1 new instance is started/starting
Your application is successfully deployed.
Application logs:
BUILD_IN_EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=https://azure-spring-workshop-20240206.svc.azuremicroservices.io/eureka/eureka
BUILD_IN_SPRING_CLOUD_CONFIG_URI=https://azure-spring-workshop-20240206.svc.azuremicroservices.io/config
BUILD_IN_SPRING_CLOUD_CONFIG_FAILFAST=true
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2024-02-20 04:52:18.706Z INFO  c.m.applicationinsights.agent - Application Insights Java Agent 3.4.18 started successfully (PID 1, JVM running for 6.514 s)
2024-02-20 04:52:18.710Z INFO  c.m.applicationinsights.agent - Java version: 17.0.9, vendor: Microsoft, home: /usr/lib/jvm/msopenjdk-17

  .   ____          _            __ _ _
 /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\
( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\
 \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.8)

2024-02-20T04:52:25.304Z  INFO 1 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication v0.0.1-SNAPSHOT using Java 17.0.9 with PID 1 (/tmp/7d13edd3-aa57-4997-966f-4f8c911847b7.jar started by cnb in /home/cnb)
2024-02-20T04:52:25.315Z  INFO 1 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2024-02-20T04:52:25.419Z  INFO 1 --- [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : <https://azure-spring-workshop-20240206.svc.azuremicroservices.io/config/>
2024-02-20T04:52:25.419Z  INFO 1 --- [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Located environment: name=todo-service, profiles=[default], label=null, version=b2dd9969510baead7d0c186f78e2809f26a4be8f, state=null
2024-02-20T04:52:27.530Z  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2024-02-20T04:52:28.114Z  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 575 ms. Found 1 JPA repository interface.
2024-02-20T04:52:28.800Z  INFO 1 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=6019977f-6731-31e8-aa84-3225f3c89db9
2024-02-20T04:52:29.927Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 1025 (http)
2024-02-20T04:52:30.803Z  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-02-20T04:52:30.803Z  INFO 1 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.18]
2024-02-20T04:52:31.112Z  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-02-20T04:52:31.113Z  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 5689 ms
2024-02-20T04:52:31.711Z  INFO 1 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-02-20T04:52:31.895Z  INFO 1 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.2.20.Final
2024-02-20T04:52:31.899Z  INFO 1 --- [           main] org.hibernate.cfg.Environment            : HHH000406: Using bytecode reflection optimizer
2024-02-20T04:52:32.542Z  INFO 1 --- [           main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2024-02-20T04:52:32.629Z  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-02-20T04:52:33.416Z  INFO 1 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@52d68eb9
2024-02-20T04:52:33.421Z  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-02-20T04:52:36.221Z  INFO 1 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2024-02-20T04:52:36.605Z  INFO 1 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2024-02-20T04:52:37.821Z  WARN 1 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2024-02-20T04:52:39.227Z  INFO 1 --- [           main] DiscoveryClientOptionalArgsConfiguration : Eureka HTTP Client uses RestTemplate.
2024-02-20T04:52:39.493Z  WARN 1 --- [           main] iguration$LoadBalancerCaffeineWarnLogger : Spring Cloud LoadBalancer is currently working with the default cache. While this cache implementation is useful for development and tests, it's recommended to use Caffeine cache in production.You can switch to using Caffeine cache, by adding it and org.springframework.cache.caffeine.CaffeineCacheManager to the classpath.
2024-02-20T04:52:39.611Z  INFO 1 --- [           main] o.s.c.n.eureka.InstanceInfoFactory       : Setting initial instance status as: STARTING
2024-02-20T04:52:39.722Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Initializing Eureka in region us-east-1
2024-02-20T04:52:39.730Z  INFO 1 --- [           main] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration
2024-02-20T04:52:39.821Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Disable delta property : false
2024-02-20T04:52:39.822Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Single vip registry refresh property : null
2024-02-20T04:52:39.822Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Force full registry fetch : false
2024-02-20T04:52:39.822Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Application is null : false
2024-02-20T04:52:39.822Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Registered Applications size is zero : true
2024-02-20T04:52:39.823Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Application version is -1: true
2024-02-20T04:52:39.823Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Getting all instance registry info from the eureka server
2024-02-20T04:52:40.101Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : The response status is 200
2024-02-20T04:52:40.107Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Starting heartbeat executor: renew interval is: 30
2024-02-20T04:52:40.115Z  INFO 1 --- [           main] c.n.discovery.InstanceInfoReplicator     : InstanceInfoReplicator onDemand update allowed rate per min is 4
2024-02-20T04:52:40.121Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Discovery Client initialized at timestamp 1708404760121 with initial instances count: 2
2024-02-20T04:52:40.124Z  INFO 1 --- [           main] o.s.c.n.e.s.EurekaServiceRegistry        : Registering application TODO-SERVICE with eureka with status UP
2024-02-20T04:52:40.125Z  INFO 1 --- [           main] com.netflix.discovery.DiscoveryClient    : Saw local status change event StatusChangeEvent [timestamp=1708404760125, current=UP, previous=STARTING]
2024-02-20T04:52:40.130Z  INFO 1 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_TODO-SERVICE/todo-service-default-12-7977dc9d8f-mnwlq:todo-service:1025: registering service...
2024-02-20T04:52:40.302Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 1025 (http) with context path ''
2024-02-20T04:52:40.303Z  INFO 1 --- [           main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 1025
2024-02-20T04:52:40.392Z  INFO 1 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_TODO-SERVICE/todo-service-default-12-7977dc9d8f-mnwlq:todo-service:1025 - registration status: 204
2024-02-20T04:52:40.401Z  INFO 1 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 18.682 seconds (process running for 28.209)
{
  "id": "/subscriptions/51cf59b2-3184-4d6c-abb6-6ab69af3415c/resourceGroups/azure-spring-workshop/providers/Microsoft.AppPlatform/Spring/azure-spring-workshop-20240206/apps/todo-service/deployments/default",
  "name": "default",
  "properties": {
    "active": true,
    "deploymentSettings": {
      "addonConfigs": null,
      "apms": null,
      "containerProbeSettings": null,
      "environmentVariables": null,
      "livenessProbe": {
        "disableProbe": false,
        "failureThreshold": 3,
        "initialDelaySeconds": 300,
        "periodSeconds": 10,
        "probeAction": {
          "type": "TCPSocketAction"
        },
        "successThreshold": 1,
        "timeoutSeconds": 3
      },
      "readinessProbe": {
        "disableProbe": false,
        "failureThreshold": 3,
        "initialDelaySeconds": 0,
        "periodSeconds": 5,
        "probeAction": {
          "type": "TCPSocketAction"
        },
        "successThreshold": 1,
        "timeoutSeconds": 3
      },
      "resourceRequests": {
        "cpu": "1",
        "memory": "1Gi"
      },
      "scale": null,
      "startupProbe": null,
      "terminationGracePeriodSeconds": 90
    },
    "instances": [
      {
        "discoveryStatus": "UP",
        "name": "todo-service-default-12-7977dc9d8f-mnwlq",
        "reason": null,
        "startTime": "2024-02-20T04:51:56Z",
        "status": "Running",
        "zone": null
      }
    ],
    "provisioningState": "Succeeded",
    "source": {
      "jvmOptions": null,
      "relativePath": "resources/f18ce5a8a2f955670e093983542115ddadd14c88e75a2cebdd89286a0bff806d-2024022004-cf3ba3bc-5c1e-4522-b48f-0ea6a2e6276a",
      "runtimeVersion": "Java_17",
      "type": "Jar",
      "version": null
    },
    "status": "Running"
  },
  "resourceGroup": "azure-spring-workshop",
  "sku": {
    "capacity": 1,
    "name": "S0",
    "tier": "Standard"
  },
  "systemData": {
    "createdAt": "2024-02-08T15:00:07.687094+00:00",
    "createdBy": "IOCC_sato@gbssandbox.onmicrosoft.com",
    "createdByType": "User",
    "lastModifiedAt": "2024-02-20T04:51:47.723670+00:00",
    "lastModifiedBy": "IOCC_sato@gbssandbox.onmicrosoft.com",
    "lastModifiedByType": "User"
  },
  "type": "Microsoft.AppPlatform/Spring/apps/deployments"
}

動作確認

Azure Spring Appsのアプリケーションを選択して「テストエンドポイント」のURLをコピー、curlでアクセスしてみる。

% curl <https://primary:hhXQBIoGngnjNclXRtlCVCS7gRLQKrM1kH8wUUP4Mjz6ziG0h3G8lDOaWNRRdMWx@azure-spring-workshop-20240206.test.azuremicroservices.io/todo-service/default/>
[{},{},{}]

TodoController.javaのinit()メソッドで初期値をデータベースに保存しているデータが表示されないためデバッグして原因を追求、一旦デプロイまで完了したので、デバッグ方法については別の記事で記載する。

まとめ

ようやくまとめ。。

今回のLearnでは、多くのことを学習した。

  • Azure CLIのデフォルト値によるコマンドパラメータの省略方法
  • Azure Spring Appsの構成
  • Config Server による構成情報の一元管理方法
  • Azure Database for MySQLの単一サーバーがEOSとなり、フレキブルサーバーを利用する必要がある。
  • Azure Spring Appsで利用可能なSpring Bootバージョン
  • ゼロダウンタイムを実現するデプロイ方法のオプション
  • Spring Bootの拡張モジュールであるNetflix EurekaによるKubernetesレプリカの監視・管理とローリングアップグレード

これらの処理を実際に行う時に発生したエラーの対処方法も確認した。

まだまだ詳細まで理解が及んでいないが、今後の課題として学習リストに入れておいた。

また、Webアプリの初期処理としてデータベースの初期データの登録処理を書いたのだがうまく動作していないので、今後デバッグ方法を確認しながらエラーを取り除いていく方法も学習する。

コメント

タイトルとURLをコピーしました