AnsibleでGCEインスタンスを管理する

AnsibleにはGoogle Compute Engineの
リソース管理用モジュール([GCEモジュール](http://docs.ansible.com/guide_gce.html))が
組み込まれています。

GCEモジュールを使うことで、

* Instanceの生成
* network access制御
* persistant diskの利用
* load balancerの管理

を自動化できます。
さらに、
inventoryプラグインもあり、
生成したGCEinstanceの情報をAnsible dynamic inventoryに自動的に吸い上げ、tagやプロパティでグループ化できます。

しかし、
使う際には、必要な認証や設定がいくつかある上に、
ドキュメントの記述が複雑でわかりづらい部分もあるため
結構ハマりました。
その辺りを回避できるように解説していきます。

## 準備
AnsibleのGCEモジュールは、pythonのapache-libcloudモジュールに依存しているので、
pip installします。

```bash
$ pip install apache-libcloud
```

##ディレクトリ構成
GCEモジュールのドキュメントには、
Playbookファイルなどのディレクトリ構成の記載がありません。
[ベスト・プラクティス](http://docs.ansible.com/playbooks_best_practices.html)を見習うべきですが、
簡単に始めるには、以下のような構成で良いんじゃないかと思います。

```
~/gce_ansible/
 play.sh          # playbook実行シェルスクリプト
 master.yml       # master playbook
 credentials/     # 証明書管理Dir
    cacert.pem     # libcloud用 CA bundleファイル
    pkey.pem       # GCE用 証明書ファイル
    secrets.py     # 証明書指定ファイル
 inventory/       # inventory管理用Dir
    gce.ini        # GCE用設定ファイル
    gce.py         # GCE用モジュール
    hosts          # inventoryファイル
 vars/
    gce_auth.yml   # GCE認証情報変数
    instance.yml   # GCEinstance設定変数
```

以下、ディレクトリごとに解説します。

## credentials | 証明書の取得
###libcloud用 CA bundleファイル
Mac OS Xで利用している場合、libcloud用 CA bundleファイルが必要です。
[cURL - Extract CA Certs](http://curl.haxx.se/docs/caextract.html)
で「HTTP from curl.haxx.se: cacert.pem」をダウンロードして、
前述ディレクトリの credentials に配置しましょう。

ちなみに、これが無いとplaybook実行時に以下のようなエラーが出ました。

```
RuntimeError: No CA Certificates were found in CA_CERTS_PATH.
```

### GCE用 証明書ファイル
次に、GCEにアクセスするための証明書ファイルが必要です。
[Google Developer Console](https://console.developers.google.com/project)
で、(Project名) > APIS&AUTH > 認証情報
を選択、OAuthの「新しいクライアントIDを作成」をクリックします。
「サービスアカウント」を選択してクライアントIDを作成しましょう。
完了画面にパスワード’notasecret’ が表示され、
自動的にp12形式証明書ファイルを取得できます。

完了画面を閉じた後「サービスアカウント」欄に追加されたメールアドレスは後で必要になります。

証明書をapache-libcloudで使うため、
以下のコマンドで、p12ファイルをpem形式(openSSL標準）に変換します。

```bash
$ openssl pkcs12 -in (p12ファイルパス).p12 -passin pass:notasecret -nodes -nocerts | openssl rsa -out ~/gce_ansible/credentials/pkey.pem
```

### 証明書指定ファイル(secrets.py)
GCE用証明書ファイルをansibleに認識させるため、
以下のようにpythonファイル(secrets.py)を作成します。

```py
GCE_PARAMS = ('i...@project.googleusercontent.com', '~/gce_ansible/credentials/pkey.pem')
GCE_KEYWORD_PARAMS = {'project': 'project-name'}
```

記載するのは、前述の「サービスアカウント」欄のメールアドレスと、
pemファイルパス、対象のGCEプロジェクト名です。

##inventory | 実行対象ホスト
###ローカルホスト指定(hosts)
GCEのinstance生成は、ローカルPCから実行するので、
まずはlocalhostを設定します。

```
[localhost]
127.0.0.1
```

###GCE Dynamic Inventory
Dynamic Inventoryという仕組みを使うと、
GCEで生成したinstanceのIPアドレスを自動で取得して、
inventoryとして判断してくれるようになります。

Ansibleが提供している GCE inventory plugin を使います。
[Ansibleのgithubリポジトリ](https://github.com/ansible/ansible)から

```
- contrib/inventory/gce.ini
- contrib/inventory/gce.py
```

を取得して、それぞれ

* inventory/gce.ini
* inventory/gce.py

に置きましょう。

gce.iniに、secrets.pyの絶対パスを指定します。例えば、

```
libcloud_secrets = /Users/shuhei/gce_ansible/credentials/secrets.py
```

###動作確認
Dynamic Inventoryが正常に動作するかどうかを確認します。

```bash
$ cd ~/gce_ansible
$ export SSL_CERT_FILE=$HOME/gce_ansible/credentials/cacert.pem # Mac OSX の場合のみ
$ ./inventory/gce.py --list
```

GCE上にすでにinstanceが動作している場合は、そのhostの情報がjsonで出力されるはずです。

また、以下のコマンドでさらに詳細な情報が出力されます。

```bash
$ cd ~/gce_ansible
$ export GCE_INI_PATH=$HOME/gce_ansible/inventory/gce.ini
$ ansible all -i inventory/gce.py -m setup
hostname | success >> {
  "ansible_facts": {
    "ansible_all_ipv4_addresses": [
      "x.x.x.x"
    ],
  ....
  }
}
```

##master.yml | playbook本体
いよいよplaybookの作成です。

今回は、GCEのinstanceを生成するだけにしておきます。
###master.yml

```yaml
- name: Create new GCE instances
  hosts: localhost
  gather_facts: no
  vars_files:
    - "vars/instance.yml"
    - "vars/gce_auth.yml"
  tasks:
    - name: Launch instances
      local_action:
        module: gce
        instance_names: "{{ names }}"
        machine_type: "{{ type }}"
        image: "{{ image }}"
        zone: "{{ zone }}"
        service_account_email: "{{ service_account_email }}"
        pem_file: "{{ pem_file }}"
        project_id: "{{ project_id }}"
        tags: webserver
```

##vars | 変数の管理
playbook内の二重波括弧 {{ … }} で囲っている部分は変数で、
var_files: として指定している以下の２ファイルに値を記載します。
###vars/gce_auth.yml
(credentials/secrets.py と内容がかぶっているので何とかならないか...?)

```yaml
service_account_email: i...@project.googleusercontent.com
pem_file: ~/gce_ansible/credentials/pkey.pem
project_id: project-name
```

###vars/instance.yml
```yaml
names: www1
type: f1-micro
image: debian-7
zone: us-central1-b
```

##play.sh | playbook実行用シェルスクリプト
playbookを実行する前に、実行するためのシェルスクリプトを作成しておきます。

```bash
#!/bin/bash
PLAYBOOK="$1"

if [ -z $PLAYBOOK ]; then
  echo "You need to pass a playback as argument to this script."
  exit 1
fi

export GCE_INI_PATH=$(pwd)/inventory/gce.ini
export SSL_CERT_FILE=$(pwd)/credentials/cacert.pem
export ANSIBLE_HOST_KEY_CHECKING=False

if [ ! -f "$SSL_CERT_FILE" ]; then
  curl -O http://curl.haxx.se/ca/cacert.pem
fi

ansible-playbook -v -i inventory/ "$PLAYBOOK"
```

##実行！

```bash
$ cd ~/gce_ansible
$ ./play.sh master.yml

PLAY [Create new GCE instances] ******************
...(略)...
PLAY RECAP ********************************************************************
127.0.0.1                  : ok=1    changed=1    unreachable=0    failed=0
```

実行後、ふたたび [Google Developer Console](https://console.developers.google.com/project) にアクセスして
該当プロジェクトの

[Compute Engine] > [VMインスタンス]

を見ると、www1 という名前のinstanceが生成されているはずです。
ということで、今回はこの辺りでおしまいです。

次回は、Google Compute Engine(GCE)に生成したinstanceに
アプリケーションをセッティングしていく予定です。

:exclamation: この記事は私のブログ「[Let's go Curious](http://www.curious-eyes.com/blog/shuhei/?p=1177)」で書いたのですが、
Qiitaさんの方が、多くの人に読んでもらえそうなので、続きはこちらで書こうと思います。