BASE開発チームブログ

Eコマースプラットフォーム「BASE」( https://thebase.in )の開発チームによるブログです。開発メンバー積極募集中! https://www.wantedly.com/companies/base/projects

ECS(Fargate)でコンテナアプリケーションを動かすための設定情報の扱い方

あけましておめでとうございます。 BASE BANK株式会社でソフトウェアエンジニアをやっている東口(@hgsgtk)です。

2018年末から年明けにかけて、EKSが東京リージョンに来たりAWSからのリリースが賑わいを見せていますが、その中から、AWS Fargateの次の新機能を実際のプロダクトに適用しました。

AWS Fargate プラットフォームバージョン 1.3 でシークレットのサポートを追加

これを期に、コンテナアプリケーションにおける設定情報を扱う考え方・それを実現するためのAWSのサービス構成と得られた利点についてまとめてみようと思います。

目次

  • 設定情報の扱いに対する要件
  • コンテナベースの設計思想・原則
  • ECS(Fargate)を用いる場合の設定情報の扱い方

設定情報の扱いに対する要件

開発背景

BASE BANK社では、即時に資金調達ができる金融サービス「YELL BANK(エールバンク)」というプロダクトを開発しています。 以前本ブログに投稿した「GoのAPI開発現場におけるユニットテストTips」 や「Goを運用アプリケーションに導入する際のレイヤ構造模索の旅路 | Go Conference 2018 Autumn 発表レポート」の通り、Go言語でアプリケーションを開発しており、docker buildしたイメージから作成したコンテナをECS(Fargate)上で動かすことで機能を提供しています。

設定情報とは

本記事内での「設定情報」は、データベース、Memcached、他バックエンドサービスなどのリソースハンドルのための認証情報を指します。これらは、本番・ステージング・開発・QAなど環境ごとにそれぞれ異なる値を持つ属性があります。

コンテナアプリケーションの設計として、設定情報をどう取り扱っていくか次に考えていきます。

コンテナベースの設計思想・原則

コンテナベースで動かすアプリケーションでの設計思想について次の2点の資料がよく参照されます。

この2つの資料からコンテナベースアプリケーションにおける設定情報の扱い方について見ていきます。

Beyond the Twelve-Factor App

2012年に、モダンなクラウドアプリケーションのベストプラクティス「The Twelve-Factor App」をHerokuの中の人が書きました。Beyond the Twelve-Factor Appは、2016年にPivotal社が、オリジナルのガイドラインのアップデート・ガイドラインの追加したものです。

クラウド環境を活用したクラウドネイティブアプリケーションの新たなベストプラクティスをガイドラインとしてまとめてくださっています。次のURLから実際にPDFをダウンロードすることができます。

content.pivotal.io

また、次のスライドが大変わかりやすくガイドラインについてまとめてくださっているので、そちらもご参照いただくと良いかと思います。

Chapter 5 Configuration, Credentials and Code

この資料内で、特に設定情報については、Chapter 5 Configuration, Credentials and Codeという章で言及されています。ピックアップすると大きく次の内容です。

  • 設定や認証情報はコードから分離すべき
  • 環境は無限に増えていくため、環境(dev, prd...etc)ごとに設定をグルーピングすべきではない。
  • 設定をコードから分離する一番の方法は環境変数への格納である。

設定情報とコードを分離することを求めており、分離できているかどうかの指標として、今すぐにでもコードをオープンソース化できるかを上げています。

次にもう一つredhatが公開している設計原則について見てみましょう。

Principles of container-based application design (Redhat)

redhatが公開しているホワイトペーパーで、コンテナベース・アプリケーションの設計原則についてまとめています。 次のURLにて日本語版の資料をダウンロードすることができます。

www.redhat.com

IMAGE IMMUTABILITY PRINCIPLE (IIP) イメージ不変性の原則

いくつかの原則を説明している中で設定情報についての扱いに関連のあるものとしてこちらの原則があります。

コンテナ化アプリケーションは不変であるため、ビルドされた後、異なる環境間で変化することは想定されていません。つまり、環境ごとにコンテナの作成や修正を行うのではなく、ランタイムデータの保存に外部手段を利用し、外部化した設定を環境によって使い分けます。

上記の記述により、yamlファイルなどでアプリケーション内で設定情報を分けてそれぞれビルドする方法ではなく、ビルドするアプリケーション内に環境によって異なる情報は入れず、外部に保存した設定情報を使用することが推奨されています。

2つの思想・原則から方針を見出す

これまで見てきた2つの資料から、コードから設定情報を分離し、環境変数から外部設定した設定情報を注入する構成としていきます。

ECS(Fargate)を用いる場合の設定情報の扱い方

設計方針が決まったところで実際にAWS、特にECS(Amazon Elastic Container Service)を利用した場合の設定情報の扱い方についてです。

概要構成

概要構成は次のようなものになります。

f:id:khigashigashi:20190115190403p:plain
概要構成

以下、AWS Fargate プラットフォームバージョン 1.3 でシークレットのサポートを追加で発表された機能を利用するため、プラットフォームバージョンは1.3以上を前提とします。

アプリケーション

環境変数から設定情報を取得する実装とします。例えば、Go言語の場合はosパッケージを利用して次のようなコードを書くことになるでしょう。

dbHost := os.Getenv("DB_HOST")

ビルドしたアプリケーションのコンテナイメージは、ECR(Amazon Elastic Container Registry)Docker Hubといったコンテナレジストリサービスにpushします。

設定情報の保存

設定情報は、SSM(Systems Manager) Parameter Storeに保管していきます。

SSMでは、パラメータを階層的に管理する事が可能です。

docs.aws.amazon.com

具体的には、/stage1/stage2/stage3 といった形で階層を掘っていく形になります。

この際、DBのパスワードなど暗号化して保存したい場合は、KMS(Key Management Store)を用いて暗号化キーを作成し、そのキーを持って暗号化します。

SSMに設定する情報を暗号化する際は登録時に暗号化オプションを付け、KMSキーを指定します。

aws ssm put-parameter --name /goecssample/database/sample/master/password --type "SecureString" --value "password" --key-id "< KeyId >" --description "データベースのmasterユーザーパスワード" --region ap-northeast-1

具体的には、typeをSecureStringとし、key-idに作成したKMSキーを設定するだけです。

コンテナから設定情報を利用

SSMに保存した設定情報を、コンテナに挿入するためにはタスク実行ロールに対してSSMのパラメータ取得・KMSキーでの復号を許可する必要があります。 具体的には、タスク実行ロールに対して以下のようなポリシーを追加します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameters",
        "kms:Decrypt"
      ],
      "Resource": [
        "arn:aws:ssm:region:aws_account_id:parameter/parameter_name",
        "arn:aws:kms:region:aws_account_id:key:key_id"
      ]
    }
  ]
}

