アプリケーションの設定情報の管理にAppConfigを利用する – JMDC TECH BLOG

背景・概要

こんにちは、医療機関支援事業本部の森田です。
最近は急に寒くなりましたね。体調を崩さないように気をつけていきましょう。

さて、私が携わるプロジェクトではAWS Amplify Gen2とNext.jsを用いたサーバーレスアプリケーションを開発しています。このアプリケーションではマルチテナントでの運用を想定しており、テナントごとに異なる設定情報をどのように管理するかが課題でした。

Amplifyは環境変数の管理機能を提供しており、ビルド時にアプリケーションにこれらの値を安全に注入してくれます。しかし、この方法では値を変更するたびにAmplifyのビルド&デプロイが必要になります。アーキテクチャ設計の観点から言えばアプリケーションコードのデプロイがコードの変更を伴わない設定値の変更に影響されるのは避けたいです。

この課題を解決するため、AWS AppConfigを導入してみました。

本記事ではAmplify (Next.js)環境においてAppConfigを選んだ点とAppConfig導入方法について解説します。

AWS AppConfigについて

AWS AppConfigはAWS Systems Managerの機能の一つで、アプリケーションの設定情報を動的に管理・デプロイするために設計されたサービスです。特にフィーチャーフラグ管理に手厚い印象があります。  

主な特徴は以下の通りです。

アプリケーションから独立しており、アプリケーションの再デプロイなしに設定値の変更のみを動的に適用できます。

  • デプロイメント戦略

設定値の変更を即時に反映させるだけでなく、まず20%のターゲットにだけ反映させるようないわゆる「カナリアリリース」といった安全なデプロイ戦略を実行できます。

新しい設定値をデプロイする前にそれが正しい形式であるか(JSONスキーマに準拠しているか等)を検証できます。

AppConfig利用のメリット、デメリット

まずはAppConfig利用のメリットとデメリットを確認してみます。

メリット

  • 設定変更とアプリケーションデプロイの完全な分離

AppConfigのデプロイにより、アプリケーションが設定を読み込む内容を動的に変更できます。アプリケーションは読み込みのタイミングで新しい設定を参照できます。
これによりアプリケーションのデプロイ作業がなくなるので作業コストを軽減できます。

  • バリデーションによる安全なデプロイが可能

AppConfigには登録内容のバリデーション機能があり、JSONスキーマやLambdaを利用して設定値のデプロイ前に検証を行うことができます。
環境変数の本番環境への適用は人の目でダブルチェックなどをすることが多いと思いますが、この機能によりチェックの一部を担わせることでヒューマンエラーを軽減させることができます。

  • 設定値のバージョン管理による切り戻しが可能

AppConfigは設定値のバージョン管理が可能です。データの保存先にもよりますが、AWS AppConfig ホスト型構成ストアであれば1GBまでの履歴を保持することができます。
履歴が残っていることでバリデーションエラー発生時などの切り戻しで手入力することがなくなり、二重被害の可能性が低くなります。

デメリット

  • 豊富な機能を使いこなすための学習コスト

AppConfigの各種仕組み、具体的には環境や設定プロファイル、デプロイ戦略といった独自の仕組みが存在するため、使いこなすまでの学習コストがかかります。
またAppConfigへのデータアクセスにはロールの設定と実装を行う必要があります。

AppConfigはAPIの呼び出し回数に基づいて課金されます。高トラフィックなアプリケーションで頻繁にAPIを呼び出すと、コストが跳ね上がる可能性があります。

検討してみる

設定値の管理だけを見ればAppConfigではなくDynamoDBやParameter Storeといったデータストアでも対処できるため、学習コストを鑑みれば他のデータストアの方がよさそうです。
ただ今回のプロジェクトでは運用にかけられるコストが少なく、バリデーションや切り戻しできるメリットは有用と判断しました。あとはコストの問題を解決する必要があります。

コストを抑えたデータ取得方法

前述のコストの問題はAppConfig活用の最大の障壁ですが、これを解決するための方法はAWSから提供されていました。「AWS AppConfig Agent」の利用です。

AWS AppConfig Agentはアプリケーション実行環境にサイドカーのようにアタッチされ、アプリケーションのライフサイクルと並行して動作するプロセスです。

