ちょっと話題の記事

GitHub上のAnsiblePlaybookをSystems Managerで実行する

2018.01.06

はじめに

中山(順)です

以前、弊社の大栗より、「EC2の管理を行えるSystems ManagerでgithubやS3に配置したファイルをローカルにダウンロードして実行することが可能になりました。」という内容をご紹介しました。

Systems Managerのスクリプト実行でgithubとS3をサポートしました

今回、これを応用してGitHub上のAnsiblePlaybookをEC2インスタンス上で実行させてみました。

おさらい

"AWS-RunRemoteScript"でやっていることは、以下の2つです。

やってみた

これを使用して、以下の処理を実行します。

  • Ansible Playbook一式のダウンロード
  • Ansible Playbookのローカル実行

手順は以下の公式ドキュメントにもあるので、適宜参照してください。

Run Ansible Playbooks from GitHub

事前準備

Systems Managerを利用するための準備作業および動作要件は、公式ドキュメントもしくは弊社ブログをご確認ください。

Setting Up AWS Systems Manager

EC2 Systems Manager – 特集カテゴリー –

前提条件

今回の検証は以下の前提のもとで実施します。

  • 以下のようなのEC2インスタンスを用意
  • Ansibleをインストール
  • Public Subnetに配置
  • Linuxインスタンス (Amazon Linux AMI 2017.09.1 (HVM), SSD Volume Type - ami-19910c7f) を利用
  • Run Commandを実行できる必要最低限の権限をインスタンスに付与(インスタンスプロファイル)

Ansibleは以下のコマンドでインストールしました。

sudo pip install ansible

Playbookの作成

今回は、以下のタスクを実行するだけの簡単なPlaybookを作成します。

  • yum update
  • httpdのインストールおよび起動

その際、それぞれを別のロールで定義します。

Playbookは以下の手順で作成しました。

  • GitHubにリポジトリーを作成
  • 作業環境にリポジトリーをクローン
  • ディレクトリの作成
  • Playbookの作成
  • GitHubにリポジトリーをプッシュ

GitHubにリポジトリーを作成

GitHubにリポジトリを作成します。

Create A Repo

以降の手順で作成されるPlaybookはこちらにありますので、ご参考までに。

nakayamanobuhiro/example-ansible-playbook-ssm

作業環境にリポジトリーをクローン

作業用ディレクトリを作成します。

mkdir ~/tmp_devio
cd ~/tmp_devio

リポジトリーをクローンします

git clone https://github.com/nakayamanobuhiro/example-ansible-playbook-ssm.git
cd ./example-ansible-playbook-ssm

ディレクトリの作成

ロールを配置するディレクトリを作成します。 今回はansible-galaxyコマンドを利用してディレクトリと(空の)ファイルを作成します。(地味に便利)

mkdir ./roles

ansible-galaxy init --init-path=roles common
ansible-galaxy init --init-path=roles web

ls -R
.:
README.md roles

./roles:
common web

./roles/common:
README.md defaults files handlers meta tasks templates tests vars

./roles/common/defaults:
main.yml

./roles/common/files:

./roles/common/handlers:
main.yml

./roles/common/meta:
main.yml

./roles/common/tasks:
main.yml

./roles/common/templates:

./roles/common/tests:
inventory test.yml

./roles/common/vars:
main.yml

./roles/web:
README.md defaults files handlers meta tasks templates tests vars

./roles/web/defaults:
main.yml

./roles/web/files:

./roles/web/handlers:
main.yml

./roles/web/meta:
main.yml

./roles/web/tasks:
main.yml

./roles/web/templates:

./roles/web/tests:
inventory test.yml

./roles/web/vars:
main.yml

Playbookの作成

ansible-playbookコマンドを実行する際に指定するPlaybookのファイル名を指定します。

FILE_NAME_TOP="playbook.yml"

Playbookを生成します。 リモートホストで実行することも想定し、hosts: allとします。 (Playbookを実行する際に、ansible-playbook -i "localhost," -c local playbook.ymlのように実行します。)

cat << EOF > ${FILE_NAME_TOP}
---
- name: httpd
hosts: all
remote_user: ec2-user
become: yes

roles:
- common
- web
EOF

yum updateを実行するためのPlaybookを作成します。

FILE_NAME_COMMON="./roles/common/tasks/main.yml"

cat << EOF > ${FILE_NAME_COMMON}
---
- name: upgrade all packages
yum:
name: '*'
state: latest
EOF

httpdをインストールするためのPlaybookを作成します。

FILE_NAME_WEB="./roles/web/tasks/main.yml"

cat << EOF > ${FILE_NAME_WEB}
---
- name: install the latest version of httpd
yum: name=httpd state=latest

- name: httpd service state
service: name=httpd state=started enabled=yes
EOF

GitHubにリポジトリーをプッシュ

リポジトリーにPlaybookをプッシュします。

git add .
git commit -m "First commit"
git push origin master

Playbookの実行

パラメーターの準備

"AWS-RunRemoteScript"ドキュメントを実行するにあたり、以下のパラメーターを決定する必要があります。

  • 実行対象のインスタンスIDもしくはタグ
  • 今回はインスタンスIDで対象を指定します
  • owner
  • リポジトリーのオーナー名
  • repository
  • リポジトリー名
  • path
  • ダウンロードしたいファイルもしくはディレクトリのパス
  • 今回はリポジトリー上のファイルをすべて取得します
  • getOptions
  • ブランチ名もしくはコミットID
  • 指定は任意(デフォルトはbranch:master,commitID:head)

プライベートリポジトリーを利用する場合は、これらに加えてtokenInfoにGitHubのアクセストークンを指定する必要があります。 パラメーターストアにKMSで暗号化して保存し、それを参照することも可能です。

