Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PhinxによるDBマイグレーションとサービス運用

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.
Avatar for zosokh zosokh
October 08, 2023

 PhinxによるDBマイグレーションとサービス運用

Avatar for zosokh

zosokh

October 08, 2023
Tweet

More Decks by zosokh

Other Decks in Technology

Transcript

  1. さらに構築していく ├── Dockerfile ├── README.md ├── composer.json ├── composer.lock ├──

    db   ├── migrations   ├── seeds   └── sql ├── docker ├── docker-compose.yml ├── phinx.php └── vendor
  2. さらに構築していく ├── Dockerfile ├── README.md ├── composer.json ├── composer.lock ├──

    db   ├── migrations   ├── seeds   └── sql ├── docker ├── docker-compose.yml ├── phinx.php └── vendor migrationやseedingファイル
  3. さらに構築していく ├── Dockerfile ├── README.md ├── composer.json ├── composer.lock ├──

    db   ├── migrations   ├── seeds   └── sql ├── docker ├── docker-compose.yml ├── phinx.php └── vendor ローカル開発環境ファイル
  4. さらにさらに構築していく ├── .git ├── .github ├── .gitignore ├── .php-cs-fixer.php ├──

    Dockerfile ├── README.md ├── composer.json ├── composer.lock ├── db ├── deploy ├── docker ├── docker-compose.build.yml ├── docker-compose.yml ├── phinx.php └── vendor
  5. さらにさらに構築していく ├── .git ├── .github ├── .gitignore ├── .php-cs-fixer.php ├──

    Dockerfile ├── README.md ├── composer.json ├── composer.lock ├── db ├── deploy ├── docker ├── docker-compose.build.yml ├── docker-compose.yml ├── phinx.php └── vendor fixerによる静的解析
  6. さらにさらに構築していく ├── .git ├── .github ├── .gitignore ├── .php-cs-fixer.php ├──

    Dockerfile ├── README.md ├── composer.json ├── composer.lock ├── db ├── deploy ├── docker ├── docker-compose.build.yml ├── docker-compose.yml ├── phinx.php └── vendor マイグレーションデプロイファイ ル
  7. Phinxのローカル設定 <?php return [ … 'environments' => [ 'default_migration_table' =>

    'phinxlog', 'default_environment' => local, 'local' => [ 'adapter' => 'mysql', 'host' => 'mysql', 'name' => 'local_mysql', 'user' => 'mysql_user', 'pass' => 'mysql_pw', 'port' => '3306', 'charset' => 'utf8', ], ], … ] ローカル用env設定を追加 hostはdockerサービス名を指 定 DB名・user・passはローカル MySQLを設定 phinx.php
  8. Phinxのローカル設定 <?php return [ … 'environments' => [ 'default_migration_table' =>

    'phinxlog', 'default_environment' => local, 'local' => [ 'adapter' => 'mysql', 'host' => 'mysql', 'name' => 'local_mysql', 'user' => 'mysql_user', 'pass' => 'mysql_pw', 'port' => '3306', 'charset' => 'utf8', ], ], … ] default_environmentをローカ ル用envにする phinx.php
  9. 以下のようなコマンドでdump sqlを作成する テーブルのdump mysqldump --no-tablespaces -hdumpしたいDB host -uユーザー名 -pパス --single-transaction

    DB名 --no-data --set-gtid-purged=OFF | sed 's/ AUTO_INCREMENT=[0-9]*\b//' > init.sql 例としてInitialTablesという名前でPhinxのマイグレーションファイルを新 規作成 phinx create InitialTables
  10. dumpしたSQLをPhinxでマイグレーション利用 <?php declare(strict_types=1); use Phinx\Migration\AbstractMigration; final class InitialTables extends AbstractMigration

    { public function up() { $file = dirname(dirname(__FILE__)) . '/sql/init.sql'; $this->execute(file_get_contents($file)); } } db/migrations/20231008000000_initial_tables.php こちらに配置
  11. マイグレーションテスト name: exec-test on: push: jobs: exec-test: runs-on: ubuntu-latest services:

    mysql: image: mysql:8.0 ports: - 3306:3306 env: MYSQL_ROOT_PASSWORD: **** MYSQL_DATABASE: **** MYSQL_USER: **** MYSQL_PASSWORD: **** MYSQL_ALLOW_EMPTY_PASSWORD: "yes" steps: - uses: actions/checkout@v3 - name: composer install run: | 〜〜cpmoser install処理~~ - name: execute test shell: bash run: | vendor/bin/phinx migrate vendor/bin/phinx rollback -e local -t 20231008000000 dumpファイルを利用したマイ グレーションはロールバックが できないため、 ブレイクポイントを設定して ロールバック時のエラーを回 避 マイグレーションとロールバックを両方実 行をして、マイグレーションファイルの内 容が問題ないかテスト .github/workflows/test.yml
  12. デプロイフック name: deploy on: push: branches: - main - release

    workflow_dispatch: jobs: deploy_job: … main・releaseブランチへのマージと手 動実行時にアクションを発火させる 次にマイグレーション先の設 定を書いていく .github/workflows/deploy.yml
  13. マイグレーション先 AWSのアクセスキーやシークレット、 AssumeRoleのARN値をGitHubの secretsに登録しておく マージまたは実行ブランチによって環境 変数を書き換えるようにしておく steps: ... # 環境による

    env設定 - name: prd setting if: github.ref_name == 'main' run: | echo PHINX_ENV=production >> $GITHUB_ENV echo DEPLOY_ENV=production >> $GITHUB_ENV echo AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_PRD }} >> $GITHUB_ENV echo AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_PRD }} >> $GITHUB_ENV echo AWS_ASSUME_ROLE_ARN=${{ secrets.AWS_ASSUME_ROLE_ARN_PRD }} >> $GITHUB_ENV ... - name: stage setting if: github.ref_name == 'release' run: | echo PHINX_ENV=stage >> $GITHUB_ENV echo DEPLOY_ENV=stage >> $GITHUB_ENV echo AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_STG }} >> $GITHUB_ENV echo AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_STG }} >> $GITHUB_ENV echo AWS_ASSUME_ROLE_ARN=${{ secrets.AWS_ASSUME_ROLE_ARN_STG }} >> $GITHUB_ENV ... - name: dev setting if: github.ref_name != 'main' && github.ref_name != 'release' run: | echo PHINX_ENV=dev >> $GITHUB_ENV echo DEPLOY_ENV=dev >> $GITHUB_ENV echo AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_DEV }} >> $GITHUB_ENV echo AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_DEV }} >> $GITHUB_ENV echo AWS_ASSUME_ROLE_ARN=${{ secrets.AWS_ASSUME_ROLE_ARN_DEV }} >> $GITHUB_ENV ... subnetやsecurity group の情報等も設定 .github/workflows/deploy.yml マージ先によって扱う設定ま わりを切り替え
  14. ECRプッシュ AWSの認証させECRにマイグレーション アプリケーションをプッシュする # AWS認証 uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{

    env.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} role-to-assume: ${{ env.AWS_ASSUME_ROLE_ARN }} role-duration-seconds: 1200 # ECRログイン id: login-ecr uses: aws-actions/amazon-ecr-login@v1 # イメージビルドとECRプッシュ id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} IMAGE_TAG: ${{ github.sha }} run: | docker compose -f docker-compose.build.yml build docker compose -f docker-compose.build.yml push echo "image=$ECR_REGISTRY/ECRのリポジトリ:$IMAGE_TAG" >> $GITHUB_OUTPUT .github/workflows/deploy.yml
  15. ECRプッシュ # AWS認証 uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }}

    aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} role-to-assume: ${{ env.AWS_ASSUME_ROLE_ARN }} role-duration-seconds: 1200 # ECRログイン id: login-ecr uses: aws-actions/amazon-ecr-login@v1 # イメージビルドとECRプッシュ id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} IMAGE_TAG: ${{ github.sha }} run: | docker compose -f docker-compose.build.yml build docker compose -f docker-compose.build.yml push echo "image=$ECR_REGISTRY/ECRのリポジトリ:$IMAGE_TAG" >> $GITHUB_OUTPUT デプロイ用のdocker-composeを用意 version: '3.8' services: php: image: ${ECR_REGISTRY}/ECRリポジトリ名:${IMAGE_TAG} build: context: . dockerfile: Dockerfile docker-compose.build.yml
  16. タスク定義反映 マイグレーション実行 # タスク定義設定 id: task-def uses: aws-actions/amazon-ecs-render-task-definition@v1 with: task-definition:

    deploy/task-definition-${{ env.DEPLOY_ENV }}.json container-name: ECSコンテナ名 image: ${{ steps.build-image.outputs.image }} # タスクデプロイ id: deploy-task uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: ${{ steps.task-def.outputs.task-definition }} cluster: ${{ env.ECS_CLUSTER }} # マイグレーション実行 uses: noelzubin/[email protected] id: migration with: cluster: ECSクラスター名 task-definition: タスク定義名 subnets: ${{ env.SUBNETS }} security-groups: ${{ env.SECURITY_GROUPS }} assign-public-ip: ENABLED override-container: ECSコンテナ名 override-container-command: | sh -c . ./deploy/read-envs.sh && vendor/bin/phinx migrate -e ${{ env.PHINX_ENV }} .github/workflows/deploy.yml
  17. タスク定義反映 # タスク定義設定 id: task-def uses: aws-actions/amazon-ecs-render-task-definition@v1 with: task-definition: deploy/task-definition-${{

    env.DEPLOY_ENV }}.json container-name: ECSコンテナ名 image: ${{ steps.build-image.outputs.image }} # タスクデプロイ id: deploy-task uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: ${{ steps.task-def.outputs.task-definition }} cluster: ${{ env.ECS_CLUSTER }} # マイグレーション実行 uses: noelzubin/[email protected] id: migration with: cluster: ECSクラスター名 task-definition: タスク定義名 subnets: ${{ env.SUBNETS }} security-groups: ${{ env.SECURITY_GROUPS }} assign-public-ip: ENABLED override-container: ECSコンテナ名 override-container-command: | sh -c . ./deploy/read-envs.sh && vendor/bin/phinx migrate -e ${{ env.PHINX_ENV }} タスク定義jsonファイルを用意 { "executionRoleArn": "AmazonECSTaskExecutionRolePolicyがあるIAM Role", "containerDefinitions": [ { "logConfiguration": { "logDriver": "awslogs", "secretOptions": null, "options": { "awslogs-group": "CloudWatch ロググループ", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "ecs" } }, "image": "ECRリポジトリURI", "name": "名前" } ], "placementConstraints": [], "family": "ファミリー名", "requiresCompatibilities": [ "FARGATE" ], "networkMode": "awsvpc", "cpu": "256", "memory": "512", "volumes": [] } deploy/task-definition-dev.json
  18. タスク定義反映 マイグレーション実行 # タスク定義設定 id: task-def uses: aws-actions/amazon-ecs-render-task-definition@v1 with: task-definition:

    deploy/task-definition-${{ env.DEPLOY_ENV }}.json container-name: ECSコンテナ名 image: ${{ steps.build-image.outputs.image }} # タスクデプロイ id: deploy-task uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: ${{ steps.task-def.outputs.task-definition }} cluster: ${{ env.ECS_CLUSTER }} # マイグレーション実行 uses: noelzubin/[email protected] id: migration with: cluster: ECSクラスター名 task-definition: タスク定義名 subnets: ${{ env.SUBNETS }} security-groups: ${{ env.SECURITY_GROUPS }} assign-public-ip: ENABLED override-container: ECSコンテナ名 override-container-command: | sh -c . ./deploy/read-envs.sh && vendor/bin/phinx migrate -e ${{ env.PHINX_ENV }} envを指定してマイグレーションを実行させる
  19. タスク定義反映 マイグレーション実行 # タスク定義設定 id: task-def uses: aws-actions/amazon-ecs-render-task-definition@v1 with: task-definition:

    deploy/task-definition-${{ env.DEPLOY_ENV }}.json container-name: ECSコンテナ名 image: ${{ steps.build-image.outputs.image }} # タスクデプロイ id: deploy-task uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: ${{ steps.task-def.outputs.task-definition }} cluster: ${{ env.ECS_CLUSTER }} # マイグレーション実行 uses: noelzubin/[email protected] id: migration with: cluster: ECSクラスター名 task-definition: タスク定義名 subnets: ${{ env.SUBNETS }} security-groups: ${{ env.SECURITY_GROUPS }} assign-public-ip: ENABLED override-container: ECSコンテナ名 override-container-command: | sh -c . ./deploy/read-envs.sh && vendor/bin/phinx migrate -e ${{ env.PHINX_ENV }} DB情報の環境変数を反映させるシェルを用意し実行
  20. DB情報と環境変数 • Phinx は PHINX_ というプレフィックスが付 いた環境変数を自動的に取得できるため、 Phinx設定ファイルにはDB情報を記載せず、 環境変数から取得するように設定させる •

    環境変数のDB情報はAWS System Managerから取得し設定させる 'production' => [ 'adapter' => 'mysql', 'host' => '%%PHINX_DBHOST%%', 'name' => '%%PHINX_DBNAME%%', 'user' => '%%PHINX_DBUSER%%', 'pass' => '%%PHINX_DBPW%%', 'port' => '3306', 'charset' => 'utf8', ] phinx.php
  21. マイグレーションログ表示 # ecs-cliインストール shell: bash run: | sudo curl -Lo

    /usr/local/bin/ecs-cli https://amazon-ecs-cli.s3.amazonaws.com/ecs-cli-linux-amd64-v1.21.0 sudo chmod +x /usr/local/bin/ecs-cli # コンテナタスクのログを取得 - name: Migration logs here!!! shell: bash run: | task_arn=${{ steps.migration.outputs.task-arn }} task_id=${task_arn//*\//''} ecs-cli logs --timestamps --cluster ${{ env.ECS_CLUSTER }} --task-id ${task_id} GitHub Actions側にマイグレーションログが表示される .github/workflows/deploy.yml
  22. サービスアプリケーション側 マイグレーションをGit Submoduleで管理 ├── migration-application ├── README.md ├── app ├──

    artisan ├── bootstrap ├── composer.json ├── composer.lock ├── config ├── database ├── package.json ├── phpunit.xml ├── public ├── resources ├── routes … git submodule add https://github.com/sample/migration-application.git
  23. 1. submoduleのマイグレーションアプリでマイグレーション実行 2. マイグレーション側ローカルDBからdumpファイル作成 3. dumpファイルをテストアプリケーション側DBにimport dumpファイルを作成してDB構築 migration-local: docker-compose -f

    migration-application/docker-compose.yml run --rm php bash -c "\ composer install;\ phinx migrate;\ mysqldump --no-tablespaces -uユーザー -pパス -hmysqlサービス --single-transaction db名 --no-data > dump.sql;\ " && \ docker-compose run --rm php bash -c "\ mysql -uユーザー -pパス -hmysqlサービス db名 < migration-application/dump.sql;\ rm -rf migration-application/dump.sql;\ "; makefile例
  24. 1. submoduleのマイグレーションアプリでマイグレーション実行 2. マイグレーション側ローカルDBからdumpファイル作成 3. dumpファイルをテストアプリケーション側DBにimport dumpファイルを作成してDB構築 migration-local: docker-compose -f

    migration-application/docker-compose.yml run --rm php bash -c "\ composer install;\ phinx migrate;\ mysqldump --no-tablespaces -uユーザー -pパス -hmysqlサービス --single-transaction db名 --no-data > dump.sql;\ " && \ docker-compose run --rm php bash -c "\ mysql -uユーザー -pパス -hmysqlサービス db名 < migration-application/dump.sql;\ rm -rf migration-application/dump.sql;\ "; makefile例 migration-application service-application
  25. GitHub Actions上で のマイグレーションとユ ニットテスト jobs: migration: uses: ./.github/workflows/migration.yml phpunit: runs-on:

    ubuntu-latest needs: - migration container: image: cimg/php:8.2 services: mysql: image: mysql:8.0 ports: - 3306:3306 env: MYSQL_ROOT_PASSWORD: **** MYSQL_DATABASE: **** MYSQL_USER: **** MYSQL_PASSWORD: **** MYSQL_ALLOW_EMPTY_PASSWORD: "yes" steps: - name: Checkout uses: actions/checkout@v3 - name: composer install run: | 〜〜cpmoser install処理~~ - name: load migration uses: actions/download-artifact@v3 with: name: migration - name: setup run: | mysql -uユーザー名 -pパス -hmysql DB名 < dump.sql - name: exec phpunit run: | ./vendor/bin/phpunit マイグレーションアプリ側でのdumpファ イル作成は別ファイルで行い、 artifactでdumpファイルを渡す形の例 です
  26. GitHub Actions上で のマイグレーションとユ ニットテスト マイグレーションアプリ側でのdumpファ イル作成は別ファイルで行い、 artifactでdumpファイルを渡す形の例 です jobs: migration:

    uses: ./.github/workflows/migration.yml phpunit: runs-on: ubuntu-latest needs: - migration container: image: cimg/php:8.2 services: mysql: image: mysql:8.0 ports: - 3306:3306 env: MYSQL_ROOT_PASSWORD: **** MYSQL_DATABASE: **** MYSQL_USER: **** MYSQL_PASSWORD: **** MYSQL_ALLOW_EMPTY_PASSWORD: "yes" steps: - name: Checkout uses: actions/checkout@v3 - name: composer install run: | 〜〜cpmoser install処理~~ - name: load migration uses: actions/download-artifact@v3 with: name: migration - name: setup run: | mysql -uユーザー名 -pパス -hmysql DB名 < dump.sql - name: exec phpunit run: | ./vendor/bin/phpunit
  27. GitHub Actions上で のマイグレーションとユ ニットテスト マイグレーションアプリ側でのdumpファ イル作成は別ファイルで行い、 artifactでdumpファイルを渡す形の例 です jobs: migration:

    uses: ./.github/workflows/migration.yml phpunit: runs-on: ubuntu-latest needs: - migration container: image: cimg/php:8.2 services: mysql: image: mysql:8.0 ports: - 3306:3306 env: MYSQL_ROOT_PASSWORD: **** MYSQL_DATABASE: **** MYSQL_USER: **** MYSQL_PASSWORD: **** MYSQL_ALLOW_EMPTY_PASSWORD: "yes" steps: - name: Checkout uses: actions/checkout@v3 - name: composer install run: | 〜〜cpmoser install処理~~ - name: load migration uses: actions/download-artifact@v3 with: name: migration - name: setup run: | mysql -uユーザー名 -pパス -hmysql DB名 < dump.sql - name: exec phpunit run: | ./vendor/bin/phpunit