kakakakakku blog

Weekly Tech Blog: Keep on Learning!

docker login をせずに ECR を操作できる awslabs/amazon-ecr-credential-helper

3月末に参加した「JAWS-UG コンテナ支部」で知った ECR (Amazon EC2 Container Registry) の便利ツール Amazon ECR Docker Credential Helper を試した.Amazon ECR Docker Credential Helper を使うと ECR のログインを省略できる.

kakakakakku.hatenablog.com

github.com

前提

以下のバージョンを満たす必要がある.

  • Docker 1.11 以上
  • Go 1.5 以上

今回は以下のバージョンで試した.

  • Docker 17.05
  • Go 1.8.1

普通に ECR を使う場合

aws ecr get-login コマンドを叩くと返ってくる docker login コマンドを実行して,ECR にログイン(12時間だけ有効なセッションを取得する)する必要がある.もし Docker 17.06 以上を使っている場合はオプション構成が変わっていて --no-include-email を付ける必要がある.

$ aws ecr get-login --region ap-northeast-1
$ aws ecr get-login --region ap-northeast-1 --no-include-email

よって,デプロイなど自動化が必要な場合は,以下のような形で実装する必要があった.

$ $(aws ecr get-login --region ap-northeast-1)
$ $(aws ecr get-login --region ap-northeast-1 --no-include-email)

Amazon ECR Docker Credential Helper をインストールする

go get した後に make を実行して実行ファイルを生成する.

$ go get github.com/awslabs/amazon-ecr-credential-helper
$ cd ${GOPATH}/src/github.com/awslabs/amazon-ecr-credential-helper
$ make
$ ls -l ${GOPATH}/src/github.com/awslabs/amazon-ecr-credential-helper/bin/local/docker-credential-ecr-login

次に docker-credential-ecr-login をシンボリックリンクにする.今回は /usr/local/bin に置いた.

ln -s ${GOPATH}/src/github.com/awslabs/amazon-ecr-credential-helper/bin/local/docker-credential-ecr-login /usr/local/bin/docker-credential-ecr-login

最後に ~/.docker/config.json を修正して以下のエントリーを追加しておく.

{
    "credsStore": "ecr-login"
}

ログインなしで ECR を操作する

すると aws ecr get-login コマンドを実行する必要がなくなり,ECR に対して直接 docker push を実行できるようになる.地味な機能ではあるけど,個人的には凄く便利になるなと感じた.

まとめ

  • ECR は通常 aws ecr get-login コマンドを使って事前にログインをする必要がある
  • Amazon ECR Docker Credential Helper を使うとログインを意識せずに ECR を操作できるようになる

関連記事

AWS Blog にも記事が出ていて,Jenkins と連携する方法なども紹介されている.

CircleCI + ecs-deploy で ECS にデプロイをする

引き続き ECS のデプロイを調査していて,今回は導入された話を比較的よく聞く ecs-deploy を試した.AWS CLI と jq に依存しているけど,Shell 100% で実装されているため,実行環境の構築が不要という手軽さが1番のメリットだと思う.

github.com

ちなみに前回試したのは CircleCI + AWS CLI で ECS にデプロイする方法で,今回の ecs-deploy も構成としては似ているため,deploy.sh で ecs-deploy を使う形を実装してみた.

kakakakakku.hatenablog.com

構成

f:id:kakku22:20170427104839j:plain

前提

動かす API は前回と同じ CircleCI の circleci/go-ecs-ecr で使った main.go を使う.

  • ECR リポジトリ名 : test
  • クラスタ名 : test-cluster
  • サービス名 : test-service
  • タスク定義名 : test-task

また今回も CircleCI の Environment Variables で,以下の環境変数を設定しておく.

  • AWS_ACCOUNT_ID
  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

circle.yml

machine:
  services:
    - docker

dependencies:
  post:
    - docker build -t $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/test:$CIRCLE_SHA1 .

deployment:
  prod:
    branch: master
    commands:
      - ./deploy.sh

