Rubyが苦手な人のためのインフラストラクチャ自動化ツールAnsible


## はじめに

世の中なんでもかんでもRubyで自分もRubyを学び始めようかなとは思うけど、同じことをpythonで書けたら良いなと思う今日この頃、インフラストラクチャ自動化ツールとして有名なchefの陰で、pythonによる自動化ツールがあると聞いて、早速使ってみた。ここではリモート環境をvagrantで立ち上げ、そのリモートマシンをansibleでセットアップする手順を紹介する。ホストの環境はMac OS X Mavericks。

※なお、同じpythonのインフラストラクチャ自動化ツールでも他に有名なソフト(Salt)があるので、別にこれがデファクトスタンダードなわけではない。でも一番シンプルそうなので、使ってみる（sshプロトコル使っているとかも良い）

## vagrantのインストール

vagrantのインストールについては、以前にまとめたので、そこを見てほしい。

* http://qiita.com/mountcedar/items/4bba761f06592ce1f4ce

## ansibleのインストール

pipでインストールするだけ。

```
$ pip install ansible
```

※ pipのインストール方法はpython環境によってまちまちなので、ここでは割愛。

## 準備

まず、リモートマシンを用意する。

```
$ mkdir ansible_test
$ cd ansible_test
$ vagrant box add ubuntu12.04_amd64 https://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box
$ vagrant init ubuntu12.04_amd64
```

既に適当なboxを持っている人は、box addコマンドを実行しなくても良い。今回は諸事情で、ubuntu 12.04のamd64バージョンを使う。他のサイトではcentosで紹介していることが多く、apt-getで扱う方法も書いてあるサイトがあると便利だろうと思って、そうした。

vagrant initを済ませると、作業ディレクトリ中にVagrantfileが作成される。その内容を、ホストとゲスト間でネットワークを構成するように修正する。

```
config.vm.network :private_network, ip: "192.168.33.10"
```

具体的には上記のコメントアウトを外すだけ。IPアドレスはデフォルトのままだけど、好みに合わせて変えても良い。

次に、パスワードなしでアクセスできるように~/.ssh/configに設定を書く。

```
Host 192.168.33.10
  User vagrant
  IdentityFile /Users/<username>/.vagrant.d/insecure_private_key
```

最後にansible用のhostsファイルを設定する。これはデフォルトのものでも良いけど、使い勝手が悪いので、作った方が良い

```
[servers]
192.168.33.10  # ゲストOSのIPを指定する
```

上記の内容をansible-testディレクトリのhostsという内容のファイルに書き込む。serversの名前のとおり複数書いても構わない。

適宜、自分の環境に合わせて、編集すること。

これで、下準備は完了。

## ウォーミングアップ

本当はこの章いらないんだけど、playbookに行く前に基本的なansibleの使い方を紹介。この内容は他のサイトと同じだから、もういいやという人は次に進むと良い。

まずはお決まりのping確認。

```bash
$ ansible -i hosts servers -m ping
192.168.33.10 | success >> {
    "changed": false,
    "ping": "pong"
}
```

コマンド実行

```bash
$ ansible -i hosts servers -a 'pwd'
192.168.33.10 | success | rc=0 >>
/home/vagrant
```

コピペしてできたら、次へ。

## 本番：playbookの作成

このツールの真髄は遠隔操作ではなく（遠隔操作ならsshでもできる）、インフラストラクチャの自動構成なので、playbookというyamlファイルを使った環境自動構成を試してみる。

これまでみたページで紹介されているのが、yumを使った方法なので、ここで紹介するplaybookはubuntuのapt-get仕様（もちろん、aptを使うサイトもあります）。
実用的な使い方ができるか試すために、やや複雑な手順が必要な環境を整えてみる。今回は、DeepLearningで使われるPythonのパッケージであるTheanoをインストールしてみる。

