Amazon Web Services ブログ

AWS AppConfigとAWS CodePipelineの統合による機能リリースの自動化

昨年、AWS AppConfigをリリースしました。これはアプリケーション設定の作成、管理及び迅速なデプロイを行う、AWS Systems Managerの新機能です。AppConfigを使用すると、デプロイメントを行う前にアプリケーション設定を検証でき、制御及び監視可能な方法で設定をデプロイできます。

AWS AppConfigを使用すると、アプリケーションコードのデプロイメントとは独立して、設定の変更をデプロイ可能です。つまり、アプリケーション設定を更新しても、アプリケーションの再起動やサービスの停止を行う必要がありません。AWS AppConfigを使用すれば、アプリケーションは更新した設定をすぐに使用できます。具体的には、AWS AppConfig API、AWS CLIやAWS SDKを使用することで、更新した設定を取得することができます。

ローンチ以来、お客様はさまざまなユースケースにAWS AppConfigを採用しており、なかでもコードのデプロイメントとは独立して新機能をリリースする機能がトップユースケースの1つでした。アプリケーション設定のデプロイメントはコードのデプロイメントより高速です。なぜならコンフィグレーションファイルは、ビルドステージを必要とせず、アプリケーションを停止すること無く実行中にデプロイすることができるためです。

機能リリースにあたって、バックエンドサービスの設定とフロントエンドの設定を正しい順序で行う必要があります。そのためには、しばしば複数のチームや手作業を調停する必要があります。こういった手作業によるデプロイメントは、カスタマーエクスペリエンスに影響を与える作業遅延を引き起こす可能性があります。

複数の環境、アプリケーションやリージョンに、アプリケーション設定をデプロイするという手動タスクをシンプルにするために、我々はAWS AppConfigとAWS CodePipelineの統合を発表しました。このローンチは、お客様が AWSの数千のチームが使用するベストプラクティスを選択することを可能にします。それは、コード変更を機能リリースから容易に分離し、安全且つ効率的な方法でこれらの機能リリースを自動化するというものです。

この統合により、次の機能を追加しました。

  • 新しい設定ソース(訳注: 設定の保存場所)  – AWS CodePipelineは、AWS AppConfig設定プロファイルの新しい設定ソースになりました。これは、既存の設定ソース(Amazon S3オブジェクト、AWS Systems Managerドキュメント、AWS Systems Managerパラメータストア、および最近リリースされたAWS AppConfigでホストされた設定ストア)への追加です。
  • 新しいデプロイアクションプロバイダ – AWS CodePipelineのデプロイアクションに、AWS AppConfigが追加されました。

コードデプロイメントのベストプラクティスに従い、Gitなどのバージョン管理システムに設定を保存すると、ブランチを作成してバージョンの変更を追跡できます。重要な変更の場合は、手動の承認アクションによってリスクを軽減することができます。したがって、設定のデプロイメントのための分離したパイプラインによって、リリースプロセスが自動化されるだけでなく、手動のデプロイメントによって発生するエラーの可能性も減少します。

以前のAWS AppConfigに関するブログ記事では、AWSマネジメントコンソールからアプリケーション設定をデプロイする方法をご紹介しました。今回の記事では、AWS AppConfigとAWS CodePipelineの新しい統合を活用して、本格的なCI/CDパイプラインをすばやく構築し、複数の環境への設定のデプロイメントを自動化する方法を解説します。

サンプルアプリケーションの概要

この新機能を説明するために、AWSサービスのリストを返すシンプルなサーバーレスのREST APIを使用します。アプリケーションはAmazon API GatewayとAWS Lambdaによって構成されします。変更のリリースフローをシミュレートするため、development、testing、productionの三つの環境で作業します。

このAPIに新機能を追加し、パラメータで指定した数だけAWSサービスを返すようにします。機能名とパラメータ値をAWS AppConfigに保存します。AWS Lambdaのコードを更新してAWS AppConfigからアプリケーション設定を取得するようにします。

