Push型デプロイへの回帰 — 2.7GBのRailsアプリを2秒で同期 —

1.5K Views

March 14, 25

スライド概要

概要:
DeNAのゲームサービス事業における、大規模グローバル配信向け第2世代共通ゲームサーバ Sakasho2p のデプロイは Capistrano で実現されています。歴史を紐解くと、Push型デプロイ、Pull型デプロイを経て、再びPush型デプロイに回帰するという進化を遂げました。本発表では、デプロイの変遷を追体験するとともに、現行版のPush型デプロイである、 Capistrano::BundleRsync の git_turbo モードを支える速さの仕組みを解説します。

補足:
・2025/3/13 に開催されたイベント、「DeNA.rb」で発表したスライドです。
・イベント connpass:https://dena.connpass.com/event/339746/
・資料を見やすくする目的等で、微修正を行っております。
・資料中の git_turbo は、 Capistrano::BundleRsync の v0.6.0 から利用が可能です。

https://github.com/sonots/capistrano-bundle_rsync/blob/v0.6.0/CHANGELOG.md#060-20250314

profile-image

DeNA が社会の技術向上に貢献するため、業務で得た知見を積極的に外部に発信する、DeNA 公式のアカウントです。DeNA エンジニアの登壇資料をお届けします。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

(ダウンロード不可)

関連スライド

各ページのテキスト
1.

Push型デプロイへの回帰 — 2.7GBのRailsアプリを2秒で同期 — 越智琢正 ゲームサービス事業本部 開発運営統括部 第三技術部 サーバーグループ 株式会社ディー・エヌ・エー © DeNA Co., Ltd. 1

2.

自己紹介 越智 琢正 株式会社 ディー・エヌ・エー ゲームサービス事業本部 ● ● ● © DeNA Co., Ltd. 元人工衛星開発エンジニア 6年以上: Rails製ゲームサーバの 全般的なプロダクトマネジメント および、テックリード 10年以上: Rails歴 2

3.

Sakasho2pについて ● 大規模グローバル配信向け、第2世代共通ゲームサーバ (Ruby on Rails製) ○ ● ● ゲームならではの複雑な仕様を、周辺ツール含めて、丁寧に作り込む 高負荷 ○ 更新リクエストの割合、数十% ○ 1秒あたり、約100万リクエスト ○ DB / API サーバ台数、数千インスタンス 高品質 © DeNA Co., Ltd. ※ 性能は負荷試験時の実績値 ※ 品質は運用時の実績値 ○ 不具合・不整合ゼロが基本 ○ 平常時エラー率: 約0.001%以下 (主にクラウド基盤等の一時的な不調起因) ○ 実績稼働率: 約 99.99 % (エラー率5%以上をダウンタイムとして、SLO 99.9 %) 3

4.

Deploymentの改善に関して © DeNA Co., Ltd. 4

5.

第一部: Push型Deployの誕生 © DeNA Co., Ltd. 5

6.

Capistrano A remote server automation and deployment tool written in Ruby. - Ruby製のDeploymentツール © DeNA Co., Ltd. 6

7.

Capistranoの基本 ● ● ● 2種類のサーバが登場 ○ 人間が触る配布用サーバ ○ リクエストをさばくWebサーバ 処理の概要 ○ 人間が配布用サーバで、 cap production deploy コマンドを実行 ○ 機械が全Webサーバ上で、新versionをセットアップして、アプリケーション切り替え 詳細なWebサーバ上での処理の流れ © DeNA Co., Ltd. ○ 「git clone」等で、リポジトリの情報を取得 ○ 「git archive | tar」で、新versionのファイル一式を、分離された環境に展開 ○ 「bundle install」や「rake assets:precompile」等の事前処理を実行 ○ 「ln -s」で新バージョンに切り替え、プロセス再起動 7

8.

標準的なディレクトリ構造 ├── current -> /var/www/my_app_name/releases/20150120114500/ ├── releases │ ├── 20150080072500 │ ├── 20150090083000 │ ├── 20150100093500 │ ├── 20150110104000 │ └── 20150120114500 ├── repo │ └── <VCS related data> ├── revisions.log └── shared └── <linked_files and linked_dirs> https://capistranorb.com/documentation/getting-started/structure/ より ※ 概略のみ説明 © DeNA Co., Ltd. 8

9.

