はじめに
CodeCommitへのpushイベントはCodePipelineがいい感じにトリガー作ってくれるのですが、プルリクエストでも起動させてレビュー前に静的解析ツールなど実行できないかと思い調べてみました。
環境
■操作環境
EC2(Amazon Linux)
■使用サービス
・CodePipeline(CodeCommit,CodeBuild)
CodeCommitからソースを取得して、コンテナを起動しLarastanを実行します。
・EventBridge
プルリクエスト作成を検知してLambdaを実行させます。
・Lambda
CodePipelineに設定されているブランチをプルリクエスト元ブランチに変更してCodePipelineを実行させます。
・ECR
CodePipelineで使用するコンテナイメージを保存します。
本記事でやったこと
CodeCommitに保存したLaravelのソースへのプルリクエストをトリガーに、CodePipelineを起動してコンテナ立ち上げてプルリクエスト元のブランチのソースにLarastanを実行するまでを自動化します。一連の流れを通すのがメインになるので各サービスについての解説はしていません。
手順
###1.実行環境用意
まず、EC2にコンテナイメージ作成とLaravelインストール用の環境を構築します。
GitとDockerをインストール
sudo yum install -y git docker
docker-composeインストール
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
サービス起動
sudo service docker start
dockerをec2-userで実行できるようにユーザーグループに加える
sudo usermod -a -G docker ec2-user
ec2-userでコマンド使用できるか確認する、permission deniedとか出る場合はSSH閉じて接続し直してください。
docker info
Dokerコンテナの実行環境構築
作業ディレクトリ作成
mkdir /home/ec2-user/docker
ここにDockerfileなどのコンテナ作成用のファイルを格納します。
mkdir /home/ec2-user/docker/web
Laravelのソース格納用、ここをCodeCommitに紐づけます
mkdir -p /home/ec2-user/docker/sample-laravel/src
Dockerfile作成
vi /home/ec2-user/docker/web/Dockerfile
FROM php:8.0-apache
WORKDIR /var/www
COPY 000-default.conf /etc/apache2/conf-enabled/000-default.conf
RUN apt-get update \
&& apt-get install -y \
git \
zip \
unzip \
vim \
libpng-dev \
libpq-dev
# Composerのインストール
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer
# Laravelで必要になるmodRewriteを有効化する
RUN ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load \
&& a2enmod rewrite
000-default.conf作成
vi /home/ec2-user/docker/web/000-default.conf
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/sample-laravel/public
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory /var/www/sample-laravel/public>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
docker-compose.yml作成
vi /home/ec2-user/docker/docker-compose.yml
services:
web:
container_name: web
build:
context: ./web
ports:
- '8080:80'
volumes:
- ./sample-laravel/src:/var/www/sample-laravel
コンテナ起動
cd /home/ec2-user/docker
docker-compose up -d
起動確認
docker ps
上手く起動していれば以下のようにwebって名前のコンテナが表示されます。
コンテナにログイン
docker exec -it web bash
コンテナ内でLaravelをインストールします。
composer create-project laravel/laravel sample-laravel
ログディレクトリなどのパーミッション変更
本当はちゃんと適切に設定してあげないといけないのですが。。。今回は割愛します。
cd /var/www/sample-laravel/storage
chmod -R 777 app logs framework
ここでセキュリティグループで8080ポートをアクセスできる状態にすると
http://EC2のパプリックIP:8080
でLaravelの初期画面が表示されると思います。
Larastanインストール
cd /var/www/sample-laravel
composer require --dev nunomaduro/larastan
一番緩いレベル0で実行
./vendor/bin/phpstan analyse -l 0 app
レベル7で実行
./vendor/bin/phpstan analyse -l 7 app
###2.LaravelのコードをCodeCommitリポジトリと紐づける
さてと、準備だけでかなり長くなりましたがここからが本題です。
まずCodeCommitでリポジトリを作成します。
AWSコンソールを開いてデベロッパー用ツール > CodeCommit >リポジトリ から
リポジトリを作成します。
今回はリポジトリ名を「sample-laravel」として他デフォルトで作成します。
リポジトリが作成できたら以下のような画面が表示されますので
Git認証を作成していない場合は案内に従って認証を作成してください。
EC2に戻ってsample-laravel配下をCodeCommitに紐づけます。
※コンテナからは出てください。
cd /home/ec2-user/docker/sample-laravel
既存ソースをCodeCommitに紐づける手順はAWSのドキュメント参考にしました。
https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-migrate-repository-local.html#how-to-migrate-local-version
git init
git config --local init.defaultBranch master
git add .
git commit -m "Initial commit"
git push https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/sample-laravel --all
###3.コンテナイメージをECRにアップロード
AWSコンソールを開きAmazon ECR > リポジトリからリポジトリを作成を押下。
リポジトリ名を「sample-laravel-image」とし他はデフォルトとしました。
リポジトリが作成できたらEC2のDockerfileから作成したイメージをアップします。
まず、実行環境のEC2にI AMロールでECRの権限を付与します。
EC2のコンソールでアクション > セキュリティ > IAMロールを変更から「新しい IAM ロールを作成」で
作成したロールに「AmazonEC2ContainerRegistryFullAccess」を割り当てました。
EC2に戻ってDockerflieのある階層に移動します。
cd /home/ec2-user/docker/web
ここからECRへのアップロードまではコンソールの「プッシュコマンドの表示」の手順そのままなので、そちらを見たほうがよいかもしれないです。
ECRへのログイン、ここで弾かれる場合はロールの割当がうまくいっていない可能性が高いです。
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com
docker build -t sample-laravel-image .
docker tag sample-laravel-image:latest アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/sample-laravel-image:latest
docker push アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/sample-laravel-image:latest
ECRへのpushまで終わるとECR側でもアップロードしたイメージが反映されます。
後ほど使用するので「URIのコピー」からURIをメモしておいてください。
CodeCommitとECRの準備はできたので、次はCodePipelineと連携させていきます。
###4.CodePipeline構築
まず、EC2に戻ってCodePipelineで使うbuildspec.ymlとDockerfileを作成します。
mkdir /home/ec2-user/docker/sample-laravel/pipeline
vi /home/ec2-user/docker/sample-laravel/pipeline/buildspec.yml
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- $(aws ecr get-login --no-include-email --region ap-northeast-1)
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t web -f pipeline/Dockerfile .
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker run web php vendor/bin/phpstan analyse -l 0 app
vi /home/ec2-user/docker/sample-laravel/pipeline/Dockerfile
FROM 先程メモしたイメージのURI
COPY ./src /var/www/src
WORKDIR /var/www/src
RUN cp -p .env.example .env
RUN composer install
追加したファイルはリポジトリにpushしておいてください。
AWSコンソールを開きデベロッパー用ツール > CodePipeline > パイプラインから「パイプラインを作成する」でパイプラインを新規作成します。
パイプライン名は「sample-laravel-pipeline」としました。
ソースプロバイダーをCodeCommitとしsample-laravelのmasterブランチを選択します。
ビルドステージのプロバイダーをCodeBuildに設定し「プロジェクトを作成する」を押下します。
buildプロジェクト名を入力します。
プロジェクト名は「sample-laravel-build」としました。
特権付与にチェック入れます
buildspec.ymlのパス「pipeline/buildspec.yml」を入力しバッチ設定、ログはデフォルトのままにします。
今回はデプロイは行いませんので「導入段階をスキップ」を選択します。
パイプラインを作成すると自動的に実行されますが、パイプラインに設定したロールにECRの権限設定をしていないため一度失敗します。
CodeBuild > ビルドプロジェクト > sample-laravel-buildからビルドの詳細タブで設定されたサービスロールをクリックします。
ロールの許可タブから「ポリシーをアタッチ」を選択し「AmazonEC2ContainerRegistryReadOnly」をアタッチします。
CodePiplineに戻り「変更をリリースする」から再実行すると、ロールに権限割り当てたので成功します。
Buildステージの詳細からログを確認すると、Larastanもちゃんと実行されています。
###5.CodePipelineのブランチをプルリクエスト元のブランチに変更して、パイプラインを起動するLambda関数を作成する
Lambda > 関数 > 関数の作成で作成します。
関数名を「sample-laravel-pipline-trigger」としてランタイムは「Python3.9」を設定します。他はデフォルトにしました。
コードタブに以下のスクリプトを貼り付けてDeployします。
import json
import boto3
# パイプライン名
PIPELINE_NAME = 'sample-laravel-pipeline'
codepipeline = boto3.client('codepipeline')
def lambda_handler(event, context):
# 現在のcodepipelineの設定値を取得
response = codepipeline.get_pipeline(
name=PIPELINE_NAME
)
#EventBridgeから引き渡されるプルリク元のブランチ名を取得
targetBranch = event['detail']['sourceReference'].replace('refs/heads/', '')
#pipelineに設定するためプルリク元のブランチで更新パラメータを作成
response['pipeline']['stages'][0]['actions'][0]['configuration']['BranchName'] = targetBranch
#更新に不要な値削除
del response['metadata'],response['ResponseMetadata']
#pipelineの設定を更新
updateResponse = codepipeline.update_pipeline(pipeline=response['pipeline'])
#codepipelineを開始
startResponse = codepipeline.start_pipeline_execution(
name=PIPELINE_NAME
)
return
スクリプトにbotoによるCodePipelineの更新が入っているので、関数のロールにCodePipelineの編集権限を付与します。
設定タブからアクセス権限を選択してロールをクリックします。
ロールの編集画面でアクセス許可を追加から「AWSCodePipelineFullAccess」のポリシーをアタッチします。
###6.プルリクエスト時に起動するようにEventBridgeのトリガーを変更する
まず、CodePipelineを作った最に自動で作成されたルールを削除してリポジトリへのpushではパイプラインを起動しないようにします。
※こちらはやらなくてもpushで余計にパイプラインが走るくらいで後の工程に支障はないので飛ばしても問題ないです。
Amazon EventBridge > ルール > codepipeline-sample-master-xxxxx-rule
ルール作成時の命名規則がいまいちよくわからないですがイベントパターンの
resourcesにsample-laravelが指定されているルールを削除します。
そのままルールの新規作成で、sample-laravelへのプルリクエストをトリガーに
Lambdaを起動させるルールを追加します。
ルール名は「sample-laravel-lambda-trigger」としました。
イベントをカスタムパターンにしてsample-laravelへのプルリクエスト作成のイベントで発火するよう定義する。
アカウントIDは利用しているAWSアカウントのIDに書き換えてください。
{
"source": ["aws.codecommit"],
"detail-type": ["CodeCommit Pull Request State Change"],
"resources": ["arn:aws:codecommit:ap-northeast-1:アカウントID:sample-laravel"],
"detail": {
"event": ["pullRequestCreated"]
}
}
ターゲットに先程作成したLambda関数の「sample-laravel-pipline-trigger」を選択してルールを作成
###7.プルリクエストをしてみる
環境が整ったのでいよいよプルリクエストを作成してみます。
CodeCommitのブランチからブランチの作成。
ブランチ名はなんでもいいですが、「develop」にしました。
試しにbuildspec.ymlを編集してLarastanをレベル7に編集します。
Larastanインストール時にレベル7だと怒られていたのでビルドエラーを返すはず
プルリクエストの作成から[develop]->[master]にプルリクエストを作成します。
差分は先程編集したbuildspec.ymlです。
Sourceの「AWS CodeCommit」をクリックすると遷移する先がパイプライン構築時には存在していなかった「develop」になっています。
buildステージの詳細から遷移してログを確認するとやっぱりLarastanに怒られてBuild失敗のステータスとなっていました。
怒られて終わりはモヤモヤするので、Larastanの実行レベルを0に戻してプルリクエストを作り直しました。
無事成功ステータスで終了してますね。
今回は触れませんが実行結果をプルリクのコメントに書き込むなど、通知も自動化しておくとレビュアーは楽できそうですね。
おわりに
今回は簡単に導入できるLarastanで試しましたが、PHPUnitなんかも実行できますし
そもそも実行環境に自作コンテナイメージ使ってるので大抵のことはできるかと思います。
EventBridgeやCodeシリーズを使いこなせると自動化はかどりそうだなーと思いました。
精進せねば!