EC2 Image Builder + Ansible + AWS CLI を用いたゴールデンAMIの更新/反映自動化の検討

Daichi Harada
Dec 19, 2019 · 15 min read
Image for post
Image for post
re:Invent2019 で発表された EC2 Image Builder

この記事は「eureka Advent Calendar 2019」19日目の記事です。昨日の記事は Yuya Kaidoによる Android開発におけるモジュール分割でした。

みなさんこんにちは! エウレカSRE チームの Daichi Harada です。

先日、念願の AWS re:Invent に行ってきました!様々な新サービス/新機能の発表がありワクワクしています。中でも一番刺さったサービスが、EC2 Image Builderでした。今回はこれを触ってみようと思います。

AWSさま主導 + Rettyさま、Metapsさま、C Channelさま、eurekaの4社で合同開催した住友不動産麻布十番ビルRecapイベントのレポート記事も合わせてご覧ください!

この記事で得られること

この記事では以下について検証しています。

  • EC2 Image BuilderでAnsible playbookを流す方法
  • ゴールデンAMIのASGへの反映をAWS CLIで自動化する方法

背景

弊社では2017年頃からゴールデンAMI方式のプロビジョニングを採用しており、ゴールデンAMIの作成には Packer + Ansible を使用しています。

以下記事でも指摘されているように、ゴールデンAMIのプロビジョニングと起動設定の変更は自動化されておらず、セキュリティパッチをあてる頻度や、反映作業の手間が問題となっていました。

PairsのAPIサーバはこの課題をコンテナ化で大枠解決したものの、その他のコンポーネントはまだEC2での運用となっているので、まだしばらくこのAMI反映のつらさとは付き合わないといけなさそうです。

今回はEC2 Image Builder と AWS CLI を使うことでゴールデンAMIの作成、反映までを省力化できないか検証してみました。

① EC2 Image BuilderでAnsible playbookを実行するPipelineの作成

EC2 Image Builderのパイプライン(Packerにあたる)と、テストの仕組み(Serverspecなどにあたる) には乗りたいものの、既存の Ansible playbook を書き直すのは大変なので、なるべくそのまま使いたいです。

そこで、EC2 Image BuilderでAnsibleを使えるのか試してみます。

Ansible playbook

cowsay を入れるだけの Ansible playbook です。 (本来は、これまでゴールデンAMIを作成するために使っていた複雑なplaybookが入る想定です)

---
- hosts: 127.0.0.1
tasks:
- name: install cowsay
yum:
name: cowsay
state: latest

https://github.com/dharada1/ansible-install-cowsay.git

EC2 Image Builder コンポーネント

EC2 Image Builderのコンポーネントのymlです。Ansible playbookを呼び出して実行するものです。

name: execute ansible playbook
schemaVersion: 1.0
phases:
- name: build
steps:
- name: install-ansible
action: ExecuteBash
inputs:
commands:
- sudo amazon-linux-extras install ansible2 -y
- name: install-git
action: ExecuteBash
inputs:
commands:
- sudo yum install git -y
- name: clone-playbook-repository
action: ExecuteBash
inputs:
commands:
- git clone https://github.com/dharada1/ansible-install-cowsay.git
- name: run-playbook
action: ExecuteBash
inputs:
commands:
- ansible-playbook ./ansible-install-cowsay/playbook.yml
- name: test
steps:
- name: test-cowsay
action: ExecuteBash
inputs:
commands:
- cowsay -f skeleton "I've finally installed with ansible playbook & EC2 Image Builder."

パイプラインの作成

以下手順でパイプラインを作成します。(詳細は割愛します。)

  • 上記ymlをコピペでコンポーネントの作成
  • コンポーネントを呼ぶレシピ作成
  • レシピからパイプラインを作成

実行

パイプラインを実行します。(コンソール上のRun Pipelineを押す)

Image for post
Image for post
Run Pipeline

実体としては裏でSSM Automationが動いていて、その様子はこちらで見ることができます。

Image for post
Image for post
SSM Automation

動作確認

