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

クックパッドでの Webアプリケーション開発 2017 / Web application development in Cookpad 2017

Kohei Suzuki
November 16, 2017

クックパッドでの Webアプリケーション開発 2017 / Web application development in Cookpad 2017

Rails Developers Meetup #7 東京会場
https://techplay.jp/event/631428

Kohei Suzuki

November 16, 2017
Tweet

More Decks by Kohei Suzuki

Other Decks in Technology

Transcript

  1. クックパッドでの Webアプリ
    ケーション開発 2017
    Kohei Suzuki (@eagletmt)

    View Slide

  2. 自己紹介
    - Kohei Suzuki (@eagletmt)
    - クックパッド 技術部開発基盤グループ
    - Docker を使ってアプリケーションを動かす基盤を全面的に担当

    View Slide

  3. アウトライン
    - 去年の時点での開発環境
    - アプリケーションをデプロイするまでのフロー
    - Docker、ECS、Hako
    - 今年に入ってからやってきたこと
    - Web アプリケーションの統合管理コンソール
    - pull-request 単位のステージング環境
    - 分散トレーシング
    - 次にやっていきたいこと

    View Slide

  4. これまでのあらすじ
    - クックパッドではマイクロサービスを推進してきた
    - Web API のインターフェイスを決める Garage、GarageClient
    - 各アプリケーションから共通で使えるツールの整備
    - 定期実行ジョブ管理システム Kuroko2
    - 非同期ジョブキューシステム Barbeque
    - エラートラッカーとしては Sentry を採用
    - アプリケーションの実行基盤としては Amazon ECS を採用

    View Slide

  5. Web アプリケーションのデプロイフロー

    View Slide

  6. Web アプリケーションのデプロイフロー
    - GitHub Enterprise
    - アプリケーションと Dockerfile を書く
    - hako_apps という1つのリポジトリに Hako の定義ファイル (YAML) を書く
    - ビルドパイプライン
    - Jenkins でテストを実行し docker build して Amazon ECR に docker push
    - デプロイ実行
    - Slack から Ruboty (chatbot) を経由して Rundeck で hako deploy コマンドを実行
    - Amazon ECS で Docker コンテナ化された Web アプリケーションを動かす
    - バッチ実行
    - Rundeck が Kuroko2 や Barbeque に変わるだけ

    View Slide

  7. Amazon ECS、Hako
    - ECS: EC2 インスタンス上で Docker コンテナを動かしてくれる AWS のマネージド
    サービス
    - Hako: ECS を利用してデーモンを動かしたりワンショットのタスクを実行するための
    自作ツール
    - https://github.com/eagletmt/hako

    View Slide

  8. Hako が作られた経緯
    - 基盤チームがアプリ開発のボトルネックになりたくない
    - マイクロサービス化で新規に Web アプリを作成することが増えた
    - インフラ面で強い権限を持つチームに作業依頼をするフローをできるだけ減らす
    - サーバを立ててほしい、これを設定してほしい、 etc.
    - セルフサービス化
    - 同じような作業の自動化
    - Docker によりサーバのプロビジョニングは全アプリサーバで共通にできる
    - AWS と Docker でどんな Web アプリケーションもだいたい同じ構成になる
    - Route 53、ELB、RDS、ElastiCache、……

    View Slide

  9. Hako
    - アプリケーションの定義を YAML で記述し、それを元に ECS 等の API を叩いて操
    作する
    - Docker イメージ、環境変数といったタスク定義
    - ECS に関連付ける ELB の設定
    - デプロイ中に差し込む処理の指定
    - Route 53 で指定した ELB にドメインを設定したり
    - Jenkins から最新の安定ビルドを取得してその Docker イメージのタグを設定したり
    - gem で拡張可能

    View Slide

  10. 秘密の値
    - 環境変数の一部は秘密の値を含むため Git リポジトリにコミットしたくない
    - データベースのパスワード
    - API キー
    - などなど
    - デプロイ時に環境変数の一部を別のところから取得して埋め込みたい
    - 秘密の値のストレージとして HashiCorp Vault を採用

    View Slide

  11. Vault による秘密の値の管理
    - secret/hako/${team_name}/${app_name}/${var_name} のようなパス
    に秘密の値を書き込んでおく
    - secret/hako/${team_name} 以下にはそのチームのメンバーが読み書きで
    きるようにしておく
    - Hako で環境変数を定義するときに #{var_name} のような文法で Vault 内の値
    を参照できるように

    View Slide

  12. Hako
    scheduler:
    type: ecs
    region: ap-northeast-1
    cluster: hako-production
    role: ecsServiceRole
    task_role_arn: arn:aws:iam::xxxx:role/EcsAwesomeApp
    elb_v2:
    vpc_id: vpc-01234567
    listeners:
    - port: 80
    protocol: HTTP
    - port: 443
    protocol: HTTPS
    certificate_arn:
    arn:aws:acm:ap-northeast-1:xxxx:certificate/yyyy
    ……
    ECS の設定
    IAM ロール
    ELB の設定

    View Slide

  13. ECS / Docker へのパラメータ
    Hako
    ……
    app:
    image: 012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/awesome-app
    cpu: 3072
    memory: 2048
    env:
    $providers:
    - type: vault
    addr: https://vault:8200
    directory: hako/dev-infra/awesome-app
    DATABASE_URL: mysql2://user:#{mysql_password}@awesome-app.rds.amazonaws.com/main
    RAILS_ENV: production
    ……
    Docker イメージ
    Vault から秘密の値を
    提供
    秘密の値を埋め込み

    View Slide

  14. Hako
    - これらの script は gem で拡張可能
    - 社内の事情にあわせて独自の script を用意
    ……
    scripts:
    - type: jenkins_tag
    job: docker-awesome-app
    - type: route53_subdomain
    hosted_zone: ZABCDEFGHIJKL
    Jenkins の docker-awesome-app ジョブから最新の安定ビルドのリ
    ビジョンを取得して Docker イメージタグを書き換え
    elb_v2 で指定された ELB を指す
    awesome-app.example.com を Route 53 で設定

    View Slide

  15. 新規 Web アプリケーションの開発フロー
    - アプリケーションを書く
    - rails new
    - ヘルスチェック用のエンドポイントを用意するために revision_plate gem を入れる
    - sentry-raven gem でエラーログを残せるようにする
    - 他アプリから使われる API は Garage で書く
    - Docker イメージを用意する
    - Dockerfile を書く
    - Jenkins で docker build して Amazon ECR に docker push

    View Slide

  16. 新規 Web アプリケーションの開発フロー
    - Hako の定義ファイルを書く
    - hako_apps リポジトリに pull-request
    - Web アプリやワーカのようなデーモンの場合
    - Rundeck にデプロイ、ロールバック用のジョブを登録して Ruboty を使って Slack からデプロイ
    - オフラインジョブの場合
    - 定期実行するときは Kuroko2 に登録
    - 非同期実行したい場合は barbeque_client gem を導入し Barbeque に登録
    - モニタリングは Amazon CloudWatch や Datadog で

    View Slide

  17. 課題感
    - 様々なツールを使っているため1つのアプリに関する情報を把握しきるのが困難
    - 1つの GitHub リポジトリを見るだけでは終わらない
    - Jenkins ジョブ、Hako の定義ファイル、Sentry のプロジェクト、CloudWatch、……
    - README や社内 Wiki に毎回同じようなリンク集を書くことに
    - 1つの環境を作るときにやることが多い
    - 新機能を別ブランチで開発して普段とは別のステージング環境で動かしたい
    - しかしそのためには似たような Hako の定義や Jenkins ジョブ等を書かなければならない

    View Slide

  18. 統合コンソールの開発
    - hako-console という社内 Web アプリを開発することにした
    - アプリケーション毎に関連するページへのリンク集を自動的に表示
    - よく見る CloudWatch メトリクスはその場で表示
    - 典型的な開発での作業を一部自動化
    - 1つの pull-request に紐付いたステージング環境作成を支援

    View Slide

  19. hako-console

    View Slide

  20. hako-console

    View Slide

  21. hako-console

    View Slide

  22. hako-console

    View Slide

  23. hako-console
    Hako の定義ファイル アプリのリポジトリ
    Jenkins ジョブ Sentry プロジェクト
    Rundeck ジョブ
    使っている ELB
    使っている RDS

    View Slide

  24. hako-console
    動いてるインスタンス
    ECS のメトリクス (CloudWatch)
    コンテナ毎のメトリクス (Datadog)

    View Slide

  25. hako-console
    ELB の情報
    どのアプリが使っているか
    ELB のメトリクス (CloudWatch)

    View Slide

  26. hako-console のポリシー
    - 手動で入力する箇所をなくし、可能な限り自動でページを作る
    - 手動メンテは必ず情報が古くなる
    - 実際の状態を API で取得したり、実際に使われている設定ファイルを元にすべき

    View Slide

  27. 情報収集手段
    - Hako の定義ファイルでの指定をデータソースにする
    - ELB の名前や ECS のサービス名から CloudWatch のデータを表示
    - 規約 (Convention) を元に探す
    - Jenkins や Rundeck のジョブ一覧を API で取得して、名前で寄せる
    - Hako の定義ファイルに書かれた環境変数を元にする
    - DATABASE_URL を見て使ってる DB インスタンスを特定
    - rds.amazonaws.com や cache.amazonaws.com という文字列から Amazon RDS や Amazon
    ElastiCache のインスタンスを特定
    - Sentry DSN っぽい文字列から Sentry のプロジェクトを特定

    View Slide

  28. 開発フローの変化
    - モニタリングはまずは hako-console を見ればよくなった
    - どの DB を使ってるか分かる
    - その DB の負荷状況の概要も分かる
    - どのインスタンスで動いているか分かる
    - どの ELB を使っているかが分かる
    - その ELB のアクセス数やエラーレートの概要も分かる
    - 開発に必要なものも分かる
    - GitHub リポジトリはどこにあるのか分かる
    - Docker イメージを作ってる Jenkins ジョブはどれなのか分かる
    - 新規に参加する開発者が把握しやすい

    View Slide

  29. pull-request staging

    View Slide

  30. pull-request staging
    - 開発中の機能をステージング環境で動かしたい
    - 本番と同じようなデータや複数人で使ってる状況で動かしたい
    - ディレクターや他のスタッフに使ってみてもらいたい
    - 普段とは別のステージング環境の作成を楽にしたい
    - 機能開発は pull-request で進む
    - pull-request と同じライフサイクルの環境を作ればいいのではないか

    View Slide

  31. pull-request staging
    - hako-console に GitHub の pull-request に関するイベントを受けとる webhook を
    用意
    - “make staging please” とコメントするとその pull-request のブランチから専用のステージング環境
    が自動的に作成される
    - pull-request がクローズ (マージ含む) されたらそのステージング環境は破棄

    View Slide

  32. pull-request staging
    make staging please

    View Slide

  33. pull-request staging
    merged

    View Slide

  34. pull-request staging
    - 作成時
    - Jenkins ジョブを複製して push する Docker イメージの名前を置換
    - YAML 内で指定している Docker イメージを↑で置換したものに変更
    - Rundeck ジョブを複製して↑で push した YAML ファイルでデプロイするように変更
    - ここで作った Jenkins ジョブや YAML ファイルや Rundeck ジョブを DB に保存
    - 削除時
    - hako remove で ECS からアプリケーションを削除
    - DB に保存されたジョブや YAML ファイルを削除

    View Slide

  35. 分散トレーシング

    View Slide

  36. マイクロサービス化の課題
    - マイクロサービス化により、システム全体を把握することが難しくなる
    - 障害発生時の原因究明が難しい、システム全体でのパフォーマンスの分析が難しい
    - アプリも DB も負荷は高くなってないのに何故遅くなった ?
    - マイクロサービス化にあたって必ず出てくる問題

    View Slide

  37. 分散トレーシング
    - システム外からのあるリクエスト (例: エンドユーザ) を起因としたシステム内のサー
    ビスへのリクエストを追跡できるようにするもの
    - どのサービスに何回リクエストしているのか
    - 各サービスへのリクエストにどれくらい時間がかかってるのか
    - 最初にエラーを返したサービスはどれなのか

    View Slide

  38. 分散トレーシング
    - 最初のリクエストを受けたときに「トレース ID」を発行し、他サービスへリクエストす
    るときのヘッダにトレース ID を付加する
    - トレース ID つきのリクエスト、レスポンスのデータを分散トレーシングシステムに送信
    app-A
    app-B
    app-C app-D
    トレーシング
    システム
    エンドユーザ
    トレース ID を発行
    trace-X
    trace-X
    trace-X
    トレースデータ
    - トレース ID
    - リクエスト
    - レスポンス

    View Slide

  39. AWS X-Ray
    - 分散トレーシングシステムとして AWS X-Ray を採用
    - トレースデータを送信する SDK で Ruby の公式サポートは現時点では無し
    - @taiki45 が非公式の gem を作成 https://github.com/taiki45/aws-xray

    View Slide

  40. E
    AWS X-Ray + ECS
    - SDK から送信されてくるデータを AWS X-Ray に送信するデーモンを一緒に起動
    する
    - このデーモンは公式のものが用意されているのでそれを Docker イメージへ
    - アプリには aws-xray gem を導入して一緒に起動している xray コンテナにデータ
    を送信する
    aws-xray gem
    app
    container
    xray daemon
    xray
    container
    AWS X-Ray
    ECS task

    View Slide

  41. AWS X-Ray + ECS
    サービスマップ

    View Slide

  42. AWS X-Ray + ECS
    障害時 ダメそうなサービスが分かる

    View Slide

  43. AWS X-Ray + ECS
    トレースの詳細

    View Slide

  44. AWS X-Ray + hako-console
    X-Ray のデータを使ってどのアプリとどのアプリが通信しているのかを表示

    View Slide

  45. 今後やっていきたいこと

    View Slide

  46. 今後やっていきたいこと
    - サービスメッシュ
    - 1つのアプリが複数の別のアプリの Web API を使っている状況
    - 各アプリがどれくらいリクエストしているのかメトリクスを知りたい
    - うまく接続したい
    - 接続先が過負荷でダウンしたときは接続を止めたい (サーキットブレーカー )
    - 適切にリトライしたい
    - タイムアウトの値を調整したい
    - 接続先をうまく管理したい (サービスディスカバリ )
    - @taiki45 が構築中

    View Slide

  47. 今後やっていきたいこと
    - RPC
    - Garage による REST API の限界
    - スキーマがほしい
    - どんな JSON が返ってくるのか、各フィールドの型は何なのか
    - REST へのマッピングが困難
    - GET とか POST とか考えずにメソッドのようなエンドポイント
    - クライアントのコード生成
    - gRPC が候補か?

    View Slide