ご無沙汰してしまいました、kurodaです。
最近、仕事の準備としてコンテナやら仮想マシンやらを作ってその中であれこれ設定したりビルドしたりということが増えてきました。世間ではそういう作業を自動化するために構成管理ツールを使う事が何年も前から流行っていましたが、僕もようやく重い腰を上げてみたというのが今回のお話です。
構成管理ツールと一口に言っても色々ありますが、管理対象にソフトをインストールしなくても良いと言う点が気に入って、Ansibleを選びました。
さて、ここでは管理対象としてlxcのコンテナが2つあり、そいつらに最新版のRubyを野良ビルドしてインストールするというのを課題として、これをAnsibleで解決してみます。
前提として、管理対象のコンテナでは以下の準備が整っているとします。
trusty.l.syngram.co.jp, wily.l.syngram.co.jp とするetc/apt/sources.list には deb-src 行も入っている(ubuntuホストにlxc-createでubuntuを入れると入っていないので追加する)この状況で、以下のような作業を自動化してみましょう。
apt-get install する/opt/ruby/2.2.3 にインストールされるように configure して make して make install する最初に、作業をするマシンにAnsibleその物を用意します。今回は管理対象のコンテナを置いてあるホストマシン(charlieという名前で、Ubuntu 15.10が動いています)で作業しました。Ansibleには管理対象にsshで接続させるので、それが可能なら特にコンテナホストである必要もありません。
Ansibleは以下のように公式のパッケージを使ってインストールします。
kuroda@charlie:~$ sudo apt-get ansible
つぎに、管理対象のホスト名やアドレスを列挙する インベントリ というものを用意します。名前は慣例的に hosts とします( 。/etc/hosts と紛らわしいので好きではないのですが)
[target] trusty.l.syngram.co.jp wily.l.syngram.co.jp
実際の作業手順はYAML形式で書きます。これはプレイブックと呼ばれます。
今回のプレイブック main.yml の冒頭部はこんな感じです。
---
- hosts: target # <- 操作対象のグループ名(インベントリに書いた物)
tasks: # <- 作業手順は配列で書きます
- name: apt-get update # <- 各手順には任意で名前(タイトル)を付けられます
apt: # <- apt-getの
update_cache: yes # <- updateと
upgrade: dist # <- dist-upgradeを実行します
cache_valid_time: 3600 # <- 前回実行時から1時間経過していない場合は実行しません
become: true # <- 上記手順を sudo で実行します
これだけで、インベントリの target グループに列挙した2台のホスト上で、 sudo apt-get update; sudo apt-get dist-upgrade を実行してくれます。
プレイブックは以下のように実行します。
$ ansible-playbook -i hosts --ask-become-pass main.yml SUDO password:
--ask-become-pass オプションを付けているため、targetでsudoするときのパスワードを聞いてくるので入力します。
kuroda@charlie:~/work/ansible/single$ ansible-playbook -i hosts --ask-become-pass main.yml SUDO password: PLAY [target] ***************************************************************** GATHERING FACTS *************************************************************** ok: [trusty.l.syngram.co.jp] ok: [wily.l.syngram.co.jp] TASK: [apt-get update] ******************************************************** ok: [wily.l.syngram.co.jp] ok: [trusty.l.syngram.co.jp] PLAY RECAP ******************************************************************** trusty.l.syngram.co.jp : ok=2 changed=0 unreachable=0 failed=0 wily.l.syngram.co.jp : ok=2 changed=0 unreachable=0 failed=0
残りの手順も同じ要領で書いてしまいます。
# sudo apt-get build-dep ruby-full する
- name: build-dep ruby-full
apt:
name: ruby-full
state: build-dep
become: true
# build-depで入らないライブラリを、 sudo apt-get instal libXX-dev する
- name: install libXX-dev
apt:
name: "{{ item }}" # <- こうやって書いておくと、
state: installed
with_items: # <- 与えた配列でループしてくれます
- zlib1g
- libreadline-dev
- libssl-dev
become: true
# tar-ballの展開先にするビルド用ディレクトリを作って
- name: mkdir for unarchive
file:
path: /tmp/ansible-build-ruby
state: directory
# ローカルに置いたruby-2.2.3.tar.gz をリモートに展開します
- unarchive:
src: ruby-2.2.3.tar.gz
dest: /tmp/ansible-build-ruby/
# お馴染みの ./configure と、
- command: ./configure --prefix=/opt/ruby/2.2.3
args:
chdir: /tmp/ansible-build-ruby/ruby-2.2.3
# make に、
- command: make
args:
chdir: /tmp/ansible-build-ruby/ruby-2.2.3
# make install です。
- command: make install
become: true
args:
chdir: /tmp/ansible-build-ruby/ruby-2.2.3
# 使い終わったビルド用ディレクトリを削除
- name: cleanup build directory
file:
path: /tmp/ansible-build-ruby
state: absent
記述の具体的な意味については、 公式のドキュメント を参照してください。
手順の種類を表す apt, command, unarchive, file はそれぞれモジュールと呼ばれるもので、ドキュメントの Module Index からたどったりサイドバーの検索窓から検索したりすれば見つかります。例えば、apt モジュールと file モジュールのドキュメントは、それぞれ http://docs.ansible.com/ansible/apt_module.html と http://docs.ansible.com/ansible/file_module.html にあります。
プレイブックの書式は純粋なYAMLではなく、Jinja2というテンプレートエンジンの文法で書けます(ライブラリのインストールで書いている {{ item }} がそれ)。
ansible-playbook コマンドに指定したプレイブックのあるディレクトリが、実行時の基準ディレクトリになります。なので、事前にダウンロードした ruby-2.2.3.tar.gz もそこに置いておきます。
作業ディレクトリには hosts main.yml ruby-2.2.3.tar.gz の3つを置くことになります。
kuroda@charlie:~/work/ansible/single$ ls hosts main.yml ruby-2.2.3.tar.gz
実行すると、こんな感じで進みます
kuroda@charlie:~/work/ansible/single$ ansible-playbook -i hosts --ask-become-pass main.yml SUDO password: PLAY [target] ***************************************************************** GATHERING FACTS *************************************************************** ok: [trusty.l.syngram.co.jp] ok: [wily.l.syngram.co.jp] TASK: [apt-get update] ******************************************************** changed: [trusty.l.syngram.co.jp] changed: [wily.l.syngram.co.jp] TASK: [build-dep ruby-full] *************************************************** changed: [trusty.l.syngram.co.jp] changed: [wily.l.syngram.co.jp] TASK: [install libXX-dev] ***************************************************** changed: [trusty.l.syngram.co.jp] => (item=zlib1g,libreadline-dev,libssl-dev) changed: [wily.l.syngram.co.jp] => (item=zlib1g,libreadline-dev,libssl-dev) TASK: [mkdir for unarchive] *************************************************** changed: [trusty.l.syngram.co.jp] changed: [wily.l.syngram.co.jp] TASK: [unarchive ] ************************************************************ changed: [trusty.l.syngram.co.jp] changed: [wily.l.syngram.co.jp] TASK: [command ./configure --prefix=/opt/ruby/2.2.3] ************************** changed: [wily.l.syngram.co.jp] changed: [trusty.l.syngram.co.jp] TASK: [command make] ********************************************************** changed: [wily.l.syngram.co.jp] changed: [trusty.l.syngram.co.jp] TASK: [command make install] ************************************************** changed: [trusty.l.syngram.co.jp] changed: [wily.l.syngram.co.jp] TASK: [cleanup build directory] *********************************************** changed: [trusty.l.syngram.co.jp] changed: [wily.l.syngram.co.jp] PLAY RECAP ******************************************************************** trusty.l.syngram.co.jp : ok=10 changed=9 unreachable=0 failed=0 wily.l.syngram.co.jp : ok=10 changed=9 unreachable=0 failed=0
これで、ビルドしたRubyがコンテナの中で動くようになりました。
kuroda@trusty:~$ /opt/ruby/2.2.3/bin/ruby -v -e 'p(require("openssl"))'
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]
true
実は、そもそもの前提にしているコンテナ2つ自体を用意するプレイブックも作ったのですが、手元の環境固有の条件もあり、今回は話が長くなってしまうので省略です。実際の作業ディレクトリは、実はこんな風になっています:
kuroda@charlie:~/work/ansible/single$ ls destroy_containers.sh init-containers rollback_containers.sh hosts main.yml ruby-2.2.3.tar.gz
今回のサンプルを全部まとめてGitHubに用意しましたが、おおまかには、以下のような感じになっています
/zroot/lxc/ 以下に配置しています。デフォルトでこのパスを使うように設定していて、 lxc-create ではオプションに -B zfs を与えています。init-containers のプレイブックの中では既定のパスワードを usermod で指定しています(GitHubに置いたリポジトリ中のファイルに書いてあるのはダミーなので実際には使えません)destroy_containers.sh はコンテナ2つを破棄するための物ですrollback_containers.sh は、コンテナ2つを破棄する代わりに、init-containers/main.yml 実行直後の状態に戻す物で、zfsのsnapshotを使っています。コンテナを10回くらい作り直した後で用意しました(汗。 apt-get できなかったので、 /etc/apt/sources.list 自体も適当なミラーを指定したファイルを別途用意して設置していますsetup-lxc-ubuntu.yml を作り、これをmain.ymlから変数の値を変えて include して呼び出していますlxc_container モジュールがSEGVしてしまうので、command モジュールから lxc-attach を使ってますさてさて。今回用意したプレイブックでは、以下のような問題が残っています。
--ask-become-pass ではパスワードを1つしか入力できないので管理対象のパスワードを統一する必要があるこれを解決するために、Ansibleの公式ドキュメントがオススメしているベストプラクティスにのっとってプレイブックを分割して、色々工夫してみようというのが次回のお話です。
続きます!(なるべく早いうちに..)