Laravelアプリケーションをリリースする際、データベースのマイグレーションは欠かせません。
以前は、アプリケーションをデプロイした後に踏み台サーバーへSSH接続し、手動でマイグレーションを実行していました。
作業自体は難しくありませんが、
- 実行忘れ
- 手順のばらつき
- 誰が実行したか分かりづらい
といった課題がありました。
そこで、CodePipelineとCodeBuildを利用し、マイグレーションをリリースフローへ組み込みました。
導入前
以前は次のような流れでした。
踏み台サーバーへSSH
↓
php artisan migrate
↓
動作確認
この構成でも運用できますが、リリース手順が人に依存していました。
導入後
現在はCodePipeline上で実行しています。
GitHub
↓
CodeBuild
(migrate:status)
↓
Manual Approval
↓
CodeBuild
(migrate)
↓
CodeBuild
(migrate:status)
マイグレーション前に現在の状態を確認し、その内容を確認してから承認・実行する流れにしています。
migrate:statusを最初に実行
まずは現在の状態を取得します。
php artisan migrate:status
CodeBuildのログに、
- 適用済みMigration
- 未適用Migration
が表示されます。
これを確認してから承認することで、
「今回はどのMigrationが実行されるのか」
を事前に把握できます。
Manual Approval
migrate:statusの結果を確認後、
CodePipelineのManual Approvalステージで承認します。
migrate:status
↓
Manual Approval
↓
migrate
例えば、
- 本番DBへ影響が大きい変更
- カラム削除
- インデックス追加
などの場合でも、一度内容を確認してから実行できます。
完全自動にはせず、人による最終確認を残しています。
マイグレーション実行
承認後に実行します。
php artisan migrate --force
CodeBuildでは対話モードが利用できないため、
--force
を付けています。
実行後も状態確認
実行後にも状態を確認しています。
php artisan migrate:status
これにより、
実際にMigrationが適用されたことをログから確認できます。
buildspec.yml
Migration用のCodeBuildでは次のような処理を実行しています。
version: 0.2
phases:
build:
commands:
- php artisan migrate:status
- php artisan migrate --force
- php artisan migrate:status
マイグレーション前後の状態が残るため、後から監査しやすい構成です。
RDSはプライベートサブネット
RDSはインターネットへ公開せず、プライベートサブネットへ配置しています。
そのため、CodeBuildもVPCへ接続しています。
CodeBuild
↓
Private Subnet
↓
RDS
外部公開することなく、安全にマイグレーションを実行できます。
NAT Gateway
CodeBuildをVPCへ接続すると、
インターネットへ直接アクセスできなくなります。
Composerの取得やAWS APIへのアクセスが必要なため、
Private SubnetからはNAT Gateway経由で通信しています。
CodeBuild
↓
Private Subnet
↓
NAT Gateway
↓
Internet
Security Group
RDSでは、
CodeBuild用Security Groupのみ接続を許可しています。
RDS
↓
Inbound
↓
CodeBuild SG
CIDR指定ではなくSecurity Group同士で許可することで、
不要なアクセスを防いでいます。
Secrets Manager
データベース接続情報はSecrets Managerで管理しています。
CodeBuildでは環境変数として取得します。
env:
secrets-manager:
DB_PASSWORD: prod/database:password
認証情報をbuildspec.ymlへ記載する必要はありません。
CDKで構築
インフラはAWS CDKで管理しています。
例えばCodeBuildは次のように定義しています。
const project = new codebuild.Project(this, "MigrationProject", {
vpc,
subnetSelection: {
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
},
securityGroups: [migrationSecurityGroup],
environment: {
buildImage: codebuild.LinuxBuildImage.STANDARD_7_0,
},
});
CodePipelineもCDKで定義しています。
pipeline.addStage({
stageName: "MigrationStatus",
actions: [migrationStatusAction],
});
pipeline.addStage({
stageName: "Approval",
actions: [
new codepipeline_actions.ManualApprovalAction({
actionName: "ApproveMigration",
}),
],
});
pipeline.addStage({
stageName: "Migration",
actions: [migrationAction],
});
pipeline.addStage({
stageName: "Deploy",
actions: [deployAction],
});
インフラもコードとして管理できるため、
環境差分が発生しにくく、再現性の高い構成になっています。
導入して感じたこと
一番良かったのは、
マイグレーションの状態を確認してから実行できるようになったことです。
以前は踏み台サーバーへ接続し、
php artisan migrate
を実行するだけでした。
現在は、
- 現在のMigration状態を確認
- 人が承認
- Migration実行
- 実行後も状態確認
- アプリケーションをデプロイ
という流れになっています。
自動化しつつも、本番DBへの変更は人が最終確認するため、安心して運用できています。
まとめ
今回構築したパイプラインでは、
-
migrate:statusで実行予定を確認 - Manual Approvalで最終判断
-
migrate --forceを実行 - 実行後も
migrate:statusで確認 - アプリケーションをデプロイ
という流れを採用しています。
インフラはCDKでコード化し、CodeBuildはVPC内からRDSへ接続する構成です。
手作業で踏み台サーバーへ接続していた頃と比べ、マイグレーションもCI/CDの一部として管理できるようになり、再現性の高いリリースフローを実現できました。