標準的なディレクトリ構造 ├── current -> /var/www/my_app_name/releases/20150120114500/ ├── releases │ ├── 20150080072500 │ ├── 20150090083000 │ ├── 20150100093500 │ ├── 20150110104000 │ └── 20150120114500 ├── repo │ └── <VCS related data> ①リポジトリ内容を取得 ├── revisions.log └── shared └── <linked_files and linked_dirs> https://capistranorb.com/documentation/getting-started/structure/ より ※ 概略のみ説明 © DeNA Co., Ltd. 9

10.

標準的なディレクトリ構造 ├── current -> /var/www/my_app_name/releases/20150120114500/ ├── releases │ ├── 20150080072500 │ ├── 20150090083000 │ ├── 20150100093500 │ ├── 20150110104000 │ └── 20150120114500 ②分離された環境に新バージョンを展開 ├── repo │ └── <VCS related data> ①リポジトリ内容を取得 ├── revisions.log └── shared └── <linked_files and linked_dirs> https://capistranorb.com/documentation/getting-started/structure/ より ※ 概略のみ説明 © DeNA Co., Ltd. 10

11.

標準的なディレクトリ構造 ├── current -> /var/www/my_app_name/releases/20150120114500/ ├── releases │ ├── 20150080072500 │ ├── 20150090083000 │ ├── 20150100093500 ③bundle install 等の事前処理を実行 │ ├── 20150110104000 │ └── 20150120114500 ②分離された環境に新バージョンを展開 ├── repo │ └── <VCS related data> ①リポジトリ内容を取得 ├── revisions.log └── shared └── <linked_files and linked_dirs> https://capistranorb.com/documentation/getting-started/structure/ より ※ 概略のみ説明 © DeNA Co., Ltd. 11

12.

標準的なディレクトリ構造 ├── current -> /var/www/my_app_name/releases/20150120114500/ ├── releases │ ├── 20150080072500 ④新バージョンに切り替え │ ├── 20150090083000 │ ├── 20150100093500 ③bundle install 等の事前処理を実行 │ ├── 20150110104000 │ └── 20150120114500 ②分離された環境に新バージョンを展開 ├── repo │ └── <VCS related data> ①リポジトリ内容を取得 ├── revisions.log └── shared └── <linked_files and linked_dirs> https://capistranorb.com/documentation/getting-started/structure/ より ※ 概略のみ説明 © DeNA Co., Ltd. 12

13.

標準的なディレクトリ構造 ├── current -> /var/www/my_app_name/releases/20150120114500/ ├── releases │ ├── 20150080072500 ④新バージョンに切り替え │ ├── 20150090083000 │ ├── 20150100093500 ③bundle install 等の事前処理を実行 │ ├── 20150110104000 │ └── 20150120114500 ②分離された環境に新バージョンを展開 ├── repo │ └── <VCS related data> ①リポジトリ内容を取得 ├── revisions.log └── shared 全Webサーバでやるには └── <linked_files and linked_dirs> 重い処理が存在 https://capistranorb.com/documentation/getting-started/structure/ より ※ 概略のみ説明 © DeNA Co., Ltd. 13

14.

重い処理のイメージ図 GitHub 配布用サーバ Webサーバ © DeNA Co., Ltd. 14

15.

Push型Deployの誕生 ● ● ● 標準手法が抱えていた課題 ○ N台のWebサーバで、「git clone 」 ○ N台のWebサーバで、「bundle install」 ○ 台数が増えると、計算コストや、リポジトリへの負荷が高い Push型Deployによる解決 ○ 1台の配布用サーバで「git clone」 ○ 1台の配布用サーバで「bundle install」 ○ N台のWebサーバに、「rsync」 ○ 重い処理を配布用サーバで実施するのがポイント Capistrano::BundleRsync の誕生 ○ © DeNA Co., Ltd. https://github.com/sonots/capistrano-bundle_rsync 15

16.

Push型Deployにおける改善 ├── current -> /var/www/my_app_name/releases/20150120114500/ ├── releases │ ├── 20150080072500 ④新バージョンに切り替え │ ├── 20150090083000 │ ├── 20150100093500 ③bundle install 等の事前処理を実行 │ ├── 20150110104000 │ └── 20150120114500 ②分離された環境に新バージョンを展開 ├── repo │ └── <VCS related data> ①リポジトリ内容を取得 ├── revisions.log └── shared 重い処理は配布サーバで実施 └── <linked_files and linked_dirs> 成果物を rsync https://capistranorb.com/documentation/getting-started/structure/ より ※ 概略のみ説明 © DeNA Co., Ltd. 16

