この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
藤本です。
※ 本ブログ執筆時点ではAnsible 2.0はrc3です。GAリリース時にこのブログの通りにいかないことがあるかもしれませんのでご注意ください。
概要
Ansible 2.0からDockerのConnection Pluginが追加されました。Ansible 1.x系では標準モジュールだけでDockerコンテナをリモートから構成管理したい場合、コンテナ内でsshdを起動して、SSHポートをDockerホストと紐付けたりする必要があり、エージェントレス(ホストの構成を変えずに)が特徴のAnsibleとしては微妙な感じでした。Ansible 2.0で追加されたDockerのConnection Pluginを利用すれば、Docker Remote APIを利用したコンテナのプロビジョニングが可能となりました。Ansible実行環境からDockerホストへRemote APIを実行できる環境であれば、Dockerコンテナでsshdを起動しておく必要がありません。
今回はOSX上のVirtualBox上のDockerホスト(Docker Toolbox)に対して、ansible-playbookでWEBサーバをプロビジョンすることを目標します。
Docker Connection Plugin
Docker Connection Pluginはソースコードをザッと目を通した限り、ファイル転送にdocker cp
、コマンド実行をdocker exec
で実行しているようです。インベントリファイルではDockerホストを指定するのではなく、コンテナ名を指定します。そのためデフォルトではローカルホスト上のDockerコンテナに対して、処理を実行します。リモートホスト上のDockerコンテナに対して処理を実行したい場合、環境変数のDOCKER_HOST
でリモートホストを指定する必要があります。
環境
- Ansible実行環境
- OS : OSX
- Python : 2.7.10
- Ansible : v2.0.0-0.8.rc3
- Dockerホスト
- IPアドレス : 192.168.99.100
- on VirtualBox on OSX(Docker Toolboxにより構築)
やってみた
ansibleインストール
今回は2.0系を利用したいのでpip install ansible
ではなく、githubから最新版のリリースをインストールします。
$ pip install git+https://github.com/ansible/ansible.git
Collecting git+https://github.com/ansible/ansible.git
Cloning https://github.com/ansible/ansible.git to /var/folders/vp/2b51_dy91r3b5bbjsv1lbj4c0000gq/T/pip-nNfLOk-build
Collecting paramiko (from ansible==2.1.0)
Using cached paramiko-1.16.0-py2.py3-none-any.whl
Collecting jinja2 (from ansible==2.1.0)
Using cached Jinja2-2.8-py2.py3-none-any.whl
Collecting PyYAML (from ansible==2.1.0)
Requirement already satisfied (use --upgrade to upgrade): setuptools in /Users/fujimoto.shinji/.pyenv/versions/ansible/lib/python2.7/site-packages (from ansible==2.1.0)
Collecting pycrypto>=2.6 (from ansible==2.1.0)
Collecting ecdsa>=0.11 (from paramiko->ansible==2.1.0)
Using cached ecdsa-0.13-py2.py3-none-any.whl
Collecting MarkupSafe (from jinja2->ansible==2.1.0)
Installing collected packages: ecdsa, pycrypto, paramiko, MarkupSafe, jinja2, PyYAML, ansible
Running setup.py install for ansible
Successfully installed MarkupSafe-0.23 PyYAML-3.11 ansible-2.1.0 ecdsa-0.13 jinja2-2.8 paramiko-1.16.0 pycrypto-2.6.1
$ pip list
ansible (2.1.0)
ecdsa (0.13)
Jinja2 (2.8)
MarkupSafe (0.23)
paramiko (1.16.0)
pip (7.1.0)
pycrypto (2.6.1)
PyYAML (3.11)
setuptools (18.0.1)
wheel (0.24.0)
ansibleがインストールされました。(なぜか2.1.0と表示される。。。)
環境変数設定
Docker Remote APIを外部ホストに実行したい場合、dockerコマンドのオプションで指定可能ですが、Docker Connection Pluginではオプション指定することができません。その代わりに環境変数に設定することで指定が可能です。Dockerホスト、TLSの有効化、およびSSH鍵関連パスを環境変数に設定します。
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/fujimoto.shinji/.docker/machine/machines/docker01"
ちなみに以下のコマンドでも同様の設定が可能です。
$ eval "$(docker-machine env docker01)"
Remote APIの接続確認を実施します。
$ docker info
Containers: 1
Images: 5
Server Version: 1.9.1
Storage Driver: aufs
Root Dir: /mnt/sda1/var/lib/docker/aufs
Backing Filesystem: extfs
Dirs: 7
Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 4.1.13-boot2docker
Operating System: Boot2Docker 1.9.1 (TCL 6.4.1); master : cef800b - Fri Nov 20 19:33:59 UTC 2015
CPUs: 1
Total Memory: 1.956 GiB
Name: default
ID: 6E4I:7UKE:4RFA:HPFR:NJSN:ASG4:IRZI:J7DY:JMDZ:52XW:CCZH:UFJ2
Debug mode (server): true
File Descriptors: 12
Goroutines: 18
System Time: 2015-12-24T06:45:56.410777786Z
EventsListeners: 0
Init SHA1:
Init Path: /usr/local/bin/docker
Docker Root Dir: /mnt/sda1/var/lib/docker
Labels:
provider=virtualbox
情報取れました。接続できています。
インベントリファイルの作成
インベントリファイルにはDockerコンテナ作成用にDockerホスト、Dockerコンテナのプロビジョニング用にコンテナID、もしくはコンテナ名を指定します。今回はコンテナ名で指定します。
$ vi hosts
---
[docker_host]
docker01
[container]
web01
---
$ vi ssh_config
---
Host docker01
HostName 192.168.99.100
User docker
UserKnownHostsFile /dev/null
IdentityFile ~/.docker/machine/machines/docker01/id_rsa
StrictHostKeyChecking no
---
$ vi ansible.cfg
---
[defaults]
inventory = hosts
[ssh_connection]
ssh_args = -F ssh_config
scp_if_ssh = True
---
Dockerホストへ接続確認します。
$ ansible -i hosts docker01 -m ping
docker01 | FAILED! => {
"changed": false,
"failed": true,
"module_stderr": "",
"module_stdout": "sh: /usr/bin/python: not found\r\n",
"msg": "MODULE FAILURE",
"parsed": false
}
boot2dockerのOSイメージはpythonがインストールされていない!!というかこのOSは何者?
・・・調べたら、Tiny Core LinuxというOSらしいです。エクステンションと呼ばれるパッケージをtce-loadコマンドでインストール出来るようです。
$ ssh -F ssh_config docker01
Warning: Permanently added '192.168.99.100' (RSA) to the list of known hosts.
## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\_______/
_ _ ____ _ _
| |__ ___ ___ | |_|___ \ __| | ___ ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__| < __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
Boot2Docker version 1.9.1, build master : cef800b - Fri Nov 20 19:33:59 UTC 2015
Docker version 1.9.1, build a34a1d5
docker@docker01:~$ tce-load -wi python
python.tcz.dep OK
tk.tcz.dep OK
readline.tcz.dep OK
Downloading: libffi.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
libffi.tcz 100% |**************************************************************************************************************************************| 16384 0:00:00 ETA
libffi.tcz: OK
(略)
Downloading: python.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
python.tcz 100% |*******************************************************************************************************************************************************************************************************************************| 7980k 0:00:00 ETA
python.tcz: OK
docker@docker01:~$ python --version
Python 2.7.10
docker@docker01:~$ curl https://bootstrap.pypa.io/get-pip.py | sudo python -
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1379k 100 1379k 0 0 1831k 0 --:--:-- --:--:-- --:--:-- 1829k
Collecting pip
Downloading pip-7.1.2-py2.py3-none-any.whl (1.1MB)
100% |################################| 1.1MB 401kB/s
Collecting setuptools
Downloading setuptools-19.1.1-py2.py3-none-any.whl (463kB)
100% |################################| 466kB 774kB/s
Collecting wheel
Downloading wheel-0.26.0-py2.py3-none-any.whl (63kB)
100% |################################| 65kB 4.9MB/s
Installing collected packages: pip, setuptools, wheel
Successfully installed pip-7.1.2 setuptools-19.1.1 wheel-0.26.0
docker@docker01:~$ sudo pip install docker-py
Collecting docker-py
Downloading docker-py-1.6.0.tar.gz (63kB)
100% |################################| 65kB 2.2MB/s
Collecting requests>=2.5.2 (from docker-py)
Downloading requests-2.9.1-py2.py3-none-any.whl (501kB)
100% |################################| 503kB 748kB/s
Collecting six>=1.4.0 (from docker-py)
Downloading six-1.10.0-py2.py3-none-any.whl
Collecting websocket-client>=0.32.0 (from docker-py)
Downloading websocket_client-0.34.0.tar.gz (193kB)
100% |################################| 196kB 1.8MB/s
Building wheels for collected packages: docker-py, websocket-client
Running setup.py bdist_wheel for docker-py
Stored in directory: /root/.cache/pip/wheels/e6/d9/a1/fe57c3e479387975813b221e7f01dcaa0f73e9149744472c71
Running setup.py bdist_wheel for websocket-client
Stored in directory: /root/.cache/pip/wheels/c2/af/03/d3899019a2100d8b39625e578e8680f444096278d16b7dc4a4
Successfully built docker-py websocket-client
Installing collected packages: requests, six, websocket-client, docker-py
Successfully installed docker-py-1.6.0 requests-2.9.1 six-1.10.0 websocket-client-0.34.0
docker@docker01:~$ sudo ln -s /usr/local/bin/python /usr/bin/python
Pythonがインストールされました。ついでにpip、docker-pyをインストールして、pythonコマンドのパスにシンボリックリンク張りました。
もう一度、確認します。
$ ansible -i hosts docker01 -m ping
docker01 | SUCCESS => {
"changed": false,
"ping": "pong"
}
接続できました。ちなみにこの接続はDockerホストへの接続なので、まだ今回の主役のDocker Connection Pluginは関係ありません。SSH Connection Pluginによる接続です。
Playbook作成
Dockerコンテナの作成とプロビジョニングを一つのPlaybookにまとめて記述します。
まずはDockerコンテナの作成部分です。コンテナのプロビジョニングはAnsibleに任せたいのでただ起動させるだけです。
$ vi site.yml
---
- hosts: docker_hosts
become: yes
remote_user: docker
tasks:
- name: deploy centos container
docker: image=centos:centos6 name=web01 ports=80:80 expose=80 tty=yes
実行してみます。
$ ansible-playbook site.yml
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [docker01]
TASK [deploy centos container] *************************************************
changed: [docker01]
PLAY RECAP *********************************************************************
docker01 : ok=2 changed=1 unreachable=0 failed=0
コンテナが起動していることを確認してみます。
docker@docker01:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6636521e80f0 centos:centos6 "/bin/bash" 3 minutes ago Up 3 minutes 0.0.0.0:80->80/tcp web01
起動していますね。
ここからが主題です。
先ほどのPlaybookにコンテナへの設定内容を追記します。Docker Connection Pluginを利用する設定としてはconnection: docker
を指定するだけです。コンテナの設定内容はシンプルでhttpd、filebeatをインストールして、index.html、filebeatの設定ファイルを置き換えて、サービスを起動するだけです。
$ vi site.yml
---
- hosts: containers
connection: docker # ここだけ
tasks:
- name: install packages
yum: name={{item}} state=installed
with_items:
- httpd
- "https://download.elastic.co/beats/filebeat/filebeat-1.0.1-x86_64.rpm"
- name: copy filebeat.yml
copy: src=filebeat.yml dest=/etc/filebeat/filebeat.yml backup=yes
- name: start services
service: name={{item}} enabled=yes state=started
with_items:
- httpd
- filebeat
それではPlaybookを実行してみましょう。
$ ansible-playbook site.yml
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [docker01]
TASK [deploy centos container] *************************************************
ok: [docker01]
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [web01]
TASK [install packages] ********************************************************
changed: [web01] => (item=[u'httpd', u'https://download.elastic.co/beats/filebeat/filebeat-1.0.1-x86_64.rpm'])
TASK [copy index.html] *********************************************************
changed: [web01]
TASK [copy filebeat.yml] *******************************************************
changed: [web01]
TASK [start services] **********************************************************
changed: [web01] => (item=httpd)
changed: [web01] => (item=filebeat)
PLAY RECAP *********************************************************************
docker01 : ok=2 changed=0 unreachable=0 failed=0
web01 : ok=4 changed=4 unreachable=0 failed=0
Webアクセスしてみます。
$ cat index.html
index page on docker container
$ curl 192.168.99.100
index page on docker container
コピーしたindex.htmlの内容が返ってきました。
まとめ
いかがでしたでしょうか。
今回の主題ではないところで躓いてしまって、伝えたい内容がブレブレになった気もしますが、、、今まではDockerコンテナの構築はDockerfile、ツールを利用したい人はPackerといったところが主流だったでしょうか。ただプロビジョニングツールとしてはAnsibleを利用している方も多いと思います。Docker Connection Pluginの導入により、DockerコンテナのプロビジョニングにAnsibleを採用するケールも増えるのではないでしょうか。