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

112件のシェア(ちょっぴり話題の記事)

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

中山(順)です

以前、弊社の大栗より、「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
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
        <head>
                <title>Test Page for the Apache HTTP Server on Amazon Linux AMI</title>
                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
                <style type="text/css">
                        /*<![CDATA[*/
                        body {
                                background-color: #fff;

留意事項

この記事を作成するにあたり、何度かGitHubからのPlaybookダウンロード時にエラーが発生しました。
リクエスト数の上限に抵触するものでした。
お試しの際にはお気をつけください。

Rate Limiting

まとめ

このように、gitで管理されたAnsible PlaybookをSystems Managerを使って実行してみました。

Systems Managerを利用することで簡単に作業の証跡を残せますし、ドキュメントを独自に作成してより使いやすくすることもできると思います。

「おっ!」と思った方は是非試してみてください。