アプリケーションの設定情報をどのAWSサービスから取得すべきか

#1.はじめに

先日公開した[こちらの記事](https://qiita.com/horit0123/items/cd0bdefd99ef0f4c7f5f)では、AWS AppConfigの使いどころについて考えを書いてみました。サービスに対して安全に設定を注入するツールとしてAWS AppConfigは適したサービスだと考えています。
　とはいえ、AWS AppConfig以外にもアプリケーションが設定情報を取得する方法はいくつかあります。当記事では、そのあたりを整理したいと思います。

#2. サマリ
- 設定情報のライフサイクルを考慮した格納先の決定が重要
- 安全なデプロイを推進する場合はAWS AppConfig
- シークレット情報が必要な場合はParameter Store or Secrets Manager

#3.アプリケーションの設定情報とは
アプリケーションを動かす上で必要な情報は様々あります。例えば、AWS Lambdaのようなサービスを利用すると、「インフラ」という部分をAWSのマネージドなサービスとして提供されて、アプリケーション開発者がすべてを実装できるケースもでてきます。すると、アプリケーションの設定情報としてAWS Lambdaの設定情報（メモリサイズ、タイムアウト値やアクティブトレーシング（AWS X-Rayへの連携）等）も含めてアプリケーションの設定情報と考えられる方もいるかもしれません。ただ、当記事では、プログラムのコード内で扱う情報で、コードに埋め込みたくない変数化された値をアプリケーションの設定情報と位置付け、AWS Lambdaのメモリサイズやタイムアウト値等の設定情報は当記事の対象外とします。

##3.1 アプリケーションの設定情報

アプリケーションを動かす上で必要な情報とは・・・と考えてみると色々あります。ここでは以下の５種類を例に挙げ、当記事で対象とする設定情報の認識合わせをしたいと思います。


|No.|説明|値の例|
|---|---|---------|---------|
|1| 他のサービスに接続するためのエンドポイント情報|URL,ポート
|2| 他のサービスに接続するためのCredential情報|ID,Password
|3| 多言語対応等で必要な方法|メッセージ、ラベル、エラーメッセージ|
|4| アプリケーション内の制御で使う閾値|アプリ内のスロットリングの閾値等,ホワイトリスト/ブラックリスト用のID(*1)機能の有効/無効フラグ|

*1 サービスを提供する中で、特定のユーザーだけ利用できるようにするためのIDリストをイメージしました。

これらの情報はアプリケーションのフェーズによっても変わりますし、実行時にも定期的、または、不定期で変化していくものと考えられます。設定情報のライフサイクルも様々で、例えば、Credential 情報は短い有効期限のものもあれば、数十日単位にローテーションさせるようなものもあるでしょうし、メッセージやラベルといったものは変化の頻度が不定で、かつ、変更頻度が低い可能性もあります。
では、これらの設定情報はどこで保持しておくとよいのでしょうか？どこで保持しておくとアプリケーションから使いやすいのでしょうか？

##3.2 アプリケーションの設定情報の格納先
[AWS AppConfigの記事](https://qiita.com/horit0123/items/cd0bdefd99ef0f4c7f5f)でも書きましたが、アプリケーションの設定情報は様々な場所に格納して利用することできます。
例えば、以下の４つについては、次の観点でザックリとまとめてみました。

- 値を設定（変更）のタイミング
- 値を変更したい場合のビルドの必要性
- 値を変更したい場合のデプロイの必要性
- 値取得時のレイテンシ
 
|No.|格納先|設定タイミング|ビルド|デプロイ|レイテンシ|適用ケース|
|---|---|---------|---------|---------|---------|---------|
|1| 実行モジュール内に同梱するファイル|ビルド前|必要|必要|低|設定値がコードのライフサイクルと同じ場合|
|2|実行モジュール外に配置し ランタイムから参照できるファイル|デプロイ前|不要|必要|低|コードとは別のライフサイクルで設定を反映する場合|
|3|環境変数|デプロイ前|不要|必要|低|コードとは別のライフサイクルで設定を反映する場合|
|4|データストア|任意のタイミング|不要|不要|高|任意のタイミングで変更する場合|

当記事では、アプリケーションの設定情報を「データストア」に格納するケース、つまり、設定情報が頻繁に変更されるようなケースでアプリケーションモジュールのデプロイをいちいち実施しなくてもよい格納先について検討します。

#4. 設定情報をデータストア
## 4.1 データストアの種類と特徴
ここでは、アプリケーションの設定情報を格納するデータストアに該当するAWSサービスを例に挙げます。

|No.|格納先|説明|設定方法|取得時のrps上限|履歴保持|型チェック|暗号化|
|---|---|---|---|---|---|---|---|
|1|AWS AppConfig|アプリケーションで利用する設定をより安全にデプロイすることを支援するサービス|API|記載なし|なし※1|可能|なし※1|
|2|Parameter Store|システムで取り扱う設定情報を保持するサービス。､設定データ管理と機密管理のための安全な階層型ストレージを提供。|API|スループット: 40rps|最大100履歴|なし|SecureStringを利用可能|
|3|Secrets Manager|IT リソースへのアクセスに必要なシークレットの保護を支援。シークレット（パスワード）のローテーションを自動で実施。|API|スループット:  取得 2000rps 更新 40 rps|およそ100|なし| 可能|
|4|Amazon DynamoDB|低レイテンシーでアクセス可能なKey-Value Store|API|記載なし|なし|なし|なし※2|
|5|Amazon RDS|マネージドなリレーショナルデータベース|SQL/RDBMSのコマンド|記載なし|なし|スキーマレベル|なし※2|

※1 AppConfigのパラメータの格納先としてParameter Storeを利用可能でその場合は履歴や暗号化も可能
※2 個々の行、列、項目を個別に暗号化するという点では暗号化機能はないため、なしと表記。

## 4.2 データ取得の計測
ここでは、上記の１～４のAWSサービスについて実際にアプリケーションでデータを取得して計測してみたいと思います。

### A. データ取得の計測の環境
- 取得用にAWS Lambda関数を利用（東京リージョン）。AWS Lambda(VPC内リソースにアクセスするLambdaで計測（NAT Gateway 経由でAWS APIにアクセス）
- 取得回数は100回。
- X-Rayを利用した取得時間で計測
- 2020年8/18に計測

### B. X-Rayの計測結果
今回利用したLambda関数は下図の「ConfigTestStac2-AppConfigTest」であり、この関数内からAWS AppConfig, Parameter Store, Secrets Manager, そして、Amazon DynamoDBに対してデータ取得のAPIを呼び出し取得を実施しました。
実施した結果、下表のとおりの取得時間となった。前回作成した[AppConfigの記事](https://qiita.com/horit0123/items/cd0bdefd99ef0f4c7f5f)では、AppConfigが遅い結果が出たが今回はなぜか速くなっており、原因はわからないです。前回と違いがあるとすれば、Configに格納したデータ量が小さいということはあるが、それでも20-30バイト程度の差であり誤差のはずなんですが・・・・。今回はこの点に対するDive Deepはやめておきます。

|No.|格納先|取得時のレイテンシ（計測結果）|
|---|---|---|
|1|AWS AppConfig| 54 ms|
|2|Parameter Store|81 ms|
|3|Secrets Manager| 55 ms|
|4|Amazon DynamoDB|47 ms|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535037/140236b4-1aa5-8161-1006-2176081bb2a7.png)


こうした設定情報は、呼び出し頻度や必要とする応答時間に依存しますが、アプリケーションの起動時等にデータストアから取得しメモリ上にキャッシュして保持し、一定期間経過したら再度データストアから取得するのが常套手段かと思います。そうすることで、設定情報取得のレイテンシーの最小化、API呼び出し回数の最小化によるAWSサービス利用料の最小化が実現できます。ということで、そうした使い方をする場合はレイテンシーに基づいたデータストアの選定はあまり重要ではなくなります。

# 5.どのAWSサービスを利用すべきか。
前述した通り、私は、アプリケーションが都度、設定情報をデータストアに取りに行くことは応答時間やAWSサービス利用料、そして、スロットリングの観点からおすすめしません。もちろん、たまにしか呼ばれないアプリケーションであれば、スロットリング、応答時間、AWSサービス利用料の影響よりも、最新の設定情報を取得するほうが実装はシンプルでよいという判断もあります。今回は、頻繁に利用されるアプリケーションであるという前提で、アプリケーション内、例えばAWS Lambda関数であれば、そのグローバル変数にて設定情報を一定期間、たとえば5秒程度、保持し、取得後その時間が経過したらデータストアに問い合わせを実施する場合を想定し、考えたいと思います。

## 5.1. 安全なデプロイが必要ですか？
設定情報の型チェック、設定情報のデプロイ時のカナリアリリースや自動ロールバックが必要であれば、AWS AppConfigをお勧めします。AppConfigでは、前述した通り履歴管理をする機能はありませんが、ただ、CodeCommitやCodePipelineと連携することでCodeCommitで履歴を管理しデプロイプロセスもCodePipelineで記録されます。これは、こちらの[ブログ](https://aws.amazon.com/jp/blogs/news/automating-feature-release-using-aws-appconfig-integration-with-aws-codepipeline/)を参照してみてください。簡単に実装可能です。AppConfigを利用することで、カナリアリリースやリニアなデプロイも可能です。もちろん、設定上の不具合が発生したらロールバックも可能です。これを利用することで、エンドユーザーへの影響を極力最小化したデプロイの実現を自動化できます。AWS AppConfigを利用しCodeCommitとCodePipelineを利用するケースは、前述の「3.2 アプリケーションの設定情報の格納先」の表に対して下表のとおりNo.5を追加した以下の形になることを意味します。アプリケーションモジュールのデプロイメントパイプラインとは別のパイプラインで設定情報をデプロイするということになります。


|No.|格納先|設定タイミング|ビルド|デプロイ|レイテンシ|適用ケース|
|---|---|---------|---------|---------|---------|---------|
|1| 実行モジュール内に同梱するファイル|ビルド前|必要|必要|低|設定値がコードのライフサイクルと同じ場合|
|2|実行モジュール外に配置し ランタイムから参照できるファイル|デプロイ前|不要|必要|低|コードとは別のライフサイクルで設定を反映する場合|
|3|環境変数|デプロイ前|不要|必要|低|コードとは別のライフサイクルで設定を反映する場合|
|4|データストア|任意のタイミング|不要|不要|高|任意のタイミングで変更する場合|
|5|データストア|任意のタイミング|不要|必要|高|任意のタイミングで変更する場合|

## 5.2. 設定情報はシークレット等のセキュアな情報ですか？
設定情報はシークレットやセキュアな情報であれば、Parameter Store または、Secrets Managerが候補となります。もし、RDS等のパスワードでローテーションが必要なのであれば、その場合は、Secrets Mangerを利用してAWS Lambdaと連携したローテーションをすることにより、パスワード運用の手間やパスワード漏洩のリスクが軽減されるでしょう。もちろん、DynamoDBを利用し、シークレット情報設定時に設定側でAWS KMS(Key Management Service)を利用して暗号化しておけば、KMSのCMK(Customer Master Key)の利用権限がないアプリケーションは復号できないためDynamoDBでも同様のことは可能でし、AppConfig内の設定についても前述したCodeCommitに格納する際にKMSで暗号化しておけば取得後、復号して利用する際にKMSの利用権限がないアプリケーションは復号できないためアクセス制御が可能となります。

## 5.3.DynamoDBやParameter Storeは？
アプリケーションプログラム内の設定情報を保持する先として、DynamoDBやParameter Storeももちろん利用可能です。
先ほど安全なデプロイでおすすめしたAppConfigではおそらく複数の情報をまとめて設定することになるとおもいます。例えばあるサービスで利用する情報をJSON形式でまとめて１つの設定ファイルとして設定可能です。ただ、その設定ファイル内の個々の設定項目を個別に管理し、個別にアクセス制御したいという場合は、AppConfigでは１つの設定ファイル単位で取得・アクセスになるため、IAMで細かな設定はできません。ですから個々の設定項目単位にアクセス制御をしたい、デプロイをしたいという場合は、設定情報を階層化したパスで定義しIAMによるアクセス制御が可能なParameter Storeも利用できますし、属性単位にIAMでアクセス制御を指定可能なDynamoDBも候補になるかと思います(FGAC:Fine Grained Access Control)。

# 6.まとめ
アプリケーションプログラムから参照する設定情報はいろいろな特徴があると思います。デプロイ時から変更が発生しない設定情報もあれば状況に応じて設定値を変えて動作制御をするような設定情報まで様々です。設定情報のライフライクル、デプロイの独立性・しやすさ、セキュリティ等を考慮して適切なサービスを選択いただければと思います。全てを完全にカバーするサービスというものはなく、結局のところ、アプリケーションの特性や運用に求められる要件で選択することが必要ということです。