前回に引き続き、dockerコンテナの設定を行います。
今回は各種設定をansibleで行ってみます。
コンテナの設定ということであればDockerfileで行うべきところですが、 物理サーバや各種Virtual Machineにも流用することを考えると、 ansibleやchefなどの利用は有効だと思います。
事前準備
最初に前回作成したコンテナを起動して、IPアドレスを控えておきます。
$ docker start srv1 srv1 $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0211f0f72f4b croissant/ubuntu-base:latest "/usr/sbin/sshd -D" 5 days ago Up 4 seconds 22/tcp srv1 $ docker inspect srv1 | grep IPAddress "IPAddress": "172.17.0.1",
ansibleはpython製ツールですが、実際にはYAMLを編集するだけなので、chefと比べてかなりシンプルに設定できるようです。
今回は開発用環境の構築が主眼になっていますので、 必要なものをまとめてインストールすることにします。
具体的には以下のことを自動化します。
- mysqlのインストール (aptitude)
- rbenv (事前に必要なパッケージをインストールして、git clone等)
- emacs (事前に必要なパッケージをインストールして、ソースをビルド)
ansibleのインストール
ansibleのインストールはaptitudeで行います。
インストールの確認も兼ねて、疎通確認のコマンドを実行してみます。
$ sudo aptitude install ansible ... $ echo 172.17.0.1 > host $ ansible -i host -u docker 172.17.0.1 -m ping 172.17.0.1 | success >> { "changed": false, "ping": "pong" }
実例 - mysqlのインストール
mysqlはaptitudeでインストールします。
通常ではコマンドライン一行でインストールできます。
パッケージ情報のアップデートを行ってからインストールするため、通常では実行するコマンドは以下のようになります。
$ sudo aptitude update $ sudo aptitude install mysql-server mysql-client libmysqlclient-dev
これを実行する設定をYAMLに記述します。
ansibleでは、このYAMLファイルのことを’playbook’と呼んでいるようで、実行の際のコマンドは’ansible-playbook’になります。
playbookの記述
最低限必要な設定は概ね以下のとおりです。
- hosts: – 対象のホスト hostファイルの記述方法によって、グルーピング可能
- sudo: コマンドをsudoで実行するフラグ
- user: 実行ユーザ
- tasks: 実行内容のリスト
’- name: ‘ でタスクに名前を設定します。
‘command: ‘ で実行するコマンドを設定します。
パスワードの自動設定
mysql-serverをインストールする際、rootユーザのパスワード入力を要求されますが、 そのままではそこでインストール処理がストップします。
この問題に対しては、かなり強引ですが、ひとまず仮パスワードを予め設定しておき、
入力しなくて済むようにすることで回避します。
$ sudo debconf-set-selections <<< 'mysql-server-5.6 mysql-server/root_password password root' $ sudo debconf-set-selections <<< 'mysql-server-5.6 mysql-server/root_password_again password root'
上記のやりかたでも通らないようです。
ansibleでは’shell:’の場合、コマンドが/bin/sh起動になるので、 chefでのインストール例で使用しているものはそのままではエラーになります。
また、’command:’ではリダイレクト等の記号を使用できないため、動作しません。
結局、環境変数(or シェル変数?)’DEBIAN_FRONTEND’を設定する方法で回避することにしました。
具体的な設定例は以下のようになりました。
mysql.yml
--- - hosts: 172.17.0.1 sudo: true user: docker tasks: - name: install aptitude command: apt-get -y install aptitude - name: update packages command: aptitude -y update - name: install mysql command: aptitude -y install mysql-server-5.6 mysql-client-5.6 environment: DEBIAN_FRONTEND: 'noninteractive'
インストールを実行します。
-i オプションで指定するhostファイルは疎通確認の際に使用したものです。
$ ansible-playbook -i host mysql.yml PLAY [172.17.0.1] ************************************************************* ... PLAY RECAP ******************************************************************** 172.17.0.1 : ok=4 changed=3 unreachable=0 failed=0
実例 - rbenvのインストールと設定
mysqlはaptitudeでインストールしましたが、rbenvについてはgit cloneでインストールしたあと、 任意のバージョンのrubyをインストールします。
変数の定義
playbook内で使用できる変数を定義できます。
以下のような記述です。
vars: ruby_ver: 2.2.3 target: /home/docker/.rbenv
先ほどのmysqlの場合と同じ要領で設定を行います。
--- - hosts: 172.17.0.1 user: docker vars: ruby_ver: 2.2.3 target: /home/docker/.rbenv tasks: - name: install dependencies command: > sudo aptitude -y install build-essential bison libreadline6-dev curl zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev autoconf libncurses5-dev - name: clone rbenv command: test -e || git clone https://github.com/sstephenson/rbenv.git - name: clone ruby-build command: test -e /plugins/ruby-build || git clone https://github.com/sstephenson/ruby-build.git /plugins/ruby-build - name: add PATH command: touch ~/.bashrc ; echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc - name: add eval command: echo 'eval "$(rbenv init -)"' >> ~/.bashrc - name: reload bashrc command: source /home/docker/.bashrc - name: install ruby command: rbenv install
とりあえずこのような感じで実行しましたが、エラーが発生しました。
DNS設定
rbenvはdockerユーザのホームディレクトリにインストールするため、sudo設定を行っていません。
dockerユーザでのコマンドでは、どうやら名前解決に失敗しているようで、cloneができません。
これはdocker自体の設定によるものなので、設定を修正しました。
/etc/default/docker
DOCKER_OPTS="--dns 8.8.8.8 -g /path/to/docker"
- -g /path/to/dockerはディレクトリを変更したためで、通常は不要です。
ディレクトリが存在する場合は実行しない
git cloneはclone対象ディレクトリがある場合はエラーになります。
存在確認を行って、存在する場合は実行しないコマンドにしたつもりですが、 commandの場合はこの記述ではダメで、ansibleの機能で制御する必要があるようです。
when: 1 == result というような記述で、
あらかじめ存在確認のコマンド(test -e など)で結果を保存しておき、
存在確認の結果で実行制御できるようです。
後で動かしてみると、これもダメみたいでした。
他の方法を調べたところ、stat: というモジュールがありましたので、そちらの記述に変更しました。
stat: は指定したpathの状態を取得できるもので、今回は存在確認ができれば良いので、以下のようにしています。
- stat: path= register: rbenv - name: clone rbenv command: git clone https://github.com/sstephenson/rbenv.git when: not rbenv.stat.exists
ファイルをコピーする
copy: src=xxx dest=xxx というフォーマットで実現できます
環境変数の追加と反映について
.bashrcに追加した環境変数の反映は、通常ではsource等で行いますが、 ansibleで追加したあとはそのままでは反映できないようです。
また、コマンドを実行する記述については、command: の他に shell: もあるようで、 shell: の場合は/bin/sh による実行で、&&や||が使用できます。
playbook内に環境変数を別途定義するか、bash -lc でコマンド実行する方法が紹介されていましたが、 これらも動作しませんでした。
これらはcommandとshellの違いか、環境の違いによるものと思われますが、 PATHの場合は、絶対パス指定という確実な方法があるため、そちらを使用することにしました。
以下の内容がインストールに成功したものです。
rbenv.yml
--- - hosts: 172.17.0.1 user: docker sudo: false vars: ruby_ver: 2.2.3 target: /home/docker/.rbenv rbenv_path: /home/docker/.rbenv/bin/rbenv tasks: - name: send bashrc copy: src=files/.bashrc dest=~/.bashrc - stat: path= register: rbenv - name: clone rbenv command: git clone https://github.com/sstephenson/rbenv.git when: not rbenv.stat.exists - stat: path=/plugins/ruby-build register: ruby_build - name: clone ruby-build command: git clone https://github.com/sstephenson/ruby-build.git /plugins/ruby-build when: not ruby_build.stat.exists - command: touch ~/.bashrc - name: add PATH shell: echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc && echo 'eval "$(rbenv init -)"' >> ~/.bashrc - name: install ruby shell: ( init -); install -s ; rehash ; global
実例 - emacsのインストール
今度は、emacsをソースコードからビルド&インストールします。
ファイルのダウンロード
ansibleにはファイルをダウンロードする機能があります。
以下のように記述することで、ネット上からファイルをダウンロードできます。
- name: get emacs src get_url: url=http://ftp.jaist.ac.jp/pub/GNU/emacs/emacs-.tar.xz dest=
ディレクトリの変更
tar.gzなどをダウンロードしてビルドする際、展開したディレクトリに移動する必要があります。
ansibleのタスクは、前のタスクの状態を引き継がないようになっているようなので、 カレントディレクトリの変更を明示する必要があります。
- name: make emacs command: make chdir=/emacs-
ビルド自体はrbenvでも行っていますので、ここでは大きな問題は発生しないと思いつつ、 前回のポイントも踏まえてplaybookを記述します。
emacs.yml
--- - hosts: 172.17.0.1 sudo: true user: docker vars: emacs_ver: 24.5 src: /usr/src tasks: - name: get emacs src get_url: url=http://ftp.jaist.ac.jp/pub/GNU/emacs/emacs-.tar.xz dest= - name: extract emacs src command: tar xJf emacs-.tar.xz chdir= creates=/ - name: configure emacs command: > ./configure --without-toolkit-scroll-bars --without-xaw3d --without-compress-info --without-sound --without-pop --without-xpm --without-tiff --without-rsvg --without-gconf --without-gsettings --without-selinux --without-gpm --without-makeinfo --without-x chdir=/emacs- - name: make emacs command: make chdir=/emacs- - name: install emacs command: make install chdir=/emacs-
includeについて
include: を使用することで、playbook内から他のplaybookを呼び出すことができます。
再利用という観点は今回のケースではそれほど重要ではありませんが、 種別ごとにタスクをまとめておくことで利用しやすくなるでしょう。
再利用については、すでに作成済みのplaybookを呼び出すだけになるので、 とても簡潔に記述できます。
今回作成したものをすべてインストールする場合は、例えば以下のようになります。
srv1.yml
--- - hosts: 172.17.0.1 sudo: true user: docker - include: common.yml - include: mysql.yml - include: rbenv.yml - include: emacs.yml
- mysql以外のインストールパッケージはcommon.ymlにまとめています。
- includeしているファイルのhosts設定はすべてallに変更しました。