Image for post
Image for post
cowsay

完成したamiでEC2を起動し、sshしてcowsayを打ってみます。

cowsay -f skeleton "I've finally installed with ansible playbook & EC2 Image Builder."

(テストを通っているので当たり前ですが) Ansible playbookが実行された結果、cowsayがインストールされていることが確認できます。

感想

  • ansible-vault での暗号化を使っている場合 SSM Parameter Store / Secrets Managerなどにpasswordを保存しておき、それを読み込むのがよいかと思います。
  • Packer + Ansibleモデルで採用していた、local環境/CodebuildからEC2へのAnsible playbook実行というのは実現しづらそうでした。playbookを1リポジトリで管理している場合、全体をEC2の中に全コピーするのは気持ち悪さがあるので、このパイプラインの手前に環境毎のplaybookを切り出してS3などに配置しておくレイヤーを挟むのもいいかもと思いました。

②AWS CLIでゴールデンAMIの反映を自動化

ここまで触ってみてわかったのですが、EC2 Image BuilderはいまのところゴールデンAMIの配布までしかしてくれないので、ASG / EC2への反映は別の仕組みでやる必要があります。

(アップデートに期待…)

今回はAWS CLIでスクリプトを組んでみたいと思います。

AWS CLIによるパイプライン実行からAMI取得までの自動化

Run Pipeline -> AMIの取得までは、コンソールを開かずとも以下のようなスクリプトで自動化可能です。

IMAGE_PIPELINE_ARN=<<パイプラインのARNをコピペしてください>># image builderの開始
IMAGE_BUILD_VERSION_ARN=`\
aws imagebuilder start-image-pipeline-execution \
--image-pipeline-arn $IMAGE_PIPELINE_ARN \
| jq .imageBuildVersionArn -r \
`
# 完了待ち
while [[ $STATUS != "AVAILABLE" ]]
do
STATUS=`\
aws imagebuilder get-image \
--image-build-version-arn $IMAGE_BUILD_VERSION_ARN \
| jq .image.state.status -r \
`
ECHO $STATUS
sleep 5
done
ECHO $STATUS
# ami取得
aws imagebuilder get-image \
--image-build-version-arn $IMAGE_BUILD_VERSION_ARN \
| jq .image.outputResources.amis[0].image -r

AWS CLIによるASGへのAMI反映の自動化

このスクリプトで取得したamiをtest-asgのlaunch configに反映します。これで、AutoScalingGroupで新しいEC2を立てるときは更新後のamiが使われるようになります。

DATE=$(date "+%s")
aws autoscaling create-launch-configuration \
--launch-configuration-name test-$DATE \
--image-id <<取得したAMI ID>> \
--instance-type t2.small
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name test-asg \
--launch-configuration-name test-$DATE

新AMIから作成したEC2の展開 / 旧EC2の退役

ASGにAMIが反映されたら、新AMIから作成したEC2の展開 / 旧EC2の退役を行います。以下の手順で作業しています。

  • EC2台数を二倍にする (新AMIでEC2が起動する)
  • EC2台数を元に戻す (終了ポリシーにより旧AMIのEC2が退役する)

AWS CLIでこの操作を自動化するスクリプトを仮組みしてみました。※インスタンスのヘルスチェックやエラーハンドリングなど考慮できていない点が多いので、本番で使うにはまだ不安があります。

