概要
CircleCIを使ってインフラのCIをやってみました。
Docker使う例が多いですが、ここはCloudformationでEC2を起動させます。
やることは下記です。
- CloudformationでEC2インスタンスを起動する
- Ausibleでデプロイ
- Serverspecでテスト
コード
Githubにアップしたコードは、ここにあります。
https://github.com/taishin/ec2-ci
主なものを説明します。
{
"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キーは変更可能なように、パラメーターで定義しています。
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を押します。
一旦、上部のcancel buildを押して、Buildを中止し、Project Settingsを押します。
AWS Permissionsを押し、AccessKey、SecrectAccesskeyを入力します。
Environment Variablesをクリックし、
AWS_DEFAULT_REGION(リージョン名)、AWS_KEY_NAME(SSHキーの名前)を追加します。
Buildの画面に戻り、Rebuildをクリックすると、Buildが始まります。
成功するとSuccessの画面になります。
コードを修正し、GithubにPushすると、自動的にBuildが始まります。
まとめ
CircleCIを使うことで、CloudFormationによるAWS環境、Ansibleによるデプロイ、Serverspecによるテストまで、自動的に行う環境ができました。
ただ、Cloudformationは完了まで結構時間がかかります。(circle.ymlにsleep 120を入れいています。)
CFnの完了まで待ってくれる、aws cloudformation waitというコマンドを教えてもらいましたので、変更しました。
今回はEC2の起動だけですが、内容にもってはもっと時間を長くする必要があると思います。