17.

Push型のイメージ図 GitHub 配布用サーバ Webサーバ © DeNA Co., Ltd. 17

18.

第一部 完 めでたしめでたし © DeNA Co., Ltd. 18

19.

第二部: Pull型Deployの誕生 © DeNA Co., Ltd. 19

20.

Push型のイメージ図 GitHub 配布用サーバ 10^0〜10^2 Webサーバ © DeNA Co., Ltd. 20

21.

Push型のイメージ図(サーバ台数が非常に多い時) GitHub 配布用サーバ 10^2〜10^3 Webサーバ © DeNA Co., Ltd. 21

22.

Pull型Deployの誕生 ● Push型Deployが抱えていた課題 ○ 1台の配布用サーバから、N台のWebサーバに対して「rsync」 ○ Nが非常に大きくなると、配布用サーバ(ネットワーク帯域)で律速 ■ ● ● 例: 300台 x 300 MB = 90 GB !! (1Gbpsでも10分以上) Pull型Deployによる解決 ○ 1台の配布用サーバで「git archive | tar」で展開し、「bundle install」で準備 ○ 1台の配布用サーバで「tar czf」でアーカイブ作成 ○ 1台の配布用サーバで「s3 cp」でアップロード ○ N台のWebサーバで、「s3 cp」でダウンロード ○ N台のWebサーバで、「tar xzf」で展開 Capistrano::NetStorage の誕生 ○ © DeNA Co., Ltd. https://github.com/DeNA/capistrano-net_storage 22

23.

Pull型のイメージ図 GitHub 配布用 クラウドストレージ (例: Amazon S3, Google Cloud Storage) 無限に近い帯域 10^2〜10^3 Webサーバ © DeNA Co., Ltd. 23

24.

第二部 完 大量のサーバでも、Pull型Deployにより O(1) の時間でDeploy可能に めでたしめでたし © DeNA Co., Ltd. 24

25.

第三部: Push型Deployへの回帰 © DeNA Co., Ltd. 25

26.

あれ、O(1) 時間でも、 なんかDeploy遅くない? © DeNA Co., Ltd. 26

27.

アプリケー ションサイ ズ の壁 © DeNA Co., Ltd. 27

28.

アプリケーションサイズの壁 ● ● GB級の大きなRailsアプリケーションは O(1) でも遅い ○ 運用を続けると大きくなる ○ 依存ライブラリまで含めると... とあるDeployでの計測結果 ○ ○ 配布用サーバで 3分30秒 ■ 1分3秒「git archive | tar」による展開 ■ 2分10秒「tar czf」によるアーカイブ作成 ■ 17秒「アップロード処理」(〜1Gbps) Webサーバで 1分24秒 ■ © DeNA Co., Ltd. 1分24秒: ダウンロード&展開処理 28

29.

アプリケーションサイズの壁 ● ● GB級の大きなRailsアプリケーションは O(1) でも遅い ○ 運用を続けると大きくなる ○ 依存ライブラリまで含めると... とあるDeployでの計測結果 ○ ○ 配布用サーバで 3分30秒 ■ 1分3秒「git archive | tar」による展開 ■ 2分10秒「tar czf」によるアーカイブ作成 ■ 17秒「アップロード処理」(〜1Gbps) 一般的には十分な速度 Sakasho2pとしては 十分ではない Webサーバで 1分24秒 ■ © DeNA Co., Ltd. 主要処理: 5分程度 合計処理: 6分程度 1分24秒: ダウンロード&展開処理 29

30.

アプリケーションサイズの壁 ├── current -> /var/www/my_app_name/releases/20150120114500/ ├── releases │ ├── 20150080072500 ④新バージョンに切り替え │ ├── 20150090083000 │ ├── 20150100093500 ③bundle install 等の事前処理を実行 │ ├── 20150110104000 │ └── 20150120114500 ②分離された環境に新バージョンを展開 ├── repo │ └── <VCS related data> ①リポジトリ内容を取得 ├── revisions.log └── shared 新バージョンを作るという └── <linked_files and linked_dirs> 基本的な部分がボトルネック https://capistranorb.com/documentation/getting-started/structure/ より ※ 概略のみ説明 © DeNA Co., Ltd. 30

31.

どうすべきか? © DeNA Co., Ltd. 31

32.

