SlideShare a Scribd company logo
1 of 47
Download to read offline
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Yasuharu GOTO (@ono_matope)
2017/03/25
Goでヤフーの分散オブジェクトストレージを作った話
Go Conference 2017 Spring
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
About me
名前: Yasuharu GOTO
Twitter: @ono_matope
Github: @matope
所属: ヤフー株式会社 データプラットフォーム開発本部
Go歴:3年
コントリビューション:
Expect:100-Continueのクライアント実装 (Go1.6)
2
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Agenda
• Goでヤフーの基盤ストレージ Dragon を作った話
• Goでの耐障害性向上テクニック
3
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
Dragon
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Dragon
• ヤフーで開発している分散オブジェクトストレージ
• デザインゴール:高速、高スケーラビリティ、高可用性、低コスト
• Go言語
• S3 互換 API
• 2016年1月 リリース (14ヶ月の本番稼働実績)
5
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Why we built a new Object Storage?
6
• Octagon(2011-)
• 最初の内製オブジェクトストレージ
• 諸々の技術的課題から、代替を検討
• 遅い・不安定・運用しづらい・レガシー・etc...
• 既存のOSS?
• Riak CS : 一部で導入するも、性能がサービス要件を満たさず
• OpenStack Swift : スケーラビリティに不安
• パブリッククラウド?
• 自社DCと比べてコスト面で不利
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Why we built a new Object Storage?
7
じゃあ作ろう
2014年 実装スタート
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.8
クラスタ数: 2
格納オブジェクト数: 100億
格納データ量: 9PB
サービス利用多数(右)
その他社内システム多数
Presto (in experiment)
利用規模
Yahoo!オークション (画像)
Yahoo!ニュース・トピックス/個人 (画像)
Yahoo!ディスプレイアドネットワーク (画像/動画)
Yahoo!ブログ (画像)
Yahoo!スマホきせかえ (画像)
Yahoo!トラベル (画像)
Yahoo!不動産 (画像)
Yahoo!知恵袋 (画像)
Yahoo!飲食店予約 (画像)
Yahoo!みんなの政治 (画像)
Yahoo!ゲーム (コンテンツ)
Yahoo!ブックストア (コンテンツ)
Yahoo!ボックス (データ)
ネタりか (記事画像)
etc...
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Performance (with Riak CS/参考値)
• Dragon: API*1, Storage*1,Cassandra*3
• Riak CS: haproxy*1, stanchion*1, Riak (KV+CS)*3
• CassandraとStanchion以外はすべて同一構成のHWを使用。
9
0
500
1000
1500
2000
2500
3000
3500
1 5 10 50 100 200 400
Requests/sec
# of Threads
GET Object 10KB Throughput
Riak CS
Dragon
0
100
200
300
400
500
600
700
800
900
1000
1 5 10 50 100 200 400
Requests/sec
# of Threads
PUT Object 10KB Throughput
Riak CS
Dragon
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
10
Architecture
API Nodes
HTTP (S3 API)
Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node
...
Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node
Storage Cluster
Blob
Metadata Meta DB
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
11
Architecture
API Nodes
HTTP (S3 API)
Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node
...
Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node
Storage Cluster
Blob
Metadata Meta DB
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
12
Upload
API Nodes
HTTP PUT
Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node
Meta DB
...
Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node
格納位置を含む
オブジェクトメタデータ
HTTP PUT
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
13
Download
API Nodes
HTTP GET
Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node
Meta DB
...
Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node
Storage Cluster
格納位置を含む
オブジェクトメタデータ
HTTP GET
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Architecture
• シンプル is ベスト
• ブラックボックスを減らす
• メタDBとしてCassandraを利用
• 十分な可用性とスケーラビリティ
• 他にもいろいろな工夫が
• 今日は省略
14
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
Go Failure Tlerance Tips:
1. Circuit Breaker
2. Timeout for Streaming
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
Go Tips 1: Circuit Breaker
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Circuit Breaker
Storage Nodeが障害で停止・ネットワーク断の場合
• ストレージへのリクエストがコネクションタイムアウトの間ブロック
• 他ノードにフェイルオーバーするまでのレイテンシがユーザーリクエストに影響
• 落ちているノードへのリクエストは避けたい
17
API Node
Storage Nodes
数秒間ブロック数秒間ブロック
😢
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.18
Circuit Breaker
Circuit Breakerパターン
ある処理のエラー頻度が閾値をこえたら、
しばらくは処理を省略(Circuit Open)して
即座にエラーを返すパターン
タイムアウト待ちを省略してエラーを返せる
Remote Server
Success
Error!(1)
Error!(2)
Trip
Error!(3)
Circuit Open!
Circuit Breaker
Trip
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Circuit Breaker
http://github.com/rubyist/circuitbreaker
19
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
API Node
20
Circuit Breaker
Dragonでは、Circuit Breakerを
HTTPクライアントのDialContextに適用
• 接続先アドレスのDialをCBで管理
• n回連続でDialに失敗したNodeは
Circuit Openし、一定期間Dialしない
• 即座にフォールバック可能
Storage NodesCircuit
Breakers
node1
node2
node3
node4
node5
client := http.Client{
Transport: &http.Transport{
DialContext:
(&CircuitDialer{}).DialContext,
},
}
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
type CircuitDialer struct {
mu sync.Mutex
dialer net.Dialer
breakers map[string]*circuit.Breaker
}
func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
d.mu.Lock()
if d.breakers == nil {
d.breakers = map[string]*circuit.Breaker{}
}
if _, ok := d.breakers[addr]; !ok {
d.breakers[addr] = circuit.NewConsecutiveBreaker(4)
}
cb := d.breakers[addr]
d.mu.Unlock()
err = cb.CallContext(ctx, func() error {
conn, err = d.dialer.DialContext(ctx, network, addr)
return err
}, 0)
return conn, err
}
Circuit Breaker
21
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
type CircuitDialer struct {
mu sync.Mutex
dialer net.Dialer
breakers map[string]*circuit.Breaker
}
func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
d.mu.Lock()
if d.breakers == nil {
d.breakers = map[string]*circuit.Breaker{}
}
if _, ok := d.breakers[addr]; !ok {
d.breakers[addr] = circuit.NewConsecutiveBreaker(4)
}
cb := d.breakers[addr]
d.mu.Unlock()
err = cb.CallContext(ctx, func() error {
conn, err = d.dialer.DialContext(ctx, network, addr)
return err
}, 0)
return conn, err
}
Circuit Breaker
22
接続先ごとにCircuitBreakerを用意
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
type CircuitDialer struct {
mu sync.Mutex
dialer net.Dialer
breakers map[string]*circuit.Breaker
}
func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
d.mu.Lock()
if d.breakers == nil {
d.breakers = map[string]*circuit.Breaker{}
}
if _, ok := d.breakers[addr]; !ok {
d.breakers[addr] = circuit.NewConsecutiveBreaker(4)
}
cb := d.breakers[addr]
d.mu.Unlock()
err = cb.CallContext(ctx, func() error {
conn, err = d.dialer.DialContext(ctx, network, addr)
return err
}, 0)
return conn, err
}
Circuit Breaker
23
接続先のCircuitBreaker
がなければ作成
接続先ごとにCircuitBreakerを用意
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
type CircuitDialer struct {
mu sync.Mutex
dialer net.Dialer
breakers map[string]*circuit.Breaker
}
func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
d.mu.Lock()
if d.breakers == nil {
d.breakers = map[string]*circuit.Breaker{}
}
if _, ok := d.breakers[addr]; !ok {
d.breakers[addr] = circuit.NewConsecutiveBreaker(4)
}
cb := d.breakers[addr]
d.mu.Unlock()
err = cb.CallContext(ctx, func() error {
conn, err = d.dialer.DialContext(ctx, network, addr)
return err
}, 0)
return conn, err
}
Circuit Breaker
24
接続先のCircuitBreaker
がなければ作成
DialContextにCircuitBreakerを適用
接続先ごとにCircuitBreakerを用意
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
API Node
Circuit Breaker
Circuit Breakerパターンにより、ノード障害時のリクエストレイテンシを保護
25
Storage Nodes
Circuit
Dialer
😄
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
Go Tips 2: I/O Timeout for Streaming
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Download
27
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
resp, err := s.HTTPClient.Get(blobURL)
if err != nil {
return err
}
defer resp.Body.Close()
_, err = io.Copy(w, resp.Body)
return err
}
バックエンドストレージに
HTTP GETリクエストを発行
ストレージからのレスポンスボディを
io.CopyでResponseWriterに転送
単純化したダウンロードハンドラ実装
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Download
28
• io.Copy(dest io.Writer, src io.Reader)
• src (io.Reader) を dest (io.Writer) にコピーする関数
• 内部では32KBバッファ bufを確保し、
src.Read(buf), dest.Write(buf)を繰り返し呼ぶ
API Node
io.Copy
dest.Write() src.Read() GET Response.BodyResponseWriter
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Case1: Storage Blocking on Download
29
1. もしストレージノードにNW障害やHW障害が起こると、
Response.Bodyが流れてこなくなる
2. io.Copy()内のsrc.Read()が無限にブロックする
3. ダウンロード転送が止まる!
API Node
io.Copy
dest.Write() src.Read() GET Response.BodyResponseWriter
😢
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Case2: Client Blocking on Download
30
1. 逆に、何らかの問題で、クライアントがレスポンスのダウンロードを止めると、
ResponseWriter.Write() が無限にブロックする
2. io.Copy()が進まず、ダウンロード転送が止まる
3. リソースリーク!
API Node
io.Copy
dest.Write() src.Read() GET Response.BodyResponseWriter
😢
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Upload
31
アップロードもio.Copyを使っている。
src: クライアントRequest.Body
dest: 3ノードへのPUTリクエストのBody(MultiWriterとPipeを経由)
API Node
PUT Request.Body
io.Copy Multi
WriterRequest.Body src.Read() dest.Write()
PUT Request.Body
PUT Request.BodyWriter – Pipe - Reader
Writer – Pipe - Reader
Writer – Pipe - Reader
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Case3: Storage Blocking on Upload
32
API Node
PUT Request.Body
io.Copy Multi
WriterRequest.Body src.Read() dest.Write()
PUT Request.Body
PUT Request.BodyWriter – Pipe - Reader
Writer – Pipe - Reader
Writer – Pipe - Reader
1. ストレージノードに障害が起こると、リクエストBodyが送れなくなる
io.Copy()内のsrc.Read()が無限にブロックする
2. アップロード転送が止まる!
😢
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Timeout for Stream
33
• まとめると…
• ストレージ、クライアントどちらかでデータ転送が止まると、
Read()またはWrite()が無限にブロックする
• ダウンロード、アップロードのストリームが止まったままになる
• リソースリークが起こる
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Timeout for Stream
34
• なんとかしてI/Oのブロックを検知して、
タイムアウトエラーとしてハンドリングしたい
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Timeout for Stream
35
• net.Conn.SetDeadline() ?
• net.ConnのRead(),Write()に時間制限を指定する機能
• http.ServeHTTPはクライアントのnet.Connにアクセスできない
• http.TimeoutHandler ?
• データサイズやユーザーの通信帯域がバラバラなので
固定のタイムアウト値が設定できない
• サイズ:1Byte〜5GB、帯域:100Kbps 〜 10Gbps
• The complete guide to Go net/http timeouts
• https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Our approach
36
• タイムアウト機能付きの io.Reader, io.Writerを実装して、
すべてのストリーム経路に仕掛ける
Request
.Body
Request
.Body
API Node
io.Copy GET Response.BodyResponseWriter
io.CopyRequest.Body
Request
.Body
Writer – Pipe - Reader
Writer – Pipe - Reader
Writer – Pipe - Reader
Multi
Writer
On Download
On Upload
Timeout
Writer
Timeout
Writer
Timeout
Writer
Timeout
Writer
Timeout
Reader
Timeout
Reader
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
TimeoutWriter/Reader
• Write()がTimeout時間で完了しなかったら関数 Fn が実行されるWriteラッパー
• シンプル!
• TimeoutReaderも同様に定義
37
type TimeoutWriter struct {
W io.Writer
Timeout time.Duration
Fn func()
}
func (w *TimeoutWriter) Write(p []byte) (int, error) {
timer := time.AfterFunc(w.Timeout, w.Fn)
defer timer.Stop()
return w.W.Write(p)
}
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Timeout for Streaming
38
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
n, err := io.Copy(w,blob)
return err
}
適用前
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Timeout for Streaming
39
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
timeoutCh := make(chan struct{}, 1)
resultCh := make(chan resultAndError, 1)
go func() {
tw := TimeoutWriter{
W: w,
Timeout: 10 * time.Second,
Fn: func() { timeoutCh <- struct{}{} },
}
n, err := io.Copy(tw,blob)
resultCh <- resultAndError{n:n, err:err}
}()
select {
case <-timeoutCh:
return RequestTimeoutError
case result := <-resultCh:
return result.err
}
}
Copyを別Goroutineに
適用後
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Timeout for Streaming
40
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
timeoutCh := make(chan struct{}, 1)
resultCh := make(chan resultAndError, 1)
go func() {
tw := TimeoutWriter{
W: w,
Timeout: 10 * time.Second,
Fn: func() { timeoutCh <- struct{}{} },
}
n, err := io.Copy(tw,blob)
resultCh <- resultAndError{n:n, err:err}
}()
select {
case <-timeoutCh:
return RequestTimeoutError
case result := <-resultCh:
return result.err
}
}
ResponseWriterを
TimeoutWriterでラップ。
io.Copyのdestをtwに
適用後
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Timeout for Streaming
41
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
timeoutCh := make(chan struct{}, 1)
resultCh := make(chan resultAndError, 1)
go func() {
tw := TimeoutWriter{
W: w,
Timeout: 10 * time.Second,
Fn: func() { timeoutCh <- struct{}{} },
}
n, err := io.Copy(tw,blob)
resultCh <- resultAndError{n:n, err:err}
}()
select {
case <-timeoutCh:
return RequestTimeoutError
case result := <-resultCh:
return result.err
}
}
Writeが10秒間ブロックしたら
timeoutChに送信
適用後
ServeHTTPを抜けると、
wはClose()してCopyはエラーに
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Timeout for Streaming
42
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
timeoutCh := make(chan struct{}, 1)
resultCh := make(chan resultAndError, 1)
go func() {
tw := TimeoutWriter{
W: w,
Timeout: 10 * time.Second,
Fn: func() { timeoutCh <- struct{}{} },
}
n, err := io.Copy(tw,blob)
resultCh <- resultAndError{n:n, err:err}
}()
select {
case <-timeoutCh:
return RequestTimeoutError
case result := <-resultCh:
return result.err
}
}
io.Copyが終了したら
resultChに送信
適用後
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Performance Impact?
43
• io.Read(), io.Write()のタイムアウトをシンプルな実装でハンドルできた😄
• でも遅いんでしょう?
• すべてのRead()/Write()にタイマーを仕掛けるなんて…
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Performance Impact?
44
性能インパクトは僅少
• 100KB ダウンロード スループット: -2% 〜 0%
• 100KB アップロード スループット: +3% 〜 -5%
0
2000
4000
6000
8000
10000
12000
20 50 100 200 400 800
Requests/sec
# of Threads
GET Object 100KB Throughput
No Timeout
Timeout
0
500
1000
1500
2000
2500
3000
3500
4000
20 50 100 200 400 800
Requests/sec
# of Threads
PUT Object 100KB Throughput
No Timeout
Timeout
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Go Failure Tlerance Tips
45
大規模な分散システムに要求される耐障害性をシンプルに実装
• CircuitBreaker
• Timeout Read/Write
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
Conclusion
Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.
Conclusion
• ヤフーではGoで大規模な分散オブジェクト
ストレージDragonを開発・運用中です
• サービス基盤のモダン化を進行中
• We’re Hiring
• Thank you!
47