次にAWS CodePipelineを構築します。ここではアプリケーションコードをビルドしてデプロイするのではなく、アプリケーション設定を、複数の環境へ自動かつシームレスにデプロイします。

次の図は、このソリューションの設計とAWS CodePipelineのフローを表しています。

Step 1: AWS CloudFormationを使用して、ベースとするアプリケーションのスタックを作る

Amazon API GatewayとAWS Lambdaを含むベースアプリケーションを配置するために、CloudFormationを使用します。

  1. まず、次のコードブロックをテンプレートとして、お手元のコンピュータのローカルファイルに.yml拡張子を付けて保存します。
  2. 次にCloudFormationコンソールを開き、[ スタックの作成 ]をクリックして、新しいリソースを使用するオプションを選択します。

次の画面のテンプレートの指定セクションで、テンプレートファイルのアップロードを選択し、保存したファイルを指定します。[ 次へ ]をクリックして、スタックにListServices-devのような名前をつけ、Environmentパラメータの値としてdevelopmentを選択します。[ 次へ ]をクリックし、オプションでタグを定義して、もう一度[ 次へ ]をクリックします。最後の画面で、機能と変換セクションの三つのチェックボックスをオンにして、[ スタックを作成 ] ボタンをクリックしてください。

そして、testingとproductionの2つの環境に、この作業を繰り返します。スタック名としてListServices-testまたはListServices-prodを指定し、Environmentパラメータにはそれぞれtestingまたはproductionを指定します。その後、スタックがCREATE_COMPLETEステータスになるまで待ちます。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS SAM template for AWS AppConfig-Codepipeline integration demo
Parameters: 
  Environment: 
    Type: String
    Default: development
    AllowedValues: 
      - development
      - testing
      - production
Resources:
  ApiGatewayApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: 
        Ref: Environment
  ApiFunction: # Adds a GET api endpoints at "/" to the ApiGatewayApi via an Api event
    Type: AWS::Serverless::Function
    Properties:
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /
            Method: get
            RestApiId:
              Ref: ApiGatewayApi
      Environment:
        Variables:
          appEnv:
            Ref: Environment
      Policies:
        - Statement:
          - Sid: GetConfig
            Effect: Allow
            Action:
            - appconfig:GetConfiguration
            Resource: !Sub 'arn:aws:appconfig:${AWS::Region}:${AWS::AccountId}:*'
          - Sid: ReadS3
            Effect: Allow
            Action:
            - s3:GetObject
            - s3:ListBucketVersions
            Resource: !Sub 'arn:aws:s3:::codepipeline-${AWS::Region}-*'            
          - Sid: WriteLogs
            Effect: Allow
            Action:
            - logs:CreateLogStream
            - logs:CreateLogGroup
            - logs:PutLogEvents
            Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*'
      Runtime: nodejs12.x
      Handler: index.handler
      InlineCode: |
          exports.handler = async (event) => {
            let result = getServices();
            let appEnv = process.env.appEnv;
            const response = {
              statusCode: 200,
              body: JSON.stringify(result),
            };
            return response;
          };

          function getServices() {
            return [
              {
                name: 'AWS AppConfig'
              },
              {
                name: 'Amazon SageMaker Studio'
              },
              {
                name: 'Amazon Kendra'
              },
              {
                name: 'Amazon CodeGuru'
              },
              {
                name: 'Amazon Fraud Detector'
              },
              {
                name: 'Amazon EKS on AWS Fargate'
              },
              {
                name: 'AWS Outposts'
              },
              {
                name: 'AWS Wavelength'
              },
              {
                name: 'AWS Transit Gateway'
              },
              {
                name: 'Amazon Detective'
              }
            ];
          }
Outputs:
  APIGateway:
    Description: The API Gateway exposing your testing app
    Value: !Sub "https://${ApiGatewayApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}"

CloudFormationスタックの出力タブでエンドポイントを参照して、作成したアプリケーションをテストします。アプリケーションは、AWSサービスのリストを含むJSONを返すでしょう。

