読者です 読者をやめる 読者になる 読者になる

Amazon EC2 Container Service(ECS)でPrivate Docker Registryを立ててみた

AWS ECS Docker CircleCI

初夏ですね(もうあちーよ)。

さて、今回はAmazon EC2 Container Service(以下、ECS)についてのネタです。

そもそもECSとは

aws.amazon.com

ECSはAmazon EC2を利用してDockerコンテナのクラスタを構築したり、スケジューリング機能をサポートしたサービスです。ECSはこれまでプレビュー版でUSリージョンにしかありませんでしたが、先月無事GAになって東京に襲来したので今やってるプロジェクトでProduction投入しようと画策しています。

ECSの概念

Task Definition(タスク定義)

ECSを始める際にまず最初にやるべきことはTask Definition(タスク定義)の作成です。タスクとは何ぞや?という感じがしますが、ここですることはコンテナの集合を定義することです(docker-compose.ymlを想像してもらえると良い)。コンテナ間のリンクやCPUやメモリのリソース割り当てや、ホストとコンテナ間のポートマッピング等を定義します。

Task(タスク)

Task Definitionを実行したもの

Service(サービス)

ServiceはTaskを常時実行させるためのスケジューラーとしての役割を持ちます。KubernetesのServiceと混同しそうですが、スケジューラーとして覚えておけばよいでしょう。