```playbook-theano.yaml
- hosts: servers
  sudo: true
  user: vagrant
  tasks:
    - name: apt-get update
      apt: update_cache=yes
    - name: apt-get upgrade
      apt: upgrade=yes
    - name: apt-get dist-upgrade
      apt: upgrade=dist
    - name: apt-get install git make python-dev python-setuptools libblas-dev gfortran g++ python-pip python-numpy python-scipy liblapack-dev
      apt: name={{ item }} state=latest
      with_items:
        - git
        - make
        - python-dev
        - python-setuptools
        - libblas-dev
        - gfortran
        - g++
        - python-pip
        - python-numpy
        - python-scipy
        - liblapack-dev
    - name: pip install nose
      pip: name=nose
    - name: pip install theano
      command: /usr/bin/pip install --upgrade --no-deps git+git://github.com/Theano/Theano.git
    - name: get deb package from cuda, http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1204/x86_64/cuda-repo-ubuntu1204_6.0-37_amd64.deb
      get_url: url="http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1204/x86_64/cuda-repo-ubuntu1204_6.0-37_amd64.deb" dest="/home/vagrant"
    - name: dpkg install cuda repo
      command: dpkg -i /home/vagrant/cuda-repo-ubuntu1204_6.0-37_amd64.deb
    - name: apt-get update
      apt: update_cache=yes
    - name: apt-get install cuda
      apt: name=cuda state=latest
    - name: add cuda path to environmental variable (1/2)
      lineinfile: dest=/home/vagrant/.bashrc state=present line="export PATH=$PATH:/usr/local/cuda-6.0/bin:" insertafter=EOF
    - name: add cuda path to environmental variable (2/2)
      lineinfile: dest=/home/vagrant/.bashrc state=present line="export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-6.0/lib64:" insertafter=EOF
```

まず、このyamlが文法的に間違っていないかをチェック。

```bash
$ ansible-playbook -i hosts playbook_theano.yaml --syntax-check
```

間違っていなかったら、実行。

```bash
$ ansible-playbook -i hosts playbook_theano.yaml
```

今回サーバの指定とかが必要ないのは、yamlファイル中で指定されているからで、後は各コマンドを順次実行する。実行結果は以下の通り

```
PLAY [servers] **************************************************************** 

GATHERING FACTS *************************************************************** 
The authenticity of host '192.168.33.10 (192.168.33.10)' can't be established.
RSA key fingerprint is XXXXXXXXXXXXXX
Are you sure you want to continue connecting (yes/no)? yes
ok: [192.168.33.10]

TASK: [apt-get update] ******************************************************** 
ok: [192.168.33.10]

TASK: [apt-get upgrade] ******************************************************* 
changed: [192.168.33.10]

TASK: [apt-get dist-upgrade] ************************************************** 
ok: [192.168.33.10]

TASK: [apt-get install git make python-dev python-setuptools libblas-dev gfortran g++ python-pip python-numpy python-scipy liblapack-dev] *** 
changed: [192.168.33.10] => (item=git,make,python-dev,python-setuptools,libblas-dev,gfortran,g++,python-pip,python-numpy,python-scipy,liblapack-dev)

TASK: [pip install nose] ****************************************************** 
changed: [192.168.33.10]

TASK: [pip install theano] **************************************************** 
changed: [192.168.33.10]

TASK: [get deb package from cude, http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1204/x86_64/cuda-repo-ubuntu1204_6.0-37_amd64.deb] *** 
changed: [192.168.33.10]

TASK: [dpkg install cuda repo] ************************************************ 
changed: [192.168.33.10]

TASK: [apt-get update] ******************************************************** 
ok: [192.168.33.10]

TASK: [apt-get install cuda] ************************************************** 
changed: [192.168.33.10]

TASK: [add cuda path to environmental variable (1/2)] ************************* 
changed: [192.168.33.10]

TASK: [add cuda path to environmental variable (2/2)] ************************* 
changed: [192.168.33.10]

PLAY RECAP ******************************************************************** 
192.168.33.10              : ok=13   changed=9    unreachable=0    failed=0   

```

実際にインストールされているかをチェック。

```
$ vagrant ssh
vagrant@vagrant-ubuntu-precise-64:~$ python
Python 2.7.3 (default, Feb 27 2014, 19:58:35) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import theano
>>> 
vagrant@vagrant-ubuntu-precise-64:~$ 
```

大丈夫そうである。なお、今回のyamlファイルでは、theanoをGPUで利用するためのパッケージ一式のインストールもしているが、VirtualBoxでは使用不可能なので、あしからず。