deploy.sh

AWS CLI の細かな制御は ecs-deploy で考慮されているため,非常にシンプルにデプロイすることができる.

#!/bin/sh

eval $(aws ecr get-login --region ap-northeast-1)
docker push $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/test:$CIRCLE_SHA1
./ecs-deploy --cluster test-cluster --service-name test-service --image $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/test:$CIRCLE_SHA1

ecs-deploy の解説

デプロイをする場合

デプロイをする場合,以下の必須オプションを設定する必要がある.また,個人的にオプション名が省略されているとすぐわからないため,フルネームのオプションを使うようにしている.以下のコマンドを実行すると,まず,タスク定義の新規リビジョンを作成し,コンテナイメージのタグを --image 引数で与えられた値に更新する.今回の例で言うと $CIRCLE_SHA1 の値がタグなので,この値に更新する.次にサービスのタスク定義を更新し,デプロイが完了する.

  • --cluster ... クラスタ名
  • --service-name ... サービス名
  • --image ... コンテナイメージ(${ECR_URL}/${IMAGE_NAME}:${TAG_NAME} というフォーマットになる)
./ecs-deploy --cluster test-cluster --service-name test-service --image $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/test:$CIRCLE_SHA1

Desired Count を変更する場合

タスクの Desired Count を変更する場合,--desired-count オプションを使うと,自動的にサービスに反映される.ただし,通常のデプロイも行われるため,Desired Count だけを変更するということはできなかった.これができると良いのになーとは思う.

  • --desired-count ... サービスに紐付くタスクの Desired Count
./ecs-deploy --cluster test-cluster --service-name test-service --image $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/test:$CIRCLE_SHA1 --desired-count 4

その他

他にも Minimum Healthy Percent (--min)Maximum Percent (--max) なども,オプションで指定をしてデプロイすることができる.詳しくは README を参照で!

トラブルシューティング

--image で指定したコンテナイメージがタスク定義に反映されない場合

仕様として,現在設定されているタスク定義のコンテナイメージの表記が,--image で指定したものと前方一致(タグ以外の部分が一致)になっている必要がある.最初のリビジョンで,適当なコンテナ名を入力していたため,ずっと反映されず,ハマった.createNewTaskDefJson 関数の中に以下の実装があり,ここを読んで気付いた.

DEF=$( echo "$TASK_DEFINITION" \
        | sed -e "s|\"image\": *\"${imageWithoutTag}:.*\"|\"image\": \"${useImage}\"|g" \
        | sed -e "s|\"image\": *\"${imageWithoutTag}\"|\"image\": \"${useImage}\"|g" \
        | jq '.taskDefinition' )

ecs-deploy の不満

ecs-deploy にバージョンの概念があったら良いのにと思う.GitHub のリリースを見ても去年10月から作成されていないし,日々変更が develop ブランチに取り込まれているため,自分が今どのバージョンを使っているのかがよくわからなくなってしまう.

まとめ

  • ECS にシンプルにデプロイするなら ecs-deploy は便利
  • Shell 100% で実装されているのも個人的には好印象だった
  • オプション次第では Desired Count なども変更できるため,運用でも使えそう
  • 個別要件がある場合は AWS CLI で実装する必要がある(結局何かしらありそう)

関連記事

kakakakakku.hatenablog.com

実際に試したリポジトリ

以下のリポジトリで試した.既に ECS クラスタは削除してある.

github.com

「JAWS-UG コンテナ支部 入門編 #4」に参加して ECS の事例を聞いてきた

3/30(木) に参加して既に3週間もたってしまったけど,ECS の事例を聞きたいなと思って「JAWS-UG コンテナ支部 入門編 #4」に参加してきた.今回は「入門編」だったので,僕のレベル的にちょうど良いなと思った.あと JAWS 系のイベントは “19-21時” でサクッと終わるため,集中して学んで帰れるのも嬉しいポイントだと思う.