要件を再整理 ● 速度が必要な背景 ○ ○ ● 人間によるDeploy ■ 人間の作業時間や集中力の持続時間という負荷を下げる ■ Deployのハードルやコンテキストスイッチを下げ、開発効率を上げる 機械によるDeploy ■ Auto Scaling時に、最新のバージョンを反映する時間を短くする ■ 応答性の向上は、可用性の向上やインフラコストの削減につながる 詳細な要件 © DeNA Co., Ltd. ○ インクリメンタルで高速な開発に対して、高速な応答性を持つことが重要 ○ 「小さな差分を高速に同期」 32

33.

小さな差分を高速に同期 © DeNA Co., Ltd. 33

34.

「無変更ファイルを更新しない」 を地道にやれば良い © DeNA Co., Ltd. 34

35.

rsync © DeNA Co., Ltd. 35

36.

hardlink + rsync © DeNA Co., Ltd. 36

37.

git worktree + hardlink + rsync © DeNA Co., Ltd. 37

38.

Push型デプロイへの回帰 git worktree + hardlink + rsync © DeNA Co., Ltd. 38

39.

利用するツールや仕組みの概要 ● git worktree ○ ● ● 指定したディレクトリに、指定したコミットのファイルを展開する hardlink ○ 同じファイルの実体を、別のファイル名で指す ○ 作成時に、ファイルの実体のコピーが不要 ○ 削除時に、別のファイル名が存在すれば、実体の削除は不要 rsync ○ © DeNA Co., Ltd. source と target 間において、差分ファイルのみを同期する 39

40.

新Push型デプロイ (:git_turbo) のフロー ● 配布用サーバ ○ 「git worktree && cp -al」により、新バージョンを展開 ■ ● Webサーバ ○ 「cp -al」により、直前バージョンを、新バージョンの基礎として展開 ■ ● ファイル実体の更新は、直前バージョンと新バージョンの差分 ファイル実体の更新は、ゼロ 配布用サーバ => Webサーバ ○ 「rsync」により、Webサーバに対して、新バージョンを同期 ■ © DeNA Co., Ltd. ファイル実体の更新は、直前バージョンと新バージョンの差分 40

41.

新Push型デプロイ (:git_turbo) のフロー ● 配布用サーバ ○ 「git worktree && cp -al」により、新バージョンを展開 ■ ● Webサーバ ○ 「cp -al」により、直前バージョンを、新バージョンの基礎として展開 ■ ● ファイル実体の更新は、直前バージョンと新バージョンの差分 ファイル実体の更新は、ゼロ 配布用サーバ => Webサーバ ○ 「rsync」により、Webサーバに対して、新バージョンを同期 ■ ファイル実体の更新は、直前バージョンと新バージョンの差分 直前バージョンとの差分のみが更新 スケールに必要なネットワーク帯域も非常に小さい © DeNA Co., Ltd. 41

42.

新Push型デプロイ (:git_turbo) のファイルシステム 配布用 Webサーバ ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 ├── git_turbo_worktree │ └── <Working Tree> ├── mirror │ └── <Repository> ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 © DeNA Co., Ltd. 42

43.

新Push型デプロイ (:git_turbo) のファイルシステム 配布用 Webサーバ ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 ├── git_turbo_worktree │ └── <Working Tree> ├── mirror │ └── <Repository> ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 ① 新バージョンを checkout © DeNA Co., Ltd. 43

44.

新Push型デプロイ (:git_turbo) のファイルシステム 配布用 Webサーバ ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 ├── git_turbo_worktree │ └── <Working Tree> ├── mirror │ └── <Repository> ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 ② 新バージョンをハードリンクで展開 ① 新バージョンを checkout © DeNA Co., Ltd. 44

45.

新Push型デプロイ (:git_turbo) のファイルシステム 配布用 Webサーバ ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 ├── git_turbo_worktree │ └── <Working Tree> ├── mirror │ └── <Repository> ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 ③ 直前バージョンのハードリンクを基礎 ② 新バージョンをハードリンクで展開 ① 新バージョンを checkout © DeNA Co., Ltd. 45

46.

新Push型デプロイ (:git_turbo) のファイルシステム 配布用 Webサーバ ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 ├── git_turbo_worktree │ └── <Working Tree> ├── mirror │ └── <Repository> ├── releases │ ├── 20250080072500 │ ├── 20250110104000 │ └── 20250120114500 ④ 直前バージョンとの差分のみを rsync ③ 直前バージョンのハードリンクを基礎 ② 新バージョンをハードリンクで展開 ① 新バージョンを checkout © DeNA Co., Ltd. 46

