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 を準備しました。
こちらに関しては、別途スクリーンショットを取得して公開したいと思います。
以上、最後までお読みくださりありがとうございました。
この記事が気に入ったらサポートをしてみませんか?