なお、ちゃんとビルドが通るまでに何回かvagrant destroy, vagrant upを繰り返してクリーンインストール可能かを確かめることになると思うが、用いるipアドレスが同じ場合、sshがエラーをはく。毎回、~/.ssh/known_hostsを削除する（か該当部分のみ削除する）ことをお忘れなく。

## Playbook Tips

Playbookで使っているtask集について簡単にまとめる

### apt-get

aptモジュールというのが用意されている。

* http://docs.ansible.com/apt_module.html

```
- name: apt-get update
  apt: update_cache=yes
- name: apt-get upgrade
  apt: upgrade=yes
- name: apt-get dist-upgrade
  apt: upgrade=dist
- name: apt-get install git
  apt: name=git state=latest
```

上から順番に、apt-get update, apt-get upgrade, apt-get dist-upgrade, apt-get installの設定例である。

複数のパッケージを一括でインストールしたい場合はループを組み込むと良い。

```
- name: apt-get install git make python-dev python-setuptools libblas-dev gfortran g++ python-pip python-numpy python-scipy liblapack-dev
  apt: name={{ item }} state=latest
  with_items:
    - git
    - make
    - python-dev
    - python-setuptools
    - libblas-dev
    - gfortran
    - g++
    - python-pip
    - python-numpy
    - python-scipy
    - liblapack-dev
```

### ソースを外部から取得

パッケージ管理システムを介さずソースからビルドしたいときもある。そうした場合、ソースはget_urlモジュールで取得する。

* http://docs.ansible.com/get_url_module.html

```
- name: get deb package from cuda
  get_url: url="http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1204/x86_64/cuda-repo-ubuntu1204_6.0-37_amd64.deb" dest="/home/vagrant"
```

### コマンド実行

もっと純粋にコマンド実行をしたい場合はcommandモジュールを使う

* http://docs.ansible.com/command_module.html


```
- name: dpkg install cuda repo
  command: dpkg -i /home/vagrant/cuda-repo-ubuntu1204_6.0-37_amd64.deb
```

### pip実行

pip用のモジュールも用意されている。

* http://docs.ansible.com/pip_module.html

```
- name: pip install nose
  pip: name=nose
```

が、ちょっと複雑なことをしようとすると、はまるので、コマンドで直接指定した方が良いこともある。

```
- name: pip install theano
  command: /usr/bin/pip install --upgrade --no-deps git+git://github.com/Theano/Theano.git
```

### 設定ファイルの書き込み

PATHなどの設定を変更するときには、lineinfileモジュールを使う

* http://docs.ansible.com/lineinfile_module.html

```
- name: add cuda path to environmental variable (1/2)
  lineinfile: dest=/home/vagrant/.bashrc state=present line="export PATH=$PATH:/usr/local/cuda-6.0/bin:" insertafter=EOF
```

今回はファイル末尾に行を追加しているだけだが、正規表現で指定行を探して書き換えるといった機能も提供している。

#### 複数行の入力

複数行の入力は単に改行子を入れるだけではうまくいかない。git cloneしてから適当なロケーションにコピーしたり、gistからwgetで取得するほうが素直だが、そこまでするほどでもないという場合は以下のようにループ構造で書く手がある。

```
- name: add wsgi settings
  lineinfile:
    dest=/etc/apache2/httpd.conf
    state=present
    line={{ item }}
    insertafter=EOF
  with_items:
    - "'WSGIScriptAlias / /var/www/hoge/wsgi.py'"
    - "'WSGIPythonPath /var/www/hoge/'"
    - "'<Directory /var/www/hoge/>'"
    - "'<Files wsgi.py>'"
    - "'Order deny,allow'"
    - "'Allow from all'"
    - "'</Files>'"
    - "'</Directory>'"
```

上記はpythonのwsgiをapache2のhttpd.confに追記する場合の設定。シングルクォーテーションとダブルクォーテーションの2つを使って、forループとlineにおけるクォーテーションを入れることがポイント。

## 最後に

実はここまでではあまりうれしいことはなくて、このvagrant+ansible環境が威力を発揮するのは、amazon ec2サービスと組み合わせた場合だ。次回はその方法についても触れていく。