47.

Capistrano::BundleRsyncに :git_turbo モード爆誕 https://github.com/sonots/capistrano-bundle_rsync/pull/38 set :bundle_rsync_scm, :git_turbo © DeNA Co., Ltd. 47

48.

どれくらい速いのか? ※ deployは計測手法や環境に大きく左右されます 厳密な対照実験ではない為、あくまで参考値として受け止めてください © DeNA Co., Ltd. 48

49.

従来のPush型との比較 © DeNA Co., Ltd. 49

50.

従来のPush型との比較 ファイル数: ~25,000 サイズ: ~2.7GB 配布用サーバ準備: 6秒 Webサーバ転送: 76秒 © DeNA Co., Ltd. 50

51.

従来のPush型との比較 ファイル数: ~25,000 サイズ: ~2.7GB 配布用サーバ準備: 6秒 => 8秒 Webサーバ転送: 76秒 => 2秒 © DeNA Co., Ltd. 51

52.

従来のPush型との比較 ファイル数: ~25,000 サイズ: ~2.7GB 準備処理は2秒増えたが、 スケールに必要な部分は 30倍速以上 配布用サーバ準備: 6秒 => 8秒 Webサーバ転送: 76秒 => 2秒 © DeNA Co., Ltd. 52

53.

従来のPull型との比較 © DeNA Co., Ltd. 53

54.

従来のPull型との比較 ファイル数: ~400 サイズ: ~1.5MB (+bundler ~300MB弱) 配布用サーバ準備: 235秒 Webサーバ転送: 70秒 © DeNA Co., Ltd. 54

55.

従来のPull型との比較 ファイル数: ~400 サイズ: ~1.5MB (+bundler ~300MB弱) 配布用サーバ準備: 235秒 => 4秒 Webサーバ転送: 70秒 => 7秒 © DeNA Co., Ltd. 55

56.

従来のPull型との比較 準備処理は50倍速以上 スケールに必要な部分も 10倍速程度 ファイル数: ~400 サイズ: ~1.5MB (+bundler ~300MB弱) 配布用サーバ準備: 235秒 => 4秒 Webサーバ転送: 70秒 => 7秒 © DeNA Co., Ltd. 56

57.

git_turbo 本番環境 Auto Scale時の実測 © DeNA Co., Ltd. 57

58.

git_turbo 本番環境 Auto Scale時の実測 スケール対象台数: 38台 © DeNA Co., Ltd. 58

59.

git_turbo 本番環境 Auto Scale時の実測 スケール対象台数: 38台 配布用サーバ準備: 1秒 Webサーバ転送: 11秒 Capistranoすべて: 60秒 © DeNA Co., Ltd. 59

60.

git_turbo 本番環境 Auto Scale時の実測 スケール対象台数: 38台 配布用サーバ準備: 1秒 Webサーバ転送: 11秒 コア部分は10秒程度 全処理で60秒 Capistranoすべて: 60秒 © DeNA Co., Ltd. 60

61.

新Push型デプロイ (:git_turbo) の特徴 ● release:cleanup も高速化 ○ ● ポータビリティの高さと、セットアップの楽さ ○ ● リポジトリ、配布用サーバ、Webサーバの最小構成で良い 理論上は、 10^2〜10^3 前半の台数程度であれば、十分な性能が出る ○ ● 更新差分にないファイルの実体は影響を受けないため 初期セットアップはネットワーク帯域が大きいので注意 配布用サーバのスペックの強さは無駄にはならない © DeNA Co., Ltd. ○ メモリやCPUも潤沢なので、踏み台サーバや、本番環境での作業用を兼ねる ○ 高スペックであり、重要なコンポーネントとして安定性につながる ○ 配布用サーバのみスペックが高ければ良いので、コスト効率が高い 61

62.

まとめ ● Deployの歴史は問題解決の歴史 ○ デフォルト戦略 ○ Push型 による効率的なデプロイ ○ Pull型 による O(1) でのデプロイ ○ 新Push型 による超高速化とスケーラビリティの両立 ● 精緻に観察し、地道に向き合えば、問題は解決する ● Capistrano::BundleRsync の :git_turbo モード、是非使ってフィードバック下さい ○ © DeNA Co., Ltd. set :bundle_rsync_scm, :git_turbo 62

63.

© DeNA Co., Ltd. 63