More Related Content

What's hot

イベント・ソーシングを知る
イベント・ソーシングを知るイベント・ソーシングを知る
イベント・ソーシングを知る
Shuhei Fujita
 
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くしたNginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
toshi_pp
 

What's hot (20)

実運用して分かったRabbit MQの良いところ・気をつけること #jjug
実運用して分かったRabbit MQの良いところ・気をつけること #jjug実運用して分かったRabbit MQの良いところ・気をつけること #jjug
実運用して分かったRabbit MQの良いところ・気をつけること #jjug
 
イベント・ソーシングを知る
イベント・ソーシングを知るイベント・ソーシングを知る
イベント・ソーシングを知る
 
MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
 
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くしたNginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
 
RESTfulとは
RESTfulとはRESTfulとは
RESTfulとは
 
Python 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそうPython 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそう
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
Apache Sparkに手を出してヤケドしないための基本 ~「Apache Spark入門より」~ (デブサミ 2016 講演資料)
Apache Sparkに手を出してヤケドしないための基本 ~「Apache Spark入門より」~ (デブサミ 2016 講演資料)Apache Sparkに手を出してヤケドしないための基本 ~「Apache Spark入門より」~ (デブサミ 2016 講演資料)
Apache Sparkに手を出してヤケドしないための基本 ~「Apache Spark入門より」~ (デブサミ 2016 講演資料)
 
マイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPCマイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPC
 
DockerとPodmanの比較
DockerとPodmanの比較DockerとPodmanの比較
DockerとPodmanの比較
 
ストリーム処理勉強会 大規模mqttを支える技術
ストリーム処理勉強会 大規模mqttを支える技術ストリーム処理勉強会 大規模mqttを支える技術
ストリーム処理勉強会 大規模mqttを支える技術
 
TLS, HTTP/2演習
TLS, HTTP/2演習TLS, HTTP/2演習
TLS, HTTP/2演習
 
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
 
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
 
Rest ful api設計入門
Rest ful api設計入門Rest ful api設計入門
Rest ful api設計入門
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 
C# 8.0 非同期ストリーム
C# 8.0 非同期ストリームC# 8.0 非同期ストリーム
C# 8.0 非同期ストリーム
 
分散トレーシング技術について(Open tracingやjaeger)
分散トレーシング技術について(Open tracingやjaeger)分散トレーシング技術について(Open tracingやjaeger)
分散トレーシング技術について(Open tracingやjaeger)
 

Viewers also liked

Viewers also liked (9)

Goをカンストさせる話
Goをカンストさせる話Goをカンストさせる話
Goをカンストさせる話
 
My client wanted their apps synced, and I made it with Go
My client wanted their apps synced, and I made it with GoMy client wanted their apps synced, and I made it with Go
My client wanted their apps synced, and I made it with Go
 