jawsug-container.connpass.com

Docker と Docker Hub の操作と概念 @zembutsu

  • 技術(コンテナ)と仕様(Docker)
  • dockerd → containerd → runC → Linux Kernel
  • 通常の Linux では /sbin/init が PID 1 だけど,コンテナの中で httpd など,起動しているプロセスが PID 1 になる
  • コンテナホストから見ると,その httpd は PID 6 になる(例えば)
  • docker v1.13 からコマンドの体系が変わった
    • docker imagesdocker image ls
    • docker psdocker container ls
    • などなど
  • docker container prune
    • 停止しているコンテナを全て削除する
  • docker image prune
    • 使用していないコンテナイメージを全て削除する
  • docker system prune
    • コンテナ,コンテナイメージだけじゃなく,不要なネットワークとボリュームも全て削除する

www.slideshare.net

Docker は少し前から個人利用をしていたし,社内でハンズオン勉強会を主催したこともあるので,ある程度は理解できていた.ただ,v1.13 からコマンドの体系が変わったのは知らなくて,まだ無意識に旧コマンドを打ってしまっている状態.また prune 系のコマンドも知らなくて,今までこまめに消していて非常に面倒だったので,最近はよく使うようにしている.コマンド体系の変更は以下に詳細に載っている.

qiita.com

ECS 概要 / アップデート情報 AWSJ 浅野さん

(資料公開なし)

とある社内ビックデータ基盤にバッチ用コンテナ基盤を構築してみた @Thiro1123

  • 背景
    • 大量のバッチが存在している
    • 大量のサーバが存在している
  • バッチ間の依存を無くして,バージョンアップや,リリースをできるようにする必要があった
    • オンプレ側の JP1 でバッチの実行結果を確認したいという要件もあった
  • JP1 から API Gateway + Lambda に POST することで,curl さえあれば連携できるようにした
  • Lambda が起動して ECS Task を起動する

www.slideshare.net

ECS をバッチ実行に活用する事例は凄く参考になった.ウェブサーバや API サーバでのユースケースがメインなのかなと思っていたけど,前にデータベースマイグレーションを実行するだけのコンテナを動かすという話も聞いたことがあるし,非常にポータビリティがあり良さそう.また,他の機能に影響せず,ジョブごとに順次移行できそうなのも良かった.あと ECS でバッチを動かす事例だと,最近以下の記事も出ていて参考になった.

www.wantedly.com

これから ECS をはじめる人のための ECS ベストプラクティス @sudoyu

  • SpotFleet を使うとコンテナインスタンスのコストを最適化することができる
  • ECS を使うと運用コストが減るため,結果的にエンジニアの稼働を別の開発に当てることができる
  • 勘所
    • 永続化データ (Docker Volume) を使わないように設計する
      • Docker Volume を使ってしまうと,コンテナインスタンスに依存してしまう
      • S3, CloudWatch Logs, RDS, DynamoDB など,特性に応じてデータを置くようにする
    • ログは awslogs ドライバを使う
      • 標準出力を CloudWatch Logs に転送してくれる
      • もしくは Fluentd などのログドライバを使うという選択肢もある
    • ALB でパスベースルーティングができるため,ドメイン名を分けなくても良いような場合は検討する
      • ECS Service だけを変えれば良いようになる
  • 注意点
    • ECS-optimized AMI の更新通知を SNS で受信可能になっている
    • 動的ポートマッピングを使う

www.slideshare.net

まさにこれから ECS の設計をしようとしていたので,非常にコンパクトにまとまっていて参考になった.ログを永続化する仕組みは CloudWatch Logs が良いのか,既存の Fluentd の Aggregator を使うのが良いのか,ログをどのように活用するのかを考えてから決めたいと思う.

ECS 導入における3つの明確な傾向 jhotta さん

www.datadoghq.com

  • トレンド 1 : ECS は静かに着実に人気を獲得している
  • トレンド 2 : コンテナの増加は問題の増加
  • トレンド 3 : オーケストレーション == 多産家系