Step 2: AWS AppConfigに、アプリケーション、環境及び設定プロファイルを作成する

1.はじめに、AWS Systems Managerコンソールを開き、左のナビゲーションパネルからAppConfigを選択します。

2.AppConfigコンソールでは、[ Create configuration data ]をクリックし、アプリケーションの名前を指定します。オプションの説明やタグをアプリケーションに適用できます。

(訳注: ここではアプリケーションの名前として ListServices を、オプションの説明には Configuration management for List-services applicationを指定します。)

3. アプリケーションが作られると、Environmentsタブと Configuration profilesタブのあるページに遷移します。まず[ Creating environment ]をクリックし、名前とオプションの説明を指定して環境を作ります。

4. オプションとして、タグを追加したり、監視の設定が可能です。監視では Amazon CloudWatchアラームを指定することで、デプロイ後に問題が発生した場合、アプリケーション設定をロールバックさせることができます。

(訳注: 図のように、development、testing、production の三つ環境を作ります。)

5.次は設定プロファイルを作成します。Configuration profilesタブを選択し、[ Create configuration profile ]をクリックします。

設定プロファイルの名前には、ListServicesCodePipelineConfigProfileを指定して[ Next ]をクリックします。

6.次に、設定ソースとしてAWS CodePiplineを選択し、[ Next ]をクリックします。

7.設定を検証するためのバリデータを追加するオプションが表示されたら、 JSONスキーマのバリデータを選択し、次のコードブロックの内容を指定します。

{
  "$schema": "http://json-schema.org/draft-04/schema#",
    "description": "AppConfig Validator example",
      "type": "object",
        "properties": {
    "boolEnableLimitResults": {
      "type": "boolean"
    },
    "intResultLimit": {
      "type": "number"
    }
  },
  "minProperties": 2,
    "required": [
      "intResultLimit",
      "boolEnableLimitResults"
    ]
}

8.次に、[ Create configuration profile ]をクリックして設定を作成します。作成したら、Configuration profile detailsのリンクをクリックするだけで、CodePipelineコンソールにアクセスできます。

Step 3: アプリケーション設定のソースリポジトリとしてCodeCommitを使用する

アプリケーションコードと同じように、アプリケーション設定は継続的に変化するため、アプリケーション設定とアプリケーションコードは同じレベルで管理することが望ましいです。

Gitのようなバージョン管理システムを使用すると、複数の開発者やチームが共同で変更履歴を追跡できます。

ここではアプリケーション設定のソースリポジトリとしてAWS CodeCommitを使用します。 AWS CodeCommitは、安全なGitベースのリポジトリをホストするフルマネージドのソース管理サービスです。

1. AWS CodeCommitコンソールに移動し、ListServicesという名前の新しいリポジトリを作成します。

2.空のリポジトリが作成されました。通常はローカルマシンにリポジトリをクローンして、設定ファイルを追加し、変更をプッシュします。しかしこのシナリオでは単一の設定ファイルのみを扱うため、AWSマネジメントコンソールから [ Create file ] をクリックすることで、同じことが行えます。

3.次のコードブロックの内容を使用し、config/configdoc.jsonという名前でファイルに保存します。設定には二つのパラメータがあり、一つ目はアプリケーション機能を有効・無効にし、二つ目はアプリケーションが返すAWSサービスの制限数を設定します。

{
  "boolEnableLimitResults": false,
  "intResultLimit": 6
}

4.次の図に示すように、Author NameとEmail Addressを指定し、[ Commit Changes ]をクリックしてファイルをmasterブランチに追加します。

Step 4: AWS AppConfigをデプロイアクションプロバイダとして使用したCodePipelineの作成

アプリケーション設定の変更を複数の環境にリリースするために、AWS CodePipelineを使用してCI/CDパイプラインを作成します。

1. AWS CodePipelineコンソールに移動し、[ Create pipeline ]をクリックして新しいパイプラインを作成します。

