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

Deploy Rails apps in 2021

Deploy Rails apps in 2021

事業成長を加速させたエンジニアリングのウラ側
https://medpeer.connpass.com/event/211745/

Ecad9d801d79f6c6e5df93094690685e?s=128

Takumi Shotoku

May 28, 2021
Tweet

Transcript

  1. Deploy Rails apps in 2021 事業成長を加速させたエンジニアリングのウラ側 2021/05/28 1

  2. 自己紹介 • 名前: 正徳 巧 • 会社: メドピア株式会社 • 所属:

    CTO室SRE • GitHub: @sinsoku (画像右上) • Twitter: @sinsoku_listy (画像右下) よろしくお願いします。 2
  3. 最近の業務 • AWSインフラの改善 • デプロイの改善 • Railsアプリのコードの改善 • セキュリティ関連 3

  4. ソフトウェアのデリバリ https://blogs.vmware.com/management/2020/03/vi-admin-to-devops.html 4

  5. LeanとDevOpsの科学 • デプロイの頻度 • 変更のリードタイム • MTTR(平均修復時間) • 変更失敗率 5

  6. どのくらいのデプロイ頻度を目指す? 6

  7. 7

  8. 8

  9. 9

  10. 10

  11. CircleCIのレポート 20171 • メインラインブランチの安定性 • デプロイ時間 • デプロイ頻度 1 https://circleci.com/landing-pages/assets/2017-VelocityReport-

    Updated-070219_JA.pdf 11
  12. デプロイ時間 デプロイ時間は概ねコントロールされており、80.2%の組織が15 分以内にデプロイを行っています。特にデプロイ時間の短い組織 (95パーセンタイル) では2.7分、中央値は7.6分です。それ以降 はロングテールの分布が続き、下位5パーセンタイルの組織のデ プロイ時間は30分です。 https://circleci.com/landing-pages/assets/2017-VelocityReport-Updated-070219_JA.pdf 12

  13. デプロイ頻度 全組織の75%が、最も活発なプロジェクトにおいて週に12回以下 の頻度でデプロイを行っていました。上位の組織 (95パーセンタ イル) では、メインラインブランチを週に32回デプロイしていま す。これは中央値の5倍以上、下位5パーセンタイルの約24倍で す。 https://circleci.com/landing-pages/assets/2017-VelocityReport-Updated-070219_JA.pdf 13

  14. 更にデプロイ頻度の多い会社の事例 14

  15. GitHubにおける継続的デリバリー2 2 https://speakerdeck.com/yuichielectric/how-github-builds-and-deploy-software 15

  16. GitHub Flow3 3 https://guides.github.com/introduction/flow/ 16

  17. GitHub Flow3 Deploy With GitHub, you can deploy from a

    branch for final testing in production before merging to main. つまりmainブランチは常に安定版になっており、問題が起きたら mainブランチをデプロイすることでロールバックできる。 3 https://guides.github.com/introduction/flow/ 17
  18. GitHubの週辺りのデプロイ数(2015年) https://speakerdeck.com/yuichielectric/how-github-builds-and-deploy-software 18

  19. GitHubのデプロイの変化 • 社員数の増加(588人 => 1248人) • デプロイロック、デプロイキューの混雑 • リリーストレインの導入 •

    複数のPRを1つのデプロイにまとめる 19
  20. GitHubの週辺りのデプロイ数(2019年) https://speakerdeck.com/yuichielectric/how-github-builds-and-deploy-software 20

  21. デプロイまとめ 上位の組織 • 時間: 3分程度 • 頻度: 週に30回程度 すごい会社(GitHub) •

    時間: ? • 頻度: 週に300回以上 21
  22. 少ないデプロイでは競合に勝てない時代 22

  23. デプロイ頻度を増やすために • デプロイの改善 • 半年前のMedPeerのデプロイは15〜17分 • デプロイ頻度は月20〜25回程度 • カナリアデプロイ 23

  24. MedPeerのブランチ戦略 24

  25. デプロイパイプライン GitHub 1. リリースPRのマージ CodePipeline 1. CodeBuildで ! のビルド 2.

    CodeBuildで db:migrate 3. デプロイ 25
  26. デプロイパイプライン GitHub 1. Actionsで ! のビルド4 2. リリースPRのマージ CodePipeline 1.

    CodeBuildで db:migrate 2. デプロイ 4 ビルドの高速化についてはテックブログ参照。https://tech.medpeer.co.jp/entry/2021/05/28/180408 26
  27. リリースPRの自動作成 • git-pr-release を利用5 • 毎日10:30, 14:30に実行 • 手動で実行も可能 5

    https://github.com/x-motemen/git-pr-release 27
  28. Web上から実行できる 28

  29. デプロイ頻度の計測 $ git log --merges \ --first-parent \ --since='2019-1-1' \

    --format='%cd' \ --date='format:%Y-%m' origin/master \ | uniq -c masterへのマージ == デプロイ回数 29
  30. 30

  31. デプロイ頻度を増やすには • ✅ デプロイの改善 • 15〜17分 " => 約7分 #

    • 月20〜25回 => 月45〜50回 • カナリアデプロイ • 死活監視 31
  32. MedPeerのRails appを動かすAWS構成 32

  33. AWS CodeDeploy ECSの線形デプロイ、カナリアデプロイをサポートしている。6 • カナリア: 最初の増分で10%、残り90%を5分後にシフト • カナリア: 最初の増分で10%、残り90%を15分後にシフト •

    線形デプロイ: トラフィックの10%を毎分シフト • 線形デプロイ: トラフィックの10%を3分ごとにシフト 6 太字部分のカスタマイズが可能。 33
  34. カナリアデプロイの概要 34

  35. CodePipeline vs GitHub Actions 35

  36. 36

  37. GitHub Actionsの利点 • AWS for GitHub Actionsが便利 • Railsエンジニアが触りやすい •

    ! YAML • " HCL(Terraform) • 1つのサービス(GitHub)で完結すると楽 37
  38. 新しい開発フローの設計 1. リリースPRを作る 2. デプロイ承認後、canary環境にデプロイして動作確認 3. 問題がなければ5分後にproduction環境にもデプロイ • mainブランチに自動マージ(=mainは安定版) 4.

    問題が起きたらワークフローをキャンセル • CodeDeployでロールバック 38
  39. 社内サービスで動作検証 39

  40. 実際に作ったワークフロー 40

  41. db:migrate の実行 • db/migrate/* のdiffで要否を判定する • ecs:RunTask でECS上でワンショットのタスクを実行する • sinsoku/amazon-ecs-run-task-definition

    のactionを作成7 7 https://github.com/sinsoku/amazon-ecs-run-task-definition 41
  42. db:migrate の実行 steps: - uses: actions/checkout@v2 with: fetch-depth: 2 #

    db:migrate Λ͢Δඞཁ͕͋Δ͔൑அ͢ΔͨΊɺલճͷmasterϒϥϯνͱͷdiffΛऔಘ͢Δɻ - name: Diff files in db/migrate id: migrate run: | MIGRATE_SIZE=$(git diff --name-only @^ -- db/migrate | wc -l) echo "::set-output name=size::$MIGRATE_SIZE" # ్தུ - name: Run a task on Amazon ECS if: steps.migrate.outputs.size != '0' uses: sinsoku/amazon-ecs-run-task-definition@v1 with: task-definition: ${{ steps.update-task-def-cmd.outputs.task-definition-arn }} container: ${{ env.ECS_CONTAINER }} command: '["bin/rails", "db:migrate"]' service: ${{ env.ECS_SERVICE }} cluster: ${{ env.ECS_CLUSTER }} 42
  43. デプロイ(canary) steps: - name: Deploy to Amazon ECS id: code-deploy

    uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: ${{ steps.task-def-web.outputs.task-definition }} service: ${{ env.ECS_SERVICE }} cluster: ${{ env.ECS_CLUSTER }} codedeploy-appspec: appspec.yaml codedeploy-application: example-app codedeploy-deployment-group: example-app-dg wait-for-service-stability: false - name: Wait for the canary environment to deploy run: | names=$(aws deploy get-deployment --deployment-id ${{ steps.code-deploy.outputs.codedeploy-deployment-id }} \ --query 'deploymentInfo.loadBalancerInfo.targetGroupPairInfoList[].targetGroups[].name' \ --output text) arns=$(aws elbv2 describe-target-groups --names $names \ --query 'TargetGroups[].TargetGroupArn' \ --output text) for arn in $arns; do aws elbv2 wait target-in-service --target-group-arn $arn done 43
  44. デプロイ(production) production: needs: canary runs-on: ubuntu-latest timeout-minutes: 15 environment: name:

    production url: https://example.com concurrency: deployment steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: Wait a deployment run: aws deploy wait deployment-successful --deployment-id ${{ needs.canary.outputs.deployment-id }} 44
  45. ロールバック rollback: needs: [canary, production] if: cancelled() && needs.canary.outputs.deployment-id runs-on:

    ubuntu-latest timeout-minutes: 10 steps: - name: Stop a deployment run: aws deploy stop-deployment --deployment-id ${{ needs.canary.outputs.deployment-id }} --auto-rollback-enabled - name: Run a task on Amazon ECS if: needs.canary.outputs.migrate-size != '0' uses: sinsoku/amazon-ecs-run-task-definition@v1 with: task-definition: ${{ needs.canary.outputs.task-definition-arn }} container: ${{ env.ECS_CONTAINER }} command: '["bin/rails", "db:rollback", "STEP=${{ needs.canary.outputs.migrate-size }}"]' service: ${{ env.ECS_SERVICE }} cluster: ${{ env.ECS_CLUSTER }} 45
  46. リリースPRのマージ merge: needs: production runs-on: ubuntu-latest timeout-minutes: 5 steps: -

    name: Merge a release PR env: GITHUB_TOKEN: ${{ secrets.MEDPEER_GITHUB_TOKEN }} run: gh pr merge ${{ github.event.number }} --merge --repo ${{ github.repository }} 46
  47. デプロイ頻度を増やすには • ✅ デプロイの改善 • 15〜17分 " => 約7分 #

    • 月20〜25回 => 月45〜50回 • $ カナリアデプロイ • 死活監視 47
  48. 次はMedPeerに導入して、 もっとデプロイ頻度を増やすぞ! 48

  49. この環境を体験したい人、一緒に作りたい人 https://medpeer.co.jp/recruit/ 49