まとめ

  • Docker の概念,ECS 機能紹介,バッチでの事例,ベストプラクティスなど,発表内容のバランスが非常に良く勉強になった
  • ECS のデプロイ関連の話があまり無かったため,聞きたかった…
  • Amazon ECR Docker Credential Helper はさっそく試してみる
  • ECS を使い倒すぞ!という気持ち

関連記事

「コンテナは20世紀最大の発明」という話を聞いて,コンテナ物語のことを思い出した.Docker のメタファーを学ぶならオススメの1冊かなー!

kakakakakku.hatenablog.com

さっそく ECS に入門して Golang の API を ECS にデプロイする仕組みを試してみた.

kakakakakku.hatenablog.com

関連レポート

AWS CLI で ALB 配下のインスタンスを deregister / register する

AWS CLI で ELB 配下のインスタンスをローリングデプロイする方法は去年にまとめた.aws elb deregister-instances-from-load-balanceraws elb register-instances-with-load-balancer を使うだけじゃなく,aws elb describe-instance-health を使って waiter を実装した点がポイントだった.ちなみに EC2 だと waiter を実装する必要はなく aws ec2 wait で完了を待つことができる.

kakakakakku.hatenablog.com

ALB の場合 aws elbv2 を使う

1年が経過し,今度は ALB 配下のインスタンスをローリングデプロイしようと考えていて,調べたところ ALB の場合は aws elbv2 を使う必要があった.さらに,ターゲットグループなど ELB とは違う概念があるため,サブコマンドもオプションも異なっていた.

ドキュメント

今回,以下の3コマンドを使った.

オプションは --target-group-arn にターゲットグループの ARN を指定し,--targets にインスタンス ID を指定する.ただし --targets に指定するときはフォーマットが決まっているため,以下のようにする.なお,ポートは省略することができる.

Id=string,Port=integer …

ALB のステータス

ELB は InServiceOutOfService のステータスしか存在しなかったが,ALB の場合はもっと多くある.

  • initial
  • healthy
  • unhealthy
  • unused
  • draining

ALB から deregister すると healthy → draining → unused と遷移する.register すると unused → initial → healthy と遷移する.unhealthy はヘルスチェックに失敗したときに出るため,通常 deregister / register をするときには出ない.

(正確に言うと他の遷移もあると思う)

f:id:kakku22:20170422233304j:plain

docs.aws.amazon.com

ALB ターゲットグループのドレインに注意する

ターゲットグループのデフォルトのドレインは 300 秒に設定されているため,deregister すると,毎回 300 秒 draining のままになる.アプリケーションの特性次第だけど,基本的には長いと思うので,30 秒などに短くした方が良いと思う.

docs.aws.amazon.com

ドレインに関しては前に検証した記事が参考になると思う.

kakakakakku.hatenablog.com

aws_utils.sh

共通機能は去年と同じで ALB を操作する部分を追加した.

#!/bin/sh

# ALB の状態を確認する間隔(秒)
SLEEP=3

#######################################
# AWS CLI をチェックする
#######################################
check_awscli() {
  if ! which aws >/dev/null; then
    echo 'awscli not found'
    exit 2
  fi
}

#######################################
# ホスト名からインスタンス ID を取得する
# Arguments:
#   $1 HOST NAME
# Returns:
#   INSTANCE ID
#######################################
get_instance_id() {
  instance_id=$(/usr/bin/aws ec2 describe-instances --filter "Name=tag:Name,Values=$1" --query Reservations[].Instances[].InstanceId --output text)
  if [ -z "$instance_id" ]; then
    echo 'host not found'
    exit 2
  fi
  echo ${instance_id}
}