2. パイプラインの名前にListServicesConfigPipelineと入力し、New Service roleを選択してデフォルトのロール名を使用します。

3. 次に、ソースプロバイダをAWS CodeCommit、リポジトリ名をStep 3で作成したListServicesとして、ソースステージを追加します。masterブランチを選択、Amazon CloudWatch Eventsを使用してパイプラインの実行をトリガーします。

4. [ Next ]をクリックしてビルドステージに進みます。アプリケーション設定にはビルドステージが必要ないため、このセクションはスキップします。

【訳注:手順の追加】次の手順に進む前に、デプロイ戦略の作成手順を追加します。

原文においてDeploy Strategy に指定されているAppConfig.AllAtOnceは、定義済みのデプロイ戦略で説明されているとおり、デプロイメントのたびにCloudWatchアラームを10分間監視する時間(Bake time)が設けられています。本記事の手順ではデプロイメントを迅速に行うため、Bake timeを0にした新しいデプロイ戦略を作成してデプロイステージに指定します。実際のユースケースにおいてAmazon CloudWatchアラームによるロールバックを使用する場合には、適切なBake timeを設定したデプロイ戦略を使用して下さい。

(1) AppConfigコンソールを開きます。

(2) Deployment Strategiesタブを開きます。

(3) [ Create deployment strategy ]をクリックします。

(4) 以下画像のように指定して、[ Create deployment strategy ]をクリックします。

  • Name: AllAtOnceForTrial (任意の名称で可)
  • Department type: Linear
  • Step percentage: 100
  • Department time: 0
  • Bake time: 0

【訳注:手順の追加ここまで】

5.次に、デプロイステージを追加します。以下の値を使用して、デプロイステージを作成します。

  • Deploy provider: AWS AppConfig
  • Region: 今使用しているリージョン
  • Application: ListServices
  • Environment: development
  • Configuration profile: ListServicesCodePipelineConfigProfile
  • Deploy Strategy: AllAtOnceForTrial  (訳注: 前述の追加手順で作成したデプロイ戦略を指定する。)
    (必要に応じてAppConfig.AllAtOnce等の事前定義されたデプロイ戦略の1つを選択します –  AppConfig.AllAtOnce、AppConfig.Linear50PercentEvery30SecondsおよびAppConfig.Canary10Percent20Minutes)
  • Input artifact configuration path: config/configdoc.json

6.[ Next ]をクリックしてパイプラインを作成します。最初のデプロイメントが自動的に開始され、指定した環境 、つまりdevelopmentに設定をデプロイします。

(訳注: この後に、レビュー画面が表示されますので、確認後に[ Create pipeline ]をクリックします。)

これで、AWS CodePipelineのすべての機能を使用できるようになりました。これらの機能は、必要に応じて設定することができます(テストアクションの追加、手動の承認アクションなど)。この例では、development、testing、productionの三つの環境にデプロイメントするようにAWS CodePipelineを設定します。

6-1.先に進むには、パイプラインのデプロイステージを編集して、2つの新しいアクショングループを追加します。

6-2.各アクショングループの値は、Environmentをのぞいて、最初のデプロイステージの作成で使用している値と同じです。続くアクショングループのEnvironmentの値には、testingとproductionを使用します。追加したら[ Save ]ボタンをクリックし、表示される内容を確認します。

(訳注: 最初に作られていたDeployアクショングループをDeploy-developmentに名称を変更し、Environment以外は同じ内容を指定して、Deploy-testingとDeploy-productionを追加します。Deploy-testingとDeploy-productionでは、Variable namespaceを指定する必要はありません。)

これでパイプラインが準備され、CodeCommitリポジトリから設定を取得して、development、testing、productionの三つの環境に、パイプラインの順序でデプロイします。

パイプラインをテストするには、設定ファイルを編集して、CodeCommitコンソールから変更をプッシュします。
boolEnableLimitResults設定パラメータをtrueに変更し、intResultLimitを5などの任意の正の整数に設定することにより、新しい結果表示の制限機能を有効にします。

