Successfully reported this slideshow.
Your SlideShare is downloading. ×

10年目の『エブリスタ』を支える技術

10年目の『エブリスタ』を支える技術

Download to read offline

まもなく10年目を迎える小説投稿プラットフォーム『エブリスタ』。
この10年で業界は大きく変化し、本サービスもリニューアルという大きな転換期を迎えました。
10年という歳月によりシステム全体もレガシー化し、不要なモジュール、増え続ける機能、機能の変化によるテーブル構造の不都合やパフォーマンス課題など様々な課題が浮き彫りとなってきました。
本セッションではこれらの課題を解決すべく実施したリニューアルのプロセスやその現場におけるKubernetesやGraphQLといった新技術の活用を事例を交えご紹介します。

まもなく10年目を迎える小説投稿プラットフォーム『エブリスタ』。
この10年で業界は大きく変化し、本サービスもリニューアルという大きな転換期を迎えました。
10年という歳月によりシステム全体もレガシー化し、不要なモジュール、増え続ける機能、機能の変化によるテーブル構造の不都合やパフォーマンス課題など様々な課題が浮き彫りとなってきました。
本セッションではこれらの課題を解決すべく実施したリニューアルのプロセスやその現場におけるKubernetesやGraphQLといった新技術の活用を事例を交えご紹介します。

More Related Content

More from DeNA

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

