OSS紹介 Advent Calendar 2017 - Qiita 18日目の記事です。(一週間遅れ)
Amazon ECS へのデプロイツール ecspresso と、そこで使っている環境変数を展開しつつ複数の YAML/JSON/TOML を読み込む config loader である go-config の紹介をします。
ecspresso
「エスプレッソ」と読みます。Go で書かれた Amazon ECS 用のデプロイツールです。以下の3つのファイルを用いて ECS へのサービス、タスク定義作成、入れ換えを行います。
- YAML の設定ファイル
- タスク定義のための JSON (
aws ecs describe-task-definition
出力と互換) - サービス定義のための JSON (オプション。
aws ecs describe-services
出力の services セクションと互換`)
region: ap-northeast-1 cluster: default service: app task_definition: taskdef.json service_definition: service.json timeout: 10m
create (サービス作成)、deploy (新しいタスク定義を作成しサービスに対して入れ換える)、status (deployments, events をみる)、rollback (一つ前のタスク定義に差し替えてデプロイすることでロールバックする) の機能があります。
元々 aws-cli を shell script から叩いていたデプロイ script を実装し直したもので (一番最初のバージョンは Go から aws-cli をコマンド起動するものでした)、「それ ○○ (他の ECS に対応したデプロイツール)とどう違うの」といわれると答えに窮する代物ですが…
無理矢理特徴を挙げると
- Go で実装してあるのでシングルバイナリで環境依存少なく動作する
- タスクとサービスの定義ファイルには後述の go-config による、環境変数展開機能がある
ぐらいでしょうか。
go-config
こちらは、複数の YAML / JSON / TOML をマージしつつ、Go の text/template の記法で環境変数を展開して読み込むことができるパッケージです。もともと社内のとあるアプリケーションで使用するために開発されたものですが、ecspresso で使いたいがために (便利なので) 公開しました。
コンテナでアプリケーションやミドルウェアを動作させる場合、設定ファイル自体はコンテナに同梱しておくが、その中の値は環境変数で指定したい、というニーズがあります。ファイルにハードコードされていると設定値を書き換えるたびにコンテナのビルドが必要になりますが、環境変数であればコンテナの起動時に動的に指定することができるため、便利になりますね。
foo: {{ env "FOO" "default_foo" }}
このような YAML
を、環境変数 FOO
に bar
が設定されている状態で読み込むと
foo: bar
となりますし、FOO
が未設定であればデフォルト値として
foo: default_foo
として読み込まれます。
また、環境変数が設定されていなければ panic することで、読みこみ時に確実に設定されていることを強要する {{ must_env "FOO" }}
という記法もあります。
やっていることは単純で、先に Go の text/template で環境変数を展開した上で YAML / JSON / TOML のパーサを通しているだけなので、ファイル自体が各フォーマットとして不正であっても展開後に正しい状態であれば、問題なく読み込むことができます。
とはいえエディタでの編集時にそれぞれのフォーマットとして正しく扱えてくれた方が楽なので、" " でクォートされた内部に展開する場合は、template の記法の方でバッククォートを用いるのがお薦めです。
{ "foo": "{{ env `FOO` `default_foo` }}" }
先述の ecspresso では、タスクとサービスの定義ファイル (JSON) の読みこみ時に go-config による環境変数展開が行われます。
{ "taskDefinition": { "containerDefinitions": [ { "image": "example.com/myapp:{{ must_env `IMAGE_TAG` }}", "environment": [ { "name": "APP_SECRET", "value": "{{ must_env `APP_SECRET` }}" }, { "name": "ENDPOINT", "value": "{{ env `ENDPOINT` `example.com` }}" } ] } ] } }
典型的には、以下のような値を展開することを想定しています。
特にクレデンシャル類は、direnv 等で使用される .envrc というファイルに記述した上で暗号化してリポジトリに保存し、デプロイ時に復号、環境変数に設定した上でデプロイを行うと便利に扱えると思います。
# .envrc export APP_TOKEN="raw_value_of_token"
AWS KMS で暗号化した値を .envrc.encrypted
として出力、リポジトリにはこれをコミット。
$ aws kms encrypt --key-id mykey \ --plaintext fileb://.envrc \ --output text --query CiphertextBlob \ --output text \ | base64 --decode > .envrc.encrypted
デプロイ直前に KMS で .envrc
を復号し、環境変数に設定してデプロイ。
$ aws kms decrypt --ciphertext-blob fileb://.envrc.encrypted \ --output text --query Plaintext \ | base64 --decode > .envrc $ source .envrc $ ecspresso --config app.yaml deploy