Cluster(クラスタ

ECSはEC2インスタンスをDockerのホストにしてクラスタを構成します。ClusterにはEC2インスタンス(コンテナインスタンス)がジョインし、ServiceがTaskの実行を制御します。

コンテナインスタンス

実態はEC2ですが、EC2エージェントという常駐プロセス(というかこれもDockerコンテナ)が稼働しているインスタンスです。エージェントが正しく稼働していなければClusterにジョインすることはできません。 基本的にはECS用のOptimized AMIが用意されているのでそれを利用し、Launch時にUserDataでClusterを指定する処理を入れておけばOKです。 

#!/bin/bash

echo ECS_CLUSTER=YOUR_CLUSTER_NAME >> /etc/ecs/ecs.config

Private Docker Registryを立てる

というわけでECSを実際に使ってみましょう。今回のお題は「ECSでPrivate Docker Registryを立てる」です。

本格的にDockerを利用してサービスを作るとなると、やはりクローズドなDocker Registryが必要です。この手のサービスはDockerHubQuay.ioが存在していて札束で殴れば目的は果たせるんですが、今回はMicroservices化して相当ヘビーにDockerを使っていく予定なので、運用は発生するけれどもPrivate Docker RegistryでEC2とS3構成にして転送速度に拘ります。

そしてRegistryはDockerイメージが用意されています。ECSがあるから乗っければいいじゃんってのがこのお題にした理由ですね。

registry.hub.docker.com

Registryだけでは味気ないので、DockerイメージをブラウジングできるWebアプリも稼働させましょう。こちらもdocker-registry-frontendというDockerイメージが用意されています。

registry.hub.docker.com

RegistryのECSにおける構成

さて、これらのイメージを使ってECS上にRegistryを構築します。構成図は以下の様な感じ。

f:id:a-yamada:20150514025444p:plain

  • docker-registry-serverとdocker-registry-frontendで構成
  • frontendはserverに対して通信する必要があるので、frontend -> serverに対してコンテナレベルでLinkさせる
  • コンテナからコンテナホストへのポートマッピングを設定。serverは5000->5000、frontendは80->8080(この辺は完全に気分なのでお好きに)
  • Clusterだけど、今回Clusterにジョインさせるインスタンスはt2.medium1つだけとする

ECSの操作

実際にECSを立てる作業ですが、クラスメソッドさんの素晴らしいエントリを参考にすると良いと思います。 dev.classmethod.jp

docker-registry-serverの設定

Imageにはregistry:latestを設定。

f:id:a-yamada:20150514032032p:plain

Environment Variables

YOUR〜なんとかの箇所はそれぞれの環境にあった値を設定します。

Key Value 備考
AWS_KEY YOUR_AWS_KEY AmazonS3FullAccess同等の権限を持つユーザーのAWS_KEY
AWS_SECRET YOUR_AWS_SECRET AmazonS3FullAccess同等の権限を持つユーザーのAWS_SECRET
SETTINGS_FLAVOR s3 イメージを保存に何を利用するか。ここではS3
AWS_BUCKET YOUR_AWS_BUCKET S3で利用するバケット名を指定
STORAGE_PATH s3 イメージが保存されるS3バケット上のパス
SEARCH_BACKEND sqlalchemy 検索バックエンド

docker-registry-frontendの設定

Imageにはkonradkleine/docker-registry-frontend:latest

f:id:a-yamada:20150514031227p:plain

Environment Variables
Key Value 備考
ENV_DOCKER_REGISTRY_HOST docker-registry-server serverのホスト名。linkしているのでdocker-registry-serverでOK
ENV_DOCKER_REGISTRY_PORT 5000 serverのポート番号

コンテナへのリソース配分

CPUはCPU Unitsという単位、メモリはMB単位で配分。CPU Unitsというのが聞きなれないですが、vCPU1つあたり1024割り当てられるようです。今回はt2.medium(vCPU=2)を1インスタンスでの構成なので、このClusterでは合計2048のCPU Unitsを利用可能ということになります。

ECSで稼働させる

Task Definitionを書いたらこれをService化して稼働させます。Task DefinitionはRevisionを持っているので、Revisionを選択してServiceを作成します。タスクの数はとりあえず1つにしておきましょう。以下のようになればOKです。

f:id:a-yamada:20150514132627p:plain

Private Docker Registryへpushする

とりあえずローカルからRegistryに対してpushしてみましょう。今回はboot2dockerを使います(docker-machineはまだ試してない)。

イメージをビルド

適当にDockerfileを用意して、ローカル(boot2dockerだけど)でビルドしてみます。ホスト名はYOUR_REGISTRY_HOSTとしてマスクしておきます(それぞれの環境に合わせてください)。

$ docker build -t test .
$ docker tag test YOUR_REGISTRY_HOST:5000/test

buildして、タグ付けするのが重要です。ではpushしてみましょう。

$ docker push YOUR_REGISTRY_HOST:5000/test
FATA[0004] Error: v1 ping attempt failed with error: Get https://YOUR_REGISTRY_HOST:5000/v1/_ping: EOF. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry YOUR_REGISTRY_HOST:5000` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/YOUR_REGISTRY_HOST:5000/ca.crt

何かエラーが出ました。どうやらHTTPSアクセスを試していて失敗しています。これはDockerがデフォルトでHTTPSアクセスをしてしまうという仕様に起因しています。これを回避するにはboot2docker上で、/var/lib/boot2docker/profileに以下の記述が必要です。

EXTRA_ARGS="--insecure-registry YOUR_REGISTRY_HOST:5000"

以下のように追記してみてください。追記したらboot2dockerの再起動が必要です。

$ boot2docker ssh
docker@boot2docker:~$ sudo su -
root@boot2docker:~# echo 'EXTRA_ARGS="--insecure-registry YOUR_REGISTRY_HOST:5000"' >> /var/lib/boot2docker/profile

再起動したらもう一度pushをトライします。

$ docker push YOUR_REGISTRY_HOST:5000/test
The push refers to a repository [YOUR_REGISTRY_HOST:5000/test] (len: 1)
Sending image list
Pushing repository YOUR_REGISTRY_HOST:5000/test (1 tags)
Image e9e06b06e14c already pushed, skipping
Image 07f8e8c5e660 already pushed, skipping
Image 4eceb72ab180 already pushed, skipping
Image a82efea989f9 already pushed, skipping
Image 37bea4ee0c81 already pushed, skipping
Image 3b82c49be997 already pushed, skipping
Pushing tag for rev [3b82c49be997] on {http://YOUR_REGISTRY_HOST:5000/v1/repositories/test/tags/latest}

うまくいったようなので、ブラウザで確認してみましょう。YOUR_REGISTRY_HOST:8080でアクセスするとみることができます。

f:id:a-yamada:20150514130122p:plain

CIとの連携

実際の開発においてはCIとの連携は不可欠です。というわけで今回はCircleCIからPrivate Docker Registryにpushしてみましょう。みんなだいすきCircleCI!

その前にCircleCIからのアクセスを許可する必要があります。それについては以下の記事が詳しいので参考にしてみてください(ちなみに弊社が利用しているCircleCIはenterpriseなので、固定IPもらってセキュリティグループで許可してます)。

CircleCIからCapistranoを利用してAWS(EC2)にデプロイする - Qiita

circle.yml

circle.ymlはこんな感じ。ビルドしてタグ打ってpushってのはboot2dockerのときと変わらんです。

machine:
  timezone: Asia/Tokyo
  pre:
    - sudo sh -c "echo 'DOCKER_OPTS=\"\$DOCKER_OPTS --insecure-registry YOUR_REGISTRY_HOST:5000\"' >> /etc/default/docker"
  services:
    - docker

dependencies:
  override:
    - docker -v
    - docker build -t hogehoge .
    - docker tag hogehoge YOUR_REGISTRY_HOST:5000/hogehoge
    - docker push YOUR_REGISTRY_HOST:5000/hogehoge

CircleCIでもHTTPSアクセスを無効にする必要があるので、machineフェーズのpreで/etc/default/dockerに--insecure-registryの設定を追記しています。

sudo sh -c "echo 'DOCKER_OPTS=\"\$DOCKER_OPTS --insecure-registry YOUR_REGISTRY_HOST:5000\"' >> /etc/default/docker"

ビルド通った。

f:id:a-yamada:20150514131632p:plain

Webで見るとちゃんとイメージがpushされていることがわかりますね。

f:id:a-yamada:20150514124101p:plain

まとめ

こんな感じでECSにPrivate Docker Registryが立てられました。冗長化はまだしてないんですけどECSはELBとも連携できますし、コンテナインスタンスやタスクを増やすことで冗長化できますね。落ち着いたらやろうと思います。

それにしてもECSが東京リージョンに来て、かつCircleCIはDocker対応してるのでこういった開発は親和性高いしなかなかライフチェンジングですね。AWS使った開発が新たなステージに入ってきた気がしてワクテカが止まりません。