メインコンテンツに移動
デベロッパーのためのクラウド活用方法

Spring Boot アプリケーションをお手軽に本番運用 ~ AWS App Runner と AWS CDK の活用 ~ 最終回

2023-11-01 | Author : 林 政利, 吉田 英史

はじめに

こんにちは、ソリューションアーキテクトの林です。普段は、コンテナという技術にフォーカスして、さまざまな業種、業態のお客様を支援させていただいています。

本シリーズは、 AWS App Runner と Spring Boot をテーマに、 簡単なアプリケーションをデプロイするところから始めて、データベースやキャッシュサーバーとの接続、秘匿情報の管理、Spring Boot Actuator によるメトリクスの取得とその活用、カナリアリリースなど、本番環境で実施したい諸々の方法を皆様にご紹介していくものです。

前回 は、App Runner サービスから、AWS Systems Manager (SSM) Parameter Store や AWS Secrets Manager に保存された設定、秘匿情報を参照する方法を解説しました。これにより、データベースの接続情報や API のキー情報をセキュアに管理できるようになりました。

最終回となるこの記事では、App Runner サービスのモニタリングについて解説します。また、モニタリングした情報を元にアプリケーションのリリースを段階的に行う、Progressive Delivery について App Runner でどのように実施すればいいのか、例をご紹介します。

X ポスト » | Facebook シェア » | はてブ »

ご注意

本記事で紹介する AWS サービスを起動する際には、料金がかかります。builders.flash メールメンバー特典の、クラウドレシピ向けクレジットコードプレゼントの入手をお勧めします。

*ハンズオン記事およびソースコードにおける免責事項 »

builders.flash メールメンバー登録

builders.flash メールメンバー登録で、毎月の最新アップデート情報とともに、AWS を無料でお試しいただけるクレジットコードを受け取ることができます。

今すぐ登録 »

AWS App Runner とモニタリング

AWS App Runner は、デフォルトでモニタリングの仕組みを備えています。

ログ、メトリクス

App Runner では、Amazon CloudWatch を利用して メトリクス や ログ を収集します。もちろん、メトリクスやログからアラームを設定して、閾値を超えた時に通知するなどの仕組みも実装できます。メトリクスとしては App Runner サービスの CPU、メモリ使用率や同時接続数、HTTP リクエスト数、各 HTTP ステータスコードのレスポンス数、レイテンシーなど、Web サービスで一般的に必要とされるメトリクスが含まれています。前回までの記事でデプロイしたサービスでこれらメトリクスを確認してみましょう。

以下のコマンドで、サービスへのリクエストを発生させておきます。

サービスへのリクエストを発生させる

コマンド

bash
watch curl -s https://xxx.xxx.awsapprunner.com # App Runner にデプロイしたサービスのURL

Metrics ダッシュボード

App Runner にデプロイしたサービスをコンソール上で確認し、「 Metrics」タブを開くと、追加の手順なしでこのようにメトリクスが取得できていることがわかります。
Screenshot of the AWS App Runner console showing service metrics for a Spring Boot application, including request count and HTTP 2xx response count graphs over a 3-hour period.

トレース

App Runner では、AWS X-Ray でトレースを取得 できます。

トレースを取得するための設定も容易です。

  • App Runner のトレース機能を有効化する
  • アプリケーションに OpenTelemetry SDK を組み込み、OpenTelemetry 形式でトレースをパブリッシュする

上記を試してみましょう。App Runner のトレース機能は、App Runner のコンソールから有効化できますが、今回はすでに AWS CDK でサービスを管理しているため、CDK から有効化してみます。

以下の通り、既存の CDK コードの lib/infra-stack.ts に設定を追加します。

Architecture diagram illustrating AWS App Runner service integrated with OpenTelemetry SDK for tracing, including trace data flows to AWS X-Ray and Amazon CloudWatch.

ib/infra-stack.ts に設定を追加

スクリプト

