10.6K Views
December 03, 19
スライド概要
「Bonfire#4 Kubernetesで課題解決」で使用した資料になります。
https://yj-meetup.connpass.com/event/153658/
2023年10月からSpeaker Deckに移行しました。最新情報はこちらをご覧ください。 https://speakerdeck.com/lycorptech_jp
k8s初心者が gRPC envoyを 導入したら色々苦労した話 2019年11月09日 ヤフー株式会社 信原 有志
ノブハラ ユウシ @ohs30359 2017年 Yahoo! JAPAN 中途入社 ヤフーショッピング配属 現在はバックエンドシステムのリプレイスを担当 K8sは半年ぐらい ( k8sは完全に理解した ) そもそもアプリケーションエンジニア
アジェンダ ■ 導入したシステム解説 - システムの概要 - 実際の構成 ■ 運用で発生した不具合 - 不具合の概要 - 解決までのプロセス
アジェンダ ■ 導入したシステム解説 - システムの概要 - 実際の構成 ■ 運用で発生した不具合 - 不具合の概要 - 解決までのプロセス
サービスの課題 カート カートチーム ランキング 検索 検索チーム ランキングチーム
コンテンツを跨いだ共通のコンポーネント デバイス情報 ユーザー情報 開催中のセール
共通コンポーネント配信システムの提供 カートチーム 検索チーム ランキングチーム
アジェンダ ■ 導入したシステム解説 - システム導入の背景 - 実際の構成 ■ 運用で発生した不具合 - 不具合の概要 - 解決までのプロセス
システム全体の構成 Front Service Back Post API Domain Service Client System Redis
Front Serviceの構成 認証用 APP Front APP Domain Service SVC
Domain Service の構成 認証用 APP Back Post API BFF APP Redis
アジェンダ ■ 導入したシステム解説 - システム導入の背景 - 実際の構成 ■ 運用で発生した不具合 - 不具合の概要 - 解決までのプロセス
不具合の概要 Domain Service で ローリングアップデートを実行 - Front Service でコネクションエラーが継続的に発生 - ローリングアップデートが終わってしばらくすると治まる Front Service Domain Service ☓
アジェンダ ■ 導入したシステム解説 - システム導入の背景 - 実際の構成 ■ 運用で発生した不具合 - 不具合の概要 - 解決までのプロセス
仮説 ■ SVC の DNSから外れているがリクエスト済みのセッションが podがKillされる前に処理されなかったためエラーになっている ? <発⾏リクエスト> リクエスト 1 リクエスト 2 リクエスト 3 リクエスト 4 <スタック> リクエスト 1 リクエスト 2 リクエスト 3 リクエスト 4 リクエスト4 が preStop終了までに処理が終わっていない?
Pod 削除フローの確認 SIGTERM PreStop 新規コネクション 接続済みコネクション SIGKILL
検証 ■ 負荷を減らした状態で pod 数をスケールさせ, pod 単位の負荷を下げる。 負荷: 1 rps Pod Replica 100
検証前イメージ ■待ち行列が処理される前に PreStopが終わってしまっている PreStop SIGTERM stack1 stack2 SIGKILL stack3 stack4 stack5
検証時イメージ ■負荷を下げ, そもそもstackを積ませなくする PreStop SIGTERM stack1 SIGKILL
検証結果 エラー発生率が下がることはなく継続的なエラーは発生 ▶ スタックが原因ではない
仮説 ■ envoyのIPテーブルとSVCのIPリストの更新に遅延が有り, 存在しないPodにリクエストが送られてしまっている ? xx.xx.xx.1 xx.xx.xx.1 xx.xx.xx.2 xx.xx.xx.3 xx.xx.xx.1 xx.xx.xx.2 xx.xx.xx.3 xx.xx.xx.2 xx.xx.xx.3 xx.xx.xx.4 ▶ xx.xx.xx.1 xx.xx.xx.2 xx.xx.xx.3 xx.xx.xx.4 xx.xx.xx.2 xx.xx.xx.3 xx.xx.xx.2 xx.xx.xx.3
検証 ■ preStop を1秒から30秒に設定を変更 ▶ envoy の IP Table が更新されるのに充分な時間をもたせる ■ RollingUpdate の maxSurge を 1 で指定 ▶ 一度に複数のIPが更新されないようにし、envoy の更新を下げる
検証結果 エラー発生率が下がることもなく、効果はなかった ▶ envoyのIPテーブルとSVCのIPリストの更新に遅延はない
仮説 ■ envoy の grpc コネクションは SVCから受け取ったIPで全更新されず、 古いセッションは残り続けている ( IPがエラーになったタイミングで消えている? ) ▶ xx.xx.xx.1 xx.xx.xx.2 xx.xx.xx.3 xx.xx.xx.1 xx.xx.xx.2 xx.xx.xx.3 xx.xx.xx.1 xx.xx.xx.2 xx.xx.xx.3 xx.xx.xx.4 xx.xx.xx.5 xx.xx.xx.6 xx.xx.xx.3 xx.xx.xx.4 xx.xx.xx.5 xx.xx.xx.6
検証 ■ Zipkin を導入してTraceId から 失敗した ID を照合する。 TraceId: xxxx1 TraceId: xxxx2 TraceId: xxxx3 TraceId: xxxx4 TraceId: xxxx5 Front 側で失敗したIDがないかを確認 https://icon-pit.com/
検証結果 集めたログに失敗したTraceIdは含まれていなかった ▶ envoy の grpcコネクションに古いIPが含まれてしまっている
どうやって治す? ■ これまでの検証から envoy に対象のpodが消えたことを通知するしかなさそう ▶ とりあえず方法が無いかドキュメントを確認
普通にあった ※今回はgrpcなのでgrpc_health_check: {} https://www.envoyproxy.io/learn/health-check
health checkの実装 アプリケーションに こちらのprotobufを実装する。 ↓ envoy は このサービスに向けて 定期的にリクエストを投げる ↓ falseが返ったタイミングで 対象のIPをコネクションから外す https://github.com/grpc/grpc/blob/master/doc/health-checking.md
アプリケーションサーバの改修 health checkの機構はdefaultでは存在しないため独自に実装 ▶ 自分がdeleteされることを受けるための /killを用意。 kill 通知を受けると grpc health check は false で返すようにする
最終的な修正 Health checkの設定追加 health check 用サービスの追加 & preStopで死活監視フラグをfalseにし、 health checkが失敗するようにする
最終的なGraceful Shutdown PreStop ▶ curl で application の /kill エントリ に POST Kill リクエストを受けて 自身の死活フラグをfalseに変更 ▶ アプリケーション側でもGraceful Shutdown を実行 health checkからfalseを受け取り IPテーブルから対象のpodを削除 SIGKILLを送り, podを削除
まとめ 現象 : BFFをローリングアップデートすると Front側でコネクションエラーが継続的に発生する 原因 : envoyのgrpcのコネクションは自動的に更新されない ( 既にKillされたPodのIPが消えない ) 解決方法 : ヘルスチェックを導入して独自に死活管理をもたせる
アイコン素材利⽤: https://icon-rainbow.com/