CFn → Ansible → ServerspecをCircleCIでCIする


# 概要

CircleCIを使ってインフラのCIをやってみました。
Docker使う例が多いですが、ここはCloudformationでEC2を起動させます。
やることは下記です。

1. CloudformationでEC2インスタンスを起動する 
2. Ausibleでデプロイ
3. Serverspecでテスト 

# コード

Githubにアップしたコードは、ここにあります。
https://github.com/taishin/ec2-ci

主なものを説明します。

```json:cloudformation.json
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters" : {
    "AmiId" : {
      "Description" : "AMI ID",
      "Type" : "String"
    },
    "InstanceType" : {
      "Description" : "Instance Type",
      "Type" : "String"
    },
    "KeyName" : {
      "Description" : "Amazon EC2 Key Pair",
      "Type" : "AWS::EC2::KeyPair::KeyName"
    }
  },  
  "Resources": {
    "SSHGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable SSH access via port 22",
        "SecurityGroupIngress": [{
          "IpProtocol" : "tcp",
          "CidrIp" : "0.0.0.0/0",
          "FromPort" : "22", "ToPort" : "22"
        }]
      }
    },
    "TestServer" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "ImageId" : { "Ref" : "AmiId" },
        "InstanceType" : { "Ref" : "InstanceType" },
        "KeyName": { "Ref": "KeyName" },
        "SecurityGroupIds" : [
          { "Ref" : "SSHGroup" }
        ]
      }
    },
    "TestServerEip": {
      "Type": "AWS::EC2::EIP",
      "Properties": {
        "InstanceId": { "Ref": "TestServer" }
      }
    }
  },
  "Outputs" : {
    "PublicIP" : {
      "Description" : "Public IP Address",
      "Value" : { "Ref" : "TestServerEip" }
    }
  }
}
```
まず、Cloudformationですが、SecurityGroupを作って、EC2を起動させています。
CircleCIからSSHでアクセスするために、EIPをOutputに出力し、参照できるようにしています。
AMIとInstanceType、SSHキーは変更可能なように、パラメーターで定義しています。

```yaml:circle.yml
machine:
  timezone: Asia/Tokyo
  ruby:
    version:
      2.1.2
  environment:
    AWS_AMI_ID: ami-29160d47
    AWS_INSTANCE_TYPE: t2.micro
    AWS_CF_STACK_NAME: ci-test
    SSH_USER: ec2-user

dependencies:
  pre:
    - sudo pip install ansible

test:
  pre:
    - aws cloudformation create-stack --stack-name $AWS_CF_STACK_NAME --template-body file://cloudformation.json --parameters ParameterKey=KeyName,ParameterValue=$AWS_KEY_NAME ParameterKey=AmiId,ParameterValue=$AWS_AMI_ID ParameterKey=InstanceType,ParameterValue=$AWS_INSTANCE_TYPE
    - aws cloudformation wait stack-create-complete --stack-name $AWS_CF_STACK_NAME
    - aws cloudformation describe-stacks --stack-name $AWS_CF_STACK_NAME | jq -r '.Stacks[].Outputs[].OutputValue' > /tmp/publicip.txt
    - echo "`cat /tmp/publicip.txt` testserver" >> /tmp/hosts.txt
    - cp /etc/hosts .
    - sudo bash -c "cat hosts /tmp/hosts.txt | tee /etc/hosts"
    - echo "Host testserver" >> ~/.ssh/config
    - echo "User $SSH_USER" >> ~/.ssh/config
    - ssh -t $SSH_USER@testserver sudo yum update -y
    - ssh -t $SSH_USER@testserver sudo yum install epel-release -y
  override:
    - ansible-playbook -i ansible/hosts ansible/nginx.yml
    - bundle exec rake spec:testserver
  post:
    - aws cloudformation delete-stack --stack-name $AWS_CF_STACK_NAME
```

CircleCIの設定ファイルですが、environmentでAMIとInstanceTypeを指定しています。
別のAMI、InstanceTypeを使用する場合はここを編集します。
SSHログインするユーザー名も外出しにしているので、OSによって変更します。
preのところでは、testserverという名前でアクセスできるようにhostsやsshconfigを編集しています。

ansible、serverspecの内容は、nginxをインストールして、テストしているだけなので、省略します。

# CircleCI

Add ProjectsからGithubのリポジトリを選択し、Build Projectを押します。

![スクリーンショット 2016-05-19 22.16.51.png](https://qiita-image-store.s3.amazonaws.com/0/3246/21e2ed6b-4f5f-7f0f-8a78-472ac35eb6be.png "スクリーンショット 2016-05-19 22.16.51.png")

一旦、上部のcancel buildを押して、Buildを中止し、Project Settingsを押します。

![スクリーンショット 2016-05-19 22.19.25.png](https://qiita-image-store.s3.amazonaws.com/0/3246/ed5d098b-4e01-f505-87f6-51782d07836c.png "スクリーンショット 2016-05-19 22.19.25.png")

AWS Permissionsを押し、AccessKey、SecrectAccesskeyを入力します。

![スクリーンショット 2016-05-19 22.34.20.png](https://qiita-image-store.s3.amazonaws.com/0/3246/b44fa015-c15d-7b6c-f428-b15be950cabb.png "スクリーンショット 2016-05-19 22.34.20.png")

Environment Variablesをクリックし、
AWS_DEFAULT_REGION(リージョン名)、AWS_KEY_NAME(SSHキーの名前)を追加します。

![スクリーンショット 2016-05-19 22.36.06.png](https://qiita-image-store.s3.amazonaws.com/0/3246/01448e6a-44c6-79af-e7db-e29b1f4131c7.png "スクリーンショット 2016-05-19 22.36.06.png")

Buildの画面に戻り、Rebuildをクリックすると、Buildが始まります。

![スクリーンショット 2016-05-19 22.39.15.png](https://qiita-image-store.s3.amazonaws.com/0/3246/12b4edb5-42cb-91cf-06b7-163a9cd07a6f.png "スクリーンショット 2016-05-19 22.39.15.png")

成功するとSuccessの画面になります。

![スクリーンショット 2016-05-19 22.41.12.png](https://qiita-image-store.s3.amazonaws.com/0/3246/4b9cff8f-ed4a-1f05-277f-f94016a26516.png "スクリーンショット 2016-05-19 22.41.12.png")

コードを修正し、GithubにPushすると、自動的にBuildが始まります。

# まとめ

CircleCIを使うことで、CloudFormationによるAWS環境、Ansibleによるデプロイ、Serverspecによるテストまで、自動的に行う環境ができました。
ただ、Cloudformationは完了まで結構時間がかかります。<del>(circle.ymlにsleep 120を入れいています。)</del>
CFnの完了まで待ってくれる、aws cloudformation waitというコマンドを教えてもらいましたので、変更しました。
今回はEC2の起動だけですが、内容にもってはもっと時間を長くする必要があると思います。