go 1.8 net/http timeouts
go 1.8 net/http timeoutsgo 1.8 net/http timeouts
go 1.8 net/http timeouts
 
Coding in the context era
Coding in the context eraCoding in the context era
Coding in the context era
 
Go conference 2017 Lightning talk
Go conference 2017 Lightning talkGo conference 2017 Lightning talk
Go conference 2017 Lightning talk
 
20171105 go con2017_lt
20171105 go con2017_lt20171105 go con2017_lt
20171105 go con2017_lt
 
Gocon2017:Goのロギング周りの考察
Gocon2017:Goのロギング周りの考察Gocon2017:Goのロギング周りの考察
Gocon2017:Goのロギング周りの考察
 
GOCON Autumn (Story of our own Monitoring Agent in golang)
GOCON Autumn (Story of our own Monitoring Agent in golang)GOCON Autumn (Story of our own Monitoring Agent in golang)
GOCON Autumn (Story of our own Monitoring Agent in golang)
 
条件式評価器の実装による管理ツールの抽象化
条件式評価器の実装による管理ツールの抽象化条件式評価器の実装による管理ツールの抽象化
条件式評価器の実装による管理ツールの抽象化
 

Similar to Goでヤフーの分散オブジェクトストレージを作った話 Go Conference 2017 Spring

FluentdとRedshiftの素敵な関係
FluentdとRedshiftの素敵な関係FluentdとRedshiftの素敵な関係
FluentdとRedshiftの素敵な関係
moai kids
 
Dockerの利用事例
Dockerの利用事例Dockerの利用事例
Dockerの利用事例
maebashi
 
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい) 泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
Akihiro Kuwano
 
drecomにおけるwinning the metrics battle
drecomにおけるwinning the metrics battledrecomにおけるwinning the metrics battle
drecomにおけるwinning the metrics battle
Mitsuki Kenichi
 

Similar to Goでヤフーの分散オブジェクトストレージを作った話 Go Conference 2017 Spring (20)

FluentdとRedshiftの素敵な関係
FluentdとRedshiftの素敵な関係FluentdとRedshiftの素敵な関係
FluentdとRedshiftの素敵な関係
 
AWS SDK for Smalltalk
AWS SDK for SmalltalkAWS SDK for Smalltalk
AWS SDK for Smalltalk
 
activerecord-turntable
activerecord-turntableactiverecord-turntable
activerecord-turntable
 
Dataworks Summit 2017 SanJose StreamProcessing - Hadoop Source Code Reading #...
Dataworks Summit 2017 SanJose StreamProcessing - Hadoop Source Code Reading #...Dataworks Summit 2017 SanJose StreamProcessing - Hadoop Source Code Reading #...
Dataworks Summit 2017 SanJose StreamProcessing - Hadoop Source Code Reading #...
 
Inside mobage platform
Inside mobage platformInside mobage platform
Inside mobage platform
 
Spring Boot on Kubernetes : Yahoo!ズバトク事例 #jjug_ccc
Spring Boot on Kubernetes : Yahoo!ズバトク事例 #jjug_cccSpring Boot on Kubernetes : Yahoo!ズバトク事例 #jjug_ccc
Spring Boot on Kubernetes : Yahoo!ズバトク事例 #jjug_ccc
 
DBpedia Japanese
DBpedia JapaneseDBpedia Japanese
DBpedia Japanese
 
Microsoft open tech night 2020 feb18
Microsoft open tech night 2020 feb18Microsoft open tech night 2020 feb18
Microsoft open tech night 2020 feb18
 
Network as a Service - Data plane evolution and abstraction by NSM
Network as a Service - Data plane evolution and abstraction by NSMNetwork as a Service - Data plane evolution and abstraction by NSM
Network as a Service - Data plane evolution and abstraction by NSM
 