#######################################
# インスタンスが ALB に反映されるまで待機する
# https://docs.aws.amazon.com/cli/latest/reference/elbv2/describe-target-health.html
# Arguments:
#   $1 TARGET GROUP ARN
#   $2 INSTANCE ID
#   $3 STATE (healthy or unused)
#######################################
alb_waiter() {
  while :
  do
    # 状態(healthy or unused)を取得する
    state=$(/usr/bin/aws elbv2 describe-target-health --target-group-arn $1 --targets Id=$2 --query TargetHealthDescriptions[].TargetHealth.State --output text)
    if [ "$3" = "${state}" ]; then
      echo "  TargetGroupARN: $1 InstanceId: $2 State:$3"
      break
    else
      echo "  TargetGroupARN: $1 InstanceId: $2 State:$state"
    fi
    sleep ${SLEEP}
  done
}

#######################################
# ALB からインスタンスを外す
# https://docs.aws.amazon.com/cli/latest/reference/elbv2/deregister-targets.html
# Arguments:
#   $1 TARGET GROUP ARN
#   $2 INSTANCE ID
#######################################
alb_deregister() {
  /usr/bin/aws elbv2 deregister-targets --target-group-arn $1 --targets Id=$2 > /dev/null
}

#######################################
# ALB にインスタンスを付ける
# https://docs.aws.amazon.com/cli/latest/reference/elbv2/register-targets.html
# Arguments:
#   $1 TARGET GROUP ARN
#   $2 INSTANCE ID
#######################################
alb_register() {
  /usr/bin/aws elbv2 register-targets --target-group-arn $1 --targets Id=$2 > /dev/null
}

start.sh

汎用的に書くとこのようになる.

#!/bin/sh

# スクリプトをインポートする
. ./aws_utils.sh

# コマンドチェックと定数の設定
check_awscli
ALB_TARGET_GROUP_ARN ='arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222'

HOSTS=(web01 web02)

for host in ${HOSTS[@]}
do
  # インスタンス ID を取得する
  INSTANCE_ID=$(get_instance_id ${host})

  # unused になるまで待機
  echo " deregister : ${INSTANCE_ID}"
  alb_deregister ${ALB_TARGET_GROUP_ARN} ${INSTANCE_ID}
  alb_waiter ${ALB_TARGET_GROUP_ARN} ${INSTANCE_ID} 'unused'

  #
  # ホストごとにデプロイなどを実行する
  #

  # healthy になるまで待機
  echo " register : ${INSTANCE_ID}"
  alb_register ${ALB_TARGET_GROUP_ARN} ${INSTANCE_ID}
  alb_waiter ${ALB_TARGET_GROUP_ARN} ${INSTANCE_ID} 'healthy'
done

実行すると,以下のように waiter をして,期待通りに動く.

 deregister : i-xxxxxxxx
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:unused
 register : i-xxxxxxxx
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:initial
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:initial
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:initial
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:initial
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:healthy

 deregister : i-xxxxxxxx
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:draining
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:unused
 register : i-xxxxxxxx
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:initial
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:initial
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:initial
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:initial
  TargetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:111111111111:targetgroup/xxxxx/2222222222222222 InstanceId: i-xxxxxxxx State:healthy

まとめ

  • ELB (elb) と ALB (elbv2) では AWS CLI のサブコマンドが異なる
  • オプションも異なる
  • ALB ターゲットグループのドレインに注意する
  • aws elbv2 describe-target-health を使って waiter を実装すると正確に状態を把握しながら deregister / register ができる

CircleCI で ECS にデプロイをするサンプルプロジェクト circleci/go-ecs-ecr を試した

CircleCI を使って,ECS に Docker コンテナをデプロイする方法を調べていたら,CircleCI が公開しているサンプルプロジェクト circleci/go-ecs-ecr の存在を知ったので,さっそく試してみた.

circleci/go-ecs-ecr を動かすと,Golang の API が動く Docker イメージを作成して,ECR にプッシュして,ECS にデプロイする,という流れを簡単に試すことができる.僕自身が ECS に入門したばかりなので,勉強のキッカケとしてもお手軽でとても良かった.

github.com

ザックリと構成図を書くと,こんな感じになる.