タスク定義

ECSのプラットフォームバージョン1.3.0より前、Fargateを利用している場合、System MangerのParameter Storeなどから設定情報をコンテナに注入するには、自前でdocker-entrypoint.shなどからSSMに対してAPIアクセスして取得する必要がありました。

しかし、今回のサポートによって、タスク定義内のコンテナ定義に設定することによって、コンテナ起動時に自動でコンテナに対して設定を注入することができるようになりました。

具体的には、コンテナ定義の環境変数に、環境変数名・SSMのパラメータ名を指定するだけです。

f:id:khigashigashi:20190115190517p:plain
コンテナ定義/環境変数設定

1.3より前は、次のようなentrypoint.shなどを書いて対応していました。

#!/usr/bin/env bash

set -e

export DB_HOST=$(aws ssm get-parameters --name /prd/database/host --query "Parameters[0].Value" --region ap-northeast-1 --output text)

exec "$@"

1.3へのアップデートによって、自前での実装が不要になり、かなりシンプルになりました。

その他

その他、より詳細の説明は、AWS公式の次のドキュメントを参照してみてください。

docs.aws.amazon.com

この構成にして得られた利点

コードから設定情報を分離できた

コードから設定情報を分離したことによって、全アプリケーションエンジニアがすべての設定情報を参照できる状況は避けることができました。 加えて、設定情報は、AWS Systems Manager Parameter Storeに設定されています。 本番・ステージングなどでアカウント自体が分かれている場合は、そもそもアカウントを発行されている人のみに閲覧を絞ることができ、加えてIAMの設定等で誰のアクセスを許可するかを制御することが可能になりました。

設定の量が増えても階層的に扱える

AWS Systems Manager Parameter Storeでは、階層的にパラメータを登録することが可能なため、情報を階層的に扱いたいモチベーションもある程度満たしてくれました。

シークレットのサポートによって得られた嬉しいこと

AWS Fargate プラットフォームバージョン 1.3 でシークレットのサポートを追加によって嬉しかった点も記載します。

コンテナ起動時の複雑性が軽減された

当初、デプロイ時の複雑性を増していたdocker-entrypointでの自前実装が不要になり、アプリケーションの複雑性が減りました。

イメージサイズが小さくなった

副次的な効果として、AWSのAPIに対するアクセスが不要になったため、実行イメージに含めていたpythonaws-cliが不要になりました。 その効果によって、約40MB程度だったイメージサイズが約10MBまで小さくなりました。

まとめ

そもそも、コンテナアプリケーションをどう扱うべきなのかという観点から、実際にAWS Fargateを利用した場合についての説明をさせていただきました。 当初扱いづらさがあった部分もアップデートによって日々使い勝手がよくなってきている印象を覚えています。 現在、ECSなどでアプリケーションを構成しようとしている・または検討している方にとって、一つの参考になれば幸いです。