2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

EC2 タグに repo名/ブランチ/スクリプトパスを書いておいて GitHub push に勝手に反応させる

Last updated at Posted at 2017-03-19

GitHub への push イベントを SNS で受け取って、「<リポジトリ名>:<ブランチ名>:<実行スクリプト>」というタグが付いている EC2 インスタンスを探して指定スクリプトを実行する、という話です。

で、スクリプト内で "git pull" するもヨシ、追加でごにょごにょするもヨシ。

前置き

CircleCI とかそんなんでなく、GitHub に push したら pull してくれればそれでいいんですよ、というケースは地味に多いわけです。

これは GitHub > SNS > Lambda > Run Command でスクリプト実行すれば良いのですが、「じゃぁリポジトリ毎に Lambda ファンクション作るの?」って話で。

そこで EC2 タグに関連するリポジトリ名とブランチ名、実行スクリプトを書いておけば 1つの lambda が全て面倒みてくれるようにします。

リポジトリが増えた時、サーバが増えた時も EC2 インスタンスにタグ付けるだけなので簡単です。

仕組み

DropShadow ~ 3.png

GitHub > SNS > Lambda はいろいろな方がやられている通り。
あとは EC2 インスタンスに

  • key = GitHub-Webhook
  • val = <リポジトリ名>:<ブランチ名>:<実行スクリプト>

というタグをつけておいて、lambda で対象 EC2 インスタンスを探して Rum Command でスクリプトを実行する。
で、スクリプトの中で git pull とか好きにせい、という構成。

例えば repo_1 というリポジトリの master に push したら、「key=GitHub-Webhook、val=repo_1:master:/hoge/hoge.sh」というタグが付いている EC2 インスタンスの /hoge/hoge.sh を実行します。

作る

SSM エージェントをインストールする

Run Command を実行するには対象インスタンスに SSM エージェントを入れておく必要があります。

SSMエージェントのインストール

40秒で支度しましょう。

$ cd /tmp
$ curl https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm -o amazon-ssm-agent.rpm
$ sudo yum install -y amazon-ssm-agent.rpm

EC2ロール割り当て

で、EC2 インスタンスに AmazonEC2RoleforSSM ポリシーをアタッチしておく。

001.png

SNS topic、GitHub webhook 専用 IAM ユーザの作成、GitHub の設定

「github-webhook」という SNS topic を作ります。
CLI でシュッと。

$ aws sns create-topic --name github-webhook

GitHub webhook 用に「github-webhook」という名前の IAM ユーザを作って、以下のようなインラインポリシーを付ける。
つまり github-webhook topic にパブリッシュする事しかできないユーザ。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "sns:Publish"
            ],
            "Sid": "Stmt0000000000000",
            "Resource": [
                "[さっき作った SNS の ARN]"
            ],
            "Effect": "Allow"
        }
    ]
}

で、GitHub 側もぱぱっと。

DropShadow ~ qiita_002.png

Lambda 準備

特になんもしてない。
エラー処理もないのでやさしく使う。

# -*- coding: utf-8 -*-

import json
import boto3

# key="GitHub-Webhook", val=<repoName>:<branchName>:<command>
# というタグを持つインスタンスを列挙する。
def search_target_instance(repoName, branchName):
    # 実行中の EC2 インスタンスを取得
    ec2 = boto3.client('ec2')
    response = ec2.describe_instances(
        Filters=[
            {
                'Name': 'instance-state-name',
                'Values': ['running']
            }
        ]
    )
    
    targetInstances = []
    
    for resv in response['Reservations']:
        for instance in resv['Instances']:
            for tag in instance['Tags']:
                if tag['Key'] == 'GitHub-Webhook':
                    tagValue = tag['Value']
                    
                    if tagValue.startswith(repoName + ":" + branchName + ":"):
                        target = instance['InstanceId'], tagValue.split(':')[-1]
                        targetInstances.append(target)

    return targetInstances

# Run Command で指定スクリプトを実行する。
def execute_command(instanceId, command):
    print instanceId + " : " + command
    ssm = boto3.client('ssm')
    
    ssmRes = ssm.send_command(
        InstanceIds = [instanceId],
        DocumentName = "AWS-RunShellScript",
        Parameters = {
            "commands": [ command ]
        })
    
    commandId = ssmRes['Command']['CommandId']
    return commandId

# Lambda エントリポイント
def lambda_handler(event, context):
    message = event['Records'][0]['Sns']['Message']
    gitHubJson = json.loads(message)
    
    # SNS からリポジトリ名とブランチ名を取得する。
    repoName = gitHubJson["repository"]["name"]
    branchName = gitHubJson["ref"].replace('refs/heads/', '')
    
    # デプロイ対象 EC2 インスタンスを取得
    targetInstances = search_target_instance(repoName, branchName)

    # EC2 タグで指定されているコマンドを実行する
    for instance in targetInstances:
        instanceId, command = instance
        print execute_command(instanceId, command)

    return {'status': 'ok'}

EC2 タグつけて push

ココまで来たら、タグ付けて GitHub に push すればよい。
Run Command の結果は AWS コンソールの EC2 / Run Command から確認できます。

002.png

※ スクリプトは root で実行されるので注意。

結果はどうやって知るのよ?

本当は list_command_invocations() で Run Command の結果をみて Slack に通知とかすべきですが長くなるので割愛。

とりあえずスクリプト内から curl で slack に post しても良いかもしれないですね。

結論

大切なこと:「ssh ログインなんか絶対にしないぞ!」という強い意思。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?