AWS CLIでの構築手順だけだとインフラのメンテナンス大変そうだなと思ったのでAWS CLIでCode4兄弟によるEC2+nginx+Laravelの継続的デプロイ環境を構築するで作ったの環境をCloudFormationとAnsibleでコード化してみました。
今回のコード
下記、タグv0.0.0
になります。
環境構成
以前の記事の構成とほとんど同じ構成です。
CloudFormation化にあたりスタックを下記の5つに分けました。
スタック | 説明 | リソース |
---|---|---|
CodeStore | コード管理 | AWS::CodeCommit::Repository |
BuildEnv | ビルド環境 | AWS::ECR::Repository, AWS::IAM::Role, AWS::CodeBuild::Project |
Network | VPC, サブネット環境 | AWS::EC2::VPC, AWS::EC2::InternetGateway, AWS::EC2::VPCGatewayAttachment, AWS::EC2::Subnet, AWS::EC2::RouteTable, AWS::EC2::Route, AWS::EC2::SubnetRouteTableAssociation |
EC2 | EC2環境 | AWS::EC2::SecurityGroup, AWS::EC2::EIP, AWS::IAM::Role, AWS::IAM::Policy, AWS::IAM::InstanceProfile, AWS::EC2::Instance, AWS::EC2::EIPAssociation |
DeployPipeline | デプロイ環境 | AWS::S3::Bucket, AWS::IAM::Role, AWS::CodeBuild::Project, AWS::CodeDeploy::Application, AWS::CodeDeploy::DeploymentGroup, AWS::CodePipeline::Pipeline, AWS::Events::Rule |
EC2インスタンス内のセットアップはUserDataとCodeDeploy/Ansibleで行うようにしました。
UserDataで行うのはamazon-linux-extrasやyumによるパッケージインストールとCodeDeploy agentのインストールです。
EC2Instance: Type: AWS::EC2::Instance Properties: # ...(省略)... UserData: !Base64 Fn::Sub: | #!/bin/bash yum -y update amazon-linux-extras install ansible2 nginx1.12 php7.3 -y yum install -y php-fpm php-mbstring php-xml php-bcmath yum install -y python2-pip ruby cd /home/ec2-user curl -O https://aws-codedeploy-${AWS::Region}.s3.amazonaws.com/latest/install chmod +x ./install ./install auto
上記以外のセットアップ処理はCodeDeployにてansible-playbookのローカル実行で行うようにしました。
# Setup EC2 ansible-playbook -i localhost, -c local ${DESTINATION_PATH}/infra/setup_ec2.yml
コードのディレクトリ構成は下記です。
. ├── Dockerfile_ecr ... ECR向けのdockerイメージのDockerfile ├── appspec.yml ... CodeDeployデプロイ定義 ├── aws_deploy.sh ... CodeDeployデプロイ処理 ├── buildspec.yml ... CodeBuildビルド定義 ├── buildspec_ecr.yml ... ECR向けのCodeBuildビルド定義 ├── infra ... CloudFormationやAnsibleのテンプレート │ └── templates ... Ansibleのテンプレート └── laravel ... Laravelプロジェクト
構築手順
README.mdの通りです。
CodeCommitを構築
前回の記事の「LaravelプロジェクトをCodeCommit管理にする」です。
コード管理の場所を用意してGitHubから取り込みます。
git clone https://github.com/nihemak/laravel-ec2-sample.git cd laravel-ec2-sample aws cloudformation validate-template \ --template-body file://infra/CodeStore.cfn.yml aws cloudformation create-stack \ --stack-name laravel-ec2-sample-CodeStore \ --template-body file://infra/CodeStore.cfn.yml git push ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/laravel-ec2-sample --all
Laravelプロジェクトのビルドに必要なECR環境を構築
前回の記事の「CodeBuild環境を整える」です。
Laravelプロジェクトのビルドで使用するEC2インスタンスと同じ環境のdockerイメージが登録されたECRリポジトリを用意します。
aws cloudformation validate-template \ --template-body file://infra/BuildEnv.cfn.yml aws cloudformation create-stack \ --stack-name laravel-ec2-sample-BuildEnv \ --capabilities CAPABILITY_NAMED_IAM \ --parameters \ ParameterKey=CodeCommitStackName,ParameterValue=laravel-ec2-sample-CodeStore \ --template-body file://infra/BuildEnv.cfn.yml CODEBUILD_ID=$(aws codebuild start-build --project-name laravel-ec2-sample-ecr --source-version master | tr -d "\n" | jq -r '.build.id') echo "started.. id is ${CODEBUILD_ID}" while true do sleep 10s STATUS=$(aws codebuild batch-get-builds --ids "${CODEBUILD_ID}" | tr -d "\n" | jq -r '.builds[].buildStatus') echo "..status is ${STATUS}." if [ "${STATUS}" != "IN_PROGRESS" ]; then if [ "${STATUS}" != "SUCCEEDED" ]; then echo "faild." fi echo "done." break fi done
VPC/サブネット環境とECインスタンスを構築
前回の記事の「最初のEC2+nginx+Laravelな環境を作成する」です。
まずEC2インスタンスへのSSHアクセスの為にキーペア/pemを作成します。
key_pair=$(aws ec2 create-key-pair --key-name test-laravel) echo $key_pair | jq -r ".KeyMaterial" > test-laravel.pem chmod 400 test-laravel.pem
そしてVPC/サブネット環境とLaravelプロジェクトを動かすECインスタンスを用意します。
aws cloudformation validate-template \ --template-body file://infra/Network.cfn.yml aws cloudformation create-stack \ --stack-name laravel-ec2-sample-Network \ --capabilities CAPABILITY_NAMED_IAM \ --template-body file://infra/Network.cfn.yml aws cloudformation validate-template \ --template-body file://infra/EC2.cfn.yml aws cloudformation create-stack \ --stack-name laravel-ec2-sample-EC2 \ --capabilities CAPABILITY_NAMED_IAM \ --parameters \ ParameterKey=NetworkStackName,ParameterValue=laravel-ec2-sample-Network \ --template-body file://infra/EC2.cfn.yml
デプロイのパイプラインを構築
前回の記事の「CodeDeploy環境を整える」「CodeBuild環境を整える」「CodePipeline環境を整える」「CodeBuildをキャッシュ対応する」「CloudWatch EventsでCodePipelineが自動実行されるよう設定する」です。
CodePipelineおよびCodeBuildとCodeDeploy、CodeCommitを監視してCodePipelineを実行するCloudWatch Eventsを用意します。
aws cloudformation validate-template \ --template-body file://infra/DeployPipeline.cfn.yml aws cloudformation create-stack \ --stack-name laravel-ec2-sample-DeployPipeline \ --capabilities CAPABILITY_NAMED_IAM \ --parameters \ ParameterKey=CodeCommitStackName,ParameterValue=laravel-ec2-sample-CodeStore \ --template-body file://infra/DeployPipeline.cfn.yml
作成が終わると自動でCodePipelineが実行されEC2インスタンスにLaravelプロジェクトがデプロイされます。
使い方
README.mdの通りです。
SSHアクセス
まずEC2のCloudFormationスタックからIPアドレスを取得します。
ec2_ip=$(\ aws cloudformation describe-stacks --stack-name laravel-ec2-sample-EC2 \ | jq -r '.Stacks[].Outputs[] | select(.OutputKey == "EC2IP").OutputValue') echo ${ec2_ip}
ssh -i test-laravel.pem ec2-user@${ec2_ip}
ブラウザアクセス
ブラウザからhttps://${ec2_ip}
にアクセスできます。
ただ今回はSSL自己証明書にしたので初回は怒られます。
xxxxにアクセスする(安全ではありません)
をクリックするとトップページを見ることができます。
自動デプロイ
masterへのpushを行うとCodePipelineが自動実行されてデプロイされます。
$ vi laravel/resources/views/welcome.blade.php $ git diff diff --git a/laravel/resources/views/welcome.blade.php b/laravel/resources/views/welcome.blade.php index 044b874..7d7190b 100644 --- a/laravel/resources/views/welcome.blade.php +++ b/laravel/resources/views/welcome.blade.php @@ -81,7 +81,7 @@ <div class="content"> <div class="title m-b-md"> - Laravel + Hello World! </div> <div class="links"> $ git add -A $ git commit -m "Test master push" $ git push ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/laravel-ec2-sample --all
しばらくしてブラウザからhttps://${ec2_ip}
にアクセスするとトップページの文字列がLaravelからHello World!に変わります。
まとめ
今回はCloudFormationとAnsibleを使ってEC2で動くLaravel環境とCode4兄弟による継続的デプロイ環境をコード管理できるようにしてみました。
- このままだとデプロイのたびにサービスが止まってしまうのでBlue/Greenデプロイくらいは対応した方が良さそう
- UserDataとAnsibleの部分をPacker+AnsibleにしてAMIの構築を別のフェーズで行う構成の方が役割分担的に良さそう
- 素直にECS/Fargate環境にした方が良さそう*1
*1:とはいえ色々な事情で気軽にコンテナを使えない現場もあるので...