Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
Mobageを支える
November 11th, 2014 !Naotoshi Seo @sonotsDeNA Co., Ltd.
Rubyの技術~ 複数DB編 ~
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
2
自己紹介・瀬尾 直利 @sonots ・DeNA, Co., Ltd ・インフラの Dev ・Rubyist ・OSS 活動家 ・Fluentd コミッタ
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
背景
3
!
Perl の会社と名高いこの DeNA でも Ruby (on Rails) のプロジェクトが立ち上がり始めている
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
インフラ要件の壁
4
!
• ホットデプロイ • カスタムフォーマットのロガー • デプロイサーバでビルドした gem を web サーバにアプリコードと共に撒く
• 社内 rubygems ミラー • Q4M 非同期ジョブキュー • プロファイリングツール、調査ツール
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
複数DB対応
5
!
• Rails で複数DBに対応する必要性 • 冗長化・高速化 • 既存のインフラの仕組みにのっかる必要性
• 今回はここに特化した話
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
目次
6
!
• DB接続を都度接続に • クライアントサイドDNSキャッシュ • DBパスワード一元管理 • DBスキーマ一元管理ツール • 複数DB接続
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
DeNAでの複数DB構成
8
!
• MySQL: Master/Slave • DNSラウンドロビンでSlaveを振り分ける • MHA で Master の高可用化 • DNS サーバには MyDNS を使用
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
MySQL Slave 1
MySQL Slave 2
MySQL Slave 3
MyDNS
Web
10.1.1.2
10.1.1.3
10.1.1.4
10.1.1.2sample_r
1回目
| id | zone | name | data | aux | |----|------|----------|----------|-----| | 2 | 1 | sample_r | 10.1.1.2 | 100 | | 3 | 1 | sample_r | 10.1.1.3 | 100 | | 4 | 1 | sample_r | 10.1.1.4 | 100 |
2回目
Slave参照: DNSラウンドロビン
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
MySQL Slave 1
MySQL Slave 2
MySQL Slave 3
MyDNS
Web
10.1.1.2
10.1.1.3
10.1.1.4
2回目
Slave参照: 永続接続
ずっとココ!! ダメ!
| id | zone | name | data | aux | |----|------|----------|----------|-----| | 2 | 1 | sample_r | 10.1.1.2 | 100 | | 3 | 1 | sample_r | 10.1.1.3 | 100 | | 4 | 1 | sample_r | 10.1.1.4 | 100 |
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
MySQL Slave 1
MySQL Slave 2
MySQL Slave 3
MyDNS
Web
10.1.1.2
10.1.1.3
10.1.1.4
10.1.1.3sample_r
2回目
Slave参照: 都度接続
1/3 の確率
| id | zone | name | data | aux | |----|------|----------|----------|-----| | 2 | 1 | sample_r | 10.1.1.2 | 100 | | 3 | 1 | sample_r | 10.1.1.3 | 100 | | 4 | 1 | sample_r | 10.1.1.4 | 100 |
Good
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
MySQL Slave 2
MySQL Slave 3
MySQL Master 1
MyDNS
Web
10.1.1.2
10.1.1.3
10.1.1.1
10.1.1.4
sample_w
MHA | id | zone | name | data | aux | |----|------|----------|----------|-----| | 1 | 1 | sample_w | 10.1.1.1 | 100 | | 2 | 1 | sample_r | 10.1.1.2 | 100 | | 3 | 1 | sample_r | 10.1.1.3 | 100 | | 4 | 1 | sample_r | 10.1.1.4 | 100 |
MySQL Slave 1
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
MySQL Slave 2
MySQL Slave 3
MySQL Master 1
MyDNS
Web
10.1.1.2
10.1.1.3
10.1.1.1
10.1.1.4
sample_w
MHA | id | zone | name | data | aux | |----|------|----------|----------|-----| | 1 | 1 | sample_w | 10.1.1.1 | 100 | | 2 | 1 | sample_w | 10.1.1.2 | 100 | | 3 | 1 | sample_r | 10.1.1.3 | 100 | | 4 | 1 | sample_r | 10.1.1.4 | 100 |
MySQL Master 2
再接続 or 都度接続
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
DBWeb
DBを再起動
restart再接続 or 都度接続
※ただし、ActiveRecord の reconnect は正しく動かなかったり ※そのため、アプリの再起動が必要だったりして、ツライ
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
MySQL Slave 1
MySQL Slave 2
MySQL Slave 3
MyDNS
Web
10.1.1.2
10.1.1.3
10.1.1.4
Slaveダウン検知(check_slave) | id | zone | name | data | aux | |----|------|----------|----------|-----| | 2 | 1 | sample_r | 10.1.1.2 | 100 | | 3 | 1 | sample_r | 10.1.1.3 | 100 | | 4 | 1 | sample_r | 10.1.1.4 | 100 |
10.1.1.3sample_r
再接続 or 都度接続
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
activerecord-refresh_connection
20
Rack レイヤーでリクエスト毎に接続を切るやつ 1行追加でおk # config/application.rb class Application < Rails::Application config.middleware.swap ActiveRecord::ConnectionAdapters::ConnectionManagement, "ActiveRecord::ConnectionAdapters::RefreshConnectionManagement" end
https://github.com/sonots/activerecord-refresh_connectionhttp://blog.livedoor.jp/sonots/archives/38797925.html
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
クライアントDNSキャッシュ
22
!
• ruby 実装 • キャッシュしないと名前解決遅い • 重み0なmasterへのfallback
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
MySQL Slave 1
MySQL Slave 2
MySQL Slave 3
MySQL Master 1
MyDNS
Web
10.1.1.2
10.1.1.3
10.1.1.1
10.1.1.4
テーブルを キャッシュselect
DNSキャッシュ | id | zone | name | data | aux | |----|------|----------|----------|-----| | 1 | 1 | sample_w | 10.1.1.1 | 100 | | 2 | 1 | sample_r | 10.1.1.2 | 100 | | 3 | 1 | sample_r | 10.1.1.3 | 100 | | 4 | 1 | sample_r | 10.1.1.4 | 100 |
10.1.1.2
ファイルに落とす
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
MySQL Master 2
MySQL Slave 2
MySQL Slave 3
MyDNS
Web
10.1.1.2
10.1.1.3
10.1.1.4
重み0へのfallback | id | zone | name | data | aux | |----|------|----------|----------|-----| | 1 | 1 | sample_w | 10.1.1.1 | 100 | | 2 | 1 | sample_w | 10.1.1.2 | 0 | | 3 | 1 | sample_r | 10.1.1.3 | 100 | | 4 | 1 | sample_r | 10.1.1.4 | 100 |
MySQL Master 1
10.1.1.1
weight:100
weight: 0
DNSプロトコルで引くと重み0のエントリが見えない。 Unbound などのDNSキャッシュを使えない。
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
ResolverMyDNS
25
resolver = ResolverMyDNS.new( zone: 'mobage.local',! cache_dir: "cache_dir/",! host: 'localhost',! username: 'root',! passowrd: nil,! database: 'mydns',!)!resolver.get_server 'foo.mobage.local' #=> 10.1.1.10
MyDNSのテーブルエントリをクライアントサイド でキャッシュして名前解決するやつ
@Spring_MT
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
26
@ryopeko
これアプリコード内でIPアドレス取得して establish_connection に渡す必要あるけど、めんどいよね?
そうすな~
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
ResolverReplace
27
resolver = ResolverMyDNS.new(...)!# Resolver を resolver_mydns にすげかえる!ResolverReplace.load_plugin('mysql2')!ResolverReplace.register!(! getaddress: resolver.method(&:get_server),! getaddresses: resolver.method(:get_server_list),! error_class: ResolverMyDNS::Error,!)
Rubyの名前解決を挿げ替えるやつ
https://github.com/sonots/resolver_replace
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
ResolverReplace
28
!
• ぶっちゃけると ruby が持っている resolv-replace のパクリ
• resolv-replace は ruby の名前解決を libc から Resolv クラスに置き換えるやつ
• 任意のクラスに置き換えられるように拡張
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
ResolverMyDNS 欲しい人???
29
ResolverReplace は OSS になってる
https://github.com/sonots/resolver_replace
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
DeNAでのDBパスワード管理
31
!
• yaml で一元管理(インフラ管理. アプリ毎に管理させない)
• DBIx::DBHResolver という perl モジュールを通して取得する
• stage, sandbox など環境毎にパスワードが違うが、同じコードで取得できる
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
dbhresolver
32
!
• 既存の yaml を処理して ActiveRecord が受け取る形式で吐き出してくれる ruby gem
production: <<: *default <%= dbh.connect_spec('SAMPLE_R') %> sample_r_production: <<: *default <%= dbh.connect_spec('SAMPLE_R') %> sample_w_production: <<: *default <%= dbh.connect_spec('SAMPLE_W') %>
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
DeNAでのスキーマ管理
34
!
• git レポジトリで一元管理(インフラ管理. アプリ毎に管理させない)
• SQL::Translator::Diff という perl モジュールを使って実際のDBスキーマとの diff をとった alter 文を作ってもらいマイグレーション
• ruby である必要はないのでそのまま利用
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
複数DB接続
36
!
• read/write 用モデルそれぞれ作ってゴリゴリ実装することもできる
• が、良い仕組みを使って実装を楽にしたい
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
37
ク社どうやってるの?octopus辛みありそう...
switch_pointというのがあってだな @sora_h
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
完全に実用段階!!!
38
SwitchPoint
詳しくはeagletmt先生の資料をご参照ください
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
SwitchPoint
39
SwitchPoint.configure do |config| config.define_switch_point :db1, readable: :db1_readonly, writable: :db1_writable, config.define_switch_point :db2, readonly: :db2_readonly end class Book1 < ActiveRecord::Base use_switch_point :db1 end class Book2 < ActiveRecord::Base use_switch_point :db2 end
https://github.com/eagletmt/switch_point
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
Adminsbar
40
http://admins.bar/3/
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
まとめ ~ 複数DB編 ~
41
!
• DB接続を都度接続に • activerecord_refresh_connection
• クライアントサイドDNSキャッシュ • resolver_mydns / resolver_replace
• DBパスワード一元管理 • DBスキーマ一元管理ツール • 複数DB対応 • switch_point
Copyright (C) 2014 DeNA Co.,Ltd. All Rights Reserved.
その他インフラ要件の壁
42
!
• ホットデプロイ • カスタムフォーマットのロガー • デプロイサーバでビルドした gem を web サーバにアプリコードと共に撒く
• 社内 rubygems ミラー • Q4M 非同期ジョブ • プロファイリングツール、調査ツール
Top Related