10年目の『エブリスタ』を支える技術

  1. 1. #denatechcon #denatechcon 10年目の 『エブリスタ』 を支える技術 井田 祐太 / 松尾 卓朗 ゲーム・エンターテインメント事業本部IPプラットフォーム事業部サービス開発部
  2. 2. #denatechcon 開発期間約1年半
  3. 3. #denatechcon 2019年春 エブリスタは 生まれ変わります
  4. 4. #denatechcon #denatechcon 10年目の 『エブリスタ』リニューアル を支える技術 井田 祐太 / 松尾 卓朗 ゲーム・エンターテインメント事業本部IPプラットフォーム事業部サービス開発部
  5. 5. #denatechcon 自己紹介 - 井田 祐太 - @da_ponta - https://note.mu/fablab - 2017年4月中途入社 - エブリスタの開発全般を担当 - Perl - Ruby - node.js - Android - Kubernetes 普段はテラリウムなどものづくり全般やってます
  6. 6. #denatechcon エブリスタとは だれもが作家になれる 小説投稿サイト
  7. 7. #denatechcon 代表作品
  8. 8. #denatechcon サービスの歴史 2007年3月 2010年6月 2019年春 モバゲー 小説コーナー エブリスタ オープン システム リニューアル予定 10年目突入!
  9. 9. #denatechcon 10年めのサービスの負債 - 誰も把握していない仕様 - コピペされたコード - 独自フレームワーク - 未知のテーブル、カラム - 存在しないテスト、ドキュメント - SJIS - フィーチャーフォン - 4,5種類存在するUI ...
  10. 10. #denatechcon 闇が深い… 作り直すしかない
  11. 11. #denatechcon リニューアル対象: DB・サーバサイド・インフラ・アプリ UI・UX・フロントエンド・機能 ≒システムすべて
  12. 12. #denatechcon リニューアルに際しての課題 - エンジニアリソース(3〜4人😰) - 機能数(コントローラ数換算:900, テーブル数:1000) - 既存サービスの運用も継続しなければならない - リリース後も高速な開発サイクルにしたい
  13. 13. #denatechcon アプリケーション編
  14. 14. #denatechcon アジェンダ - GraphQLの活用 - kubernetesをフル活用した開発環境
  15. 15. #denatechcon サービス群 MAIN API GraphQL COMICPAYMENTAUTH FRONT NOTIFICATION USER OPE BOX
  16. 16. #denatechcon GraphQLの採用理由 リリース後も開発サイクルを高速で回したい
  17. 17. #denatechcon GraphQLのリニューアルにおける利点 - UI要件の変化への対応が不要 →開発速度UP - ドキュメント作成が不要 →工数DOWN - コードがクリーンに保てる →工数増加防止 - モチベーション向上と2年間の維持ができた →開発速度維持
  18. 18. #denatechcon UI要件の変化への対応が不要 query($userId: ID!) { user(userId:$userId) { nickname introduction } } ※画面は開発中のものです 😅<ここに作品をいれたいんだ けど…
  19. 19. #denatechcon UI要件の変化への対応が不要 query($userId: ID!) { user(userId:$userId) { nickname introduction novels { title updated_at ... } } } ※画面は開発中のものです
  20. 20. #denatechcon ドキュメント作成いらず # 型定義 class ContestType < Types::BaseObject # 型の名称 graphql_name "Contest" # 型の説明文 description "公式イベント" # 取得できるデータ field :contest_id, ID, "イベントID", null: false end # Queryの定義 class FindContest < Resolvers::Base # 返す型 type ContestType, null: false # クエリの説明 description "公式イベント" argument :contest_id, ID, "公式イベントID", required: true ... end
  21. 21. #denatechcon GraphiQL # Queryの定義 class FindContest < Resolvers::Base # 返す型 type ContestType, null: false # クエリの説明 description "公式イベント" argument :contest_id, ID, "公式イベントID", required: true ... end
  22. 22. #denatechcon GraphiQL
  23. 23. #denatechcon GraphiQLだと少し足りない query($userId: ID!) { user(userId:$userId) { nickname introduction novels { title updated_at ... } } } novelsはどの型が持 っているんだ?
  24. 24. #denatechcon GraphQL Voyager
  25. 25. #denatechcon Railsあるある(?) - アソシエーションとビジネスロジックでファットなモデル - ビュー処理とモデルの呼び出しでファットなコントローラ ▶ graphql(-ruby)だとスッキリ!
  26. 26. #denatechcon エブリスタにおけるアーキテクチャ models モデルに閉じたビジネスロジック policies 権限の判定ロジック repositories DB以外も含めた外部リソース操作ロジック services 何らかの一連のビジネスロジック graphql コントローラ or ViewModel
  27. 27. #denatechcon Modelの関連をGraphQLが吸収 - 返却するデータ構造の構築が不要 - model・controllerに複雑なロジックを書く必要がない - 意識せずにテーブルと対応したnull安全なデータに # User has a novel class UserType field :novel, NovelType, null: false ... end # Novel belongs to user class NovelType field :user, UserType, null: true ... end
  28. 28. #denatechcon モチベーションについて① - Github、Facebookが採用 - 採用事例増加中 - 新しい技術は楽しい
  29. 29. #denatechcon モチベーションについて② 開発効率が良くないと乗り切れない!! - プロジェクト期間:2年 - エンジニア数:3〜4人 - 機能数:コントローラ数ベース:900 - テーブル数:1000 - 作りながら要件は変化・増加する
  30. 30. #denatechcon わずか2,3行でAPIにデータが追加される class Types::UserType < Types::BaseObject description "ユーザ" ... field :followers, Types::UserType.connection_type,"フォロワー", null: false, connection: true def followers(**args) @object.follower_users end end class User < ApplicationRecord self.primary_key = :user_id has_many :followers has_many :follower_users, through: :followers ... end
  31. 31. #denatechcon ②novel経由で取得するクエリ ①user経由で取得するクエ リ
  32. 32. #denatechcon ハマりどころも - エラーハンドリングはどう行うか? - フロントエンドから無制限にデータを参照されるのでは? - パフォーマンス面(N+1問題など)
  33. 33. #denatechcon デフォルトのエラー形式 query { user(userId:"存在しないID") { nickname } } { "data": null, "errors": [ { "message": "User not found.", # システムメッセージ "locations": [ # クエリにおける発生行 { "line": 2, "column": 3 } ], "path": [ # クエリにおける発生階層 "user" ], } ] } →メッセージとエラーの発生した階層だけなのでクライアントでのハンドリングが困難
  34. 34. #denatechcon graphql-errors + エラーカスタム class ApiError < GraphQL::ExecutionError def initialize(message, type = nil) @type = type super(message) end def to_h super.merge("type" => @type) end end graphql-errors https://github.com/exAspArk/graphql-errors GraphQL::Errors.configure(schema) do rescue_from ActiveRecord::RecordNotFound do |e| raise ApiError.new(“#{e.model} not found”, "NotFound") end # 独自エラークラス rescue_from Errors::PermissionDenied do |e| raise ApiError.new(e.message, e.class.to_s) end end
  35. 35. #denatechcon 拡張されたエラー形式 query { user(userId:"存在しないID") { nickname } } { "data": null, "errors": [ { "message": "User not found.", "locations": [ { "line": 2, "column": 3 } ], "path": [ "user" ], "type": "NotFound" } ] } →パターン化された`type`によりエラーハンドリングが可能に
  36. 36. #denatechcon クエリの実行制限 {query: “findNovel.gql”}
  37. 37. #denatechcon 開発環境
  38. 38. #denatechcon 本番と最小限の差異の開発環境 - minikubeを使ったローカルkubernetes - helmによるテンプレート化 - ドメインの割当 - SSL化
  39. 39. #denatechcon kubernetesを活用した開発環境 - minikubeを利用 - minikubeからNFSマウント - minikubeからk8sにコード共有
  40. 40. #denatechcon podへのソースコードマウント(抜粋) $ sudo nfsd start $ IP=$(minikube ip | awk -F"." '{print $1"."$2"."$3".1"}') $ minikube ssh -- sudo mount -t nfs $IP:/path/to/src /src -o rw,async,noatime,rsize=32768,wsize=32768,proto=tcp containers: - name: api image: "api:v1" volumeMounts: - mountPath: "/opt/api" name: "api-volume" volumes: - name: "api-volume" hostPath: path: "/src/api" その他の手法 Tilt,Skaffold:ビルドが発生するのでその分の遅延有り Docker for Mac:マウントするファイル数が多くなると参照エラーが発生し断念 Dockerイメージは/opt/apiを WORKDIRに指定します
  41. 41. #denatechcon Helmによるテンプレート化(抜粋) - レプリカ数 - Dockerイメージのリポジトリ - 起動コマンドの引数 - configMapの数、種類 spec: replicas: {{ .Values.replica }} template: spec: containers: - name: estar-api image: “{{.Values.REPO}}/api:latest" args: [ {{- range .Values.args }} "{{ . }}", {{- end }} ] envFrom: {{- range .Values.args }} - configMapRef: name: {{ . }} {{- end }}
  42. 42. #denatechcon まとめ - エンジニアリソースの課題 → GraphQLにより実装が簡易化されエンジニア体制が最適化 → minikubeにより環境整備の工数削減 - 900の機能数 → GraphQLによりAPI開発数の減少(150クエリに) - リリース後も開発サイクルを高速で回したい → フロントエンド主導の開発プロセスが実現
  43. 43. #denatechcon ❕注意事項 minikubeを利用するには高スペックなPCが必要です 必要要件(エブリスタチームの場合) - memory 16GB - クアッドコア
  44. 44. #denatechcon クラウド活用とサービス開発
  45. 45. #denatechcon 自己紹介 松尾卓朗 •2017年新卒 DeNA入社(2年目) • IPPF事業部 • サーバーサイド、クラウドを担当
  46. 46. #denatechcon 自己紹介 気づいたら、DevOpsの魅力に惹 かれる • SRE • カオスエンジニアリング • CI/CD
  47. 47. #denatechcon 役割の分割 コミック Web小説サービス 小説 認証 通知 課金 コミック スゴ得 コンテンツ ステークホルダーとのサービス
  48. 48. #denatechcon 役割の分割 コミック Web小説サービス 小説 認証 通知 課金 コミック 開発進めたいのはココ スゴ得 コンテンツ ステークホルダーとのサービス
  49. 49. #denatechcon サービス群 MAIN API GraphQL COMICPAYMENTAUTH BFF NOTIFICATION USER OPE BOX
  50. 50. #denatechcon モノリスとサービシーズ MAIN API GraphQL COMICPAYMENTAUTH FRONT NOTIFICATION OPE BOX開発者は小説の開発に 集中できる。 モノリス サービシーズ
  51. 51. #denatechcon クラウド
  52. 52. #denatechcon 今までの運用体制 サービスエンジニア インフラエンジニア インフラは 全てお任せ
  53. 53. #denatechcon これからの運用体制 サービスエンジニア インフラエンジニア インフラはほぼ全て 事業部エンジニアが
  54. 54. #denatechcon ですが
  55. 55. #denatechcon リソースが足りない、、、 事業部エンジニア ● アプリケーション開発 ● インフラ構築 ● ネイティブアプリ開発
  56. 56. #denatechcon 最小限の工数でクラウドを活用する。 • インフラの管理コストを削減 • アプリケーションエンジニアがクラウド業務を 兼務できるように • 実行環境差分を少なくする
  57. 57. #denatechcon クラウド環境 Amazon SES Amazon ElasticSearch Service
  58. 58. #denatechcon Amazon SES Amazon ElasticSearch Service クラウド環境 kubernetes が分かっていれば、 クラウド環境も理解できる を目指す
  59. 59. #denatechcon 最小限の工数でクラウドを活用する。 • インフラの管理コストを削減 • アプリケーションエンジニアがクラウド業務を 兼務できるように • 実行環境差分を少なくする
  60. 60. #denatechcon クラウドの管理 GCP • メモリー • CPU • アプリケーションサービスのノード数 • インスタンスのライフサイクル • ロードバランサ • ネットワーク周り • Cluster nodeの管理 • 各種ミドルウェア
  61. 61. #denatechcon Infra as a codeはどこまで? すでにcode化されている code化しない GCP • メモリー • CPU • アプリケーションサービスのノード数 • インスタンスのライフサイクル • ロードバランサ • ネットワーク周り • Cluster nodeの管理 • 各種ミドルウェア
  62. 62. #denatechcon マネージド以外は使わない Prometheu s prometheus
  63. 63. #denatechcon 最小限の工数でクラウドを活用する。 • インフラの管理コストを削減 • アプリケーションエンジニアがクラウド業務を 兼務できるように • 実行環境差分を少なくする
  64. 64. #denatechcon アプリケーションエンジニアのインフラ兼務 Local MiniKube GKE GCP Production …etc この差分をカバー さえすれば、 ほぼ運用できる。 2 1.5学習コスト比
  65. 65. #denatechcon クラウド環境 • コンテナ • ログの収集 • 監視 • ネットーワーク • デプロイ • データコンバート
  66. 66. #denatechcon コンテナ ●1コンテナ1プロセス ●プロセスの管理を抽象化 ●Dockerの管理= Railsの管理 ●各環境でも同じコンテナ ●Ubuntu ベース
  67. 67. #denatechcon Pod(kubernetes)
  68. 68. #denatechcon ログの収集 • Cluster nodeのメモリ,CPU ..etc • コンテナのメモリ,CPU ..etc
  69. 69. #denatechcon 外形監視 • 基本はすべて、 stackdriverで監視を行う • 外形監視のみmonitis 監視
  70. 70. #denatechcon GCP ネットワーク USER Cloud NAT
  71. 71. #denatechcon CI/CD Deploy Server デプロイ DeNA
  72. 72. #denatechcon データのコンバート onPremis Mysql Perl GCS転送スクリプト JSON gsutil rsync GCS受信スクリプト 受信 処理済み Rubyコンバート スクリプト gsutil rsync 差分
  73. 73. #denatechcon 2019年春 新しいエブリスタ始まる
  74. 74. #denatechcon #denatechcon

×