{
  "boolEnableLimitResults": true,
  "intResultLimit": 5
}

変更をリポジトリにプッシュすることで、パイプラインの実行がトリガーされ、設定を三つの環境にデプロイします。

(訳注: CodeCommitコンソールから ListServicesリポジトリのconfig/configdocs.jsonを編集してコミットした後に、ListServicesConfigPipelineを再表示して下さい。)

Step 5: Lambdaをアップデートして、アプリケーション設定を取得する

次に、AWS AppConfigのアプリケーション設定を使用するようにアプリケーションコードを更新します。

Step 1でCloudFormationを介してデプロイしたLambda関数は、AWS AppConfigから設定を取得していません。

1. CloudFormationコンソールを開きます

2.起動した各CloudFormationテンプレートのリソースタブを開き、論理IDがApiFunctionのリソースをクリックします。すると Lambdaコンソールが開きます。

3.関数コードの内容を次のコードで更新します。

(訳注: 以下コードでは、getConfiguration()をLambda実行のたびに呼び出す仕様になっています。実際のアプリケーションで使用する場合には、ユーザアクセスのたびに設定の取得を行うことで発生するレイテンシの増加を防ぐため、アプリケーション側で取得した値を一定時間はキャッシュするなど、ユースケースに合わせた対策の実施を推奨します)

const AWS = require('aws-sdk');
const appconfig = new AWS.AppConfig({ apiVersion: '2019-10-09' });
const appEnv = process.env.appEnv;

const constParams = {
  Application: 'ListServices',
  Configuration: 'ListServicesCodePipelineConfigProfile',
  Environment: appEnv
};

let cachedParams = {};
let cachedConfigData = {};
let parsedConfigData = {};
exports.handler = async (event) => {

  // Check if ClientId is defined
  if (!cachedParams.ClientId) {
    cachedParams.ClientId = create_UUID();
  }

  // Merge constParams and cachedParams 
  const params = { ...constParams, ...cachedParams };

  // Call GetConfiguration API
  const appConfigResponse = await appconfig.getConfiguration(params).promise();

  // Add ClientConfigurationVersion to cachedParams if not present
  if ((!cachedParams.ClientConfigurationVersion) || appConfigResponse.ConfigurationVersion !== cachedParams.ClientConfigurationVersion) {
    cachedParams.ClientConfigurationVersion = appConfigResponse.ConfigurationVersion;
  }

  const configData = await Buffer.from(appConfigResponse.Content, 'base64').toString('ascii');

  if ((!cachedConfigData) || (configData && cachedConfigData !== configData))
    cachedConfigData = configData;

  let result = getServices();

  if (configData == null || configData == '') {
    parsedConfigData = JSON.parse(cachedConfigData);
  } else {
    parsedConfigData = JSON.parse(configData);
  }

  if ((parsedConfigData.boolEnableLimitResults) && parsedConfigData.intResultLimit) {
    result = result.splice(0, parsedConfigData.intResultLimit);
  }

  const response = {
    statusCode: 200,
    body: JSON.stringify(result)
  };
  return response;
};

function getServices() {
  return [
    {
      name: 'AWS AppConfig'
    },
    {
      name: 'Amazon SageMaker Studio'
    },
    {
      name: 'Amazon Kendra'
    },
    {
      name: 'Amazon CodeGuru'
    },
    {
      name: 'Amazon Fraud Detector'
    },
    {
      name: 'Amazon EKS on AWS Fargate'
    },
    {
      name: 'AWS Outposts'
    },
    {
      name: 'AWS Wavelength'
    },
    {
      name: 'AWS Transit Gateway'
    },
    {
      name: 'Amazon Detective'
    }
  ];
}

function create_UUID() {
  let dt = new Date().getTime();
  const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    var r = (dt + Math.random() * 16) % 16 | 0;
    dt = Math.floor(dt / 16);
    return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
  });
  return uuid;
}

