Terraform+CodeDeploy[Ansible]って使い物になるのか


よく大宇宙状態(謎)になっている先輩が、terraform+codedeploy+ansibleを試していたので
本当に使えるかどうか実践してみました。

AWS CodeDeployを使ってAnsible or itamae + Serverspec
http://qiita.com/gamisan9999/items/c4e26bf99189bed82748

## 全体像

先輩の記事だけ見ていると何しているかよくわかんなかったので、わかりやすくしてみました。

ちなみに、TerraformとCodeDeploy+Ansibleは完全に別物です。
連携はまったくありませんのであしからず。

TerraformでAWS環境を構築、EC2とかCodeDeployとかもろもろ作成。
AnsibleレシピをCodedeployでS3にアップ。
CodeDeployからS3のファイルをデプロイ。
後は、対象のローカルで指定のものが動きます。
今回はAnsibleです。

![terraform-codedeploy.png](https://f001.backblaze.com/b2api/v1/b2_download_file_by_id?fileId=4_z43eaf387432065b9561d0b15_f11203b1fdff6ce26_d20151204_m043437_c001_v0001012_t0008)

# AWS環境構築
## CodeDeploy Agent

CodeDeployを使用するには、サーバー側でCodeDeployAgentを起動しておく必要があります。

http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/how-to-run-agent.html

しかしAgentを入れるのに、わざわざBaseAMIを用意するのは面倒なので

インスタンス起動時のuser_dataに以下のスクリプトを入れれば
codedeployのAgentが入ります。

http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/how-to-set-up-new-instance.html

```
#!/bin/bash
yum -y update
yum install -y ruby
yum install -y aws-cli
cd /home/ec2-user
aws s3 cp s3://bucket-name/latest/install . --region region-name
chmod +x ./install
./install auto
```

yum updateとか不要であれば外せます。

AWS-CLIで上げるならこんな感じ。

```
aws ec2 run-instances \
  --image-id amiID \
  --key-name keyName \
  --user-data file://instance-setup.sh \
  --count 1 \
  --instance-type instanceType \
  --iam-instance-profile Name=CodeDeployDemo-EC2-Instance-Profile
```

## AWS環境構築

例に習ってTerraformで環境構築。
AutoScaleとかしてないで、1インスタンス上げるだけ。

TerraFormの使い方は別途グーグル先生と公式ドキュメントを参考にしていただきたく。
別にTerraform使わなくても大丈夫です。

### EC2

気にするところは、user_dataとタグ名。

user_dataはCodeDeployAgent用、
タグはDeploy対象の時のフィルターに使います。

```
resource "aws_instance" "web-1" {
  instance_type = "${var.web_instance_type}"
  ami = "${var.ami_id}"
  subnet_id = "${aws_subnet.public-1c-subnet.id}"
  security_groups = ["${aws_security_group.common.id}"]
  associate_public_ip_address = true
  key_name  = "${var.key_name}"
  iam_instance_profile = "${aws_iam_instance_profile.public-profile.name}"
  user_data = "${file("./shells/user_data.sh")}" ※ここにuser_data指定
  tags = {
    Name = "web-1"
    Role = "web" ※codedeployに用にタグ指定
  }
  root_block_device = {
    volume_type = "gp2"
    volume_size = "100"
  }
}
```

## S3

CodeDeployで使用するS3バケットを作る。
バージョニングを有効にする。

```
resource "aws_s3_bucket" "deploy_bucket" {
    bucket = "hogehoge-deploy-bucket"
    acl= "private"
    region = "ap-northeast-1"
    tags {
        Name = "hogehoge-deploy-bucket"
    }
    versioning {
      enabled = true
    }
}
```

## Codedeploy

### IAM

CodeDeploy自体が使用するIAMRoleを作成。

http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/how-to-create-service-role.html

```
resource "aws_iam_role" "codedeploy_role" {
    name = "codedeploy_role"
    assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "codedeploy.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}


resource "aws_iam_role_policy" "codedeploy_policy" {
    name = "codedeploy_policy"
    role = "${aws_iam_role.codedeploy_role.id}"
    policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "autoscaling:CompleteLifecycleAction",
                "autoscaling:DeleteLifecycleHook",
                "autoscaling:DescribeAutoScalingGroups",
                "autoscaling:DescribeLifecycleHooks",
                "autoscaling:PutLifecycleHook",
                "autoscaling:RecordLifecycleActionHeartbeat",
                "ec2:DescribeInstances",
                "ec2:DescribeInstanceStatus",
                "tag:GetTags",
                "tag:GetResources"
            ],
            "Resource": "*"
        }
    ]
}
EOF
}
```


### CodeDeployの定義を作成

対象インスタンスのフィルターはタグ名で行ってます。
先輩の記事には、タグが2つ指定されてますが、CodeDeployのタグはAND条件ではないので、
2つ付けても対象が増えるだけです。

```
resource "aws_codedeploy_app" "web-provisioning"
{
  name = "web-provisioning"
}

resource "aws_codedeploy_deployment_group" "web-provisioning-group"
{
  app_name = "${aws_codedeploy_app.web-provisioning.name}"
  deployment_group_name = "web-group"
  service_role_arn = "${aws_iam_role.codedeploy_role.arn}"
  ec2_tag_filter {
    key = "Role"
    value = "web"
    type = "KEY_AND_VALUE"
  }
  deployment_config_name = "CodeDeployDefault.AllAtOnce"
}
```



これでAWS構築は終わり。


# Ansible + CodeDeploy

## Ansible作成

動作としては、S3に配置しているAnsibleを配布、各サーバ自身でLocalHost宛に実行します。

appspec.ymlの書き方さえわかれば、後はAnsibleの書き方がわかれば大丈夫です。

ディレクトリ階層はこちらを参考にしてます。

https://github.com/awslabs/aws-codedeploy-samples/tree/master/conf-mgmt/ansible/local-only

appspec.yml

```
version: 0.0
os: linux
files:
  - source: /
    destination: /etc/ansible/codedeploy
hooks:
  BeforeInstall:
    - location: before_install.sh
      timeout: 300
      runas: root
  ApplicationStart:
    - location: application_start.sh
      timeout: 300
      runas: root
  ValidateService:
    - location: verify_service.sh
      timeout: 300
      runas: root
```

## CodeDeployにpush

webというフォルダに、先ほどのファイル構成のものを準備してpushする。

```
aws deploy push \
--application-name web-provisioning \
--s3-location s3://hogehoge-deploy-bucket/web-ansible.zip \
--source ./web/ \
--ignore-hidden-files \
--profile hoehoge \
--region ap-northeast-1
```


## Deploy

後はCLIでpushするもよし、ConsoleからDeployするもよしとなります。

![AWS_CodeDeploy_Management.jpg](https://qiita-image-store.s3.amazonaws.com/0/49877/b657d735-403f-e955-23a9-2175a86a7160.jpeg)


#使ってみて

Terraformは便利です。

CodeDeployはAnsibleを作る手間と、一々pushするのが面倒でした。
また、pushした後のDeployも面倒。

現状だと、ローカルからAnsibleを普通に実行したほうが便利じゃないか？っと思いましたが、
バージョン管理等はCodeDeployが優れているかなと思いました。

Ansibleの資産があっても、修正する手間は多少あるのでその分の価値が見いだせるかというと微妙。

Auto Scaling 等の組み合わせがCodeDeployにはよさそうですので、もう少し触ってから結論を出したいと思います。