Terraform+ユーザデータ+Ansibleでのサーバプロビジョニングを活用する

## はじめに
クラウドを使ってシステムを運用しているとTerraformとAnsible(Chefでもpuppetでもなんでもいいです)の両方を組み合わせて使うことが多いはずであす。
（ここではAnsibleとします。）

一般的にはこう使うことが多いと思います。
1. Terraformでクラウド上にインフラを構築する
2. APIを使ってデプロイ先のサーバ情報を取得する
3. Ansibleで取得したサーバへ対して設定やアプリのデプロイを行う

一方で、こういう利用の仕方も場合によってはありだなと感じます。
1. Terraformでクラウド上にインフラを構築する
2. 仮想サーバのユーザデータ機能(※)を利用してAnsibleを実行してプロビジョニング
　※ユーザデータ機能とは仮想サーバインスタンスの起動時に任意のコマンドを実行できる機能

ここでは、Alibaba Cloudを例に、Terraform+ユーザデータ+Ansibleの活用方法を説明します。
多くの他のクラウドでも同様の考え方を利用することはできるはずです。

え、<a href="https://jp.aliyun.com" target="_blank">Alibaba Cloud</a>を知らない。。。？
2017年2月に東京リージョンできました。

## ソースコード公開
まずはじめにソースコードなどはすべてこちらに掲載しています。
Alibaba Cloud上でのTerraformのサンプル集をまとめたものです。
https://github.com/mosuke5/terraform_for_alibabacloud_examples

こちらの`basic_sample_with_ansible`が該当するサンプルです。
実際に実行したい方、中身みたい方はぜひ参照してください。

## 解説
### Terraformでインスタンス作成。ユーザデータ起動
以下はTerraformのテンプレートファイルのECS作成部分です。
Alibaba Cloud ECSは仮想サーバサービスでAWSならEC2に該当するので注意。
注目のポイントはユーザデータ部分です。

```
resource "alicloud_instance" "web" {
  instance_name = "terraform-ecs"
  availability_zone = "${var.zone}"
  image_id = "centos_7_3_64_40G_base_20170322.vhd"
  instance_type = "ecs.n4.small"
  io_optimized = "optimized"
  system_disk_category = "cloud_efficiency"
  security_groups = ["${alicloud_security_group.sg.id}"]
  vswitch_id = "${alicloud_vswitch.vsw.id}"
  password   = "${var.ecs_password}"
  
  # ユーザデータ
  user_data = "${file("provisioning.sh")}"
}
```

ユーザデータ機能では、記述したシェルスクリプトやcloud-initを実行することが可能です。
事前に用意した`provisining.sh`を実行させるようにします。
可読性を良くするために`${file("xxxxx")}`にしてファイルを読み込むことをおすすめします。

しかし、ユーザデータにはデータサイズに制限があり、より多くの処理実行させたい場合には`#include`を利用して外部ファイルを実行するといいです。(<a href="https://jp.aliyun.com/help/faq-detail/49121.htm#h4-3-2-1-linux-" target="_blank">参考</a>)

```
  user_data = "#include\nhttps://xxxxxxxx.com/provisioning.sh"
}
```

## サーバプロビジョニング
ユーザデータ機能を用いてサーバプロビジョニングができます。
しかし、サーバプロビジョニングをシェルスクリプトで頑張りたいでしょうか？

頑張りたい人はOKです。が、私はシェルスクリプトで頑張れる自信がありません。
やっぱり、AnsibleとChefとか使ってプロビジョニングしたいです。
そのためユーザデータでは、下記だけを行うようにすることでより効率的にプロビジョニングができます。

1. Ansibleのインストール
2. Playbook(※)のダウンロード
3. Playbookの実行

※ 「Playbook」とはAnsibleで利用する、サーバのプロビジョニング内容を記述したファイルのことです。

```bash
#!/bin/bash
yum install -y wget epel
yum install -y ansible
cd /root
wget https://raw.githubusercontent.com/mosuke5/terraform_for_alibabacloud_examples/add/ansible-basic-sample/basic_sample_with_ansible/playbook.yml
ansible-playbook playbook.yml
```
## Ansible palybook
Ansibleを触ったことがある人にとっては特別なことはありません。
もし、まだAnsibleやChefなどのプロビジョニングツールを利用したことがない人はぜひこの機会に触ってくだださい。

Ansibleはローカル実行として利用します。
普通はSSH越しで行いますが、Ansibleを実行するサーバにプロビジョニングするのでローカル実行が高速でおすすめです。
下記は、httpdのインストールと起動のみを書いた簡単なPlaybookです。

```yaml
---
- hosts: 127.0.0.1
  connection: local
  tasks:
  - name: be sure httpd is installed
    yum: name=httpd state=installed

  - name: be sure httpd is running and enabled
    service: name=httpd state=started enabled=yes
```

これだけで、サーバを起動後にhttpdのインストールと起動が行われWebサーバの完成してしまいます。

## メリットとデメリット
もちろんこの方法にはメリット・デメリットがあります。
メリットとしては、サーバ起動時にプロビジョニング可能なのでプロビジョニング作業を別途意識しなくてい良い点があります。
一方で、プロビジョニングには時間がかかるのでAutoScalingでの利用はおすすめできません。
また、あくまで起動したサーバへのプロビジョニングであり、他のサーバやサービスとの連携を意識したプロビジョニングは得意ではないです。

個人的には、サーバのミドルウェアレベルのインストールまではイメージ化しておき、
その上のアプリのデプロイやそのサーバ固有の設定などをユーザデータ+Ansibleで行うとより実践的かなと思います。

## まとめ
ユーザデータとAnsibleを組み合わせることで、サーバインスタンスの起動時に高度なプロビジョニングが可能です。
しかし、この方法にはメリットもデメリットもあるので、そこを理解した上で使い分けあるいは併用が必要です。

とはいえ、ユーザデータからAnsibleをこんなに簡単に実行できるのは大きな点だと思います。

## おまけ
以前、Alibaba Cloudの日本リージョンがTerraformに対応したことを書いたのでこちらも見てください。
「[Alibaba Cloud 日本リージョンがTerraformに対応したので試した](http://qiita.com/mosuke5/items/a65683ce6569bffd7ef0)」