Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

自己紹介 • 名前: 正徳 巧 • 会社: メドピア株式会社 • 所属: CTO室SRE • GitHub: @sinsoku (画像右上) • Twitter: @sinsoku_listy (画像右下) よろしくお願いします。 2

Slide 3

Slide 3 text

最近の業務 • AWSインフラの改善 • デプロイの改善 • Railsアプリのコードの改善 • セキュリティ関連 3

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

8

Slide 9

Slide 9 text

9

Slide 10

Slide 10 text

10

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

GitHubの週辺りのデプロイ数(2015年) https://speakerdeck.com/yuichielectric/how-github-builds-and-deploy-software 18

Slide 19

Slide 19 text

GitHubのデプロイの変化 • 社員数の増加(588人 => 1248人) • デプロイロック、デプロイキューの混雑 • リリーストレインの導入 • 複数のPRを1つのデプロイにまとめる 19

Slide 20

Slide 20 text

GitHubの週辺りのデプロイ数(2019年) https://speakerdeck.com/yuichielectric/how-github-builds-and-deploy-software 20

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

少ないデプロイでは競合に勝てない時代 22

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

MedPeerのブランチ戦略 24

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

デプロイパイプライン GitHub 1. Actionsで ! のビルド4 2. リリースPRのマージ CodePipeline 1. CodeBuildで db:migrate 2. デプロイ 4 ビルドの高速化についてはテックブログ参照。https://tech.medpeer.co.jp/entry/2021/05/28/180408 26

Slide 27

Slide 27 text

リリースPRの自動作成 • git-pr-release を利用5 • 毎日10:30, 14:30に実行 • 手動で実行も可能 5 https://github.com/x-motemen/git-pr-release 27

Slide 28

Slide 28 text

Web上から実行できる 28

Slide 29

Slide 29 text

デプロイ頻度の計測 $ git log --merges \ --first-parent \ --since='2019-1-1' \ --format='%cd' \ --date='format:%Y-%m' origin/master \ | uniq -c masterへのマージ == デプロイ回数 29

Slide 30

Slide 30 text

30

Slide 31

Slide 31 text

デプロイ頻度を増やすには • ✅ デプロイの改善 • 15〜17分 " => 約7分 # • 月20〜25回 => 月45〜50回 • カナリアデプロイ • 死活監視 31

Slide 32

Slide 32 text

MedPeerのRails appを動かすAWS構成 32

Slide 33

Slide 33 text

AWS CodeDeploy ECSの線形デプロイ、カナリアデプロイをサポートしている。6 • カナリア: 最初の増分で10%、残り90%を5分後にシフト • カナリア: 最初の増分で10%、残り90%を15分後にシフト • 線形デプロイ: トラフィックの10%を毎分シフト • 線形デプロイ: トラフィックの10%を3分ごとにシフト 6 太字部分のカスタマイズが可能。 33

Slide 34

Slide 34 text

カナリアデプロイの概要 34

Slide 35

Slide 35 text

CodePipeline vs GitHub Actions 35

Slide 36

Slide 36 text

36

Slide 37

Slide 37 text

GitHub Actionsの利点 • AWS for GitHub Actionsが便利 • Railsエンジニアが触りやすい • ! YAML • " HCL(Terraform) • 1つのサービス(GitHub)で完結すると楽 37

Slide 38

Slide 38 text

新しい開発フローの設計 1. リリースPRを作る 2. デプロイ承認後、canary環境にデプロイして動作確認 3. 問題がなければ5分後にproduction環境にもデプロイ • mainブランチに自動マージ(=mainは安定版) 4. 問題が起きたらワークフローをキャンセル • CodeDeployでロールバック 38

Slide 39

Slide 39 text

社内サービスで動作検証 39

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

デプロイ(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

Slide 44

Slide 44 text

デプロイ(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

Slide 45

Slide 45 text

ロールバック 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

Slide 46

Slide 46 text

リリース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

Slide 47

Slide 47 text

デプロイ頻度を増やすには • ✅ デプロイの改善 • 15〜17分 " => 約7分 # • 月20〜25回 => 月45〜50回 • $ カナリアデプロイ • 死活監視 47

Slide 48

Slide 48 text

次はMedPeerに導入して、 もっとデプロイ頻度を増やすぞ! 48

Slide 49

Slide 49 text

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