f:id:kakku22:20170419014555j:plain

circleci/go-ecs-ecr を Fork する

まず最初に circleci/go-ecs-ecr を Fork する.このままだと circle.ymldeploy.sh に定義されたリージョンが全てバージニア (us-east-1) になっているため,東京 (ap-northeast-1) に変更する必要がある.例えば以下のように.

github.com

ECR にリポジトリを作成する

ECR に go-sample-webapp という名前のリポジトリを作成しておく.また aws ecr get-login コマンドで,ECR にログインできることを確認しておく.

$ $(aws ecr get-login --region ap-northeast-1)

ECS で仮のタスク定義を作成する

CircleCI で実行するデプロイで自動的に ECS のタスク定義を作成するため,ここでは ECS クラスタを起動するためだけに仮のタスク定義を作成する.名前は sample-webapp-task にしておく.

ECS クラスタと ECS サービスを作成する

次に ECS クラスタと ECS サービスを作成する.名前は以下とする.

  • クラスタ名 : sample-webapp-cluster
  • サービス名 : sample-webapp-service
  • タスク定義名 : sample-webapp-task-family(自動的にデプロイされる)
    • タスク定義名のことをファミリーと呼ぶ

今回は検証用途なので,コンテナインスタンスは t2.micro を1台にした.ELB / ALB も使わないため,パブリックサブネットに配置して,限られた IP アドレスから 80 ポートを許可するようにした.

また Desired Count も 1 にし,Minimum Healthy PercentMaximum Percent はデフォルトのままにした.日本語コンソールの「最小ヘルス率」と「最大率」という訳が微妙すぎるでしょ…!

CircleCI の設定をする

Fork した go-ecs-ecr リポジトリで CircleCI を動くように設定したら,まずは CircleCI 専用の IAM User を作成する.名前は circleci-ecs とした.ポリシーは以下をアタッチする.

  • AmazonEC2ContainerRegistryFullAccess
  • AmazonEC2ContainerServiceFullAccess

次に CircleCI の Environment Variables で,以下の環境変数を設定しておく.

  • AWS_ACCOUNT_ID
  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

ビルドを実行する

あとは CircleCI でビルドを実行すると ECS に Docker コンテナがデプロイできる.デプロイ後にコンテナインスタンスにアクセスすると,ちゃんとレスポンスが返ってきた.

$ curl http://xxx.xxx.xxx.xxx/
Hello World!%

仕組みを学ぶ

circle.yml

  • docker build を実行して $CIRCLE_SHA1 をタグとして Docker イメージを作成する
  • テスト時には docker run でコンテナを実行して,接続を確認する
  • master ブランチにプッシュされた場合は deploy.sh を実行する

deploy.sh

  • AWS CLI の設定をする
  • ECR に Docker イメージをプッシュする
  • タスク定義のリビジョンを更新して,最新の Docker イメージを反映する
  • サービスに紐付くタスク定義を更新して ECS にデプロイをする

詳しくは CircleCI のドキュメントにも載っているので,合わせて見ると良いと思う.

circleci.com

学んだこと

ECS は設定項目が多く,ポチポチとデフォルト設定のまま進めていたら,少し気になる点を発見した.

まず,クラスタを新規に作成するときに,ネットワーキングの部分で,デフォルトでは VPC を新規に作成する設定になっていた.何となく CloudFormation のスタック変更が長いなぁと思っていたら VPC まで作られていて,既存の VPC で動かすことが多いだろうし,ここは注意しておきたいところだなと思った.あと,自動的に作られるセキュリティグループは,デフォルトで 80 ポートを 0.0.0.0 で開放していた.ここも注意しておきたいと思った.

まとめ

ECS を学ぶのに最高な Black Belt 資料

非常によくまとまっていて素晴らしい資料だと思う.今のところ毎日1回は見ている気がする.また今月に資料の更新もされていて,情報の鮮度が保たれているという点も助かる!

www.slideshare.net