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

えいのうにっき

主に Web 系技術ネタ。背景画像 is powered by grass-graph.shitemil.works

Packer でのコンテナイメージのビルド後に Amazon ECR にプッシュされるようにする

前回の Dockerfile の代わりに Packer を使って Docker に再々入門してみている - えいのうにっき の続き。今後いくつかのコンテナイメージを作っていくにあたって「ヒミツにしておきたい情報を含んだコンテナ」を作ることもありそうなので、そんなイメージの登録はプライベートなレジストリにしたい・それなら Amazon ECR はどうだろう。というのが経緯。

Amazon ECR(EC2 Container Registry)を試す

まずは料金周りの確認。以下は東京リージョンのもの(2017-03-20 現在)。

  • ストレージ
    • 0.10 USD/GB/月(一律)
    • 無料利用枠として、月 500 MB 分のストレージが1年間は無料。
  • データ転送
    • データ受信(イン)
      • すべてのデータ受信:$0.000 / GB
    • データ送信(アウト)
      • 最初の 1 GB/月:$0.000 / GB
      • 10 TB まで/月:$0.140 / GB
    • 無料利用枠の一環として、1ヶ月につき 15 GB のデータ送信が1 年間無料。
      • ただしこれは、すべての AWS の各種サービスを総合して15GB。
  • 参考:料金 - Amazon EC2 Container Registry | AWS

こんなかんじ。個人利用目的であれば、お小遣いの範囲内で十分利用できそう。ってことで、早速利用を開始してみる。

基本的にはこちらのページ AWS ECS編~EC2 Container Registry を試してみる~ | ナレコムAWSレシピ に沿って作業を実施した。ってか、「ECR」っていう独立した AWS サービスがあるのかと思ってたけど、ECS(Amazon EC2 Container Service)のうちの一機能、って感じだったんだね。誤解してた。

AWSコンソール上での ECR リポジトリの作成自体は一瞬で完了。あとはここにイメージを push してみたいわけだけど、ECR への Docker イメージの push には AWS CLI が必要で、僕はまだインストールしていなかったので、まずは以下のコマンドで AWS CLI をインストール

$ curl -kL https://bootstrap.pypa.io/get-pip.py | sudo python
$ sudo pip install awscli --upgrade --ignore-installed six
(中略)
$ aws --version
aws-cli/1.11.63 Python/2.7.10 Darwin/15.6.0 botocore/1.5.26

ただ pip install awscli とやっただけだと Operation not permitted: '/tmp/pip-saoiMq-uninstall/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/six-1.4.1-py2.7.egg-info' エラーが出たので、OS X EI Caption(10.11.1)でAWS CLIのインストールエラー - Qiita を参考にこれを回避。

その後、aws configure コマンドで認証情報をセット。そして下記コマンドを実行してみると、

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

docker login -u AWS -p ... という出力がどばっと出る。これを実行しろということのようなので実行してみると Login succeeded と出る。これで手元の docker コマンドが ECR リポジトリにもアクセスできるようになった、というイメージかな。よさそう。

続いて、前回のエントリで作成したイメージにタグを付けてみる。

$ docker tag a-know/a-know-server-base-container:0.1.0 1234567890.dkr.ecr.ap-northeast-1.amazonaws.com/a-know-server-container:0.1.0
$ docker images
REPOSITORY                                                                  TAG                 IMAGE ID            CREATED             SIZE
1234567890.dkr.ecr.ap-northeast-1.amazonaws.com/a-know-server-container     0.1.0               9c7a8703ae10        3 days ago          754 MB
a-know/a-know-server-base-container                                         0.1.0               9c7a8703ae10        3 days ago          754 MB
centos                                                                      centos7             67591570dd29        3 months ago        192 MB

なるほど。そのままこれを push してみる。

$ docker push 1234567890.dkr.ecr.ap-northeast-1.amazonaws.com/a-know-server-container:0.1.0
The push refers to a repository [1234567890.dkr.ecr.ap-northeast-1.amazonaws.com/a-know-server-container]
a85bce3bb1dd: Pushed 
0.1.0: digest: sha256:7c97bb06c8324fd9a0ab4df839b3db8a90fd12c0d9b2c49b87796ee53d24c30d size: 529

754MBもあるのでそこそこ時間がかかってしまった。。AWS コンソールの方も確認してみる。

f:id:a-know:20170319110535p:plain

okok。

…あれ、サイズが小さいぞ…? と思ったら、 イメージの表示サイズは、圧縮されている可能性があります とのこと。

Packer と ECR を連携させる

連携させる、というほど大げさなものではないんだけど、

  1. ECR に上げたイメージをベースに別のイメージのビルドを実行する
  2. ビルドの結果できたイメージをそのまま ECR に上げる

