[Docker-02] Dockerのインストールをansibleでやる


前回Dockerのインストールを手動でやりましたが、何度もやるのは面倒なので空っぽのdockerホストを動かすところまではansibleでサクッとできるようにしました。

# とりあえずできあがったもの

最終構成はこんな感じになりました。

```
.
├── Vagrantfile
└── ansible/
    ├── docker.yml                    ... roles/docker関連を読み込む
    ├── group_vars/
    │   └── dockers                   ... 変数の定義
    ├── hosts.vagrant                 ... グループとホストの定義
    ├── roles/
    │   └── docker/
    │       ├── handlers/
    │       │   └── main.yml          ... dockerインストール時のイベントハンドラの定義
    │       ├── tasks/
    │       │   └── main.yml          ... dockerのインストール手順
    │       └── templates/
    │           └── docker.list.j2    ... /etc/apt/sources.list.d/docker.listのテンプレート
    └── site.yml                      ... サイト全体の定義（今回はdocker.ymlを読み込む）
```

本当は１ファイルでも書けるのですが、分離しておくといろいろ使い回せるので分離するようにしています。
分離したせいでややこしいことになっていますので簡単に説明すると、site.ymlが一番親になっており、あとは以下の順番で参照するようになっています。

```
site.yml → docker.yml → roles/docker/tasks/main.yml
```

今回、タスクがdockerしかないので、ほとんどの手順は``roles/docker/tasks/main.yml``内に記述されることになります。

# 内容

## dockerロール
### メインの手順 (ansible/roles/docker/tasks/main.yml)

もうどこぞにあるとは思いますが、自分で作ってみました。上から、以下を行うようになっています。
1. カーネルが3.10未満なら失敗させる
2. dockerのgpgキーが登録されているか調べる
3. dockerのgpgキーが未登録の場合は登録する
4. テンプレートから/etc/apt/sources.list.d/docker.listを作る
5. apt-get updateとapt-get upgradeをする
6. lxc-dockerを削除する
7. linux-image-extraをインストールする
8. docker-engineをインストールする（インストールしたらrestart dockerイベントを発行する）
9. dockerサービスを起動時に開始するように設定する
10. group_vars/dockersに定義したユーザをdockerグループに所属させる

```ansible/roles/docker/tasks/main.yml
---
- name: Check kernel version.
  fail: msg="Kernel {{ ansible_kernel }} is not supported."
  when: "ansible_kernel | version_compare('3.10', '<')"

- name: Check docker gpg key.
  shell: apt-key list | grep 2C52609D
  register: docker_gpg
  ignore_errors: True
  changed_when: False

- name: Add docker gpg key.
  shell: apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
  when: docker_gpg | failed

- name: Copy docker.list file.
  template: src=docker.list.j2 dest=/etc/apt/sources.list.d/docker.list owner=root group=root mode=0644

- name: Upgrade packages.
  apt: upgrade=yes update_cache=yes

- name: Remove lxc-docker.
  apt: name=lxc-docker state=absent

- name: Install linux-image-extra.
  apt: name=linux-image-extra-{{ ansible_kernel }} state=present

- name: Install docker-engine.
  apt: name=docker-engine state=present
  notify:
    - restart docker

- name: Set docker service to start on boot.
  service: name=docker enabled=yes

- name: Append docker group to user.
  user: name={{ docker.user }} groups=docker append=yes
```


### ハンドラ (ansible/roles/docker/handlers/main.yml)

``restart docker``が発生したら、サービスを再起動します。

```ansible/roles/docker/handlers/main.yml
---
- name: restart docker
  service: name=docker state=restarted
```

### テンプレート (ansible/roles/docker/templates/docker.list.j2)

aptのsource listのテンプレートです。``{{ ansible_distribution_release }}``の部分が、ansibleの機能で``vivid``に置き換わってインストール対象ホスト側に設置されます。

```ansible/roles/docker/templates/docker.list.j2
deb https://apt.dockerproject.org/repo ubuntu-{{ ansible_distribution_release }} main
```

## docker.yml（ansible/docker.yml）

* ``hosts: dockers``は、``dockers``グループを対象とするようにしています（後述の``hosts.vagrant``に関係します）。
* ``become: yes``はタスク実行時に``sudo``するようになります。
* ``roles``に、``docker``と定義することで、``ansible/roles/docker``以下を読み込むようになっています。

