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

スケーラブル CI/CD with Nx モノレポ

okmttdhr
December 08, 2021

スケーラブル CI/CD with Nx モノレポ

okmttdhr

December 08, 2021
Tweet

More Decks by okmttdhr

Other Decks in Programming

Transcript

  1. Nx

  2. [ 本題] スケーラブルな CI/CD とは? - 1. アプリケーションが増えても 実行時間が速い -

    2. アプリケーションが増えても 保守できる - 3. チームが増えても 疎結合に運用できる
  3. 1. アプリケーションが増えても 実行時間が速い n = アプリケーション & ライブラリの数 モノレポでないアプリでは n

    = 1 だが、モノレポでは理論上無限にアプリが増える すべて直列だと、実行時間が n に比例して伸びるのでスケールしない ` ` ` ` ` `
  4. 1. アプリケーションが増えても 実行時間が速い Nx で影響のあるアプリケーション & ライブラリを出力することができる nx affected:test --parallel

    などもあるが、それ以上のことをしたかったので不採用 ` ` $ nx affected:apps --base=origin/main --head=HEAD # 環境によって base, head は変更可能 > NX Affected apps: - app-foo - app-bar - app-buz $ nx affected:libs --base=origin/main --head=HEAD > NX Affected libs: - lib-foo - lib-bar - lib-buz
  5. 1. アプリケーションが増えても 実行時間が速い 出力したアプリケーション & ライブラリを Github Actions の matrix

    形式 に変換 composite action 化することで、使い回せるように 1. https://github.blog/changelog/2020-04-15-github-actions-new-workflow-features/ ↩︎ 2. https://docs.github.com/en/actions/creating-actions/creating-a-composite-action ↩︎ [1] ` `[2] name: output affected outputs: affected: description: 'affected apps and libs' value: ${{ steps.print.outputs.affected }} runs: using: composite steps: - name: print id: print run: | # affected-to-matrix.js で nx affected を実行し matrix 形式に変換 MATRIX=$(node ./scripts/affected-to-matrix.js) echo "::set-output name=affected::$MATRIX" shell: bash
  6. 1. アプリケーションが増えても 実行時間が速い 出力したアプリケーション & ライブラリを Github Actions の matrix

    形式 に変換 composite action 化することで、使い回せるように 1. https://github.blog/changelog/2020-04-15-github-actions-new-workflow-features/ ↩︎ 2. https://docs.github.com/en/actions/creating-actions/creating-a-composite-action ↩︎ [1] ` `[2] MATRIX=$(node ./scripts/affected-to-matrix.js) name: output affected outputs: affected: description: 'affected apps and libs' value: ${{ steps.print.outputs.affected }} runs: using: composite steps: - name: print id: print run: | # affected-to-matrix.js で nx affected を実行し matrix 形式に変換 echo "::set-output name=affected::$MATRIX" shell: bash
  7. 1. アプリケーションが増えても 実行時間が速い 出力したアプリケーション & ライブラリを Github Actions の matrix

    形式 に変換 composite action 化することで、使い回せるように 1. https://github.blog/changelog/2020-04-15-github-actions-new-workflow-features/ ↩︎ 2. https://docs.github.com/en/actions/creating-actions/creating-a-composite-action ↩︎ [1] ` `[2] using: composite name: output affected outputs: affected: description: 'affected apps and libs' value: ${{ steps.print.outputs.affected }} runs: steps: - name: print id: print run: | # affected-to-matrix.js で nx affected を実行し matrix 形式に変換 MATRIX=$(node ./scripts/affected-to-matrix.js) echo "::set-output name=affected::$MATRIX" shell: bash
  8. 1. アプリケーションが増えても 実行時間が速い 出力したアプリケーション & ライブラリを Github Actions の matrix

    形式 に変換 composite action 化することで、使い回せるように 1. https://github.blog/changelog/2020-04-15-github-actions-new-workflow-features/ ↩︎ 2. https://docs.github.com/en/actions/creating-actions/creating-a-composite-action ↩︎ [1] ` `[2] name: output affected outputs: affected: description: 'affected apps and libs' value: ${{ steps.print.outputs.affected }} runs: using: composite steps: - name: print id: print run: | # affected-to-matrix.js で nx affected を実行し matrix 形式に変換 MATRIX=$(node ./scripts/affected-to-matrix.js) echo "::set-output name=affected::$MATRIX" shell: bash
  9. 1. アプリケーションが増えても 実行時間が速い 作成した composite action を使って、影響のあるアプリケーション & ライブラリだけをテスト `

    ` name: test lint build # リントやビルドも合わせて行っているが、並列化の余地はある (swc 等も活用し、全部込みで5 分程度で終わるので今は問題に jobs: output-affected: runs-on: ubuntu-latest outputs: affected: ${{ steps.print.outputs.affected }} steps: - uses: ./.github/workflows/actions/output-affected id: print test-lint-build: runs-on: ubuntu-latest environment: test strategy: # 生成した matrix を設置 matrix: ${{needs.output-affected.outputs.affected}} steps: # 実際のテスト実行スクリプト ( 省略)
  10. 1. アプリケーションが増えても 実行時間が速い 作成した composite action を使って、影響のあるアプリケーション & ライブラリだけをテスト `

    ` steps: - uses: ./.github/workflows/actions/output-affected id: print name: test lint build # リントやビルドも合わせて行っているが、並列化の余地はある (swc 等も活用し、全部込みで5 分程度で終わるので今は問題にな jobs: output-affected: runs-on: ubuntu-latest outputs: affected: ${{ steps.print.outputs.affected }} test-lint-build: runs-on: ubuntu-latest environment: test strategy: # 生成した matrix を設置 matrix: ${{needs.output-affected.outputs.affected}} steps: # 実際のテスト実行スクリプト ( 省略)
  11. 1. アプリケーションが増えても 実行時間が速い 作成した composite action を使って、影響のあるアプリケーション & ライブラリだけをテスト `

    ` outputs: affected: ${{ steps.print.outputs.affected }} test-lint-build: strategy: # 生成した matrix を設置 matrix: ${{needs.output-affected.outputs.affected}} name: test lint build # リントやビルドも合わせて行っているが、並列化の余地はある (swc 等も活用し、全部込みで5 分程度で終わるので今は問題にな jobs: output-affected: runs-on: ubuntu-latest steps: - uses: ./.github/workflows/actions/output-affected id: print runs-on: ubuntu-latest environment: test steps: # 実際のテスト実行スクリプト ( 省略)
  12. 1. アプリケーションが増えても 実行時間が速い 作成した composite action を使って、影響のあるアプリケーション & ライブラリだけをテスト `

    ` name: test lint build # リントやビルドも合わせて行っているが、並列化の余地はある (swc 等も活用し、全部込みで5 分程度で終わるので今は問題に jobs: output-affected: runs-on: ubuntu-latest outputs: affected: ${{ steps.print.outputs.affected }} steps: - uses: ./.github/workflows/actions/output-affected id: print test-lint-build: runs-on: ubuntu-latest environment: test strategy: # 生成した matrix を設置 matrix: ${{needs.output-affected.outputs.affected}} steps: # 実際のテスト実行スクリプト ( 省略)
  13. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ name: deploy jobs: output-affected:

    # 省略 call-testing: # 省略 call-deploy: needs: [output-affected, call-testing] runs-on: ubuntu-latest environment: ${{matrix.app-name}}-prd # environment によりアプリごとの環境変数を読み込める strategy: # 生成した matrix を設置 matrix: ${{fromJson(needs.output-affected.outputs.affected)}} steps: - uses: actions/checkout@v2 - uses: ./.github/workflows/actions/ecr-deploy with: app-name: ${{matrix.app-name}} AWS_ECR_ACCESS_KEY_ID: ${{secrets.AWS_ECR_ACCESS_KEY_ID}} AWS_ECR_SECRET_ACCESS_KEY: ${{secrets.AWS_ECR_SECRET_ACCESS_KEY}}
  14. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ output-affected: # 省略 name:

    deploy jobs: call-testing: # 省略 call-deploy: needs: [output-affected, call-testing] runs-on: ubuntu-latest environment: ${{matrix.app-name}}-prd # environment によりアプリごとの環境変数を読み込める strategy: # 生成した matrix を設置 matrix: ${{fromJson(needs.output-affected.outputs.affected)}} steps: - uses: actions/checkout@v2 - uses: ./.github/workflows/actions/ecr-deploy with: app-name: ${{matrix.app-name}} AWS_ECR_ACCESS_KEY_ID: ${{secrets.AWS_ECR_ACCESS_KEY_ID}} AWS_ECR_SECRET_ACCESS_KEY: ${{secrets.AWS_ECR_SECRET_ACCESS_KEY}}
  15. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ call-deploy: strategy: # 生成した

    matrix を設置 matrix: ${{fromJson(needs.output-affected.outputs.affected)}} app-name: ${{matrix.app-name}} name: deploy jobs: output-affected: # 省略 call-testing: # 省略 needs: [output-affected, call-testing] runs-on: ubuntu-latest environment: ${{matrix.app-name}}-prd # environment によりアプリごとの環境変数を読み込める steps: - uses: actions/checkout@v2 - uses: ./.github/workflows/actions/ecr-deploy with: AWS_ECR_ACCESS_KEY_ID: ${{secrets.AWS_ECR_ACCESS_KEY_ID}} AWS_ECR_SECRET_ACCESS_KEY: ${{secrets.AWS_ECR_SECRET_ACCESS_KEY}}
  16. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ - uses: ./.github/workflows/actions/ecr-deploy name:

    deploy jobs: output-affected: # 省略 call-testing: # 省略 call-deploy: needs: [output-affected, call-testing] runs-on: ubuntu-latest environment: ${{matrix.app-name}}-prd # environment によりアプリごとの環境変数を読み込める strategy: # 生成した matrix を設置 matrix: ${{fromJson(needs.output-affected.outputs.affected)}} steps: - uses: actions/checkout@v2 with: app-name: ${{matrix.app-name}} AWS_ECR_ACCESS_KEY_ID: ${{secrets.AWS_ECR_ACCESS_KEY_ID}} AWS_ECR_SECRET_ACCESS_KEY: ${{secrets.AWS_ECR_SECRET_ACCESS_KEY}}
  17. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ environment: ${{matrix.app-name}}-prd # environment

    によりアプリごとの環境変数を読み込める name: deploy jobs: output-affected: # 省略 call-testing: # 省略 call-deploy: needs: [output-affected, call-testing] runs-on: ubuntu-latest strategy: # 生成した matrix を設置 matrix: ${{fromJson(needs.output-affected.outputs.affected)}} steps: - uses: actions/checkout@v2 - uses: ./.github/workflows/actions/ecr-deploy with: app-name: ${{matrix.app-name}} AWS_ECR_ACCESS_KEY_ID: ${{secrets.AWS_ECR_ACCESS_KEY_ID}} AWS_ECR_SECRET_ACCESS_KEY: ${{secrets.AWS_ECR_SECRET_ACCESS_KEY}}
  18. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ name: deploy jobs: output-affected:

    # 省略 call-testing: # 省略 call-deploy: needs: [output-affected, call-testing] runs-on: ubuntu-latest environment: ${{matrix.app-name}}-prd # environment によりアプリごとの環境変数を読み込める strategy: # 生成した matrix を設置 matrix: ${{fromJson(needs.output-affected.outputs.affected)}} steps: - uses: actions/checkout@v2 - uses: ./.github/workflows/actions/ecr-deploy with: app-name: ${{matrix.app-name}} AWS_ECR_ACCESS_KEY_ID: ${{secrets.AWS_ECR_ACCESS_KEY_ID}} AWS_ECR_SECRET_ACCESS_KEY: ${{secrets.AWS_ECR_SECRET_ACCESS_KEY}}
  19. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ reusable workflow でテストを呼び出せる 1.

    https://docs.github.com/en/actions/learn-github-actions/reusing-workflows ↩︎ ` `[1] name: deploy jobs: output-affected: # 省略 call-testing: needs: [output-affected] uses: org/repo/.github/workflows/testing.yml@main with: app-name: ${{needs.output-affected.outputs.affected}} call-deploy: # 省略
  20. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ reusable workflow でテストを呼び出せる 1.

    https://docs.github.com/en/actions/learn-github-actions/reusing-workflows ↩︎ ` `[1] output-affected: # 省略 name: deploy jobs: call-testing: needs: [output-affected] uses: org/repo/.github/workflows/testing.yml@main with: app-name: ${{needs.output-affected.outputs.affected}} call-deploy: # 省略
  21. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ reusable workflow でテストを呼び出せる 1.

    https://docs.github.com/en/actions/learn-github-actions/reusing-workflows ↩︎ ` `[1] call-testing: uses: org/repo/.github/workflows/testing.yml@main app-name: ${{needs.output-affected.outputs.affected}} name: deploy jobs: output-affected: # 省略 needs: [output-affected] with: call-deploy: # 省略
  22. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけをデプロイ reusable workflow でテストを呼び出せる 1.

    https://docs.github.com/en/actions/learn-github-actions/reusing-workflows ↩︎ ` `[1] name: deploy jobs: output-affected: # 省略 call-testing: needs: [output-affected] uses: org/repo/.github/workflows/testing.yml@main with: app-name: ${{needs.output-affected.outputs.affected}} call-deploy: # 省略
  23. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけの Storybook をデプロイ name: storybook

    jobs: output-affected: # 省略 call-storybook-build: needs: [output-affected] strategy: matrix: ${{fromJson(needs.output-affected.outputs.affected)}} steps: - uses: ./.github/workflows/actions/storybook-build with: app-name: ${{matrix.app-name}} call-storybook-comment: # 省略
  24. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけの Storybook をデプロイ output-affected: #

    省略 name: storybook jobs: call-storybook-build: needs: [output-affected] strategy: matrix: ${{fromJson(needs.output-affected.outputs.affected)}} steps: - uses: ./.github/workflows/actions/storybook-build with: app-name: ${{matrix.app-name}} call-storybook-comment: # 省略
  25. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけの Storybook をデプロイ call-storybook-build: strategy:

    matrix: ${{fromJson(needs.output-affected.outputs.affected)}} app-name: ${{matrix.app-name}} name: storybook jobs: output-affected: # 省略 needs: [output-affected] steps: - uses: ./.github/workflows/actions/storybook-build with: call-storybook-comment: # 省略
  26. 1. アプリケーションが増えても 実行時間が速い 影響のあるアプリケーション & ライブラリだけの Storybook をデプロイ name: storybook

    jobs: output-affected: # 省略 call-storybook-build: needs: [output-affected] strategy: matrix: ${{fromJson(needs.output-affected.outputs.affected)}} steps: - uses: ./.github/workflows/actions/storybook-build with: app-name: ${{matrix.app-name}} call-storybook-comment: # 省略
  27. 1. アプリケーションが増えても 実行時間が速い✅ ( 理論上) n = アプリケーション がどれだけふえても、実行時間に影響はない CI/CD

    実行時間は、最も実行時間の長いアプリとほぼ等しくなる 1. 仮にボトルネックとなるアプリが現れた場合もまだまだ並列化の余地がある ↩︎ ` ` [1]
  28. 2. アプリケーションが増えても 保守できる n = アプリケーション & ライブラリの数 モノレポでないアプリでは n

    = 1 だが、モノレポでは理論上無限にアプリが増える すべて個別に CI/CD のコードを書いていると複雑化するし、スケールしない ` ` ` `
  29. 2. アプリケーションが増えても 保守できる 2.1. 技術スタックの統一 Next.js を Docker image として

    ECS 環境へデプロイ ( 将来的にはインフラ側もこちらで用意した環境を提 供したい) テスト、リント、型チェックなどは統一
  30. 2. アプリケーションが増えても 保守できる 2.2. GitHub Actions を使いこなす reusable workflow ,

    composite actions で共通のフローを切り出し 適切にモジュール化し、低コストで複数のアプリケーションでの CI/CD パイプラインを運用 ` ` ` `
  31. 3. チームが増えても 疎結合に運用できる 各ロールごとに、だれがなににオーナーシップを持つのかを定義 実装者 レビュワー マージ アプリの新規作成 エコシステム開発者 エコシステム開発者

    エコシステム開発者 アプリの機能開発/ 運用 アプリの開発者 アプリの開発者 & ( 一部) エコシステム開発者 アプリの開発者 共通ライブラリの新規作成 エコシステム開発者 エコシステム開発者 エコシステム開発者 共通ライブラリの機能開発/ 運用 エコシステム開発者 エコシステム開発者 エコシステム開発者 障害時の切り戻し … … … renovate … … … etc … … …
  32. 3. チームが増えても 疎結合に運用できる CODEOWNERS の活用により自動化 # CODEOWNERS * @platform-team */app-foo/**

    @app-foo-team @platform-team */app-bar/** @app-bar-team @platform-team */lib-bar/** @platform-team */lib-bar/** @platform-team
  33. 3. チームが増えても 疎結合に運用できる 依存関係を Lint できるため、適切でないライブラリの使用を制限できる { "rules": { "@nrwl/nx/enforce-module-boundaries":

    [ "error", { "enforceBuildableLibDependency": true, "depConstraints": [ { "sourceTag": "scope:app-foo", "onlyDependOnLibsWithTags": [ "scope:component-library", "scope:logger" ] }, ] } ] } }
  34. 3. チームが増えても 疎結合に運用できる 依存関係を Lint できるため、適切でないライブラリの使用を制限できる "depConstraints": [ { "sourceTag":

    "scope:app-foo", "onlyDependOnLibsWithTags": [ "scope:component-library", "scope:logger" ] }, ] { "rules": { "@nrwl/nx/enforce-module-boundaries": [ "error", { "enforceBuildableLibDependency": true, } ] } }
  35. 3. チームが増えても 疎結合に運用できる 依存関係を Lint できるため、適切でないライブラリの使用を制限できる "sourceTag": "scope:app-foo", { "rules":

    { "@nrwl/nx/enforce-module-boundaries": [ "error", { "enforceBuildableLibDependency": true, "depConstraints": [ { "onlyDependOnLibsWithTags": [ "scope:component-library", "scope:logger" ] }, ] } ] } }
  36. 3. チームが増えても 疎結合に運用できる 依存関係を Lint できるため、適切でないライブラリの使用を制限できる "onlyDependOnLibsWithTags": [ "scope:component-library", "scope:logger"

    ] { "rules": { "@nrwl/nx/enforce-module-boundaries": [ "error", { "enforceBuildableLibDependency": true, "depConstraints": [ { "sourceTag": "scope:app-foo", }, ] } ] } }
  37. 3. チームが増えても 疎結合に運用できる 依存関係を Lint できるため、適切でないライブラリの使用を制限できる { "rules": { "@nrwl/nx/enforce-module-boundaries":

    [ "error", { "enforceBuildableLibDependency": true, "depConstraints": [ { "sourceTag": "scope:app-foo", "onlyDependOnLibsWithTags": [ "scope:component-library", "scope:logger" ] }, ] } ] } }
  38. スケーラブルな CI/CD - 1. アプリケーションが増えても 実行時間が速い✅ Nx で影響のあるアプリケーションだけを並列で CI/CD -

    2. アプリケーションが増えても 保守できる - 3. チームが増えても 疎結合に運用できる
  39. スケーラブルな CI/CD - 1. アプリケーションが増えても 実行時間が速い✅ Nx で影響のあるアプリケーションだけを並列で CI/CD -

    2. アプリケーションが増えても 保守できる✅ 技術スタックの統一 reusable workflow , composite actions - 3. チームが増えても 疎結合に運用できる ` ` ` `
  40. スケーラブルな CI/CD - 1. アプリケーションが増えても 実行時間が速い✅ Nx で影響のあるアプリケーションだけを並列で CI/CD -

    2. アプリケーションが増えても 保守できる✅ 技術スタックの統一 reusable workflow , composite actions - 3. チームが増えても 疎結合に運用できる✅ ブランチ運用の統一 CODEOWNERS の活用 依存関係の Lint ` ` ` `
  41. スケーラブルな CI/CD ✅ - 1. アプリケーションが増えても 実行時間が速い✅ Nx で影響のあるアプリケーションだけを並列で CI/CD

    - 2. アプリケーションが増えても 保守できる✅ 技術スタックの統一 reusable workflow , composite actions - 3. チームが増えても 疎結合に運用できる✅ ブランチ運用の統一 CODEOWNERS の活用 依存関係の Lint ` ` ` `