tl;dr
AWS より以下のようなブログがポストされました.
aws.amazon.com
合わせて, Docker でも同じようなブログがポストされていました.
www.docker.com
英語は読めませんが, ざっくりと読んだ限りだと docker-compose.yml があれば, docker に ecs
コマンドが追加されて, 以下のように, docker-compose up
する感覚で, ローカル PC からダイレクトに ECS にアプリケーションが出来るようになったようなので, 試してみました.
$ docker ecs compose up
尚, この機能は現時点では, Public Beta 版として公開されており, 先述のブログ記事でも Edge バージョンの Docker for Mac を利用して確認が可能ということですので, Edge バージョンを利用して動作確認しています. そのため, 開発の状況によって, 記事の内容とは異なる結果が得られることがあるかと思いますのでご注意下さい. また, 記事内に誤りや言葉の誤用等もあるかもしれませんので合わせてご留意下さい.
以下, 動作確認環境の情報です.
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.5
BuildVersion: 19F101
$ docker version
Client: Docker Engine - Community
Azure integration 0.1.7
Version: 19.03.12
API version: 1.40
Go version: go1.13.10
Git commit: 48a66213fe
Built: Mon Jun 22 15:41:33 2020
OS/Arch: darwin/amd64
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 19.03.12
API version: 1.40 (minimum version 1.12)
Go version: go1.13.10
Git commit: 48a66213fe
Built: Mon Jun 22 15:49:27 2020
OS/Arch: linux/amd64
Experimental: true
containerd:
Version: v1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683
以下, Docker for Mac の情報です.
要件
この機能 (以後, 「ECS 連携機能」と書きます) を利用するには, 現時点では Docker for Mac の Edge バージョンが必要になります. また, IAM ユーザー又は IAM Role に以下の権限を付与する必要があるようです.
- ec2:DescribeSubnets
- ec2:DescribeVpcs
- iam:CreateServiceLinkedRole
- iam:AttachRolePolicy
- cloudformation:*
- ecs:*
- logs:*
- servicediscovery:*
- elasticloadbalancing:*
ecs-plugin
ECS 連携機能は, Docker CLI のプラグイン ecs-plugin として提供されているようです.
github.com
このリポジトリの example フォルダにサンプルアプリケーションが収められているので, 本記事の中でも利用させて頂きたいと思います. また, example フォルダ内の README に則ってチュートリルを進めます.
尚, example フォルダ内 docker-compose.yml は以下のように書き換えています.
version: "3.8"
services:
frontend:
build: app
image: ${DOCER_HUB_ACCOUNT}/timestamper
ports:
- "5000:5000"
depends_on:
- backend
backend:
image: redis:alpine
また, Docker Hub にログインしておく必要があります.
$ docker login
尚, ecs-plugin は以下の通り確認出来ます.
$ docker help | grep ecs
ecs* Docker ECS (Docker Inc., v1.0.0-beta.1)
$ docker ecs version
Docker ECS plugin v1.0.0-beta.1 (12a47cb)
docker ecs setup
ECS 連携機能を利用する為には, docker ecs setup
コマンドを使ってコンテキストを作成する必要があります.
コンテキストを作成後, docker context ls
を実行して確認してみます.
$ docker context ls
NAME TYPE DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
aws aws
default * moby Current DOCKER_HOST based configuration unix:///var/run/docker.sock https://xxx.xxx.xxx.xxx (default) swarm
コンテナイメージを Docker Hub にプッシュ
サンプルアプリケーションを一旦, ローカル PC でビルドした後 (docker-compose build
), 以下を実行して Docker Hub にサンプルアプリケーションイメージをプッシュします.
$ docker-compose push
コンテキストを切り替える
先述の docker ecs setup
で作成した ECS 連携機能用コンテキストに切り替えます.
$ docker context use aws
以下のように出力されます.
$ docker context use aws
aws
ちょっとさみしい出力ですね.
docker ecs compose up
早速, サンプルアプリケーションを起動してみたいと思います.
$ docker ecs compose up
以下のように出力されました.
$ docker ecs compose up
WARN[0000] services.build: unsupported attribute
account has not default VPC
oh... Default VPC が必要になるようです... 万事休すでした. ecs-plugin の実装を見ると, 現時点では, 以下のようにデフォルト VPC のみをサポートするようになっていました.
...
func (s sdk) GetDefaultVPC(ctx context.Context) (string, error) {
logrus.Debug("Retrieve default VPC")
vpcs, err := s.EC2.DescribeVpcsWithContext(ctx, &ec2.DescribeVpcsInput{
Filters: []*ec2.Filter{
{
Name: aws.String("isDefault"),
Values: []*string{aws.String("true")},
},
},
})
if err != nil {
return "", err
}
if len(vpcs.Vpcs) == 0 {
return "", fmt.Errorf("account has not default VPC")
}
return *vpcs.Vpcs[0].VpcId, nil
}
...
検証で利用している AWS アカウントには Default VPC が存在しておらず, しかも, 以下のように新たにデフォルト VPC を作ることが出来なかった為, 動作確認はここで断念することにしました. (新しい AWS アカウントを作る気力が無かった...)
$ aws ec2 create-default-vpc
An error occurred (OperationNotPermitted) when calling the CreateDefaultVpc operation: Accounts on the EC2-Classic platform cannot create a default VPC.
雑にコードリーディング
残念ながら
手元の環境では動作を確認することが出来なかったので, ecs-plugin のソースコードをざっくりと読んでみました.
docker ecs compose
docker ecs compose
には以下のようなサブコマンドが用意されています.
これらは, ここの部分で定義しています. それぞれの機能について, コアな実装は, pkg/amazon/backend
以下にそれぞれ実装されています.
この中から up.go について見てみます.
up.go
up.go はその名の通り, docker ecs compose up
を実行した際に呼ばれる関数達が書かれています.
github.com
以下は Up 関数の抜粋です.
func (b *Backend) Up(ctx context.Context, options cli.ProjectOptions) error {
project, err := cli.ProjectFromOptions(&options)
if err != nil {
return err
}
if b.Cluster != "" {
ok, err := b.api.ClusterExists(ctx, b.Cluster)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("configured cluster %q does not exist", b.Cluster)
}
}
update, err := b.api.StackExists(ctx, project.Name)
if err != nil {
return err
}
if update {
return fmt.Errorf("we do not (yet) support updating an existing CloudFormation stack")
}
template, err := b.Convert(project)
if err != nil {
return err
}
vpc, err := b.GetVPC(ctx, project)
if err != nil {
return err
}
subNets, err := b.api.GetSubNets(ctx, vpc)
if err != nil {
return err
}
lb, err := b.GetLoadBalancer(ctx, project)
if err != nil {
return err
}
parameters := map[string]string{
ParameterClusterName: b.Cluster,
ParameterVPCId: vpc,
ParameterSubnet1Id: subNets[0],
ParameterSubnet2Id: subNets[1],
ParameterLoadBalancerARN: lb,
}
err = b.api.CreateStack(ctx, project.Name, template, parameters)
if err != nil {
return err
}
fmt.Println()
w := console.NewProgressWriter()
for k := range template.Resources {
w.ResourceEvent(k, "PENDING", "")
}
return b.WaitStackCompletion(ctx, project.Name, compose.StackCreate, w)
}
以下のような順番で AWS リソースの操作が動いていることが解ります.
- ECS Cluster が存在するか確認
- CloudFormation スタックの確認
- CloudFormation テンプレートの生成 (後述)
- default VPC が存在しているか確認して, 存在していたら VPC ID を返す
- サブネット, ロードバランサの情報を取得
- 最後に CloudFormation のスタックを作成
そして, Create Stack が完了するまで待機するという感じだと思います. 個人的に興味深いのは, CloudFormation のテンプレートを生成している部分ですので, この部分を処理しているコードも見てみます.
convert.go
以下は, up.go 内で呼ばれている Convert 関数が実行されている convert.go です.
github.com
convert.go の冒頭の抜粋です.
func (b Backend) Convert(project *types.Project) (*cloudformation.Template, error) {
var checker compatibility.Checker = &FargateCompatibilityChecker{
compatibility.AllowList{
Supported: compatibleComposeAttributes,
},
}
compatibility.Check(project, checker)
for _, err := range checker.Errors() {
if errdefs.IsIncompatibleError(err) {
logrus.Error(err.Error())
} else {
logrus.Warn(err.Error())
}
}
...
返り値の型を見ると, *cloudformation.Template
とあるので, CloudFormation テンプレートを返すことが解ります.
Convert 関数の引数の型として, *types.Project
という型が指定されていますが, これは, compose-spec/compose-go プロジェクトで実装されている関数を呼び出すことが出来る型です.
github.com
詳しくは理解出来ていませんが, どうやら docker-compose.yml をパースして各 YAML キーの値を取り出すことを目的としているようです.
以上
せっかく, 新しいリリースを試そうと早起きしましたが, 思わぬところで躓いてしまいリリース自体を試すことは出来ませんでしたが, ecs-plugin がどのように実装しているのか, ほんの少しだけ覗くことが出来ました. 実際に試すことは出来ませんでした, docker コマンドから直接 ECS にアプリケーションがデプロイ出来ることで, 開発からデプロイまでを一貫して行えるようになるというのは非常に嬉しいことだと思いますし, 世界中のエンジニアがまち待った機能なのではないでしょうか.
そして, 早く default VPC 以外でも使えるようになることを祈っています. 以下の通り, issue 化されているので, default VPC 以外でも使える日は近いのかなと期待しております.
github.com