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

MonorepoとOneTeamでMicroservicesの課題に挑む【DeNA TechC...

DeNA_Tech
March 03, 2021

MonorepoとOneTeamでMicroservicesの課題に挑む【DeNA TechCon 2021】/techcon2021-4

2019年6月にリリースしたkencom×ほけん。アーキテクチャにマイクロサービスを導入しクラウドはGCP、コンテナオーケストレーションにGKEを採用しリリースまで走り切りました。
リリース後しばらくは新機能や機能改善に伴う開発も順調に進んでいましたが、徐々に開発がスムーズに進捗しない課題に直面しました。
細分化されすぎたマイクロサービスと呼応するように増えていくレポジトリの数、レポジトリ毎に異なる開発スタイル、サーバとインフラチームのコミュニケーション不足によるオペレーションの分断...など、マイクロサービスの採用経験がある方なら共感いただける課題はあるのではないでしょうか?
これらの課題にkencom×ほけんチームはどのように立ち向かっていったのか、課題解決の施策を中心にお話させていただきます。

DeNA_Tech

March 03, 2021
Tweet

More Decks by DeNA_Tech

Other Decks in Technology

Transcript

  1. • Monorepo • Project Layout • 起動とデプロイ • 設計方針と開発ルール •

    OneTeam • チームのあるべき姿 • 実現に向けた施策
  2. alpide ├─ cmd/ ├─ eiger │ ├─ config/ │ ├─

    e2e/ │ ├─ ... │ └─ internal/ ├─ manaslu │ ├─ e2e/ │ ├─ ... │ ├─ internal/ │ └─ pkg/ ├─ pkg │ ├─ log/ │ └─ ... ├─ .env.example ├─ ... ├─ Dockerfile ├─ Makefile ├─ go.mod ├─ go.sum └─ main.go alpide ├─ cmd/ ├─ config │ ├─ eiger/ │ └─ ... ├─ e2e │ ├─ eiger/ │ ├─ ... │ └─ manaslu/ ├─ internal │ ├─ eiger/ │ ├─ ... │ └─ manaslu/ ├─ pkg │ ├─ log/ │ └─ ... ├─ .env.example ├─ ... ├─ Dockerfile ├─ Makefile ├─ go.mod ├─ go.sum └─ main.go alpide ├─ cmd/ ├─ internal │ ├─ eiger │ │ ├─ config/ │ │ ├─ e2e/ │ │ └─ ... │ └─ manaslu │ ├─ e2e/ │ ├─ ... │ └─ pkg/ ├─ pkg │ ├─ log/ │ └─ ... ├─ .env.example ├─ ... ├─ Dockerfile ├─ Makefile ├─ go.mod ├─ go.sum └─ main.go
  3. • 簡易に一括作業が可能 # Dockerfile COPY --from=builder /config /config alpide ├─

    cmd/ ├─ config │ ├─ eiger/ │ └─ ... ├─ e2e │ ├─ eiger/ │ ├─ ... │ └─ manaslu/ ├─ internal │ ├─ eiger/ │ ├─ ... │ └─ manaslu/ ├─ pkg │ ├─ log/ │ └─ ... ├─ .env.example ├─ ... ├─ Dockerfile ├─ Makefile ├─ go.mod ├─ go.sum └─ main.go # Makefile unit-test: go test ./cmd/... ./internal/... ./pkg/... e2e-test: go test ./e2e/...
  4. • cmd: 共通起動コマンド • config: 設定ファイル • e2e: E2Eテスト •

    internal: 各マイクロサービス • pkg: 共通ライブラリ alpide ├─ cmd/ ├─ config │ ├─ eiger/ │ └─ ... ├─ e2e │ ├─ eiger/ │ ├─ ... │ └─ manaslu/ ├─ internal │ ├─ eiger/ │ ├─ ... │ └─ manaslu/ ├─ pkg │ ├─ log/ │ └─ ... ├─ .env.example ├─ ... ├─ Dockerfile ├─ Makefile ├─ go.mod ├─ go.sum └─ main.go
  5. • サービス開発しながら追加とリファクタを継続 • internal/*/pkg -> pkg に移管 alpide ├─ internal

    │ └─ himalaya │ └─ pkg │ └─ lock/ └─ pkg ├─ cache/ ├─ crypto/ ├─ logger/ ├─ ... └─ validation/ alpide ├─ internal │ └─ himalaya └─ pkg ├─ cache/ ├─ crypto/ ├─ lock/ ├─ logger/ ├─ ... └─ validation/
  6. • 入り口は main.go • サービス登録 • main()で起動 // main.go package

    main import ( "***/alpide/cmd" // register services _ "***/alpide/internal/eiger/cmd" _ "***/alpide/internal/manaslu/cmd" _ ... ) func main() { cmd.Execute() }
  7. • main.goで、1つの実行ファイルをビルド • Dockerfileで、1つのDocker Imageをビルド # Dockerfile # ... RUN

    CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ -ldflags='-w -s -extldflags "-static"' -a \ -o /app ./main.go # ... COPY --from=builder /app /app # ... ENTRYPOINT ["/app"]
  8. • サービス名 • 起動時の初期化処理 • 零 OR 複数のサーバー • 零

    OR 複数のバッチ • サーバー数 + バッチ数 > 1 type Service struct { Name string InitFunc func(context.Context) error Servers []Server Batches []Batch }
  9. • サービス名で全サーバーを起動 # start all servers of service himalaya $

    /app himalaya # k8s manifest containers: - name: himalaya command: ["/app"] args: ["himalaya"]
  10. • サーバー名 • 起動時の初期化 • 起動と停止が可能 type Server struct {

    Name string InitFunc func(context.Context) error IServer IServer } type IServer interface { Start(context.Context) error Shutdown(context.Context) error }
  11. • サービス+サーバー名で起動 # start web server of service himalaya $

    /app himalaya web-server # start worker of service himalaya $ /app himalaya worker # k8s manifest kind: Deployment spec: replicas: 5 template: spec: containers: - name: himalaya-web args: ["himalaya", "web-server"] --- kind: Deployment spec: replicas: 3 template: spec: containers: - name: himalaya-worker args: ["himalaya", "worker"]
  12. • バッチ名 • 起動時の初期化 • 起動と中断が可能 • CLIパラメーター対応 type Batch

    struct { Name string InitFunc func(context.Context) error IBatch IBatch } type IBatch interface { Start(context.Context, *cobra.Command) error Interrupt(context.Context) Customize(*cobra.Command) // cli args }
  13. • サービス名+バッチ名+CLIパラメーターで起動 # start batch export of himalaya $ /app

    himalaya export --opt whatever # k8s manifest kind: CronJob # ... spec: jobTemplate: # ... spec: containers: - name: himalaya-batch-export args: ["himalaya", "export", "--opt whatever"]
  14. • コードレビュー必須 • E2Eテスト必須 • カバレージ原則70%以上 baker: 77.6% chiroro: 83.9%

    denali: 63.1% eiger: 55.5% hiyori: 79.5% ibuki: 81.3% kailas: 78.2% manaslu: 74.0% meru: 77.8% otake: 76.8% pumori: 79.0% rainier: 74.5%
  15. • オーナーシップを持つ( ) • を担保したコミュニケーション • 技術領域を超えた成果( ) • を体現

    DeNA QualityはDeNAのミッション・ビジョンを参照 https://dena.com/jp/company/policy/
  16. • 設定したリソースの過不足 • ex) CPU/Memory limitの不足 • レビュー時の確認漏れ • ex)

    trueのクォーテーション付け漏れ • デプロイ後にエラー発生 • リソース不備が発覚 •