はじめに
Twelve Factor App に準拠するAmazon ECSでのアプリケーション開発・運用の悩みの種の一つに「環境変数地獄」1があります。
環境ごと、サービスごとに倍々に膨れ上がる環境変数をどう管理するべきかという問題です。
AWS Secrets Managerを使うとAWSでアプリケーションと環境変数を安全に分離して管理することができます。
AWS Secrets Managerを活用することでAmazon ECSにおける環境変数の管理をなるべく簡潔にしようというのがこの記事の主旨です。
Twelve Factor App ってなに?
Twelve Factor Appとは、Herokuのエンジニア集団によるSaaSを作り上げるためのベストプラクティスです。
Twelve Factor Appは名前の通り、下記の12個の項目でまとめられています。
I. コードベース
バージョン管理されている1つのコードベースと複数のデプロイ
II. 依存関係
依存関係を明示的に宣言し分離する
III. 設定
設定を環境変数に格納する
IV. バックエンドサービス
バックエンドサービスをアタッチされたリソースとして扱う
V. ビルド、リリース、実行
ビルド、リリース、実行の3つのステージを厳密に分離する
VI. プロセス
アプリケーションを1つもしくは複数のステートレスなプロセスとして実行する
VII. ポートバインディング
ポートバインディングを通してサービスを公開する
VIII. 並行性
プロセスモデルによってスケールアウトする
IX. 廃棄容易性
高速な起動とグレースフルシャットダウンで堅牢性を最大化する
X. 開発/本番一致
開発、ステージング、本番環境をできるだけ一致させた状態を保つ
XI. ログ
ログをイベントストリームとして扱う
XII. 管理プロセス
管理タスクを1回限りのプロセスとして実行する
このTwelve Factor Appの項目の中でも、今回取り上げているのは「III. 設定」です。
設定の項目には、設定とコードを分離し、設定は環境変数として格納するということが述べられています。
アプリケーションは時に設定を定数としてコード内に格納する。これはTwelve-Factorに違反している。Twelve-Factorは 設定をコードから厳密に分離すること を要求する。
Twelve-Factor Appは設定を 環境変数 に格納する。
具体的には、開発・検証・本番といった環境ごとに異なるデータベースの認証情報やAWSのシークレットキーなどの変数はconfigファイルにハードコードするのではなく、環境変数として管理しようというものです。
なぜなら、configファイルでの設定情報管理はうまくスケールしないためです。
アプリケーションのデプロイが増えるにつれて、新しい環境のconfigファイルが必要になります。
さらに、プロジェクトが拡大すると、開発者は yoshimura-staging
のような自分用の環境を追加することになり、結果として設定が組み合わせ的に爆発し、アプリケーションのデプロイの管理が非常に不安定になるということが述べられています。
詳細はIII. 設定のページを参照してください。
Twelve Factor Appのこのベストプラクティスに従いAmazon ECSでアプリケーション開発・運用をした結果待ち受けているのが、 環境変数地獄 です。
環境ごとに倍々に膨れ上がった環境変数をどう管理するのかという課題です。
この課題にAWS Secrets Managerで立ち向かいます。
AWS Secrets Managerの紹介の前にAmazon ECSについて軽く触れておきたいと思います。
Amazon Elastic Container Service(Amazon ECS) ってなに?
Amazon Elastic Container Service (Amazon ECS) は、 AWS独自のコンテナオーケストレーションサービスです。Dockerコンテナ化されたアプリケーションをAWSで簡単に実行・停止およびスケールを行うことができます。
Amazon ECSは従来までEC2インスタンスしか選択できませんでしたが、2018年7月に東京リージョンでサービススタートとなった AWS Fargateを利用することで、EC2インスタンスの管理が不要になります。これにより、EC2のスケールを意識せずともコンテナのオートスケールが可能になるなどのメリットを享受することができます。
Amazon ECSやFargateに関しては大変参考になる記事がありますので紹介させていただきます。
Amazon ECSは調べた限りだと、S3での環境変数管理を推奨しているようです。
設定情報を Amazon S3 のプライベートバケットに保存し、コンテナインスタンスの IAM ロールに読み取り専用アクセス権限を付与するのが、コンテナインスタンスの起動時に設定を許可する安全で便利な方法です
しかし、S3はマネージドコンソールで編集できないのがかなりつらく、環境変数を更新する際、ファイルをダウンロードして編集してアップロードして・・・のような職人の手作業が発生してしまいます。
これを回避するためにAWS Secrets Managerを選択しています。
CLIの1コマンドでダウンロード・編集・アップロードを行ってくれる便利ツール 2 も確認できましたが、AWSのマネージドなサービスに乗っかるのがいいのではないかという判断です。(自作ツールはメンテナンスされ続けるか不安...)
それでは、今回の主役、AWS Secrets Managerの紹介です。
AWS Secrets Manager ってなに?
AWS Secrets Managerとは、データベースの認証情報やAPIKeyなどのアプリケーションの環境変数を安全に格納・配布・交換・使用できるシークレット管理サービスです。
Amazon RDS for MySQL、PostgreSQL、Amazon Aurora への統合を組み込むことでDBの認証情報のローテーションが提供されるのが パラメータストア との違いの一つです。
今回、Amazon ECSでのアプリケーション開発・運用における環境変数管理ツールとしてAWS Secrets Managerを利用します。
※ AWS Secrets Managerのチュートリアルがあります。環境が整っていれば数十分でできてしまうのでぜひ試してみてください。実際に触ってみることが一番理解が進みます。
https://docs.aws.amazon.com/ja_jp/secretsmanager/latest/userguide/tutorials_basic.html
AWS Secrets Managerへのアクセス方法
早速、AWS Secrets Managerに環境変数を登録・取得してみましょう。アクセス方法は大きく3つあります。
1. AWSマネジメントコンソール
AWSマネジメントコンソールでは、GUIで環境変数の登録・取得・編集・削除が行えます。
環境変数の微修正などにはとても便利ですが、複数の環境変数を追加する場合は一つ一つ追加する必要があるので、とても手間がかかりますしミスをしてしまう可能性もあります。
また、AWS Secrets Managerの仕様なのか、A-Zで環境変数をソートしてくれず、追加順でしか表示できません。(ここは今後アップデートで改修されるかもしれません。)
環境変数の微修正などの目的以外は、CLIからの登録がおすすめです。
メリット
- GUIで操作できる
- 環境変数の微修正は便利
デメリット
- 一度に複数の環境変数を追加したい場合は不便
- 環境変数をA-Zでソートしてくれない
2. AWS CLI
AWS CLIが利用できるように準備をしてください。利用方法は適宜調べてください。
IAMやリージョンを適切に設定してから以下のコマンドでAWS Secrets Managerに環境変数の登録ができます。
aws secretsmanager create-secret --name xxx --description xxxx --secret-string file://example.json
create-secret
オプションは新規作成時のみ有効なので、環境変数を更新する場合は put-secret-value
オプションを利用する必要があります。同様に削除のオプションもあります。
example.json
は シークレットのプレーンテキストで表示されるフォーマットです。(ドキュメントで提示されているフォーマットだとうまくいかなかったので注意が必要です。)
{
"Key" : "Value",
"USER_NAME" : "user_name",
"PASSWORD": "password"
...
}
このフォーマットのjson形式で管理しておくと環境変数の作成や更新がCLIから行えるので便利です。
例えば新たに環境を増やす場合、ベースとなる環境のjsonファイルをコピーして値を適切な値に書き換えるだけで、そのjsonファイルを元に新しい環境の環境変数をAWS Secrets Managerに登録することができます。
CLIでの登録は環境変数の値のバイト数が長すぎると登録できない場合があるので注意が必要です。
メリット
- CLIで操作できる
- jsonファイルでテンプレート化ができる
デメリット
- 環境変数の値のバイト数が長すぎると登録できない場合がある
3. AWS SDK
プログラミング言語ごとに用意されているAWS SDKを利用することでも環境変数の登録を行えます。
クライアントをPythonで実装してみる
登録した環境変数を呼び出すクライアントの実装をPythonでやってみます。
PythonのAWS SDKである Boto3 を利用します。
import json
import boto3
from botocore.exceptions import ClientError
class SecretsManager:
def __init__(self, aws_region, aws_access_key_id=None, aws_secret_access_key=None):
self.aws_region = aws_region
self.aws_access_key_id = aws_access_key_id
self.aws_secret_access_key = aws_secret_access_key
def get_secret_values(self, secret_name):
client = boto3.client(
'secretsmanager',
aws_access_key_id=self.aws_access_key_id,
aws_secret_access_key=self.aws_secret_access_key,
region_name=self.aws_region
)
try:
resp = client.get_secret_value(SecretId=secret_name)
secret_values = json.loads(resp['SecretString'])
except ClientError:
raise
except KeyError:
raise
except json.JSONDecodeError:
raise
else:
return secret_values
def get_secret_value(self, secret_key, secret_values):
try:
secret_value = secret_values[secret_key]
except KeyError:
raise
else:
return secret_value
※ AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
に関しては適切なIAM Roleをアタッチすることで設定不要です。デフォルトのポリシーで SecretsManagerReadWrite
があります。
AWS Secrets Managerから環境変数ごとに取得することもできますが、アクセスの回数を抑えるために一度全ての環境変数を取得して、その環境変数から検索するという実装にしています。
サードパーティライブラリaws-smを公開しました
上記の SecretsManager
クラスを pip install
で使えるようにしました。
インストール
$ pip install aws-sm
使い方
from aws_sm import SecretsManager
AWS_ACCESS_KEY_ID = ***************
AWS_SECRET_ACCESS_KEY = ***************
secretsmanager = SecretsManager('ap-northeast-1', AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
secrets = secretsmanager.get_secret_values('tutorials/MyFristTutorialSecret')
USER_NAME = secretsmanager.get_secret_value('USER_NAME', secrets)
PASSWORD = secretsmanager.get_secret_value('PASSWORD', secrets)
これは USER_NAME
と PASSWORD
を tutorials/MyFristTutorialSecret
というシークレットから取得するときのシンプルな例です。
AWS Secrets Managerを運用してみた所感
マネージドなサービスに乗っかれるので色々と楽をできます。
しかし、AWS Secrets Managerを利用してもイケてないと感じる点があります。
それは、AWS Secrets Managerに環境変数管理を統一できないことです。
どういうことかというと、アプリケーションの起動やAWS Secrets Managerへのアクセスに必要な環境変数はAWS Secrets Managerで管理できないため、別途管理が必要になるということです。
例えば、AWS Secrets Managerへの接続情報に必要なAWSリージョン(IAMでクレデンシャルを管理している場合はIAMも)やアプリケーションフレームワークのシークレットキーがこれに該当します。これらの環境変数はアプリケーションの起動前やAWS Secrets Managerへのアクセス前に値を渡してあげる必要があるため、AWS Secrets Managerで管理することができません。(Djangoアプリケーションでの話なので、他のフレームワークでは少し異なるかもしれません。)
順番としてはアプリケーションの起動後にAWS Secrets Managerへアクセスして環境変数を取得するという流れになるため当然と言えば当然です。
これらの環境変数はどうしようもないため、 Docker Composeのenvironmentで管理しています。
(Amazon ECS CLI + GitLabでデプロイしていますが、Docker ComposeのenvironmentをGitLabの Variables から渡しています。)
AWS Secrets Managerで環境変数の作成や取得が楽になった反面、環境変数の管理が二重化してしまうというデメリットもありました。
おわりに
Twelve Factor Appに準拠するAmazon ECSでのアプリケーション開発・運用のつらみ、環境変数地獄をAWS Secrets Managerを利用して対処してみました。
やはりマネージドなサービスに乗っかるのが色々と楽をできます。
しかし、AWS Secrets Managerを利用しても上記のようなつらみがあるのも事実です。
AWSはPython以外の言語もSDKを公開しており、同じようにSecrets Managerを利用することができると思うのでぜひ活用してみてください。
-
DjangoCongress JP 2018 「レガシーDjangoアプリケーションの現代化」 で取り上げられていた問題です。 ↩