背景
クラウドサービスではインスタンスが時間課金のため、JenkinsなどのCIサーバを起動すると利用しない時間は無駄なコストが発生してしまいます。
コンバートなどの高負荷ジョブを実行している場合はインスタンスタイプも大きなものを用意する必要があるため、AWS Batchを利用したくなります。
AWS Batch – AWSでバッチ処理ジョブを実行する
ですが、AWS Batchはプレビューのため、申請しないと使うことができません。
そんな時、以下の二つの記事
Jenkinsのジョブの実行環境にAmazon EC2 Container Service ( ECS ) を活用
Spot FleetでAmazon ECSクラスタを強力に
を見つけたので、JenkinsとECSとSpot Fleetをうまく組み合わせられないかということで試してみました。
本記事で利用しているCFnテンプレートはGitHubで公開しています。
https://github.com/shigewa/jenkins-using-ecs-and-spotfleet
aws cliのバージョン確認
aws --version
aws-cli/1.11.32 Python/2.7.10 Darwin/15.6.0 botocore/1.4.89
構成概要
Jenkinsインスタンス構築前の準備
SpotFleetが利用するIAMロール作成
- SpotFleet初回利用時にデフォルトで作成されるIAMロールが存在するか確認
aws iam get-role \
--role-name "aws-ec2-spot-fleet-role"
存在する場合は JenkinsインスタンスとECS Clusterが利用するIAMロールの作成
に進んでください。
- デフォルトIAMロールを作成
aws cloudformation --region ap-northeast-1 create-stack \
--stack-name SpotFleetRole \
--capabilities CAPABILITY_NAMED_IAM \
--template-body file://create-spotfleet-role.yaml
- ロールのARNを確認
SPOT_FLEET_ROLE_ARN=$( \
aws iam get-role \
--role-name "aws-ec2-spot-fleet-role" \
--query "Role.Arn" \
--output text \
) \
&& echo "${SPOT_FLEET_ROLE_ARN}"
JenkinsインスタンスとECS Clusterが利用するIAMロール、インスタンスプロファイルの作成
- IAMロール名の決定
JENKINS_ROLE_NAME='Jenkins_Instance_Role'
- 同じ名前のロールがないことを確認
既に存在する場合は別名にするもしくはIAMロールの確認作業
までスキップしてください。
aws iam get-role \
--role-name ${JENKINS_ROLE_NAME}
- IAMロールポリシーの決定
管理ポリシーを確認します。
aws iam list-policies \
--scope AWS \
--max-items 1000 \
--query 'Policies[].PolicyName'
利用するポリシーを決めます。
以下は設定例です。
IAM_POLICY_NAME1='AmazonEC2FullAccess'
IAM_POLICY_NAME2='AmazonEC2ContainerServiceFullAccess'
- IAMロールにアタッチするポリシーのARNを取得
IAM_POLICY_ARN1=$( \
aws iam list-policies \
--max-items 1000 \
--query "Policies[?PolicyName==\`${IAM_POLICY_NAME1}\`].Arn" \
--output text \
) \
&& echo "${IAM_POLICY_ARN1}"
IAM_POLICY_ARN2=$( \
aws iam list-policies \
--max-items 1000 \
--query "Policies[?PolicyName==\`${IAM_POLICY_NAME2}\`].Arn" \
--output text \
) \
&& echo "${IAM_POLICY_ARN2}"
aws cloudformation --region ap-northeast-1 create-stack \
--stack-name ${JENKINS_ROLE_NAME} \
--capabilities CAPABILITY_NAMED_IAM \
--template-body file://create-jenkins-instance-role.yaml \
--parameters \
ParameterKey=AttachJenkinsRoleARNs,ParameterValue="${IAM_POLICY_ARN1}\,${IAM_POLICY_ARN2} \
ParameterKey=RoleName,ParameterValue="{JENKINS_ROLE_NAME}"
- 作成したIAMロールとインスタンスプロファイルのARNを確認
JENKINS_INSTANCE_PROFILE_ARN=$( \
aws iam list-instance-profiles \
--query "InstanceProfiles[?contains(Arn,\`${JENKINS_ROLE_NAME}\`)].Arn" \
--output text \
)
&& echo "${JENKINS_INSTANCE_PROFILE_ARN}"
JENKINS_ROLE_ARN=$( \
aws iam list-instance-profiles \
--query "InstanceProfiles[?contains(Arn,\`${JENKINS_ROLE_NAME}\`)].Roles[].Arn" \
--output text \
)
&& echo "${JENKINS_ROLE_ARN}"
結果が返却されない場合はCFnがスタック生成中の可能性がありますので、時間を置いて再実行してみてください。
Jenkinsインスタンスの構築
- 以下の変数は実行環境に応じて修正してください。
REAGION="ap-northeast-1"
VPC_ID="hogehoge"
JENKINS_MASRER_SUBNET="fugafuga"
JENKINS_SLAVE_SUBNET='hoge\,fuga'
IAM_FLEET_ROLE_ARN=$( \
aws iam get-role \
--role-name "aws-ec2-spot-fleet-role" \
--query "Role.Arn" \
--output text \
) \
IAM_INSTANCE_PROFILE_ARN=$( \
aws iam list-instance-profiles \
--query "InstanceProfiles[?contains(Arn,\`${JENKINS_ROLE_NAME}\`)].Arn" \
--output text \
)
KEY_NAME="Jenkins_Using_ECS_and_SpotFleet"
MASTER_JENKINS_INSTNCE_TYPE="t2.micro"
SLAVE_JENKINS_SPOT_INSTANCE_TYPE="c3.large"
# Amazon Linux
MASTER_JENKINS_AMIID="ami-0c11b26d"
# ECS Optimized Amazon linux
SLAVE_JENKINS_AMIID="ami-08f7956f"
SLAVE_CAPACITY=1
MAX_SPOT_PRICE=0.128
- Master Slave Jenkinsが通信するSGを作成
aws cloudformation --region ${REAGION} create-stack \
--stack-name JenkinsSG \
--template-body file://create-jenkins-sg.yaml \
--parameters ParameterKey=VpcId,ParameterValue=${VPC_ID}
- Master Jenkinsインスタンスを作成
デフォルトでアタッチするSG等がある場合はcreate-jenkins-master-instance.yaml
のSecurityGroupIds
を修正してください
aws cloudformation --region ${REAGION} create-stack \
--stack-name JenkinsMasterStack \
--template-body file://create-jenkins-master-instance.yaml \
--parameters \
ParameterKey=VpcId,ParameterValue=${VPC_ID} \
ParameterKey=SubnetId,ParameterValue=${JENKINS_MASRER_SUBNET} \
ParameterKey=KeyName,ParameterValue=${KEY_NAME} \
ParameterKey=AmiId,ParameterValue=${MASTER_JENKINS_AMIID} \
ParameterKey=InstanceType,ParameterValue=${MASTER_JENKINS_INSTNCE_TYPE}
- Slave Jenkins用のインスタンスとECS Clusterを作成
デフォルトでアタッチするSG等がある場合はcreate-spotfleet-jenkins-slave.yaml
のSecurityGroups
を修正してください
aws cloudformation --region ${REAGION} create-stack \
--stack-name SpotFleetJenkinsSlave \
--template-body file://create-spotfleet-jenkins-slave.yaml \
--parameters \
ParameterKey=MaxSpotPrise,ParameterValue=${MAX_SPOT_PRICE} \
ParameterKey=TargetCapacity,ParameterValue=${SLAVE_CAPACITY} \
ParameterKey=InstanceType,ParameterValue=${SLAVE_JENKINS_SPOT_INSTANCE_TYPE} \
ParameterKey=SpotFleetAMI,ParameterValue=${SLAVE_JENKINS_AMIID} \
ParameterKey=KeyName,ParameterValue=${Diffchecker_Jenkins} \
ParameterKey=IamFleetRoleARN,ParameterValue=${IAM_FLEET_ROLE_ARN} \
ParameterKey=SubnetId,ParameterValue=${JENKINS_SLAVE_SUBNET}
Jenkinsの初期設定
構築されたJenkins masterインスタンスにアクセスし、画面表示どおり /var/lib/jenkins/secrets/initialAdminPassword
に書かれたパスワードを入力します。
特にこだわりの設定がない場合は「Install suggested plugins」を選択肢、デフォルトのプラグインをインストールします。
adminユーザー情報を入力します。
Jenkins pluginの導入
以下の手順に従ってECS pluginを導入し、指定するECS Clusterは上記で作成したECS Clusterを指定してください。
https://wiki.jenkins-ci.org/display/JENKINS/Amazon+EC2+Container+Service+Plugin
環境の削除
aws cloudformation --region ${REAGION} delete-stack \
--stack-name SpotFleetJenkinsSlave
aws cloudformation --region ${REAGION} delete-stack \
--stack-name JenkinsMasterStack
aws cloudformation --region ${REAGION} delete-stack \
--stack-name JenkinsSG
aws cloudformation --region ${REAGION} delete-stack \
--stack-name SpotFleetRole
備考
SpotFleetを利用しているため、 MAX_SPOT_PRICE
が低すぎる場合はスポットインスタンスが起動できません。
参考URL
Jenkins Amazon EC2 Container Service Plugin
AWSで継続的インテグレーション 〜 Jenkins編 | アドカレ2013 : CFn #9
Jenkinsのジョブの実行環境にAmazon EC2 Container Service ( ECS ) を活用
[JAWS-UG CLI] IAM:#22 IAMロールの作成 (ECSコンテナインスタンス)
Spot FleetでAmazon ECSクラスタを強力に