といったことをしてみる。毎回手作業で ECR に push するのもアレだしね。

1.については単純で、 buildersimage の指定を先ほど push した "image": "1234567890.dkr.ecr.ap-northeast-1.amazonaws.com/a-know-server-container:0.1.0" にすればいい。docker が ECR のプライベートリポジトリからイメージを pull するためには、上述の docker login が行われてある必要がある。さらにこれは一定時間経つと失効してしまうので、その点は頭の片隅に置いておく必要がある(失効してしまっていたら aws ecr get-login --region ap-northeast-1 をもう一度実行し docker login コマンドを再度取得・ログインしなおす)。

2.は、Packer の post-processorstype に指定できる docker-pushAmazon ECR にも対応しているということなので、こちらの記事 Packer + Ansible + ECRを使ったDcokerコンテナイメージ作成の自動化 | Developers.IO を多いに参考にさせていただきつつ、公式ページや Packer の GitHub issue なども確認しながら、結果的に以下のような Packer json を作成することで Packer と ECR との連携を行なうことができた。

{
  "variables": {
    "ecr_repo": "{{env `APP_CONTAINER_ECR_REPO`}}",
    "ecr_url": "{{env `ECR_URL`}}",
    "aws_access_key": "{{env `AWS_ACCESS_KEY`}}",
    "aws_secret_key": "{{env `AWS_SECRET_KEY`}}",
    "tag": "0.0.1"
  },
  "builders":[
    {
      "type": "docker",
      "image": "1234567890.dkr.ecr.ap-northeast-1.amazonaws.com/a-know-server-container:0.1.0",
      "export_path": "image.tar"
    }
  ],
  "provisioners":[
    {
      "type": "shell",
      "inline": [
          "mkdir -p /var/www/a-know-home/current",
          "git clone https://github.com/a-know/a-know-home-rails.git /var/www/a-know-home/current"
      ]
    }
  ],
  "post-processors": [
    [
      {
        "type": "docker-import",
        "repository": "a-know/a-know-server-container-app",
        "tag": "{{user `tag`}}"
      },
      {
        "type": "docker-tag",
        "repository": "{{user `ecr_repo`}}",
        "tag": "{{user `tag`}}"
      },
      {
        "type": "docker-push",
        "ecr_login": true,
        "aws_access_key": "{{user `aws_access_key`}}",
        "aws_secret_key": "{{user `aws_secret_key`}}",
        "login_server": "{{user `ecr_url`}}"
      }
    ]
  ]
}

この json の内容について、自分がハマってしまったところも含め、細かい注意事項を以下にまとめておく。

  • variables
    • この json 内で使いまわしたい値などを変数として定義することができる。便利!(利用するときは {{user}} を使う)
    • {{env}} により環境変数から値を取得できる。env を使用できるのはこの variables の中でだけ。
  • post-processors
    • docker-tag により ECR のためのタグを付け、docker-push で ECR にアップロードする。
    • docker-push による ECR へのアップロード周りに Packer 側でのアップデートが入っているので、古い記述の仕方だとエラーになる。
    • post-processors に複数ステップを指定する場合は Array<Array<Hash>> みたいな感じになるっぽいので、これも注意が必要。

この内容の jsonpacker build を実行すると、

  • ECR に上がっている自分用のベースイメージを pull して、
  • ディレクトリを掘り、アプリケーションコードを git clone し、
  • タグを付け、
  • ECR に push する

…といったことが行われる。

f:id:a-know:20170319110718p:plain

ok!

まとめ

意図せずして二部構成になってしまったけど、今回いろいろと試してみたことによって、Docker に関するツールの僕の中での選択肢を

  • Docker Hub か Amazon ECR か
  • Dockerfile か Packer か
    • Dockerfile で書くのがつらい場合・ベースイメージを作り上げる過程では Packer で、
    • Packer で作ったベースを元にちょっと手を加える用途では Dockerfile で十分できそう
    • …という感じかなというのが今のところの感触

というかんじで広げられたので、よかった。

あとこれはまぁ実際にここまでやってみて思ったことなんだけど、今回自分で作ったベースのイメージも単に Ruby が入っていればよかっただけなので、そういうイメージを探してそれをベースとして使えば Packer を使うまでもなかったのかもしれないなぁ、と。

今後は

  • docker だけインストールした EC2 インスタンスを用意する
    • docker-machine を使って用意するかどうかは考え中
  • fluentd・nginx・mackerel-agent などのコンテナ(多分これらは自分で用意するのではなく公式のものを使う感じになるんだと思う)を docker-compose で利用し、そのインスタンス上で実行することで全体としてちゃんと動作するかどうかを確認する

といったところまでやってみたいなーと思う!

参考



follow us in feedly