[初心者歓迎] Packer + Ansible + TerraformでWordPressを構築

この記事は[フロムスクラッチ Advent Calendar 2016](http://qiita.com/advent-calendar/2016/fromscratch)の13日目の記事です。
# 1. はじめに。
「Keep it Simple, stupid! But fail fast! learn a lot!」

![ショック.png](https://qiita-image-store.s3.amazonaws.com/0/95804/67ec581a-49cb-1ff8-e112-0abcb91b6040.png)
　　　↓↓↓
![考える人.png](https://qiita-image-store.s3.amazonaws.com/0/95804/6c0442d2-afd6-a232-1d7f-d98336c4317c.png)


いかに素早く失敗して
そこから学ぶかが重要だと思う今日この頃。
ご機嫌いかがでしょうか？jkです。

# 2. 想定読者
1. 最近っぽいインフラをやってみたい人
2. アプリの人でインフラをちょっとかじってみたい人
3. hashicorpツールをちょっとかじったことある人

# 3. インフラは、インフラだけのものではない。

いろいろな場面で、以下のようなシーンがある気がする。

「これは、インフラの問題だろ！」
「いやいや、アプリの使い方が悪いんだろ！」

もうどうでもいい。みんな全部やれ！笑笑

ということで
なんか開発の人でも、ぱっとできそうな感じで
インフラ構築「やってみた」を、紹介したいと思います。

# 4. 今回使うツール
- Packer： イメージ管理 （https://www.packer.io/)
- Ansible： 構成管理 （https://www.ansible.com/)
- Terraform： サーバー構築 （https://www.terraform.io/)
- Wordpress： ブログソフトウェア （https://ja.wordpress.org/)

主に、最近流行りの
hashicorpのツールから
「Packer」と「Terraform」に関して、簡単に紹介します。
Terraformは結構紹介されているのですが
Packerに関してはあまり紹介されていない気がするので
まずは、簡単にPackerの紹介。

# 5. Packer導入のメリット・デメリット
#### ◆ メリット
- プロビジョニング処理
- 類似の環境構築時の容易化
- Packer + Ansibleで作成したイメージでの、バージョン管理

#### ◆ デメリット
- ミドルウェアのバージョンアップを忘れそう。（セキュリティ等）
- 統合管理する無料ツールがない。（Atlasは有料・・・？）
https://www.hashicorp.com/atlas.html
- まだ開発途中（v0.12.0）なので、できないこともしばしば。

# 6. 早速、実際にやってみる
#### ◆ やること
Packer + AnsibleでWordpressをインストールされたAWS AMIを作成し
そのAMIを元に、Terraformで、サーバー構築すること

#### ◆ 前提
1. Wordpressのansibleのソースコードは以下が、ベース。
https://github.com/ansible/ansible-examples/tree/master/wordpress-nginx
2. 各種バージョン
    4. packer 0.12.0
    5. ansible 2.2.0.0
    6. Terraform v0.7.4

あとは、基本的には、ここみればok。
https://github.com/jkkitakita/hashicorp
以下、主要な部分抜粋。
## Packer + Ansible
https://www.packer.io/docs/provisioners/ansible-local.html

```variables.json
{
  "scripts_dir": "scripts",
  "ssh_username": "ec2-user",
  "aws_region": "ap-northeast-1",
  "aws_instance_type": "t2.micro",
  "aws_environment": "production",
  "aws_source_ami_id": "ami-########"
}
```

```packer-wordpress.json
{
  "builders": [{
    "type": "amazon-ebs",
    "region": "{{user `aws_region`}}",
    "source_ami": "{{user `aws_source_ami_id`}}",
    "instance_type": "{{user `aws_instance_type`}}",
    "ssh_username": "{{user `ssh_username`}}",
    "ssh_timeout": "5m",
    "ami_name": "Packer-ansible-{{timestamp}}",
    "tags": {
      "Name": "aws-{{user `aws_region`}}",
      "Environment": "{{user `aws_environment`}}",
      "Release": "Latest"
    }
  }],
  "provisioners": [
    {
      "type": "shell",
      "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'",
      "inline": [
        "mkdir -p /ops/{{user `scripts_dir`}}",
        "chmod a+w /ops/{{user `scripts_dir`}}"
      ]
    },
    {
      "type": "file",
      "source": "{{user `scripts_dir`}}/.",
      "destination": "/ops/{{user `scripts_dir`}}"
    },
    {
      "type": "shell",
      "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'",
      "inline": [
        "bash /ops/{{user `scripts_dir`}}/bootstrap.sh",
        "bash /ops/{{user `scripts_dir`}}/ansible.sh"
      ]
    },
    {
      "type": "ansible-local",
      "group_vars": "./ansible/group_vars",
      "playbook_file": "./ansible/site.yml",
      "role_paths": ["./ansible/roles/common", "./ansible/roles/mysql", "./ansible/roles/nginx", "./ansible/roles/php-fpm", "./ansible/roles/wordpress"],
      "staging_directory": "/tmp/packer-provisioner-ansible-local"
    }
  ]
}
```

実際に実行。

```
~/hashicorp/packer

 ~/h/packer ❯❯❯ packer build -var-file=variables.json packer-wordpress.json
amazon-ebs output will be in this color.

==> amazon-ebs: Prevalidating AMI Name...
    amazon-ebs: Found Image ID: ami-504f2037

~~ 中略 ~~

Build 'amazon-ebs' finished.

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:

ap-northeast-1: ami-504f2037
 ~/h/packer ❯❯❯
```

→ 作成されたAMI： <font color="Red">ami-504f2037</font>

## Terraform
https://www.terraform.io/docs/index.html

```main.tf
provider "aws" {
  region = "${var.region}"
}
```

```variable.tf
provider "aws" {
  region = "${var.region}"
}
 ~/h/terraform ❯❯❯ more variables.tf
variable "region" {
  default = "ap-northeast-1"
}

variable "ec2_config" {
  default = {
    key_name            = "jkkitakita"
    instance_type       = "t2.micro"
  }
}
```

```wordpress.tf
data "aws_ami" "wordpress" {
  filter {
    name = "state"
    values = ["available"]
  }
  filter {
    name = "tag:Release"
    values = ["Latest"] // PackerでAMI作成時につけたタグ
  }
  most_recent = true // 一番新しいものを取得
}

resource "aws_instance" "wordpress" {
  ami           = "${data.aws_ami.wordpress.id}"
  key_name      = "${lookup(var.ec2_config, "key_name")}"
  instance_type = "${lookup(var.ec2_config, "instance_type")}"
  tags {
      "Name" = "${format("wordpress%02d", count.index + 1)}"
  }
}
```

Dry-runからの、terraform実行

```
 ~/h/terraform ❯❯❯ terraform apply
data.aws_ami.wordpress: Refreshing state...
aws_instance.wordpress: Creating...
  ami:                      "" => "ami-504f2037" // Packerで作成したAMIを取得できてる。
  availability_zone:        "" => "<computed>"
  ebs_block_device.#:       "" => "<computed>"
  ephemeral_block_device.#: "" => "<computed>"
  instance_state:           "" => "<computed>"
  instance_type:            "" => "t2.micro"
  key_name:                 "" => "jkkitakita"
  network_interface_id:     "" => "<computed>"
  placement_group:          "" => "<computed>"
  private_dns:              "" => "<computed>"
  private_ip:               "" => "<computed>"
  public_dns:               "" => "<computed>"
  public_ip:                "" => "<computed>"
  root_block_device.#:      "" => "<computed>"
  security_groups.#:        "" => "<computed>"
  source_dest_check:        "" => "true"
  subnet_id:                "" => "<computed>"
  tags.%:                   "" => "1"
  tags.Name:                "" => "wordpress01"
  tenancy:                  "" => "<computed>"
  vpc_security_group_ids.#: "" => "<computed>"
aws_instance.wordpress: Still creating... (10s elapsed)
aws_instance.wordpress: Still creating... (20s elapsed)
aws_instance.wordpress: Creation complete

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate
 ~/h/terraform ❯❯❯
```

<img width="1064" alt="スクリーンショット 2016-12-13 17.49.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/95804/0cb3f701-3e22-9674-4131-2aa5ac462dda.png">


# 7. つまづいた&苦労したところ
1. pipのバージョンを合わせるのに少し手こずった。

2. mysql-pythonのバージョンはansible-exampleをそのまま使えなかったので、role/mysqlの方にタスクを追加した。

3. terraformのsyntaxに引っかかった。。。（Invalid dot index found）
http://qiita.com/kenjiskywalker/items/766859ac550650df4530

4. packerのprovisionorで、ansible（remote）でやろうとしたが、うまくいかなかったので、ansible（local）で書いた。（なんでうまくいかなかったのか忘れた。。）
https://www.packer.io/docs/provisioners/ansible-local.html

# 8. 今後やりたいこと
まだまだ「おもちゃ感」がすごいので、なんかちゃんと書きたい。

1. packer
    2. 複数種類のイメージ管理 <font color="Red">【やりたい！】</font>
    3. ansible以外の構成管理ツールとの連携 <font color="Red">【やりたい！】</font>
1. ansible
    2. ワードプレスの構成はコピーしただけなので、見直し
    3. 全くバージョン指定していないので、ちゃんとしたバージョン管理
    4. とりあえずなんでもかんでも、become（旧sudo）を入れているので、見直し
    5. ダイナミックインベントリとかも使ってみたい。
6. Terraform
    7. S3などでのバージョン管理 <font color="Red">【やりたい！】</font>
9. packer→terraformの連携が「タグ」と「起動時刻」で判定しているから不安でしかない。
→ Atlasが無料で使えれば・・・ <font color="Red">【やりたい！】</font>
10. Serverspec等による構成のテスト <font color="Red">【やりたい！】</font>
3. オートスケーリング <font color="Red">【やりたい！】</font>
4. Blue/Greenデプロイ <font color="Red">【やりたい！】</font>
5. Slackからのサーバー構築&自動デプロイ（チャットボット）<font color="Red">【やりたいが...】</font>
6. AWSだけでなく、GCPでもやってみたい。

# 9. まとめ
hashicorpのツールはまだまだ発展途上のものが多いが
すごい期待できそうな感じだった。
各種ツールでも同じようなツールがあるはずなので
それも今後試していこうと思います。
皆さんも簡単なので、試してみてください(｀・ω・´)ｷﾘｯ