見出し画像

AWS Systems Manager を利用して、Backlog の Git から EC2 へリリース

概要

Backlog の GIT リポジトリを更新すると、Amazon EC2 インスタンスで Web コンテンツを自動で更新する CI/CD 環境を作ってみました。

やっていることは単純で、AWS Systems Manager の Run Command を利用して git pull します。API Gateway 経由で Lambda にて Run Command を実行します。

#1. Amazon EC2 インスタンス

まず、Web サーバーを作るところから書いていきます。

インスタンスプロファイルロール

セッションマネージャー経由でインスタンスに接続したいので、Amazon EC2 インスタンスを起動する前に、インスタンスにアタッチするための IAM ロールを作ります。

EC2 用の IAM ロールに、AmazonSSMManagedInstanceCore ポリシーを入れます。できたロールの信頼関係に ssm.amazonaws.com を追加します。

インスタンスを起動

IAM ロールができたら、Amazon Linux 2022 サーバーにて Nginx で Web サーバーを稼働させます。

前の手順で作成した IAM ロールをアタッチして、以下のようなユーザーデータを投入して起動します。ユーザーデータでは、SSM Agent と Nginx のインストールを行うので、インスタンスはパブリックサブネットに起動して、パブリック IP アドレスを付与するか、プライベートサブネットに設置したい場合は、別途、NAT Gateway が必要です。

#!/bin/bash
dnf -y update
# タイムゾーンを東京に変更
timedatectl set-timezone Asia/Tokyo
# ロケールを日本語に変更
localectl set-locale LANG=ja_JP.utf8
# SSM Agent インストール
dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
# Nginx インストール
dnf install -y nginx
# Nginx スタート
systemctl enable nginx.service
systemctl start nginx.service

ユーザーデータのインストールには、インスタンスが起動した後 10 分程度の時間がかかります。インスタンスができたら、セッションマネージャー経由で接続しておきます。

#2. Backlog GIT リポジトリ

次に Backlog で GIT を有効にしてリポジトリを作ります。
ここでは、site-test というリポジトリを作成しました。

Amazon Linux 2022 にセッションマネージャーで接続して、Backlog Git のリポジトリを Clone します。

sh-
sudo -i

% git clone xxxxx@xxxx.git.backlog.jp:/XXX/site-test.git
Cloning into 'site-test'...
The authenticity of host 'xxxx.git.backlog.jp (XX.XX.XXX.XXX)' can't be established.
RSA key fingerprint is SHA256:vXxx+xx0XXpk/0Zx00SIXx/+0xXxxmN/C/+nxxV/00g.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'xxxx.git.backlog.jp' (RSA) to the list of known hosts.
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 12 (delta 1), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (12/12), done.
Resolving deltas: 100% (1/1), done.
%

#3. AWS Systems Manager

AWS Systems Manager にて、Run Command を作成します。
あとは、以下コマンドに入れました。

cd /root/site-test
git pull

#4. AWS Lambda

上記、Run Command を Lambda で実行します。エラー分岐や、具体的な処理は省略していますが、要は飛んできたイベントが正しいかチェックして、git pull しているだけです。

import logging
import boto3
ssm = boto3.client('ssm')

def lambda.hundler():
    # POST 判定
    if event.get("httpMethod") != "POST":
        logger.warn('This request is not POST.')
        return { 'statusCode': 403, 'body': json.dumps('This request is forbidden.') }
 
    # body 取得
    json_body = event.get('body')
    if not json_body:
        logger.warn('BAD Request.')
        return { 'statusCode': 400, 'body': json.dumps('BAD Request.') }

    # payload 取得
    json_payload = urllib.parse.parse_qs(json_body)
    logger.info('payload:')
    logger.info(json_payload)
    
    json_request_body = json.loads(json_payload['payload'][0])
    logger.info('request_body:')
    logger.info(json_request_body)
    
    logger.info('repository:')
    logger.info(json_request_body['repository']['name'])

    # 処理先のリポジトリ判定
    pull_target = ''
    str_command = []

    # マージプルリクなら処理
    if not ("Merge pull request" in str(json_request_body['revisions'][0]['message'])):
        logger.info(json_request_body['revisions'][0]['message'])
        logger.warn('This Request is not Merge Request.')
        return { 'statusCode': 404, 'body': json.dumps('This is different request.') }

    if json_request_body['repository']['name'] == '<リポジトリ名>':
        logger.info('git pull target:')
        instance_id = os.environ['INSTANCE_ID']
        str_command.append("cd /DocumentRoot/")
        str_command.append("git fetch")
        str_command.append("<git pull リポジトリ URL> master")

    if not str_command:
        logger.warn('This Request is not target.')
        return { 'statusCode': 404, 'body': json.dumps('Not Found.') }

    headers = event.get("headers")
    user_agent = headers.get("User-Agent")
    
    if "Backlog::Git::Hook" in user_agent:
        try:
            ssm.send_command(
                InstanceIds = [instance_id],
                DocumentName = "AWS-RunShellScript",
                Parameters = {
                    "commands": str_command,
                    "executionTimeout": ["3600"]
                },
            )
            logger.info('Run ShellScript.')
        except Exception as e:
            logger.error(e)
            raise e

    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Run Shell Script.')
    }

#5. API Gateway

最後に Lambda のエンドポイントとして API Gateway を準備しました。
こちらに関しては、別途スクリーンショットを取得して公開したいと思います。

以上、最後までお読みくださりありがとうございました。

この記事が気に入ったらサポートをしてみませんか?