Lambda関数の更新したバージョンでは、AWS AppConfigからアプリケーション設定を取得します。

これを行うには、GetConfiguration APIを呼び出し、次のパラメータを含めます。

  • ClientId – このクライアントアプリケーションのための一意な識別子。
  • Application – Step 2で作成したAppConfigのアプリケーション名 (ListServices)。
  • Configuration – Step 2で作成したAppConfigの設定プロファイル名 (ListServicesCodePipelineConfigProfile)。
  • Environment – Step 2で作成した三つの環境の一つ。API Gatewayの各環境のURLを呼び出したときに appEnv環境変数で渡されます。
  • ClientConfigurationVersion – GetConfiguration APIは、ClientConfigurationVersionパラメータの値を使用して、ターゲットが最後に受信した設定バージョンを識別します。GetConfigurationを呼び出す際にClientConfigurationVersionパラメータを送信しない場合やターゲットに既にその設定がある場合でも、ターゲットは使用可能な最新の設定を受け取ります。受信した設定の数に基づいて課金されるため、ClientConfigurationVersionを送信しない場合、不必要な課金が発生する可能性があります。

APIオペレーションを呼び出して、アプリケーションを再度テストします(CloudFormationスタックの出力タブでエンドポイントを参照します)。最後のコミットで指定した制限数の結果のみを返すでしょう。

自由に設定ファイルを編集して、アプリケーション設定に別の変更をプッシュしてください。これにより、パイプラインを再度トリガーします。デプロイメントが成功したら、APIの振る舞いの変化を確認してください。

後片付け

完了したら、以下を削除してリソースをクリーンアップします。

  • 三つのCloudFormationスタック (訳注: ListServices-で始まる各環境のスタック)
  • CodePipelineのListServicesConfigPipelineパイプライン
  • CodeCommitのListServicesリポジトリ
  • AppConfigのListServicesアプリケーション (訳注: 最初にアプリケーション内のEnvironmentsとConfiguration profilesを削除する必要があります。)

まとめ

この記事では、AWS AppConfigとAWS CodePipelineの新しい統合を使用して、アプリケーション設定用に独立したCI/CDパイプラインを作成する方法を説明しました。

ビルドステージが必要ないため、より迅速でシームレスな設定のデプロイメントが可能です。デプロイパイプラインの自動テストを有効にするために、AWS AppConfigの次の機能を使用できます。

  • バリデータの追加 – バリデータは、デプロイしたい設定が意図したとおりに機能することを確実にするために、構文または意味のチェックを提供します。アプリケーションの設定データを検証するために、設定に対して実行するスキーマまたはLambda関数を提供できます。設定のデプロイメントまたは更新は、設定データが有効な場合にのみ続行できます。
  • 監視の追加 – Amazon CloudWatchアラームは、各環境に設定できます。システムは、設定のデプロイメント中にアラームを監視します。アラームがトリガーされると、システムは設定をロールバックします。

アラームが発生している場合、デプロイメントは停止しパイプラインを通過しません。

AWS AppConfigの詳細については、ユーザーガイドをご覧ください。

 

著者について

Luis Gómezは、AWSのソリューションアーキテクトで、スペインの公共部門で働いています。クラウド環境の構築と運用、DevOpsの実践に数年の経験を持っています。彼はお客様の目標や課題を理解し、AWSを利用してお客様の目的を達成するために必要なアドバイスを提供しています。

 

 

 

Prasad Raoは、イギリスを拠点とするAWSのパートナーソリューションアーキテクトです。彼の専門分野は、AWS上での.NETアプリケーションのモダン化とWindowsワークロードです。その経験を活かし、EMEA全域のAPNパートナーがAWS上でスケーラブルなアーキテクチャを構築するための長期的な技術支援を行っています。また、クラウドに慣れていない人やAWSを使い始めたいと考えている人など、様々な人のメンターとしても活躍しています。

 

 

 

原文はこちら。翻訳はソリューションアーキテクトの木村(友)が担当しました。