この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
コンニチハ、千葉です。
Systems ManagerのAutomation機能を利用するとカスタムAMIの作成を自動化できます。
AutoScaling利用時、Packer等でカスタムAMIを作成すると思いますがこれの置き換えを想定しています。EC2上でPackerを動かしている場合、EC2を減らすことができるかなと思います。
環境
以下の環境で試しました
- ソースAMI:Amazon Linux
- リポジトリ:CodeCommit
- プロビジョニング:Ansible 2.2.3.0
やってみた
リポジトリ作成
Ansibleコードを格納するリポジトリを作成します。リポジトリは、GitHubやS3などでもいいと思いますが、今回はEC2利用時のリポジトリへのアクセスをシームレスに行えるCodeCommitを選択しました。EC2へIAMロールをアタッチすることで簡単でセキュアにCodeCommitへアクセスすることができます。
リポジトリを作成します。
Ansible Playbookをリポジトリへプッシュ
Ansible PlaybookをCodeCommitへpushします。
今回はサンプルとして以下のファイルをpushしました。Nginxをインストールし起動します。
local$ tree
.
├── hosts
├── nginx
│ └── tasks
│ └── main.yml
└── web.yml
各ファイルの中身です
local$ cat hosts
[local]
localhost
local$
calocal$ cat web.yml
#
# web.yml
#
- hosts: local
connection: local
become: yes
roles:
- nginx
local$ cat nginx/tasks/main.yml
---
- name: nginx installed
yum:
name: nginx
- name: nginx service started
service:
name: nginx
state: started
enabled: yes
IAMロール作成
2つIAMロールを作成します。
Automation実行用IAMロール
1つめのIAMロールとして、Automationへ割り当てるIAMロールを作成します。 このIAMロールは、Automationサービスが実行できる権限の指定となります。
今回は、AutomationRole という名前で作成しました。ロールにはAmazonSSMAutomationRole をアタッチします。
追加でインラインポリシーに以下のポリシーを作成します。追加する権限はEC2インスタンスにIAMロールをアタッチするための権限です。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:PassRole"
],
"Resource": [
"*"
]
}
]
}
次に信頼関係を編集します。SSMサービのassumeRoleを許可します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ssm.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
EC2インスタンスアタッチ用IAMロール
EC2インスタンスにアタッチするIAMロールを作成します。このIAMロールには、CodeCommitの読み取りと、SSMの実行許可を付与します。
ドキュメント作成
Automationで実行する内容を定義したドキュメントを作成します。ドキュメントの書き方はAmazon EC2 Systems Manager の Automation をやってみた #reinventを参考に。
まず、EC2のコンソールからドキュメントを選択します。
ドキュメントのタイプは「Automation」を選択します。下記のコードをコピってはります。
{
"schemaVersion":"0.3",
"description":"Update a Linux AMI. By default this provisioning with Ansible.",
"assumeRole":"{{AutomationAssumeRole}}",
"parameters":{
"SourceAmiId":{
"type":"String",
"description":"(Required) The source Amazon Machine Image ID."
},
"InstanceIamRole":{
"type":"String",
"description":"(Required) An IamInstanceProfileName that is allowed to perform RunCommand on an EC2 instance."
},
"AutomationAssumeRole":{
"type":"String",
"description":"(Required) Role under which to execute this automation."
},
"TargetAmiName":{
"type":"String",
"description":"(Optionacd) The name of the new AMI that will be created. Default is a system-generated string including the source AMI id, and the creation time and date.",
"default":"UpdateLinuxAmi_from_{{SourceAmiId}}_on_{{global:DATE_TIME}}"
},
"InstanceType":{
"type":"String",
"description":"(Optional) Type of instance to launch as the workspace host. Instance types vary by region. Default is t2.micro.",
"default":"t2.micro"
},
"AnsiblePlaybookRepo":{
"type":"String",
"description":"(Required) The URL of CodeCommitRepository(HTTPS)."
},
"AnsibleVersion":{
"type":"String",
"description":"(Option) Version of Ansible. Defalt is 2.2.3.0",
"default":"2.2.3.0"
},
"AnsiblePlaybook":{
"type":"String",
"description":"(Required) yml of AnsiblePlaybook. ex: web.yml"
}
},
"mainSteps":[
{
"name":"launchInstance",
"action":"aws:runInstances",
"maxAttempts":3,
"timeoutSeconds":1200,
"onFailure":"Abort",
"inputs":{
"ImageId":"{{SourceAmiId}}",
"InstanceType":"{{InstanceType}}",
"UserData":"IyEvYmluL2Jhc2gNCg0KZnVuY3Rpb24gZ2V0X2NvbnRlbnRzKCkgew0KICAgIGlmIFsgLXggIiQod2hpY2ggY3VybCkiIF07IHRoZW4NCiAgICAgICAgY3VybCAtcyAtZiAiJDEiDQogICAgZWxpZiBbIC14ICIkKHdoaWNoIHdnZXQpIiBdOyB0aGVuDQogICAgICAgIHdnZXQgIiQxIiAtTyAtDQogICAgZWxzZQ0KICAgICAgICBkaWUgIk5vIGRvd25sb2FkIHV0aWxpdHkgKGN1cmwsIHdnZXQpIg0KICAgIGZpDQp9DQoNCnJlYWRvbmx5IElERU5USVRZX1VSTD0iaHR0cDovLzE2OS4yNTQuMTY5LjI1NC8yMDE2LTA2LTMwL2R5bmFtaWMvaW5zdGFuY2UtaWRlbnRpdHkvZG9jdW1lbnQvIg0KcmVhZG9ubHkgVFJVRV9SRUdJT049JChnZXRfY29udGVudHMgIiRJREVOVElUWV9VUkwiIHwgYXdrIC1GXCIgJy9yZWdpb24vIHsgcHJpbnQgJDQgfScpDQpyZWFkb25seSBERUZBVUxUX1JFR0lPTj0idXMtZWFzdC0xIg0KcmVhZG9ubHkgUkVHSU9OPSIke1RSVUVfUkVHSU9OOi0kREVGQVVMVF9SRUdJT059Ig0KDQpyZWFkb25seSBTQ1JJUFRfTkFNRT0iYXdzLWluc3RhbGwtc3NtLWFnZW50Ig0KcmVhZG9ubHkgU0NSSVBUX1VSTD0iaHR0cHM6Ly9hd3Mtc3NtLWRvd25sb2Fkcy0kUkVHSU9OLnMzLmFtYXpvbmF3cy5jb20vc2NyaXB0cy8kU0NSSVBUX05BTUUiDQoNCmNkIC90bXANCmdldF9jb250ZW50cyAiJFNDUklQVF9VUkwiID4gIiRTQ1JJUFRfTkFNRSINCmNobW9kICt4ICIkU0NSSVBUX05BTUUiDQouLyIkU0NSSVBUX05BTUUiIC0tcmVnaW9uICIkUkVHSU9OIg0K",
"MinInstanceCount":1,
"MaxInstanceCount":1,
"IamInstanceProfileName":"{{InstanceIamRole}}"
}
},
{
"name":"updateOSSoftware",
"action":"aws:runCommand",
"maxAttempts":3,
"timeoutSeconds":1200,
"onFailure":"Abort",
"inputs":{
"DocumentName":"AWS-RunShellScript",
"InstanceIds":[
"{{launchInstance.InstanceIds}}"
],
"Parameters":{
"commands":[
"set -e",
"yum -y install git",
"pip install --upgrade pip",
"pip install ansible=={{AnsibleVersion}}",
"git clone --config credential.helper='!aws --region us-east-1 codecommit credential-helper $@' --config credential.UseHttpPath=true {{AnsiblePlaybookRepo}} /var/ansible-playbook",
"cd /var/ansible-playbook",
"/usr/local/bin/ansible-playbook -i hosts {{AnsiblePlaybook}}"
]
}
}
},
{
"name":"stopInstance",
"action":"aws:changeInstanceState",
"maxAttempts":3,
"timeoutSeconds":1200,
"onFailure":"Abort",
"inputs":{
"InstanceIds":[
"{{launchInstance.InstanceIds}}"
],
"DesiredState":"stopped"
}
},
{
"name":"createImage",
"action":"aws:createImage",
"maxAttempts":3,
"onFailure":"Abort",
"inputs":{
"InstanceId":"{{launchInstance.InstanceIds}}",
"ImageName":"{{TargetAmiName}}",
"NoReboot":true,
"ImageDescription":"AMI Generated by EC2 Automation on {{global:DATE_TIME}} from {{SourceAmiId}}"
}
},
{
"name":"terminateInstance",
"action":"aws:changeInstanceState",
"maxAttempts":3,
"onFailure":"Continue",
"inputs":{
"InstanceIds":[
"{{launchInstance.InstanceIds}}"
],
"DesiredState":"terminated"
}
}
],
"outputs":[
"createImage.ImageId"
]
}
Automationの実行
手順はコレが最後です。Automationを実行してAMIを作成します。EC2のコンソールから「Automation」を選択します。
先程作成したドキュメントを選択し、各パラメータを入力します。
以下は入力例です。注意点として、AutomationAssumeRoleはARN指定、InstanceIamRoleはインスタンスプロファイル名(ARNではない)を指定します。
実行し、ステップが完了することを確認します。
AMIができてます。
AMIの確認
作成されたAMIからEC2を作成し確認しました。Nginxがインストールされて、起動できてました。
[ec2-user@ip-172-31-16-39 ~]$ rpm -qa | grep nginx
nginx-1.10.2-1.30.amzn1.x86_64
[ec2-user@ip-172-31-16-39 ~]$ ps -ef | grep nginx
root 2564 1 0 08:24 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx 2567 2564 0 08:24 ? 00:00:00 nginx: worker process
ec2-user 2685 2661 0 08:35 pts/0 00:00:00 grep --color=auto nginx
[ec2-user@ip-172-31-16-39 ~]$
最後に
Systems Managerはパッチ適用だったり、コマンドの定期実行だったり可能ですが今回はAMIの自動化できるAutomationの機能を使ってみました。一度フローを作成しておくと、AMIを変更する時の効率的に対応できるようになります。是非参考になればと。