Dockerの利用事例
Dockerの利用事例Dockerの利用事例
Dockerの利用事例
 
Tech summit 2018_ad15_ver_1106
Tech summit 2018_ad15_ver_1106Tech summit 2018_ad15_ver_1106
Tech summit 2018_ad15_ver_1106
 
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい) 泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
 
Node.jsアプリの開発をモダン化するために取り組んできたこと
Node.jsアプリの開発をモダン化するために取り組んできたことNode.jsアプリの開発をモダン化するために取り組んできたこと
Node.jsアプリの開発をモダン化するために取り組んできたこと
 
Python におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころPython におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころ
 
QoS for ROS 2 Dashing/Eloquent
QoS for ROS 2 Dashing/EloquentQoS for ROS 2 Dashing/Eloquent
QoS for ROS 2 Dashing/Eloquent
 
成長を加速する minne の技術基盤戦略
成長を加速する minne の技術基盤戦略成長を加速する minne の技術基盤戦略
成長を加速する minne の技術基盤戦略
 
Airflowを広告データのワークフローエンジンとして運用してみた話
Airflowを広告データのワークフローエンジンとして運用してみた話Airflowを広告データのワークフローエンジンとして運用してみた話
Airflowを広告データのワークフローエンジンとして運用してみた話
 
サーバーレスで ガチ本番運用までやってるお話し
サーバーレスで ガチ本番運用までやってるお話しサーバーレスで ガチ本番運用までやってるお話し
サーバーレスで ガチ本番運用までやってるお話し
 
DockerCon参加報告 (`docker build`が30倍以上速くなる話など)
DockerCon参加報告 (`docker build`が30倍以上速くなる話など)DockerCon参加報告 (`docker build`が30倍以上速くなる話など)
DockerCon参加報告 (`docker build`が30倍以上速くなる話など)
 
drecomにおけるwinning the metrics battle
drecomにおけるwinning the metrics battledrecomにおけるwinning the metrics battle
drecomにおけるwinning the metrics battle
 

More from Yahoo!デベロッパーネットワーク

More from Yahoo!デベロッパーネットワーク (20)

ゼロから始める転移学習
ゼロから始める転移学習ゼロから始める転移学習
ゼロから始める転移学習
 
継続的なモデルモニタリングを実現するKubernetes Operator
継続的なモデルモニタリングを実現するKubernetes Operator継続的なモデルモニタリングを実現するKubernetes Operator
継続的なモデルモニタリングを実現するKubernetes Operator
 
ヤフーでは開発迅速性と品質のバランスをどう取ってるか
ヤフーでは開発迅速性と品質のバランスをどう取ってるかヤフーでは開発迅速性と品質のバランスをどう取ってるか
ヤフーでは開発迅速性と品質のバランスをどう取ってるか
 
オンプレML基盤on Kubernetes パネルディスカッション
オンプレML基盤on Kubernetes パネルディスカッションオンプレML基盤on Kubernetes パネルディスカッション
オンプレML基盤on Kubernetes パネルディスカッション
 
LakeTahoe
LakeTahoeLakeTahoe
LakeTahoe
 
オンプレML基盤on Kubernetes 〜Yahoo! JAPAN AIPF〜
オンプレML基盤on Kubernetes 〜Yahoo! JAPAN AIPF〜オンプレML基盤on Kubernetes 〜Yahoo! JAPAN AIPF〜
オンプレML基盤on Kubernetes 〜Yahoo! JAPAN AIPF〜
 
Persistent-memory-native Database High-availability Feature
Persistent-memory-native Database High-availability FeaturePersistent-memory-native Database High-availability Feature
Persistent-memory-native Database High-availability Feature
 
データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2
データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2
データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2
 
eコマースと実店舗の相互利益を目指したデザイン #yjtc
eコマースと実店舗の相互利益を目指したデザイン #yjtceコマースと実店舗の相互利益を目指したデザイン #yjtc
eコマースと実店舗の相互利益を目指したデザイン #yjtc
 
ヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtc
ヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtcヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtc
ヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtc
 
Yahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtc
Yahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtcYahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtc
Yahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtc
 
ビッグデータから人々のムードを捉える #yjtc
ビッグデータから人々のムードを捉える #yjtcビッグデータから人々のムードを捉える #yjtc
ビッグデータから人々のムードを捉える #yjtc
 
サイエンス領域におけるMLOpsの取り組み #yjtc
サイエンス領域におけるMLOpsの取り組み #yjtcサイエンス領域におけるMLOpsの取り組み #yjtc
サイエンス領域におけるMLOpsの取り組み #yjtc
 
ヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtc
ヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtcヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtc
ヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtc
 
Yahoo! JAPAN Tech Conference 2022 Day2 Keynote #yjtc
Yahoo! JAPAN Tech Conference 2022 Day2 Keynote #yjtcYahoo! JAPAN Tech Conference 2022 Day2 Keynote #yjtc
Yahoo! JAPAN Tech Conference 2022 Day2 Keynote #yjtc
 
新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc
新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc
新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc
 
PC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtc
PC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtcPC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtc
PC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtc
 
モブデザインによる多職種チームのコミュニケーション改善 #yjtc
モブデザインによる多職種チームのコミュニケーション改善 #yjtcモブデザインによる多職種チームのコミュニケーション改善 #yjtc
モブデザインによる多職種チームのコミュニケーション改善 #yjtc
 
「新しいおうち探し」のためのAIアシスト検索 #yjtc
「新しいおうち探し」のためのAIアシスト検索 #yjtc「新しいおうち探し」のためのAIアシスト検索 #yjtc
「新しいおうち探し」のためのAIアシスト検索 #yjtc
 
ユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtc
ユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtcユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtc
ユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtc
 

Goでヤフーの分散オブジェクトストレージを作った話 Go Conference 2017 Spring

  • 1. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Yasuharu GOTO (@ono_matope) 2017/03/25 Goでヤフーの分散オブジェクトストレージを作った話 Go Conference 2017 Spring
  • 2. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. About me 名前: Yasuharu GOTO Twitter: @ono_matope Github: @matope 所属: ヤフー株式会社 データプラットフォーム開発本部 Go歴:3年 コントリビューション: Expect:100-Continueのクライアント実装 (Go1.6) 2
  • 3. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Agenda • Goでヤフーの基盤ストレージ Dragon を作った話 • Goでの耐障害性向上テクニック 3
  • 4. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved. Dragon
  • 5. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Dragon • ヤフーで開発している分散オブジェクトストレージ • デザインゴール:高速、高スケーラビリティ、高可用性、低コスト • Go言語 • S3 互換 API • 2016年1月 リリース (14ヶ月の本番稼働実績) 5
  • 6. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Why we built a new Object Storage? 6 • Octagon(2011-) • 最初の内製オブジェクトストレージ • 諸々の技術的課題から、代替を検討 • 遅い・不安定・運用しづらい・レガシー・etc... • 既存のOSS? • Riak CS : 一部で導入するも、性能がサービス要件を満たさず • OpenStack Swift : スケーラビリティに不安 • パブリッククラウド? • 自社DCと比べてコスト面で不利
  • 7. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Why we built a new Object Storage? 7 じゃあ作ろう 2014年 実装スタート
  • 8. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.8 クラスタ数: 2 格納オブジェクト数: 100億 格納データ量: 9PB サービス利用多数(右) その他社内システム多数 Presto (in experiment) 利用規模 Yahoo!オークション (画像) Yahoo!ニュース・トピックス/個人 (画像) Yahoo!ディスプレイアドネットワーク (画像/動画) Yahoo!ブログ (画像) Yahoo!スマホきせかえ (画像) Yahoo!トラベル (画像) Yahoo!不動産 (画像) Yahoo!知恵袋 (画像) Yahoo!飲食店予約 (画像) Yahoo!みんなの政治 (画像) Yahoo!ゲーム (コンテンツ) Yahoo!ブックストア (コンテンツ) Yahoo!ボックス (データ) ネタりか (記事画像) etc...
  • 9. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Performance (with Riak CS/参考値) • Dragon: API*1, Storage*1,Cassandra*3 • Riak CS: haproxy*1, stanchion*1, Riak (KV+CS)*3 • CassandraとStanchion以外はすべて同一構成のHWを使用。 9 0 500 1000 1500 2000 2500 3000 3500 1 5 10 50 100 200 400 Requests/sec # of Threads GET Object 10KB Throughput Riak CS Dragon 0 100 200 300 400 500 600 700 800 900 1000 1 5 10 50 100 200 400 Requests/sec # of Threads PUT Object 10KB Throughput Riak CS Dragon
  • 10. Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved. 10 Architecture API Nodes HTTP (S3 API) Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node ... Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node Storage Cluster Blob Metadata Meta DB
  • 11. Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved. 11 Architecture API Nodes HTTP (S3 API) Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node ... Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node Storage Cluster Blob Metadata Meta DB
  • 12. Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved. 12 Upload API Nodes HTTP PUT Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node Meta DB ... Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node 格納位置を含む オブジェクトメタデータ HTTP PUT
  • 13. Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved. 13 Download API Nodes HTTP GET Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node Meta DB ... Storage Node Storage Node Storage Node Storage Node Storage Node Storage Node Storage Cluster 格納位置を含む オブジェクトメタデータ HTTP GET
  • 14. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Architecture • シンプル is ベスト • ブラックボックスを減らす • メタDBとしてCassandraを利用 • 十分な可用性とスケーラビリティ • 他にもいろいろな工夫が • 今日は省略 14
  • 15. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved. Go Failure Tlerance Tips: 1. Circuit Breaker 2. Timeout for Streaming
  • 16. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved. Go Tips 1: Circuit Breaker
  • 17. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Circuit Breaker Storage Nodeが障害で停止・ネットワーク断の場合 • ストレージへのリクエストがコネクションタイムアウトの間ブロック • 他ノードにフェイルオーバーするまでのレイテンシがユーザーリクエストに影響 • 落ちているノードへのリクエストは避けたい 17 API Node Storage Nodes 数秒間ブロック数秒間ブロック 😢
  • 18. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.18 Circuit Breaker Circuit Breakerパターン ある処理のエラー頻度が閾値をこえたら、 しばらくは処理を省略(Circuit Open)して 即座にエラーを返すパターン タイムアウト待ちを省略してエラーを返せる Remote Server Success Error!(1) Error!(2) Trip Error!(3) Circuit Open! Circuit Breaker Trip
  • 19. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Circuit Breaker http://github.com/rubyist/circuitbreaker 19
  • 20. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. API Node 20 Circuit Breaker Dragonでは、Circuit Breakerを HTTPクライアントのDialContextに適用 • 接続先アドレスのDialをCBで管理 • n回連続でDialに失敗したNodeは Circuit Openし、一定期間Dialしない • 即座にフォールバック可能 Storage NodesCircuit Breakers node1 node2 node3 node4 node5 client := http.Client{ Transport: &http.Transport{ DialContext: (&CircuitDialer{}).DialContext, }, }
  • 21. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. type CircuitDialer struct { mu sync.Mutex dialer net.Dialer breakers map[string]*circuit.Breaker } func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) { d.mu.Lock() if d.breakers == nil { d.breakers = map[string]*circuit.Breaker{} } if _, ok := d.breakers[addr]; !ok { d.breakers[addr] = circuit.NewConsecutiveBreaker(4) } cb := d.breakers[addr] d.mu.Unlock() err = cb.CallContext(ctx, func() error { conn, err = d.dialer.DialContext(ctx, network, addr) return err }, 0) return conn, err } Circuit Breaker 21
  • 22. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. type CircuitDialer struct { mu sync.Mutex dialer net.Dialer breakers map[string]*circuit.Breaker } func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) { d.mu.Lock() if d.breakers == nil { d.breakers = map[string]*circuit.Breaker{} } if _, ok := d.breakers[addr]; !ok { d.breakers[addr] = circuit.NewConsecutiveBreaker(4) } cb := d.breakers[addr] d.mu.Unlock() err = cb.CallContext(ctx, func() error { conn, err = d.dialer.DialContext(ctx, network, addr) return err }, 0) return conn, err } Circuit Breaker 22 接続先ごとにCircuitBreakerを用意
  • 23. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. type CircuitDialer struct { mu sync.Mutex dialer net.Dialer breakers map[string]*circuit.Breaker } func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) { d.mu.Lock() if d.breakers == nil { d.breakers = map[string]*circuit.Breaker{} } if _, ok := d.breakers[addr]; !ok { d.breakers[addr] = circuit.NewConsecutiveBreaker(4) } cb := d.breakers[addr] d.mu.Unlock() err = cb.CallContext(ctx, func() error { conn, err = d.dialer.DialContext(ctx, network, addr) return err }, 0) return conn, err } Circuit Breaker 23 接続先のCircuitBreaker がなければ作成 接続先ごとにCircuitBreakerを用意
  • 24. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. type CircuitDialer struct { mu sync.Mutex dialer net.Dialer breakers map[string]*circuit.Breaker } func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) { d.mu.Lock() if d.breakers == nil { d.breakers = map[string]*circuit.Breaker{} } if _, ok := d.breakers[addr]; !ok { d.breakers[addr] = circuit.NewConsecutiveBreaker(4) } cb := d.breakers[addr] d.mu.Unlock() err = cb.CallContext(ctx, func() error { conn, err = d.dialer.DialContext(ctx, network, addr) return err }, 0) return conn, err } Circuit Breaker 24 接続先のCircuitBreaker がなければ作成 DialContextにCircuitBreakerを適用 接続先ごとにCircuitBreakerを用意
  • 25. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. API Node Circuit Breaker Circuit Breakerパターンにより、ノード障害時のリクエストレイテンシを保護 25 Storage Nodes Circuit Dialer 😄
  • 26. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved. Go Tips 2: I/O Timeout for Streaming
  • 27. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Download 27 func (s *Server) Download(w http.ResponseWriter, r *http.Request) error { // ... resp, err := s.HTTPClient.Get(blobURL) if err != nil { return err } defer resp.Body.Close() _, err = io.Copy(w, resp.Body) return err } バックエンドストレージに HTTP GETリクエストを発行 ストレージからのレスポンスボディを io.CopyでResponseWriterに転送 単純化したダウンロードハンドラ実装
  • 28. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Download 28 • io.Copy(dest io.Writer, src io.Reader) • src (io.Reader) を dest (io.Writer) にコピーする関数 • 内部では32KBバッファ bufを確保し、 src.Read(buf), dest.Write(buf)を繰り返し呼ぶ API Node io.Copy dest.Write() src.Read() GET Response.BodyResponseWriter
  • 29. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Case1: Storage Blocking on Download 29 1. もしストレージノードにNW障害やHW障害が起こると、 Response.Bodyが流れてこなくなる 2. io.Copy()内のsrc.Read()が無限にブロックする 3. ダウンロード転送が止まる! API Node io.Copy dest.Write() src.Read() GET Response.BodyResponseWriter 😢
  • 30. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Case2: Client Blocking on Download 30 1. 逆に、何らかの問題で、クライアントがレスポンスのダウンロードを止めると、 ResponseWriter.Write() が無限にブロックする 2. io.Copy()が進まず、ダウンロード転送が止まる 3. リソースリーク! API Node io.Copy dest.Write() src.Read() GET Response.BodyResponseWriter 😢
  • 31. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Upload 31 アップロードもio.Copyを使っている。 src: クライアントRequest.Body dest: 3ノードへのPUTリクエストのBody(MultiWriterとPipeを経由) API Node PUT Request.Body io.Copy Multi WriterRequest.Body src.Read() dest.Write() PUT Request.Body PUT Request.BodyWriter – Pipe - Reader Writer – Pipe - Reader Writer – Pipe - Reader
  • 32. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Case3: Storage Blocking on Upload 32 API Node PUT Request.Body io.Copy Multi WriterRequest.Body src.Read() dest.Write() PUT Request.Body PUT Request.BodyWriter – Pipe - Reader Writer – Pipe - Reader Writer – Pipe - Reader 1. ストレージノードに障害が起こると、リクエストBodyが送れなくなる io.Copy()内のsrc.Read()が無限にブロックする 2. アップロード転送が止まる! 😢
  • 33. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Timeout for Stream 33 • まとめると… • ストレージ、クライアントどちらかでデータ転送が止まると、 Read()またはWrite()が無限にブロックする • ダウンロード、アップロードのストリームが止まったままになる • リソースリークが起こる
  • 34. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Timeout for Stream 34 • なんとかしてI/Oのブロックを検知して、 タイムアウトエラーとしてハンドリングしたい
  • 35. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Timeout for Stream 35 • net.Conn.SetDeadline() ? • net.ConnのRead(),Write()に時間制限を指定する機能 • http.ServeHTTPはクライアントのnet.Connにアクセスできない • http.TimeoutHandler ? • データサイズやユーザーの通信帯域がバラバラなので 固定のタイムアウト値が設定できない • サイズ:1Byte〜5GB、帯域:100Kbps 〜 10Gbps • The complete guide to Go net/http timeouts • https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
  • 36. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Our approach 36 • タイムアウト機能付きの io.Reader, io.Writerを実装して、 すべてのストリーム経路に仕掛ける Request .Body Request .Body API Node io.Copy GET Response.BodyResponseWriter io.CopyRequest.Body Request .Body Writer – Pipe - Reader Writer – Pipe - Reader Writer – Pipe - Reader Multi Writer On Download On Upload Timeout Writer Timeout Writer Timeout Writer Timeout Writer Timeout Reader Timeout Reader
  • 37. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. TimeoutWriter/Reader • Write()がTimeout時間で完了しなかったら関数 Fn が実行されるWriteラッパー • シンプル! • TimeoutReaderも同様に定義 37 type TimeoutWriter struct { W io.Writer Timeout time.Duration Fn func() } func (w *TimeoutWriter) Write(p []byte) (int, error) { timer := time.AfterFunc(w.Timeout, w.Fn) defer timer.Stop() return w.W.Write(p) }
  • 38. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Timeout for Streaming 38 func (s *Server) Download(w http.ResponseWriter, r *http.Request) error { // ... blob, _ := s.GetBlobStream(ctx, object, byteRange) n, err := io.Copy(w,blob) return err } 適用前
  • 39. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Timeout for Streaming 39 func (s *Server) Download(w http.ResponseWriter, r *http.Request) error { // ... blob, _ := s.GetBlobStream(ctx, object, byteRange) timeoutCh := make(chan struct{}, 1) resultCh := make(chan resultAndError, 1) go func() { tw := TimeoutWriter{ W: w, Timeout: 10 * time.Second, Fn: func() { timeoutCh <- struct{}{} }, } n, err := io.Copy(tw,blob) resultCh <- resultAndError{n:n, err:err} }() select { case <-timeoutCh: return RequestTimeoutError case result := <-resultCh: return result.err } } Copyを別Goroutineに 適用後
  • 40. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Timeout for Streaming 40 func (s *Server) Download(w http.ResponseWriter, r *http.Request) error { // ... blob, _ := s.GetBlobStream(ctx, object, byteRange) timeoutCh := make(chan struct{}, 1) resultCh := make(chan resultAndError, 1) go func() { tw := TimeoutWriter{ W: w, Timeout: 10 * time.Second, Fn: func() { timeoutCh <- struct{}{} }, } n, err := io.Copy(tw,blob) resultCh <- resultAndError{n:n, err:err} }() select { case <-timeoutCh: return RequestTimeoutError case result := <-resultCh: return result.err } } ResponseWriterを TimeoutWriterでラップ。 io.Copyのdestをtwに 適用後
  • 41. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Timeout for Streaming 41 func (s *Server) Download(w http.ResponseWriter, r *http.Request) error { // ... blob, _ := s.GetBlobStream(ctx, object, byteRange) timeoutCh := make(chan struct{}, 1) resultCh := make(chan resultAndError, 1) go func() { tw := TimeoutWriter{ W: w, Timeout: 10 * time.Second, Fn: func() { timeoutCh <- struct{}{} }, } n, err := io.Copy(tw,blob) resultCh <- resultAndError{n:n, err:err} }() select { case <-timeoutCh: return RequestTimeoutError case result := <-resultCh: return result.err } } Writeが10秒間ブロックしたら timeoutChに送信 適用後 ServeHTTPを抜けると、 wはClose()してCopyはエラーに
  • 42. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Timeout for Streaming 42 func (s *Server) Download(w http.ResponseWriter, r *http.Request) error { // ... blob, _ := s.GetBlobStream(ctx, object, byteRange) timeoutCh := make(chan struct{}, 1) resultCh := make(chan resultAndError, 1) go func() { tw := TimeoutWriter{ W: w, Timeout: 10 * time.Second, Fn: func() { timeoutCh <- struct{}{} }, } n, err := io.Copy(tw,blob) resultCh <- resultAndError{n:n, err:err} }() select { case <-timeoutCh: return RequestTimeoutError case result := <-resultCh: return result.err } } io.Copyが終了したら resultChに送信 適用後
  • 43. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Performance Impact? 43 • io.Read(), io.Write()のタイムアウトをシンプルな実装でハンドルできた😄 • でも遅いんでしょう? • すべてのRead()/Write()にタイマーを仕掛けるなんて…
  • 44. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Performance Impact? 44 性能インパクトは僅少 • 100KB ダウンロード スループット: -2% 〜 0% • 100KB アップロード スループット: +3% 〜 -5% 0 2000 4000 6000 8000 10000 12000 20 50 100 200 400 800 Requests/sec # of Threads GET Object 100KB Throughput No Timeout Timeout 0 500 1000 1500 2000 2500 3000 3500 4000 20 50 100 200 400 800 Requests/sec # of Threads PUT Object 100KB Throughput No Timeout Timeout
  • 45. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Go Failure Tlerance Tips 45 大規模な分散システムに要求される耐障害性をシンプルに実装 • CircuitBreaker • Timeout Read/Write
  • 46. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved.Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved. Conclusion
  • 47. Copyrig ht © 2017 Yahoo Japan Corporation. All Rig hts Reserved. Conclusion • ヤフーではGoで大規模な分散オブジェクト ストレージDragonを開発・運用中です • サービス基盤のモダン化を進行中 • We’re Hiring • Thank you! 47