# はじめに
こんな感じのアーキテクチャでCI/CD環境を構築する
手順が多いのでざっくり自分的メモな感じで詰まったところや感想などを記していければと思います。
# Prerequisite
Github / CircleCI / S3 / CodePipeline / CodeDeploy / EC2(2インスタンス)
#1 EC2でLaravel環境を構築する
EC2(t2.micro)一台をデフォルト設定で起動する
EC2にLaravel環境を構築するためにPHP7.2をインストールする
composerやnodeなどをインストールができたらWebサーバはapacheを使うためモジュールをインストールし起動する。
ブラウザでアクセスできない場合はセキュリティグループを見直す。
公開範囲は一旦全公開で設定
apacheの情報が表示されることを確認できたら
DocumentRootを設定したい場所にLaravelプロジェクトをインストールする。インストールができたらLaravelのpublic/index.phpにPathを設定し、Laravelのプロジェクトがブラウザで確認できることを確認する。
※ EC2を設定する際はElasticIPを紐づけること(IPが時間経過で変わるため)
※ もう一台のEC2インスタンスはAMIのイメージから作成する
#2 CircleCIを設定する
CircleCIに必要なジョブを設定しworkflowを構成する。
作成したworkflowは以下の通りです。
checkout_code: 対象ブランチのコードをcheckoutする
composer_dependencies: composer install、再ビルド時のキャッシュの生成、リストア
unit-test: 上記ができた状態でtestを開始する
aws-s3-sync: codeをzipにし、s3に移行する(#3のバケットを先に作成後)
特にs3への同期としてのジョブは以下のように書きました。
キー周りに関してはCircleCIの環境変数に設定しコードへの記載を回避しています。
orbs:
aws-s3: circleci/aws-s3@1.0.11
jobs:
aws-s3-sync:
<<: *container_config
steps:
- *restore_code
- run:
name: Run sync s3
command: |
cd ~/workspace/
zip -rm laravel.zip ./
- aws-s3/sync:
from: ./
to: 's3://ci-laravel-projects'
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
aws-region: AWS_REGION
arguments: |
--acl public-read \
--cache-control "max-age=86400"
overwrite: true
- aws-s3/copy:
from: ./
to: 's3://ci-laravel-projects'
arguments: '--dryrun'
#3 S3を作成しCircleCIからプロジェクトをコピーする
テストに成功したデプロイコードを配置するS3バケットを作成する。
これまでデプロイしたバージョンの記録もできるためS3を挟んでいます。
また直接CircleCIからEC2やCodeDeployなどに権限やIP周りの対応をできればそれでも良いと思います、ただCircleCIのIPが一意ではないこととBuildをよりスムーズに行いたいことからCodeBuildを挟んでみるなど要件が多くなった際に拡張性と管理コストは決して高いとは言い難いかもしれません。
※ バージョニングを有効にすることでCodePipelineが検知できるようになる。
#4 CodeDeployを設定する
CodeDeployのアプリケーションを作成し、デプロイグループを設定しデプロイをしてみる。
デプロイ対象はEC2にタグ付けした名前で指定することができるので事前に立てたEC2のタグ名をそれぞれ任意のタグ名で登録する(web1, web2で作成)
またCodeDeployにはライフサイクルに合わせてスクリプトを実行することができます。プロジェクトのルートディレクトリに appspec.yml
を追加します。
version: 0.0
os: linux
files:
- source: /
destination: /var/www/laravel
hooks:
BeforeInstall:
- location: scripts/before_install.sh
timeout: 400
AfterInstall:
- location: scripts/finish_install.sh
CodeDeployにはライフサイクルに合わせてスクリプトを実行することができます。上記では二つのライフサイクルしか用いていませんが実際にはより細かく設定することができます。
デプロイ実行が失敗してこのメッセージが出た際はIAMのロールをEC2にアタッチする必要があります。
The overall deployment failed because too many individual instances failed deployment
IAMでロールを作成(AmazonEC2FullAccess,AmazonS3FullAccess,AWSCodeDeployFullAccess)
EC2にアタッチする際には起動中のEC2に直接アタッチができないので一度止めてからアタッチする必要があります。
※FullAccessは実運用ではなるべく使わない
※ EC2にCodeDeploy agentを事前にインストール&起動させていなければならない。
※ before_install.shでデプロイ先のプロジェクトをrmあるいは別の場所に移動しておかないとデプロイ対象先にコードがあるというエラーが生じるため、こちらで削除あるいは移行をする必要がある。
※ デプロイに失敗しても自動ロールバックの設定をすれば最新の成功した状態まで自動でロールバックしてくれる。
#5 CodePipelineを設定する
S3のバージョンを検知し、CodeDeployに指令を送るためにCodePipelineを設定する。
CodePipelineからCloud Watchイベントとlambdaを通じてデプロイ開始から終了までのイベントをslackへ通知することもできます。
#6 試しにコードを変えて挙動を確認
welcome.blade.phpのLaravelのタイトルをCodeDeploy Testと設定し、プッシュするとそれぞれのサーバーに変更が反映されることが確認できました。
まとめ
社内のプロダクトにて本番、ステージングサーバー上でデプロイすることでCPU負荷を高くなることが懸念であったので、より楽に安全にモダンな構成にできないかを検証するために触ってみました。CodeDeployはEC2がデプロイ対象なら無料で利用できるのでコストも抑えることができます。
staging環境や本番環境にするためにはバケットを分けてパイプラインを増やすかバケットは一つでパイプラインで制御するかなどやり方は様々ですができそうです。
実際にやってみて、EC2のAutoScallingにも対応が無難にできそうなのと今後コンテナ化などすることができれば簡単にサーバーのミドルウェアのバージョンアップも気軽にできそうなのでコンテナ化を対象にした連携も構築してみたいと思います。