https://docs.aws.amazon.com/images/appconfig/latest/userguide/images/AppConfigAgent.png
What is AWS AppConfig Agent? – AWS AppConfig

今回はAmplifyを使用しているためAWS AppConfig Agent Lambda extensionを使用します。AWS AppConfig Agent Lambda extensionは以下の役割を担います。

  1. ローカルHTTPサーバーの提供

Lambda Layerで動作し、http://localhost:2772/…というローカルエンドポイント経由で設定を取得できるHTTPサーバーを起動します。

  1. 設定の事前取得とキャッシュ

Lambdaの実行環境が初期化されるタイミングで、Agentが非同期でAppConfigから設定情報を取得しローカル(実行環境内)にキャッシュします。
Lambdaがウォーム状態の間はキャッシュした値を利用するため、AppConfigアクセスにカウントされません。

サンプルコード

AWS AppConfig Agent Lambda extensionはLambdaで実行するため、Amplify (Next.js) のサーバーサイドコード(APIルートやSSRのハンドラ)からAppConfigへアクセスするには、Agentを同梱させるためのLambda関数を別途用意する必要があります。

Lambda関数を用意したらまずはLayerの設定です。

今回はCDKでLambda LayerにAgentを設定しました。

import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as path from 'path';

export class MyCdkStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    
    
    
    
    
    
    
    const appConfigLayerArn = `arn:aws:lambda:REGION:ACCOUNT:layer:AWS-AppConfig-Extension-ARCHTECTURE:LATEST_VERSION`;
    
    const myFunction = new lambda.Function(this, 'MyFunction', {
      runtime: lambda.Runtime.NODEJS_22_X,
      
      
      
      layers: [
        lambda.LayerVersion.fromLayerVersionArn(
          this,
          'hoge',
          appConfigLayerArn
        )
      ],
    });

    
    
    
    myFunction.addToRolePolicy(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: [
          'appconfig:GetLatestConfiguration',
          'appconfig:StartConfigurationSession',
        ],
        resources: [
          `arn:aws:appconfig:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:application/AppConfigのアプリケーションID/environment/AppConfigの環境ID/configuration/AppConfigの設定プロファイルID`
        ],
      })
    );
  }
}

Lambda Layerを設定できてしまえば、あとはLambda自体からAgentにlocalhost:2772でHTTPリクエストをすることでデータ取得が可能になります。

export const handler: Handler = async (event, context) => {

  const resp = await fetch(`http://localhost:2772/applications/AppConfigのアプリケーションID/environments/AppConfigの環境ID/configurations/AppConfigの設定プロファイルID`);
  const params = await resp.json();

  console.log(params)
};

まとめ

Amplify (Next.js) + Lambdaというサーバーレス環境においてアプリケーションの動的な設定を管理する構成において、Amplifyの環境変数機能では「設定変更のたびにデプロイが必要」という運用上の問題がありました。
AWS AppConfigはアプリケーションと設定値のデプロイの分離や設定内容のバリデーション、バージョン管理といった運用負荷を削減する機能があり、問題への解決策として使用しました。
直接AppConfig APIを呼び出す場合のコストが懸念点でしたが、AWSが推奨する「AWS AppConfig Agent」を使用し、キャッシュを効率的に使うことでリクエストの回数を減らすことができました。

学習コストの問題はありますが、頻繁な設定変更・動的な設定を行われるシステムにおいてAWS AppConfigは有効な手段になるものだと思います。

最後までお読みいただきありがとうございました。

JMDCでは、ヘルスケア領域の課題解決に一緒に取り組んでいただける方を積極採用中です!
フロントエンド /バックエンド/ データベースエンジニア等、様々なポジションで募集をしています。
詳細は下記の募集一覧からご確認ください。
hrmos.co

まずはカジュアルにJMDCメンバーと話してみたい/経験が活かせそうなポジションの話を聞いてみたい等ございましたら、下記よりエントリーいただけますと幸いです。
hrmos.co

★最新記事のお知らせはぜひ X(Twitter)、またはBlueskyをご覧ください!
twitter.com

bsky.app




元の記事を確認する

関連記事