BLOG

Ansibleに入門してRubyをビルドさせてみるの巻 前編

kuroda

ご挨拶

ご無沙汰してしまいました、kurodaです。

最近、仕事の準備としてコンテナやら仮想マシンやらを作ってその中であれこれ設定したりビルドしたりということが増えてきました。世間ではそういう作業を自動化するために構成管理ツールを使う事が何年も前から流行っていましたが、僕もようやく重い腰を上げてみたというのが今回のお話です。

構成管理ツールと一口に言っても色々ありますが、管理対象にソフトをインストールしなくても良いと言う点が気に入って、Ansibleを選びました。

シナリオ

さて、ここでは管理対象としてlxcのコンテナが2つあり、そいつらに最新版のRubyを野良ビルドしてインストールするというのを課題として、これをAnsibleで解決してみます。

前提として、管理対象のコンテナでは以下の準備が整っているとします。

  • Ubuntuがインストールされている。バージョンは、片方が14.04(最新LTS。通称trusty)で、もう片方が15.10(最新版。通称Wily)
  • それぞれ、ホスト名は trusty.l.syngram.co.jp, wily.l.syngram.co.jp とする
  • kurodaという名前のユーザーアカウントが登録されており、既定のパスワードでsudoもできる。パスワードは取りあえずどちらも同じ
  • ssh-serverが動いていて、公開鍵認証で接続できる
  • /etc/apt/sources.list には deb-src 行も入っている(ubuntuホストにlxc-createでubuntuを入れると入っていないので追加する)
  • Ansibleの動作に必要なので python2 をインストール済み(いまは python3 だと動きません)

この状況で、以下のような作業を自動化してみましょう。

  • rubyのビルドに必要なdebパッケージを apt-get install する
  • ダウンロード済みのruby-2.2.3.tar.gzを各コンテナで展開する
  • /opt/ruby/2.2.3 にインストールされるように configure して make して make install する

準備

最初に、作業をするマシンにAnsibleその物を用意します。今回は管理対象のコンテナを置いてあるホストマシン(charlieという名前で、Ubuntu 15.10が動いています)で作業しました。Ansibleには管理対象にsshで接続させるので、それが可能なら特にコンテナホストである必要もありません。

Ansibleは以下のように公式のパッケージを使ってインストールします。

インベントリに管理対象を列挙する

つぎに、管理対象のホスト名やアドレスを列挙する インベントリ というものを用意します。名前は慣例的に hosts とします( /etc/hosts と紛らわしいので好きではないのですが)

実際の作業手順はYAML形式で書きます。これはプレイブックと呼ばれます。
今回のプレイブック main.yml の冒頭部はこんな感じです。

これだけで、インベントリの target グループに列挙した2台のホスト上で、 sudo apt-get update; sudo apt-get dist-upgrade を実行してくれます。
プレイブックは以下のように実行します。

--ask-become-pass オプションを付けているため、targetでsudoするときのパスワードを聞いてくるので入力します。

残りの手順も同じ要領で書いてしまいます。

記述の具体的な意味については、 公式のドキュメント を参照してください。
手順の種類を表す apt, command, unarchive, file はそれぞれモジュールと呼ばれるもので、ドキュメントの Module Index からたどったりサイドバーの検索窓から検索したりすれば見つかります。例えば、apt モジュールと file モジュールのドキュメントは、それぞれ http://docs.ansible.com/ansible/apt_module.htmlhttp://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つを置くことになります。

実行すると、こんな感じで進みます

これで、ビルドしたRubyがコンテナの中で動くようになりました。

余談

実は、そもそもの前提にしているコンテナ2つ自体を用意するプレイブックも作ったのですが、手元の環境固有の条件もあり、今回は話が長くなってしまうので省略です。実際の作業ディレクトリは、実はこんな風になっています:

今回のサンプルを全部まとめてGitHubに用意しましたが、おおまかには、以下のような感じになっています

  • コンテナのファイルシステムは zfs で作った /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回くらい作り直した後で用意しました(汗。
  • このサンプルを作った頃、 Ubuntuの日本サーバーが更新中だったのかハッシュエラーが発生して apt-get できなかったので、 /etc/apt/sources.list 自体も適当なミラーを指定したファイルを別途用意して設置しています
  • コンテナ2つに同じ作業手順をいくつか条件を変えて実行するため、別途タスクファイル setup-lxc-ubuntu.yml を作り、これをmain.ymlから変数の値を変えて include して呼び出しています
  • なぜか lxc_container モジュールがSEGVしてしまうので、command モジュールから lxc-attach を使ってます

次回予告

さてさて。今回用意したプレイブックでは、以下のような問題が残っています。

  • 1つのファイルに手順を全部書くと、整理が付かず再利用しづらい
  • --ask-become-pass ではパスワードを1つしか入力できないので管理対象のパスワードを統一する必要がある
  • Rubyのバージョンが変わったときに、プレイブックの中をあちこち直す必要があって大変

これを解決するために、Ansibleの公式ドキュメントがオススメしているベストプラクティスにのっとってプレイブックを分割して、色々工夫してみようというのが次回のお話です。
続きます!(なるべく早いうちに..)