# EC2の台数を取得.
# TODO (min max desiredがinstancesと揃ってること前提 x healthy & inserviceなこと前提...)
CURRENT_INSTANCE_NUM=`\
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names=$1 \
--profile=$2 \
--region=$3 \
--query "AutoScalingGroups[0].length(Instances)" \
2>/dev/null\
`
# EC2台数を2倍にする
DOUBLED_INSTANCE_NUM=`expr 2 \* $CURRENT_INSTANCE_NUM`
echo "現在のEC2台数:" $CURRENT_INSTANCE_NUM
echo "二倍のEC2台数:" $DOUBLED_INSTANCE_NUM
echo "二倍のEC2台数 ${DOUBLED_INSTANCE_NUM} 台へアップデートします"
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name=$1 \
--profile=$2 \
--region=$3 \
--min-size=$DOUBLED_INSTANCE_NUM \
--max-size=$DOUBLED_INSTANCE_NUM \
--desired-capacity=$DOUBLED_INSTANCE_NUM \
2>/dev/null
# 2倍になるまで待機.
while [[ $UPDATING_INSTANCE_NUM != $DOUBLED_INSTANCE_NUM ]]
do
UPDATING_INSTANCE_NUM=`\
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names=$1 \
--profile=$2 \
--region=$3 \
--query "AutoScalingGroups[0].length(Instances)" \
2>/dev/null\
`
echo "EC2の立ち上がりを待機しています..."
echo "現在の台数" $UPDATING_INSTANCE_NUM "期待台数" $DOUBLED_INSTANCE_NUM
sleep 5
done
# OK!
echo "現在のEC2台数:" $UPDATING_INSTANCE_NUM
echo "二倍のEC2台数:" $DOUBLED_INSTANCE_NUM
echo "二倍になりました。"
# 下げる
echo "最初のEC2台数 ${CURRENT_INSTANCE_NUM} 台へアップデートします"
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name=$1 \
--profile=$2 \
--region=$3 \
--min-size=$CURRENT_INSTANCE_NUM \
--max-size=$CURRENT_INSTANCE_NUM \
--desired-capacity=$CURRENT_INSTANCE_NUM \
2>/dev/null
# もとに戻るまで待機.
while [[ $UPDATING_INSTANCE_NUM != $CURRENT_INSTANCE_NUM ]]
do
UPDATING_INSTANCE_NUM=`\
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names=$1 \
--profile=$2 \
--region=$3 \
--query "AutoScalingGroups[0].length(Instances)" \
2>/dev/null\
`
echo "EC2のシャットダウンを待機しています..."
echo "現在の台数" $UPDATING_INSTANCE_NUM "期待台数" $CURRENT_INSTANCE_NUM
sleep 5
done
# EC2台数がもとに戻った.
echo "現在のEC2台数:" $UPDATING_INSTANCE_NUM
echo "入れ替え完了しました."

実行すると以下のようになります。

❯ ./autoscale.sh test-asg my-account ap-northeast-1
現在のEC2台数: 1
二倍のEC2台数: 2
二倍のEC2台数 2 台へアップデートします
EC2の立ち上がりを待機しています...
現在の台数 1 期待台数 2
EC2の立ち上がりを待機しています...
現在の台数 1 期待台数 2
EC2の立ち上がりを待機しています...
現在の台数 2 期待台数 2
現在のEC2台数: 2
二倍のEC2台数: 2
二倍になりました。
最初のEC2台数 1 台へアップデートします
EC2のシャットダウンを待機しています...
現在の台数 2 期待台数 1
EC2のシャットダウンを待機しています...
現在の台数 2 期待台数 1
~~~~~~~~~~~~~~~中略~~~~~~~~~~~~~~~
現在の台数 2 期待台数 1
EC2のシャットダウンを待機しています...
現在の台数 1 期待台数 1
現在のEC2台数: 1
入れ替え完了しました.

パイプライン化

これらのスクリプトを組み合わせてローカルから流してもいいのですが、CodeBuildなりSSM Automationなどからも実行できるのではないかと思います。

まとめ

この記事では

  • EC2 Image Builder と組み合わせて Ansible playbook を実行する方法
  • AWS CLIを使って新AMIの反映、ASG/EC2への展開までを自動化する方法

を検証/検討しました。

既存のAnsible playbookを資産として活かしながら、Packerで実現していたAMI作成、またServerspecのようなテストのパイプラインをAWSの仕組みに乗ることができそうです。またAMIをEC2へ反映する部分まで含めた自動化も、AWS CLIで実現できそうです。

引き続き検証しつつ、アップデートを待ちつつ、移行するタイミングを見計らっていきたいと思います。

Eureka Engineering

Learn about Eureka’s engineering efforts, product…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store