☕️

ecspresso advent calendar 2020 day 23 - ECSの新機能への追従

2020/12/23に公開

Amazon ECS のデプロイツールである ecspresso の利用法をまとめていく ecspresso Advent calendar 23日目です。

ECS の新機能への追従

Amazon ECS は AWS が開発、運用しているマネージドサービスです。ECS 自体は OSS ではないため、実際にどのような新機能がどのタイミングでリリースされるのかは、リリースされるまで利用者は知ることができません。とはいえ ECS の新機能は運用を便利にしてくれるものが多いので、リリースされたらなるべく早く利用できるようにしたいものです。

ecspresso は、ECS の新機能がリリースされ次第、できるだけ迅速に対応する方針で開発しています。というのも、作者である自分は ecspresso 以外で管理している ECS のシステムを持っていないため、新機能を使いたい場合には ecspresso によって対応しないといけない、という事情もあります。

ここでは、どのように ECS の新機能への追従を行っているかを解説します。

ecspresso = AWS SDK の薄い wrapper

何をもって「薄い」というかは議論があるところですが、ecspresso は AWS SDK (aws-sdk-go) が提供しているインターフェースを隠さずに利用者に公開しています。具体的には、サービス定義とタスク定義ファイルです。

これらのファイルを読み込む関数は ecspresso.LoadServiceDefinitionecspresso.LoadTaskDefinition です。この関数のシグネチャを見ると分かるとおり、aws-sdk-go/service/ecs の構造体をそのまま返しています。

https://pkg.go.dev/github.com/kayac/ecspresso#App.LoadServiceDefinition

func (d *App) LoadServiceDefinition(path string) (*ecs.Service, error)

https://pkg.go.dev/github.com/kayac/ecspresso#App.LoadTaskDefinition

func (d *App) LoadTaskDefinition(path string) (*ecs.TaskDefinition, error)

kayac/go-config によってテンプレート記法の処理を行った後は、JSON を直接 SDK が提供している構造体にマッピングして読み込んでいます。

つまり、AWS SDK が新機能のサポートをこれらの構造体に追加しさえすれば、そのバージョンを使用するだけで新しいサービス定義やタスク定義の要素に対応できるのです。

ecspresso 独自の構造体を作っていないため新機能への追従が容易ですし、利用者にとっても公式の SDK や CLI で扱える JSON がそのまま読み込めるという安心感がある作りになっています。

実際の新機能追従の例

2020-11-30にアナウンスされた、deployment circuit breaker 機能を ecspresso がどのようにサポートしたかを例にしてみます。

https://aws.amazon.com/jp/blogs/containers/announcing-amazon-ecs-deployment-circuit-breaker/

2020-11-24 にリリースされた AWS SDK Go v1.35.35 を元に、Dependabot が自動的に Pull Request(PR) を作成しました。

https://github.com/kayac/ecspresso/pull/195

この PR に添付された ChangeLog をみると、(1つ前の 1.35.34 の時点で) service/ecs に、deployment circuit breaker への対応が含まれています。

service/ecs: Updates service API and documentation
This release adds support for updating capacity providers, specifying custom instance warmup periods for capacity providers, and using deployment circuit breaker for your ECS Services.

この PR をマージすると ecs.DeploymentCircuitBreaker 構造体が追加され、それだけでサービス定義ファイルに記述された deploymentCircuitBreaker 要素を読み取れるようになります。

{
  "deploymentConfiguration": {
    "deploymentCircuitBreaker": {
      "enable": true,
      "rollback": true
    },

https://github.com/kayac/ecspresso/pull/197

実際に deploymentCircuitBreaker 要素を読めるようになったことを確認するテストを追加し、手元でビルドしたバイナリでデプロイを行い、機能が使えていることを確認してから v1.1.3 をリリースしました。リリースされたのは日本時間の 2020-12-01 の昼過ぎです。

https://github.com/kayac/ecspresso/releases/tag/v1.1.3

実際にはその日の朝に deployment circuit breaker のリリースを (Twitterで) 知り、SDK が更新されているのを確認して PR をマージ、テストを数行追加して動作確認してリリース、という流れで、数時間で対応が完了しています。

このように、サービス定義やタスク定義に要素が追加されることで利用できる新機能であれば、非常に少ない手間で追従できる作りになっています。

過度な抽象化を避ける

ecspresso を開発した当初から、タスクやサービス定義の JSON の扱いが煩雑になりがちなのは認識していました。そのため、抽象化した扱いやすい定義ファイルにしたほうがよいのではないか、と考えたこともあります。とはいえ独自の設定ファイルを導入すると、実際に ECS を操作する SDK との相互変換の手間が増えてしまいます。

最初は手抜きで SDK の構造体をそのまま使ったという認識でしたが、最初のリリースから3年に渡って新機能に追従し続けた結果、抽象化せずに SDK をそのまま使っていることこそが新機能のサポートを迅速にできるポイントだった、という結論に至りました。

ecspresso を汎用的なコンテナオーケストレーションサービスのデプロイツールとして考えると、ECS 以外のサービスとの共通点を見つけて汎用的な設定ファイルにするほうが使い回しが効くのではないか、と思ってしまいます。しかし、実際にはこれは ECS に特化したツールですから、下手な抽象化はしないのが正解だったのです。

定義ファイルとして JSON のみを読み込めるようにしたことも、後から Jsonnet による定義ファイル生成 (20日目) を考案できる余地を残していたという意味で、結果的には良い判断でした。独自 DSL などを導入していたら、他のツールでの生成は容易ではなかったでしょう。


24日目は、ecspresso の兄弟的なプロダクトである lambroll と sailtrim というデプロイツールを紹介します。

https://zenn.dev/fujiwara/articles/ecspresso-20201224

Discussion