[AWS]Systems ManagerのAutomationとAnsibleを使ってAMIを作成する
コンニチハ、千葉です。
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を変更する時の効率的に対応できるようになります。是非参考になればと。