```ansible/docker.yml
---
- hosts: dockers
  become: yes
  roles:
    - docker
```

## サイト全体の定義（ansible/site.yml）

docker.ymlを読み込んでいます。

```ansible/site.yml
---
- include: docker.yml
```

## グループとホストの定義 (ansible/hosts.vagrant)

``hosts.vagrant``に``dockers``グループとそれに所属する``192.168.100.10``というホストを定義しています。この``dockers``グループは``ansible/docker.yml``の``hosts``の定義と関係しています。
ホストのIPアドレスは後述の``Vagrantfile``と関係しています。

```
[dockers]
192.168.100.10
```

## グループ変数
### dockersグループ (ansible/group_vars/dockers)

dockerを使うユーザ名を``vagrant``としています。

```
---
docker:
  user: vagrant
```

## Vagrantfile

docker1というノードを定義しています。IPアドレスは192.168.100.10としています。
*IPアドレスを変更する場合は、上記``ansible/hosts.vagrant``も合わせて変更してください。*

provisionで``:ansible``を指定し、playbookに``ansible/site.yml``、inventory_pathに``ansible/hosts.vagrant``を指定しています。これで``vagrant up``すると自動的にansibleが実行されるようになります。

```Vagrantfile
Vagrant.configure(2) do |config|
  config.vm.define :docker1 do |node|
    node.vm.box = 'ubuntu/vivid64'
    node.vm.network 'private_network', ip: '192.168.100.10'

    node.vm.provision :ansible do |ansible|
      ansible.playbook = 'ansible/site.yml'
      ansible.inventory_path = 'ansible/hosts.vagrant'
      ansible.limit = 'all'
    end
  end
end
```

# 実行

```sh
% vagrant up
Bringing machine 'docker1' up with 'virtualbox' provider...
==> docker1: Importing base box 'ubuntu/vivid64'...
==> docker1: Matching MAC address for NAT networking...
==> docker1: Checking if box 'ubuntu/vivid64' is up to date...
==> docker1: Setting the name of the VM: 02_install_with_ansible_docker1_1452545787619_21350
==> docker1: Clearing any previously set forwarded ports...
==> docker1: Clearing any previously set network interfaces...
==> docker1: Preparing network interfaces based on configuration...
    docker1: Adapter 1: nat
    docker1: Adapter 2: hostonly
==> docker1: Forwarding ports...
    docker1: 22 => 2222 (adapter 1)
==> docker1: Booting VM...
==> docker1: Waiting for machine to boot. This may take a few minutes...
    docker1: SSH address: 127.0.0.1:2222
    docker1: SSH username: vagrant
    docker1: SSH auth method: private key
    docker1: Warning: Connection timeout. Retrying...
    docker1:

（中略）

==> docker1: Running provisioner: ansible...

PLAY [dockers] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [192.168.100.10]

TASK: [docker | Check kernel version.] ****************************************
skipping: [192.168.100.10]

TASK: [docker | Check docker gpg key.] ****************************************
failed: [192.168.100.10] => {"changed": false, "cmd": "apt-key list | grep 2C52609D", "delta": "0:00:00.039824", "end": "2016-01-11 20:56:54.176799", "rc": 1, "start": "2016-01-11 20:56:54.136975", "stdout_lines": [], "warnings": []}
...ignoring

TASK: [docker | Add docker gpg key.] ******************************************
changed: [192.168.100.10]

TASK: [docker | Copy docker.list file.] ***************************************
changed: [192.168.100.10]

TASK: [docker | Upgrade packages.] ********************************************
ok: [192.168.100.10]

TASK: [docker | Remove lxc-docker.] *******************************************
ok: [192.168.100.10]

TASK: [docker | Install linux-image-extra.] ***********************************
changed: [192.168.100.10]

TASK: [docker | Install docker-engine.] ***************************************
changed: [192.168.100.10]

TASK: [docker | Set docker service to start on boot.] *************************
ok: [192.168.100.10]

TASK: [docker | Append docker group to user.] *********************************
changed: [192.168.100.10]

NOTIFIED: [docker | restart docker] *******************************************
changed: [192.168.100.10]

PLAY RECAP ********************************************************************
192.168.100.10             : ok=11   changed=6    unreachable=0    failed=0

```