typescript
...
    const instanceRole = new iam.Role(this, 'InstanceRole', {
    ...
    });
    // サービスが X-Ray にトレース情報を公開できるよう権限を追加する
    instanceRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("AWSXRayDaemonWriteAccess"));
    
    // X-Ray 機能を有効化する、App Runner のオブザーバビリティ設定を追加
    const appRunnerObservability = new apprunner.CfnObservabilityConfiguration(this, 'ObservabilityConfiguration', {
      traceConfiguration: {
        vendor: "AWSXRAY",
      },
    });
    ...
    
    // App Runner サービスを作成
    const service = new apprunner.CfnService(this, 'Service', {
      sourceConfiguration: {
      ...
      },
      
      // 上記で定義したオブザーバビリティの設定をサービスに組み込む
      observabilityConfiguration: {
        observabilityEnabled: true,
        observabilityConfigurationArn: appRunnerObservability.attrObservabilityConfigurationArn,
      },
      ...

デプロイして App Runner サービスに反映

上記の設定をデプロイして App Runner サービスに反映します。

bash
npx cdk deploy --all

成功 !

これにより、アプリケーションから X-Ray のトレース情報をパブリッシュできるようになりました。

続けて、アプリケーションが実際にトレース情報を出力するように修正します。 App Runner では、トレース情報を OpenTelemetry を使って公開することになっています。OpenTelemetry は、Cloud Native Computing Foundation (CNCF) でホストされているプロジェクトで、アプリケーションの分散トレース、メトリクス、ログを収集するための API、ライブラリ、エージェントを提供しています。

ここでは、AWS Distro for OpenTelemetry (ADOT) という、AWS が開発している OpenTelemetry のディストリビューションを利用して、アプリケーションがトレース情報を公開するように設定します。

AWS Distro for OpenTelemetry (ADOT) でトレースを取得する

ADOT は Java の自動計装に対応しており、アプリケーションコードを変更しなくてもライブラリを設定するだけでトレース情報を取得できます。 具体的には、以下のように起動時に Java エージェントの仕組みを利用することで、ADOT が Java コードにトレース出力のコードを挿入してくれます。

bash
java -javaagent <ADOT のエージェントの jar> -jar <アプリケーションのjar>

アプリケーションのビルドと実行を変更

上記のような呼び出しになるよう、アプリケーションのビルドと実行を変更しましょう。 これまで作成したコードのうち、 build.gradle を以下の通り変更します。

java
repositories {
    mavenCentral()
}

// Java エージェントのjarを格納する依存関係グループ
configurations {
    javaAgent
}

dependencies {
    ...
    
    // ADOT Java エージェントのダウンロード
    javaAgent('software.amazon.opentelemetry:aws-opentelemetry-agent:1.30.0')
}

// ダウンロードした ADOT Java エージェントをコピー
task agentLibs(type: Copy) {
    from configurations.javaAgent.singleFile
    into layout.buildDirectory.dir('libs')
    rename 'aws-opentelemetry-agent-.*\\.jar', 'aws-opentelemetry-agent.jar'
}

// アプリケーションの jar をビルドした際に ADOT の jar も取得する
bootJar {
    dependsOn agentLibs
}

環境変数を設定

そして、OpenTelemetry でトレースを出力するための環境変数をアプリケーションに設定します。lib/service-stack.ts に以下の設定を追加します。

typescript
 // App Runner サービスを作成
    const service = new apprunner.CfnService(this, 'Service', {
      sourceConfiguration: {
        ...
        codeRepository: {
          ...
          codeConfiguration: {
            configurationSource: "API",
            codeConfigurationValues: {
              ...
              runtimeEnvironmentVariables: [
                // Java エージェントを設定
                {
                  name: "JAVA_TOOL_OPTIONS",
                  value: "-javaagent:/app/aws-opentelemetry-agent.jar",
                },
                // トレースに X-Ray を設定
                {
                  name: "OTEL_PROPAGATORS",
                  value: "xray",
                },
                // OpenTelemetry はトレース以外にメトリクスも本来出力できるが、App Runner の
                // OpenTelemetry サポートはメトリクスに対応していないため無効化する
                {
                  name: "OTEL_METRICS_EXPORTER",
                  value: "none",
                },
                // X-Ray 上のサービス名を設定
                {
                  name: "OTEL_RESOURCE_ATTRIBUTES",
                  value: "service.name=expt_apprunner_springboot",
                },
              ],
              ...

CDK とアプリケーションをデプロイ

以上でトレースの出力設定は完了です。CDK とアプリケーションをデプロイしてみましょう。

bash
npx cdk deploy --all

アプリケーションをデプロイ

次に、App Runner のマネジメントコンソールから、「 Deploy」ボタンを押してアプリケーションをデプロイします。
Screenshot of the AWS App Runner service overview dashboard showing service status, incoming traffic information, default domain, service ARN, and source repository details.

トレース確認

完了後、アプリケーションに何度かアクセスすると、以下のように X-Ray でネットワーク、メソッド呼び出しのトレースが確認できるようになります。

bash
watch curl -s https://xxx.xxx.awsapprunner.com # App Runner にデプロイしたサービスのURL

画面イメージ

Screenshot of the 'Observability' tab in the AWS App Runner service overview, showing service status, domain, incoming traffic, visibility options, tracing status, and custom domains. The interface includes sections for logs, activity, metrics, and configuration.

Screenshot of the AWS X-Ray console showing AppRunner service segment details, segment timeline, and application root segment information including the overview and logs.

Actuator でアプリケーションメトリクスを取得する

ご覧いただいた通り、AWS App Runner では Web アプリケーションで一般的に必要とされるメトリクスやトレースをサービスの機能で取得できます。
しかし、プロダクション環境では、さらに詳細なアプリケーションレベルのメトリクスを取得したいケースもあるでしょう。
Java アプリケーションでは、Micrometer という Java ライブラリでアプリケーションのメトリクスを取得することが多いのではないでしょうか。この Micrometer はメトリクスのファサードとして動作し、アプリケーションのメトリクスを CloudWatch などさまざまなモニタリングのサービスにメトリクスを送信できます。
Architecture diagram showing the integration between AWS CloudWatch and Prometheus, illustrating how monitoring data flows from AWS services and custom applications to CloudWatch and Prometheus endpoints.

同じくメトリクスを CloudWatch に送信

Spring Boot では、Actuator というモジュールでさまざまなメトリクスを公開できますが、この Micrometer を利用しているので、同じくメトリクスを CloudWatch に送信できます。そのための設定を見てみましょう。

ライブラリを追加

まず、これまで同様 build.gradle を編集して、Micrometer と Actuator のライブラリを追加します。

java
dependencyManagement {
    imports {
        mavenBom 'io.awspring.cloud:spring-cloud-aws-dependencies:2.4.4'
    }
}

dependencies {
    ...
    implementation('io.awspring.cloud:spring-cloud-starter-aws')
    implementation('org.springframework.boot:spring-boot-starter-actuator')
    implementation('io.micrometer:micrometer-registry-cloudwatch')
    ...
}

Spring Boot に設定追加

次に、src/main/resources/application.yaml を下記のように編集し、メトリクスを CloudWatch に送信するよう Spring Boot に設定を加えましょう。

xml
...
spring:
  redis:
    host: ${CACHE_HOST:localhost}
    port: ${CACHE_PORT:6379}
    password: ${CACHE_PASSWORD:}
management:
  endpoint:
    health:
      show-details: always
  metrics:
    enable:
      process: true
      spring: true
      tomcat: true
      jvm: true
    export:
      cloudwatch:
        namespace: expt-apprunner-springboot
        enabled: true
        step: 1s

lib/infra-stack.ts に権限設定を追加

アプリケーションの設定は整いました。 ここまでの実装では、AppRunner から CloudWatch へメトリクスを送信する権限が不足しているため、既存の CDK コードの lib/infra-stack.ts に権限設定を追加します。

typescript
    const instanceRole = new iam.Role(this, 'InstanceRole', {
      assumedBy: new iam.ServicePrincipal('tasks.apprunner.amazonaws.com'),
      inlinePolicies: {
        ....
        
        cloudwatch: new iam.PolicyDocument({
          statements: [
            new iam.PolicyStatement({
              effect: iam.Effect.ALLOW,
              actions: [ "cloudwatch:PutMetricData" ],
              resources: [ "*" ],
            }),
          ],
        }),
      },
    });

アプリケーションをデプロイ

以上で、CloudWatch に対してメトリクスを送る準備は完了です。
それでは、デプロイして動作を確認してみましょう。まず、CDK をデプロイし、先ほどと同様にマネジメントコンソールからアプリケーションをデプロイします。

npx cdk deploy --all
Screenshot of the AWS App Runner service overview dashboard showing service status, incoming traffic information, default domain, service ARN, and source repository details.

メトリクス確認

完了後、アプリケーションに何度かアクセスすると、CloudWatch メトリクスのコンソール 上で、「expt-apprunner-springboot」以下に Spring Boot の Actuator が出力するさまざまなメトリクスが確認できるようになりました。

watch curl -s https://xxx.xxx.awsapprunner.com # App Runner にデプロイしたサービスのURL
Screenshot of an Amazon CloudWatch metrics page displaying a graph of the jvm.threads.live.value metric over time, with related JVM and process metric options shown below and no alarms triggered.

成功 !

いかがだったでしょうか。アプリケーションメトリクスやトレースでは、ライブラリの追加と少しの App Runner 設定変更で簡単にデータを収集できることがお分かりいただけたかと思います。また、CPU 使用率や HTTP リクエスト数などの Web サービスにおいて一般的に必要とされるメトリクスやログに至っては、標準機能として予め App Runner に備わっていることも知ることができました!

次は、こうして収集されたメトリクスを使って高度なリリース戦略を実現してみましょう。

アプリケーションメトリクスを元にリリースを管理する

AWS App Runner は ブルーグリーン形式でサービスをデプロイします。サービスの起動に失敗した場合は、トラフィックを失敗したサービスに振り向けることなくロールバックするため、サービスアクセスでエラーが発生することはありません。

しかし、プロダクションでアプリケーションを運用する場合、もう少し複雑なリリース戦略を採用したくなるかもしれません。例えば、現在、 App Runner は前述の通りさまざまなメトリクスを取得できますが、そのメトリクスを元にロールバックしたり、Progressive Delivery と呼ばれるような、サービスを少しずつエンドユーザーに公開し、メトリクスで異常があった場合にロールバックするというような手法などはサポートされていません。

しかし、他の AWS サービスをビルディングブロックとして組み合わせることで、App Runner でもこうしたリリース戦略を実現できます。

AWS AppConfig で Progressive Delivery を実装する

AWS AppConfig は、アプリケーションの設定を管理、デプロイするためのマネージドサービスです。アプリケーションは、AppConfig から実行時に設定を動的に読み込むことで、アプリケーションそのものの再デプロイや再起動を行うことなくその挙動を変更できます。

AppConfig には機能フラグを管理する仕組みがあります。これにより、新しい機能のリリースを AppConfig で管理できます。つまり、アプリケーションを再デプロイすることなく、AppConfig のコンソールや API を使って機能のリリースを制御できます。
AppConfig には CloudWatch のアラームを受けて設定をロールバックする機能もあるため、モニタリング結果をリリースに反映させることも可能です。

ここでは、新しい機能を本番環境の Spring Boot アプリケーションに導入する、という想定で、この AppConfig を使った機能リリースを体験してみましょう。

Architecture diagram illustrating the use of feature flags and canary releases in an AWS microservices environment, showing build, request routing, configuration management, and deployment integration.

AWS AppConfig の設定

まず、AppConfig に機能フラグを設定するために、AppConfig のコンソール 上でアプリケーションの作成を行います。この時、選択しているリージョンが App Runner でアプリケーションをデプロイしているリージョンと同じであることを確認しておきましょう。

Screenshot of the AWS Management Console showing the region selection dropdown with US West (Oregon) (us-west-2) selected. The dropdown also displays other regions such as US East (N. Virginia), US East (Ohio), and US West (N. California).

名前を設定

コンソール上では、「Create application」をクリックし、「expt-apprunner-springboot」などの名前を設定します。

Screenshot of the AWS AppConfig Applications Dashboard in AWS Systems Manager, showing the Applications tab with options to view details, delete application, and create application.

Screenshot of the AWS AppConfig interface showing the 'Create application' screen with the example application name 'expt-apprunner-springboot'. The page includes sections for application details, description, associate extensions, and tags, with options to cancel or create the application.

プロファイルを作成

次に、プロファイルを作成していきます。Configuration Profiles and Feature Flags の項目で「Create」をクリックして新しいプロファイルを用意します。

Screenshot of the AWS AppConfig user interface displaying the configuration profiles and feature flags section for the expt-apprunner-springboot application. The image shows options to delete or update the application, view configuration profiles, and manage feature flags.

プロファイルタイプ

続くプロファイルタイプとして「Feature Flag」を選び、「feature1」という名前をつけましょう。

Screenshot of the AWS AppConfig user interface displaying the configuration profiles and feature flags section for the expt-apprunner-springboot application. The image shows options to delete or update the application, view configuration profiles, and manage feature flags.

Screenshot of the AWS AppConfig interface showing the 'Select configuration profile type' dialog, where users can choose between creating a Feature Flag or a Freeform Configuration profile.

Screenshot of the AWS console form for creating a feature flag configuration profile, showing fields for profile name and optional description.

機能フラグを追加

さて、プロファイルは出来上がったので、ここに具体的な機能フラグを追加していきます。「+ Add new flag」をクリックし、詳細設定画面で Name に mode と入力しますと、Key にも自動的に反映されます。

Screenshot of the AWS AppConfig console showing configuration profile details for feature1, including profile ID, KMS Key Identifier, and flags section.
Screenshot of the AWS Feature Flag details screen showing the configuration of a flag named 'mode' with a key 'mode', an optional description field, and a flag deprecation option for short-term flags.
Screenshot of the AWS console 'Flags' section showing a warning message that changes are not saved, with a prompt to save a new version before starting deployment. The screen includes buttons for deleting or saving a new version, and an option to add a new flag.

新しいバージョンとして保存

フラグが追加できたら、「Save new version」をクリックして、この設定を新しいバージョンとして保存します。

Screenshot of the AWS console 'Flags' section showing a warning message that changes are not saved, with a prompt to save a new version before starting deployment. The screen includes buttons for deleting or saving a new version, and an option to add a new flag.

デプロイのための設定を追加

正常に保存ができたら、次は「Start deployment」をクリックしてデプロイのための設定を追加していきます。

Screenshot of the AWS AppConfig interface showing configuration profile details for feature1, including profile ID, description, and KMS Key Identifier, with deployment options visible.

環境定義

現時点では、デプロイ先の環境を選択するドロップボックスに候補がないので、「Create environment」をクリックして環境の定義を作成しましょう。

Screenshot of the AWS Console 'Start deployment' details form, displaying options to choose or create an environment, select a hosted configuration version, and select or create a deployment strategy.

名前を設定

今回は本番環境へのデプロイを想定しているため、デプロイ先の環境として「prod」という名前を設定し「Create environment」をクリックします。

Screenshot of the AWS AppConfig interface showing the creation of a 'prod' environment, with environment name and description fields visible.

AppConfig.AllAtOnece (Quick) を選択

またデプロイ戦略としては、すべてのターゲットに対して即座に設定のデプロイを行う「AppConfig.AllAtOnece (Quick)」を選択します。

AppConfig で用意しているその他のデプロイ戦略や、独自のデプロイ戦略の作成方法については AppConfig のドキュメント にまとまっています。

Screenshot of the AWS AppConfig 'Start deployment' form showing options for environment selection, hosted configuration version, deployment strategy, and an optional deployment description.

設定のデプロイ

最後に「Start deployment」をクリックして設定のデプロイを行いましょう。Deployment status のプログレスバーが進んでいることを確認してください。

Screenshot of the AWS AppConfig Deployment details page displaying Deployment 1 with deployment status 'Baking' and 100% completion, including information about retrieving configuration data.

Spring Boot アプリケーションに新機能を作成する

Spring Boot アプリケーションが AWS AppConfig から上記で設定した機能フラグを取得するために、AppConfig 用の AWS SDK をライブラリとしてアプリケーションの設定に追加していきます。

build.gradle を編集

まず、build.gradle を以下のように編集します。

java
dependencies {
    ....
    // AppConfig の SDK
    implementation('software.amazon.awssdk:appconfigdata:2.20.162')
    runtimeOnly('software.amazon.awssdk:sts:2.20.162')
    ...
}

ファイルを作成

次に、アプリケーションに新しい Controller を作成したいので、新規に以下の内容で src/main/java/com/example/exptapprunnerspringboot/FeatureOneController.java ファイルを作成してください。

java
package com.example.exptapprunnerspringboot;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FeatureOneController {

    @Autowired
    private FeatureFlag featureFlag;
    
    @GetMapping("/feature1")
    public Map<String, Boolean> root() throws InterruptedException {
        if (featureFlag.isFeatureOneEnabled()) {
            // The feature is assumed to consume a bit time.
            Thread.sleep(3000);
        }
        return Map.of("feature1", featureFlag.isFeatureOneEnabled());
    }
}

FeatureFlag クラスを作成

続いて、AppConfig から機能フラグを取得するための FeatureFlag クラスを作成します。こちらも、新規に以下の内容で src/main/java/com/example/exptapprunnerspringboot/FeatureFlag.java ファイルを作成してください。Java アプリケーションから AppConfig 上の設定を取得する詳細な方法については、AppConfig のドキュメント や AWS SDK for Java のドキュメント を参照してください。

FeatureFlag クラスを作成

コード

java
package com.example.exptapprunnerspringboot;

import java.io.UncheckedIOException;
import java.io.Serializable;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonProperty;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient;
import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationResponse;
import software.amazon.awssdk.services.appconfigdata.model.ResourceNotFoundException;
import software.amazon.awssdk.services.appconfigdata.model.StartConfigurationSessionRequest;

@Component
public class FeatureFlag {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final ObjectMapper objectMapper;

    private final AppConfigDataClient client = AppConfigDataClient.builder().build();

    private final String appName;
    private final String envName;

    private String sessionToken;

    private volatile boolean feature1 = false;

    public FeatureFlag(@Autowired ObjectMapper objectMapper,
            @Value("${settings.app.name}") String appName,
            @Value("${settings.env.name}") String envName) {
        this.objectMapper = objectMapper;
        this.appName = appName;
        this.envName = envName;
    }
    
    @Scheduled(fixedRate = 5000)
    public void scheduling() {
        String configurationProfile = "feature1";
        try {
            refresh(appName, envName, configurationProfile);
        } catch (ResourceNotFoundException ex) {
            logger.warn("The AppConfig resource has not been deployed: {}, {}, {}, {}",
                appName, envName,  configurationProfile, ex.getMessage());
        }
    }

    public void refresh(String appName, String envName, String configurationProfile) {
        if (this.sessionToken == null) {
            StartConfigurationSessionRequest request = StartConfigurationSessionRequest.builder()
                .applicationIdentifier(appName)
                .environmentIdentifier(envName)
                .configurationProfileIdentifier(configurationProfile)
                .build();
                this.sessionToken = client.startConfigurationSession(request).initialConfigurationToken();
        }

        GetLatestConfigurationResponse response = client.getLatestConfiguration((s) -> {
            s.configurationToken(this.sessionToken);
        });

        if (response.configuration().asByteArray().length > 0) {
            String featureValue = response.configuration().asUtf8String();
            logger.info("AppConfig changed: {}", featureValue);

            try {
                FeatureMode featureOne = objectMapper.readValue(featureValue, FeatureMode.class);
                this.feature1 = featureOne.isEnabled();
            } catch (JsonProcessingException | UncheckedIOException e) {
                throw new RuntimeException(e);
            }
        }

        this.sessionToken = response.nextPollConfigurationToken();
    }

    public boolean isFeatureOneEnabled() {
        return this.feature1;
    }

    private static class FeatureMode implements Serializable {
        @JsonProperty
        private Map<String, Boolean> mode;

        public boolean isEnabled() {
            if (mode != null) {
                return mode.get("enabled");
            } else {
                return false;
            }
        }
    }
}

src/main/resources/application.yaml を編集

また、src/main/resources/application.yaml を下記のように編集し、AppConfig のアプリケーション名とデプロイ先環境名の環境変数を設定します。

xml
settings:
  app:
    name: expt-apprunner-springboot
  env:
    name: ${APPCONFIG_ENV_NAME:dev}

lib/service-stack.ts に設定を追加

アプリケーションへの設定が完了したので、次は CDK コード lib/service-stack.ts にも設定を追加しましょう。 まず、App Runner から AppConfig の機能フラグを取得するために必要な権限を追加します。そして、先ほど application.yaml に記載した環境変数の設定も追加しています。

typescript
 const instanceRole = new iam.Role(this, 'InstanceRole', {
      assumedBy: new iam.ServicePrincipal('tasks.apprunner.amazonaws.com'),
      inlinePolicies: {
        appconfig: new iam.PolicyDocument({
          statements: [
            new iam.PolicyStatement({
              effect: iam.Effect.ALLOW,
              actions: [
                "appconfig:GetLatestConfiguration",
                "appconfig:StartConfigurationSession",
              ],
              resources: [ "*" ],
            }),
          ],
        }),
        
....

          codeConfiguration: {
            configurationSource: "API",
            codeConfigurationValues: {
              runtime: "CORRETTO_11",
              buildCommand: "./gradlew bootJar && cp build/libs/*.jar ./",
              startCommand: "java -jar ./expt-apprunner-springboot.jar",
              runtimeEnvironmentVariables: [
              ...
                {
                  name: "APPCONFIG_ENV_NAME",
                  value: "prod",
                },

動作確認

以上で、AppConfig を利用した機能フラグの実装が完了しました。 それでは、デプロイして動作を確認してみましょう。まず、CDK をデプロイし、先ほどと同様にマネジメントコンソールからアプリケーションをデプロイします。

bash
npx cdk deploy --all

追加した機能フラグの振る舞いを確認

デプロイが完了したら、追加した機能フラグの振る舞いを確認するために、/feature1 のパスにアクセスします。

bash
watch curl -s https://xxx.xxx.awsapprunner.com/feature1 # App Runner にデプロイしたサービスのURL

{"feature1":false}

AppConfig で mode と名付けたフラグを有効化

まだ機能フラグは有効化されていないので、feature1 に対して false が返ってきましたね。

それでは、新機能のリリースを想定して AppConfig で mode と名付けたフラグを有効化してみましょう。

まず、AppConfig のコンソール上で mode のトグルボタンをクリックしてフラグを有効にします。

Screenshot of the AWS AppConfig console showing a feature flag configuration profile UI. The image displays configuration profile details, flag management options, and a warning to save a new version before deployment.

設定のデプロイを開始

次に、「Save new version」で mode が有効な状態を新たなバージョンとして保存し、「Start deployment」をクリックして設定のデプロイを開始します。

Screenshot of the 'Start deployment' screen in AWS AppConfig showing options for environment selection, hosted configuration version, deployment strategy, and optional deployment description.

/feature1 のパスにアクセスして確認

デプロイは成功したでしょうか。再度、/feature1 のパスにアクセスして確認してみましょう。

bash
watch curl -s https://xxx.xxx.awsapprunner.com/feature1 # App Runner にデプロイしたサービスのURL

{"feature1":true}

まとめ

おめでとうございます!無事に新機能はリリースされ、feature1 に対して true が返ってきましたね。

いかがでしたでしょうか。AppConfig による機能リリースをご紹介しました。 AppConfig には、CloudWatch のアラームがトリガーされたときに、設定をロールバックする機能もあります。前述の通り、App Runner ではさまざまなメトリクスを CloudWatch で収集できるので、それらのメトリクスを使って、新機能リリースで何かエラーが発生したり、閾値を超えてしまったときに自動的にロールバックする、といったリリースも実現可能です。

さて、4 回にわたって連載してきた Spring Boot と AWS App Runner の連載も、今回で最終回となります。

App Runner では、連載を通してご覧いただいた通り、アプリケーションを簡単にデプロイすることができます。一方で、IaC で環境を管理したり、モニタリングやさまざまなリリース戦略など、複雑な運用が必要になってきたときでもさまざまな戦略を実装することが可能です。

皆さんもぜひ、お手持ちの Spring Boot アプリケーションを、運用の容易な App Runner でデプロイしてみていただければと思います。

筆者プロフィール

A person smiling outdoors in a park-like setting with colorful trees in the background, wearing a jacket. Photo taken in 2023.

林 政利 (@literalice)

アマゾン ウェブ サービス ジャパン合同会社
コンテナスペシャリスト ソリューションアーキテクト

フリーランスや Web 系企業で業務システムや Web サービスの開発、インフラ運用に従事。近年はベンダーでコンテナ技術の普及に努めており、現在、AWS Japan で Amazon ECS や Amazon EKS でのコンテナ運用や開発プロセス構築を中心にソリューションアーキテクトとして活動中。

Portrait headshot of a man in glasses and a light blue shirt, on black background

吉田 英史

アマゾン ウェブ サービス ジャパン合同会社
技術統括本部 ソリューションアーキテクト

東海地方でこれから AWS を使い始めるお客様を技術面でサポートしているソリューションアーキテクトです。
かつては製造業で製品の組み込みソフトウェア開発や工場 IoT 構築などに従事してきました。これからクラウド活用に踏み出されるお客様の支援ができることを、何より嬉しく感じています。ーションアーキテクトです。
かつては製造業で製品の組み込みソフトウェア開発や工場 IoT 構築などに従事してきました。これからクラウド活用に踏み出されるお客様の支援ができることを、何より嬉しく感じています。