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

Goを活用したサービス開発・運用の話

 Goを活用したサービス開発・運用の話

Ebfa4f20872658970537a1d1acdbfa95?s=128

sasaki

July 20, 2021
Tweet

Transcript

  1. Goを活用した サービス開発・運用の話 DMM.go #3 DMM.com 佐々木勝春 2021-07-21

  2. 自己紹介 名前:佐々木勝春 略歴: 2017年4月 一般社団法人DMMアカデミーに入社 2019年4月 DMM.comに転籍 | プラットフォーム事業部 2021年7月現在

    レビューリプレイス、通知配信&DMMポイントクラブ 余暇:落語(立川流)、映画、ラジオ、 …、 カカオ72% 2
  3. 今回お話する内容 1:monorepo&共通アーキテクチャによる複数サービス開発の話 2:安定的なサービス運用のための継続的なモニタリング (現在も模索しながら改善中です!) 3

  4. 現在運用中のサービスについて PFでありながらToC要素のあるサービスを作っているのが特徴的です 4

  5. 運用中のサービス1: DMMポイントクラブ https://lp.pointclub.dmm.com/ 5

  6. 運用中のサービス2: 通知配信サービス https://notification.dmm.com/ ↑共通ヘッダーのベルアイコン 6

  7. 開発業務内容 ・サービスの開発・運用(PRD/DesignDocの作成) ・施策の立案、実施&検証&振り返り   ・定量・定性分析(データ、ユーザーの声、SNS、...) ・外部チームとの連携  ... 「事業のための開発」 引用: https://www.irasutoya.com/2020/03/blog-post_74.html 7

  8. 効率的なサービス運営を行っていくために ・開発の工数をできるだけ削減して効率よく開発していきたい ・モニタリングをしっかりと行って障害の防止、障害発生後の素早いリカバリーを実現し たい。 ・インフラリソースの最適化も継続的見ていきたい。 8

  9. 今回お話する内容 1:monorepo&共通アーキテクチャによる複数サービス開発の話 2:安定的なサービス運用のための継続的なモニタリング 9

  10. インフラアーキテクチャ 10

  11. 11

  12. ポイントクラブが提供する機能群概要 ・Client(Nativeアプリ)への機能提供→API ・管理系(お知らせ登録・Push通知の配信)→バッチ データ ストア API バッチ 参照・登録 登録 Native

    アプリ 管理 追加実装部分 12
  13. 通知配信が提供する機能群概要 ・Client(通知一覧・通知のステータス取得)→API ・ユーザーセグメント毎の通知情報の登録→バッチ データ ストア API バッチ 参照 登録 Web

    Front 管理 追加実装部分 管理機能はサービスと同じリソー スを扱って一部の処理を共通し て使える ↑ 共通化部分はできれば再 利用したい 13
  14. monorepoを利用した既存リソースの活用 https://speakerdeck.com/ymatsuwitter/cloud-native-and-monorepo?slide=20 今回は主にこの観点から → 14

  15. +α: 既存インフラ環境の有効活用 ・バッチもAPIと同じネットワーク ・ECS Fargateの構成で構築する ことでインフラ構築工数削減(cliコ マンドとして実装する方針に) (これに加えて短時間の定期実行の処理は Lambdaで実 行してるものもあります

    ) 15
  16. ソースコードアーキテクチャ 16

  17. コードアーキテクチャ概要 ui http query usecase infra domain query: ・外部API依存のもの ・ui層用の表示用(CQRSを参考に参照用モデルとしてドメインオブジェクトとは別に定義

    ) ポイントクラブと通知配信の両サービスで利用 →アーキテクチャを共通化することで学習コストの削減、仕様さえ把握してい ればすぐに開発に着手できる cli ・関心の分離→ドメインロジックに集中 ・goではinterfaceと構造体を用いたDIの実装 17
  18. ディレクトリ構成 ・main関数のファイルを cmd/配下の/apiと/batchで分離 ・app/ui層以下が/batch/とhttp/で分離 ・usecase層以降はapiとbatchで同じディレクトリ apiとbatchで共通のメソッド(主にinfra層)を利用できる 18

  19. バッチ実装に利用しているライブラリ https://github.com/spf13/cobra 特徴 ・サブコマンド型のCLIツールの作成 ・POSIX準拠のフラグ機能を提供(long型とshort型共に) ・アプリケーションの雛形を作るための機能も提供 ・多段のネストのサブコマンドを作れる ・helpフラグやmapページの自動生成と柔軟なカスタマイズ性 ... 詳細は公式リポジトリを参考ください

    19
  20. cobraの選定理由 ・標準ライブラリ(flagパッケージなど)だけでcliを作ることもできるが、なるべく実装の工 数を減らしたい ・cobraはcliのサブコマンド型を作りたいという今回の要件にマッチ   ・今回は利用ケースが限定的→どのフレームワークを使っても実現できそう  では あった(豊富な機能や採用実績からcobraを作用) 20

  21. app/cmd/batch/main.go package main func main() { ... prepareNotificationCmd := batch.NewPreparePushCmd(pushHandler)

    batch.RootCmd.AddCommand(prepareNotificationCmd) if err := batch.RootCmd.ExecuteContext(ctx); err != nil { logger.Write(ctx, logger.LevelError, "エラーが発生しました。", err) os.Exit(1) } os.Exit(0) } main関数内でルートコマンドにサブコマンドを追加 & エラーハンドリング(エラー状況に応じてステータスコードを返却 ) 21
  22. app/ui/batch/root.go package batch import ( "github.com/spf13/cobra" ) // RootCmd :

    ルートコマンド var RootCmd = &cobra.Command{ Use: "pointclub-batch", Run: func(cmd *cobra.Command, args []string) { _ = cmd.Help() }, } main関数で利用するrootコマンドを初期化 22
  23. app/ui/batch/notification.go① package batch // Notification : お知らせ通知のためのハンドラー type Notification interface

    { Prepare(cmd *cobra.Command, args []string) error } type notification struct { u usecase.Notification } // NewNotification : お知らせ通知ハンドラーの生成 func NewNotification(u usecase.Notification) Notification { return &notification{ u: u, } } cliのhandlerのインターフェースと new関数を定義(apiと同じ構成) ↑サブコマンドで実際に呼び出す関数 23
  24. app/ui/batch/notification.go② package batch // NewPrepareNotificationCmd : お知らせ通知内容を用意するコマンド func NewPrepareNotificationCmd(n Notification)

    *cobra.Command { return &cobra.Command{ Use: "prepare-notification", Args: cobra.ExactArgs(1), Short: "お知らせ通知内容を用意(dynamoDBにインサート)する", RunE: func(cmd *cobra.Command, args []string) error { return n.Prepare(cmd, args) }, } } ・サブコマンドを初期化 ・この後紹介するhandlerのメソッドをここでコール 24
  25. app/ui/batch/notification.go③ package batch // NewPrepareNotificationCmd : お知らせ通知内容を用意するコマンド func NewPrepareNotificationCmd(n Notification)

    *cobra.Command { ... } // Prepare : お知らせ通知内容を用意 func (n notification) Prepare(cmd *cobra.Command, args []string) error { targetUser := args[0] err := n.u.Prepare(cmd.Context(), targetUser) if err != nil { return fmt.Errorf("お知らせ通知内容の用意に失敗しました。: %w", err) } return nil } ・handlerのメソッドを定義 ・ここでusecase層以降のメソッドをコール ←apiと同じ構成 25
  26. その他、API実装に関して フレームワーク 選定理由 ポイントクラブ Goa ドキュメントとGoを乖離させないため https://speakerdeck.com/yyh_gl/develop-api-server-by-goa 通知配信 echo ・レビューリプレイスの際に利用経験あり

    ・薄いフレームワークが良かった ・ドキュメントに関しては今回はクライアントはほぼ自分達だけ だったので問題なかった 参考:技術選定に関して https://inside.dmm.com/entry/2019/02/28/technology-selection 26
  27. まとめ1 monorepo&共通アーキテクチャによる開発の効率化 ・monorepoによるメソッドの共通利用により開発工数の削減 ・複数サービスで同一アーキテクチャを採用することによる学習コスト・開発コストの削減 +インフラ構成の共通化やフレームワークやライブラリの利用による開発工数の削減 27

  28. 今回お話する内容 1:モノレポ&共通アーキテクチャによる開発の話 2:安定的なサービス運用のための継続的なモニタリング 28

  29. 2:安定的なサービス運用のための継続的なモニタリング ・安定的なサービス運用をしていきたい ・初期MVPも後も継続的に施策の実施、機能追加のリリースをしていきたい ・アクセス増減によるサーバー、データストアのスペックの見直し 29

  30. モニタリング導入による効果例 ・実際のに負荷とリソースの使用状況に応じて APIのコンテナ数削減によりインフラ費用のコスト 最適化 ・メモリリークの検知 30

  31. モニタリングにSaaSのDatadogを利用 Datadog のインテグレーションを使用して、 DevOps スタック全体のメトリクスとイ ベントをシームレスに集約します。 ・インテグレーション ・ダッシュボード ・ログ管理/アラート ・APM

    ・Continuous Profiler ・ネットワークモニタリング ・Syntheticモニタリング ・リアルユーザーモニタリング ... https://www.datadoghq.com/ja/ DMMPFの標準のモニタリングツールとしても Datadogを採用しています 31
  32. Datadog ・様々な言語に対応 C#,Go, Java,JavaScript, Node, PHP, Python, Ruby, Rust… (利用機能により対応言語は変わります

    ) ・充実したドキュメント ・頻繁な機能追加・アップデート https://www.datadoghq.com/ja/blog/ https://docs.datadoghq.com/ja/ 32
  33. Datadogのモニタリング活用例(go関連) ・APM ・継続的なProfiling 33

  34. Datadogのモニタリング活用例(go関連) ・APM ・継続的なProfiling 34

  35. APM ↓タグで絞り込み検索ができる コードレベルの可視性で根本原因を素早く特定 35 https://docs.datadoghq.com/ja/tracing/visualization/

  36. APM実装コード① main関数内でAPMトレース開始と終了の処理をコール package main func main() { // APM トレース開始

    apm.StartTrace() defer apm.StopTrace() ... } 36
  37. APM実装コード② package apm // StartTrace : トレースを開始 func StartTrace() {

    tracer.Start( tracer.WithServiceName("pointclub-api"), tracer.WithGlobalTag("service", "pointclub-api"), tracer.WithGlobalTag("env", os.Getenv("APP_ENV")), ) } // StopTrace : トレースを終了 func StopTrace() { tracer.Stop() } 37
  38. APM実装コード③ トレースしたい任意の関数でspanの開始処理をコール package foo func Foo(ctx context.Context, hoge Hoge) {

    span, ctx := apm.StartTraceFromContext(ctx) defer span.Finish() ... } 38
  39. APM実装コード④ span開始の関数 package apm // Span : スパン type Span

    tracer.Span // StartTraceFromContext : スパンを開始 func StartTraceFromContext(ctx context.Context) (Span, context.Context) { return tracer.StartSpanFromContext(ctx, getFuncName(2)) } 39
  40. Datadogのモニタリング活用例(go関連) ・APM ・継続的なProfiling 40

  41. ・最小限の負荷で本番環境全体のパフォーマンスをコードレベルで分析 →コードのボトルネックを素早く見つける ・全てのスタックトレースを1つの管理画面で可視化する ・リリースversion間の比較による継続的なパフォーマンス傾向の分析 継続的なProfiling 41

  42. goの標準ライブラリ pprofとの違い ・リリースバージョンごとの比較が標準できる ・分散トレースとの相関関係を理解する ・httpサーバーにエンドポイントを差し込まなくていいので実装が楽(特にフレームワーク利用時) https://golang.org/pkg/net/http/pprof/ その他クラウドインフラ系サービスも提供 https://cloud.google.com/profiler/docs/about-profiler/?hl=ja&refresh=1 https://aws.amazon.com/jp/blogs/news/investigating-performance-issues-with-amazon-cod eguru-profiler/

    42
  43. 継続的なProfiling cmd/batch/main.go (import文省略) package main func main() { // profilerを開始

    err := profiler.Start( profiler.WithService("pointclub-api"), profiler.WithEnv(os.Getenv("APP_ENV")), profiler.WithProfileTypes( profiler.CPUProfile, profiler.HeapProfile, profiler.GoroutineProfile, ), ) if err != nil { logger.Panic("profilerの開始に失敗しました。 ", err) } defer profiler.Stop() } 参考: https://docs.datadoghq.com/ja/tracing/profiler/ enabling/?code-lang=go 43
  44. 継続的なProfiling https://docs.datadoghq.com/ja/tracing/profiler/intro_to_profiling/ 44

  45. 継続的なProfiling 項目 データ型 実装レベル 利用用途 メトリクス 時系列データ 利用者側で実装 (メソッド単位、より大きい 単位)

    ・利用者側で決めた撮り たいデータを取得できる ・前後関係は分からない プロファイル 統計データ・スナップ ショット的 ランタイムレベル (スタックトレースによる 粒度の小さい単位) ・取れるメトリクスは固定 (CPU使用時間、ヒープ サイズ、スレッド数) ・前後関係がわかるので パフォーマンスのボトル ネックを特定しやすい 45 https://logmi.jp/tech/articles/322787
  46. その他Datadogのモニタリング活用例 ・インテグレーション&ダッシュボード整備 インフラストラクチャーから全てのメトリクスとログを収集して、統合システムを全体として 把握することができる。 https://docs.datadoghq.com/ja/getting_started/dashboar ds/ 46

  47. まとめ ・継続的なサービス運用のためにモニタリングにDatadogを活用 ・データストアのパフォーマンス問題の改善 ・APIサーバーのパフォーマンスの改善は引き続きやっていきたい 47

  48. ・monorepo&共通アーキテクチャにより共通化の仕組みを活用した開発工数の削減を 解決 ・継続的なモニタリングで安定したサービスの運用の実施 ・失敗して学びながら徐々に改善していってより良いサービスを提供していきたいです まとめ 48