PackerでDockerイメージを作成してQuay.ioにpushする


Packerを使うとDockerfileを使わずにChefやPuppetからDockerのイメージを作成することができる（詳しくは["Packerを使ってChef/Puppet/AnsibleでDockerのイメージをつくる"](http://deeeet.com/writing/2014/03/02/build-docker-image-by-packer/)に書いた）．ChefとかPuppetのような資産が既にある場合は，それを使うのも良い（インフラCIでベースDockerイメージだけChefで作ってしまってみたいなことも考えているが，まだ試し中）．

イメージができたら，次はそのイメージをどこかにホストする必要がある．また業務で利用するなら認証機構のちゃんとしたプライベートレジストリを使いたい．Dockerのイメージの保存先の選択肢は，今のところ以下の3つがある．

- [docker/docker-registry](https://github.com/docker/docker-registry)を運用する
- [DockerHub](https://registry.hub.docker.com/)を利用する
- [Quay.io](https://quay.io/)を利用する

まず、自分でdocker-registryを運用するのはしんどいのでやめた方が良い．次にDockerHubは，Dockerが提供しているRegistryで，プライベートレポジトリを作成することもできる．Quay.ioは[CoreOS](https://coreos.com/)が買収したDockerのプライベートRegistryサービス．

DockerHubとQuay.ioの違いを簡単に書くと，DockerHubは安く（プライベートレポジトリ1つは無料で使える），大抵の機能は使えるが，たまにpending地獄になってビルドが遅いとかが発生したりする．Quay.ioはまあまあ値段はする（DockerHubの倍くらい）が，Dockerhubと同等の機能がありながら，1つのレポジトリに複数のユーザを追加したり，GitのブランチでAutomated Buildを指定したり，[Squashイメージを提供したり](https://coreos.com/blog/CoreOS-enterprise-docker-registry/)，ダッシュボードでイメージの差分を確認したり，などなど面白い機能が多い．

Packerはイメージ作成後にそれをレジストにpushする機能を持っている．インターネットを見渡した限り，その方法を書いている記事は見当たらなかったので，簡単に書いておく．今回はquay.ioを例に使う．

## PackerとDocker

まず，簡単にPackerとDockerについて．PackerがDockerイメージの作成でやってくれるのは以下

- ベースとなるDockerイメージのpull（builder）
- Dockerイメージのprovision（provisioner）
- Docekrイメージのタグつけ，レジストリへのpush（post-processor）

["Packerを使ってChef/Puppet/AnsibleでDockerのイメージをつくる"](http://deeeet.com/writing/2014/03/02/build-docker-image-by-packer/)に書いたので今回は詳しく書かないが，Packerが面白いのは，provisionerで，ChefやAnsible，Puppetどれでも適用できること．いろんなマシンイメージを同じように扱えること．

今回は，builderやpost-processerでレジストリ（Quay.io）とやりとりするための設定方法について書く．なおQuay.io以外でも同じようにできる．


## machine.json

例えば，公式のDockerHubからベースとなるubuntuイメージを取得して（認証は必要ない），完成したイメージをQuay.ioにpushする（認証が必要）ための`machine.json`は以下のようになる．

```json:machine.json
{     
    "builders":[
        {
            "type": "docker",
            "image": "ubuntu:latest",
            "commit": true
        }
    ],

    "post-processors": [
        [
            {
                "type": "docker-tag",
                "repository": "quay.io/tcnksm/base",
                "tag": "latest"
            },

            {
                "type": "docker-push",
                "login": true,
                "login_server": "quay.io",
                "login_username": "YOUR_NAME",
                "login_password": "YOUR_PASSWORD",
                "login_email":    "YOUR_EMAIL"
            }
            
        ]
    ]
}

```

post-processorでprovisionの終わったDockerイメージをどうするかを定義する．`docker-tag`でレポジトリの名前をつけてcommitし、`docker-push`で実際にイメージをレジストリにpushする．

Quay.ioを使う場合は、レポジトリの名前は，`quoy.io/<username>/<reponame>`とする．push先の認証設定は`docker-push`において`login_server`, `registry_username`, `login_password`, `login_email`を全て書けばよい．

ベースのDockerイメージをプライベートレポジトリからpullしたいときは，同じ項目をbuildersに書けばよい．

## Packerの設定ファイルでの認証情報の扱い

設定ファイルに認証情報を直接書くべきではない．実行時に切り替えられるとよい．Packerでは以下の2つの方法が使える．

- 環境変数を使う
- 別ファイルに切り出す

### 環境変数を使う

環境変数を使うにはmachine.jsonを以下のように書き換える．

```json:machine.json
{
    "variables":{
        "registry_username": "{{ env `REGISTRY_USERNAME`}}",
        "registry_password": "{{ env `REGISTRY_PASSWORD`}}",
        "registry_email":    "{{ env `REGISTRY_EMAIL`}}"        
    },
    
    "builders":[
        {
            "type": "docker",
            "image": "ubuntu:12.04",
            "commit": true
        }
    ],

    "post-processors": [
        [
            {
                "type": "docker-tag",
                "repository": "quay.io/tcnksm/base",
                "tag": "latest"
            },

            {
                "type": "docker-push",
                "login": true,
                "login_server": "quay.io",
                "login_username": "{{user `registry_username`}}",
                "login_password": "{{user `registry_password`}}",
                "login_email":    "{{user `registry_email`}}"
            }
            
        ]
    ]
}

```

まず，variablesという項目を追加し，そこに参照したい変数と初期値を定義する．ここに定義した値は他の設定項目からテンプレートとして`"{{user VALUE}}"`という形で参照できるようになる．そしてvariablesに与える値を`"{{env ENV_VALUE}}"`とすれば，環境変数でその値を設定できるようになる．

実行は以下のようにすればよい．

```bash
$ REGISTRY_USERNAME=<YOUR_NAME> \
  REGISTRY_PASSWORD=<YOUR_PASS> \
  REGISTRY_EMAIL=<YOUR_EMAIL>   \
  packer build machine.json
```

`"{{env ENV_VALUE}}"`を書けるのがvariablesセクションに限定されているのはとても良い．値がどうやって設定されるか混乱しないし，入力ソースも一箇所なので追跡もしやすい．

### 別ファイルに切り出す

実行時に毎回環境変数を設定するのは面倒くさい．認証情報をファイルに切り出して（もちろんレポジトリにはpushしない），それを使う方法もある．以下のように`-var-file`というオプションを使う．

```bash
$ cat variables.json
{
    "registry_username": "YOUR_NAME",
    "registry_password": "YOUR_PASS",
    "registry_email": "YOUR_EMAIL"
}
```

```bash
$ packer build -var-file=variables.json machines.json
```

## まとめ

Packerを使ってDockerのイメージを作成し、それをプライベートレポジトリにpushする方法について書いた．また、Packerで認証情報を安全に取り扱う方法についても書いた．