ご無沙汰してしまいました、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の公式ドキュメントがオススメしているベストプラクティスにのっとってプレイブックを分割して、色々工夫してみようというのが次回のお話です。
続きます!(なるべく早いうちに..)