INSTANCE_ID="i-0xxxxxxxxxxxxxxxx"
OWNER="nakayamanobuhiro"
REPOSITORY="example-ansible-playbook-ssm"
DOWNLOAD_PATH="/"
GET_OPTIONS="branch:master"

PARAMETERS="{\"sourceType\":[\"GitHub\"],\"sourceInfo\":[\"{\\\"owner\\\":\\\"${OWNER}\\\", \\\"repository\\\": \\\"${REPOSITORY}\\\", \\\"path\\\": \\\"${DOWNLOAD_PATH}\\\", \\\"getOptions\\\": \\\"${GET_OPTIONS}\\\"}\"],\"commandLine\":[\"ansible-playbook -i \\\"localhost,\\\" -c local playbook.yml\"]}" && echo ${PARAMETERS}
{"sourceType":["GitHub"],"sourceInfo":["{\"owner\":\"nakayamanobuhiro\", \"repository\": \"example-ansible-playbook-ssm\", \"path\": \"/\", \"getOptions\": \"branch:master\"}"],"commandLine":["ansible-playbook -i \"localhost,\" -c local playbook.yml"]}

Playbookの実行

Playbook(GitHubリポジトリー)とRunCommand実行時に必要なパラメーターが準備できました。 Ansibe Playbookを実行してみます。

aws ssm send-command \
--document-name "AWS-RunRemoteScript" \
--instance-ids ${INSTANCE_ID} \
--parameters "${PARAMETERS}"
{
"Command": {
"Comment": "",
"Status": "Pending",
"MaxErrors": "0",
"Parameters": {
"commandLine": [
"ansible-playbook -i \"localhost,\" -c local playbook.yml"
],
"sourceInfo": [
"{\"owner\":\"nakayamanobuhiro\", \"repository\": \"example-ansible-playbook-ssm\", \"path\": \"/\", \"getOptions\": \"branch:master\"}"
],
"sourceType": [
"GitHub"
]
},
"ExpiresAfter": 1515183224.362,
"ServiceRole": "",
"DocumentName": "AWS-RunRemoteScript",
"TargetCount": 1,
"OutputS3BucketName": "",
"NotificationConfig": {
"NotificationArn": "",
"NotificationEvents": [],
"NotificationType": ""
},
"CompletedCount": 0,
"Targets": [],
"StatusDetails": "Pending",
"ErrorCount": 0,
"OutputS3KeyPrefix": "",
"RequestedDateTime": 1515176024.362,
"CommandId": "4da78de8-5287-408d-8bd3-d7ab1a5411c4",
"InstanceIds": [
"i-097a8923a6beb618a"
],
"MaxConcurrency": "50"
}
}

動作確認

RunCommandの実行結果を確認します。 "AWS-RunRemoteScript"ドキュメントでは複数のプラグインが実行されるため、まずはlist-command-invocationsでRunCommandのステータスを確認します。 以下のように、成功していることを確認できました。

aws ssm list-command-invocations \
--command-id 4da78de8-5287-408d-8bd3-d7ab1a5411c4 \
--instance-id ${INSTANCE_ID}
{
"CommandInvocations": [
{
"Comment": "",
"Status": "Success",
"CommandPlugins": [],
"ServiceRole": "",
"InstanceId": "i-097a8923a6beb618a",
"DocumentName": "AWS-RunRemoteScript",
"NotificationConfig": {
"NotificationArn": "",
"NotificationEvents": [],
"NotificationType": ""
},
"StatusDetails": "Success",
"StandardOutputUrl": "",
"StandardErrorUrl": "",
"InstanceName": "",
"CommandId": "4da78de8-5287-408d-8bd3-d7ab1a5411c4",
"RequestedDateTime": 1515176024.547
}
]
}

RunCommandの実行ログを確認します。 まずは"AWS-RunRemoteScript"ドキュメントに含まれるプラグイン名を確認します。

aws ssm get-document \
--name AWS-RunRemoteScript \
--query "Content" \
--output text \
| jq -r .mainSteps[].name
downloadContent
runPowerShellScript
runShellScript

"runPowerShellScript"はLinux上ではスキップされるようにドキュメントが定義されているので、"downloadContent"および"runShellScript"のログを確認します。

まずは、"downloadContent"を確認します。

aws ssm get-command-invocation \
--command-id 4da78de8-5287-408d-8bd3-d7ab1a5411c4 \
--instance-id ${INSTANCE_ID} \
--plugin-name downloadContent \
--query "StandardOutputContent" \
--output text
Content downloaded to /var/lib/amazon/ssm/i-097a8923a6beb618a/document/orchestration/4da78de8-5287-408d-8bd3-d7ab1a5411c4/downloads/

次に、"runShellScript"を確認します。

aws ssm get-command-invocation \
--command-id 4da78de8-5287-408d-8bd3-d7ab1a5411c4 \
--instance-id ${INSTANCE_ID} \
--plugin-name runShellScript \
--query "StandardOutputContent" \
--output text
PLAY [nginx] *******************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [common : upgrade all packages] *******************************************
ok: [localhost]

TASK [web : install the latest version of httpd] *******************************
changed: [localhost]

TASK [web : httpd service state] ***********************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost : ok=4 changed=2 unreachable=0 failed=0

意図したとおり機能しているかを確認します。 今回はhttpdをインストールしただけなので、デフォルトのコンテンツが表示されるか確認します。 以下の通り、httpdからTest Pageが取得できることが確認できました。

curl xx.xx.xx.xx | head

``` % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 3839 100 3839 0 0 159k 0 --:--:-- --:--:-- --:--:-- 163k