2024.12.19
システムの穴を運用でカバーしようとしてミス多発… バグが大量発生、決算が合わない状態から業務効率化を実現するまで
提供:LINE株式会社
リンクをコピー
記事をブックマーク
岡田遥来氏:ではセッションを始めます。よろしくお願いいたします。
こんにちは。岡田遥来と申します。LINEでシニアソフトウェアエンジニアをやっていて、全社的に利用されるApache Kafkaプラットフォームの開発・運用を担当しています。
ご存じの方も多いかもしれませんが、Apache Kafkaは分散ストリーミングミドルウェアで、LINEでは最もよく使われるミドルウェアの1つです。高いスケーラビリティを備えているほか、ACLやQuotaといった機能も持っており、マルチテナントな運用が可能であるという特徴が挙げられます。
Kafkaについて簡単に説明します。1つのKafkaクラスターは、通常複数のブローカーと呼ばれるサーバーで構成されます。また、このKafkaにメッセージを送信するクライアントをProducerと呼び、同様にメッセージを受信してなんらかの処理をするクライアントをConsumerと呼びます。
私たちはLINEにおいて、単一のKafkaクラスターを複数のサービスで共有するという、マルチテナントモデルを採用しています。LINEやLINE公式アカウント、LINE広告を始めとしたさまざまなサービスが、私たちの提供するKafkaクラスターのユーザーです。
それぞれのサービスが、それぞれたくさんのProducerサーバー、Consumerサーバーを持っていて、それらに対するメッセージの転送を、私たちのクラスターが担っています。
現在、私たちのクラスターを利用するサービスの数は、100以上にのぼります。また、送信されるメッセージ数はピーク時で1,500万メッセージ毎秒、また1日当たりに流れる総トラフィックの量は1.4ペタバイトで、世界でも有数の規模です。
このような大規模なKafkaクラスターを高いサービスレベルで維持するため、私たちは日々パフォーマンスの計測や最適化、トラブルシューティングに努めています。私たちがこれまで行ってきた施策やトラブルシュートの事例などに興味のある方は、ぜひ2019年のLINE DEVELOPER DAYにおける私たちの発表もご覧ください。
さて、本日お話しするのも、私たちのクラスターにおいて発生した、とある問題についての紹介です。まず、発生した事象の概要についてお話しした後、どのような調査を行ったか、そして最終的にどのように問題を解決したかについて紹介いたします。
ある日のことです。私たちがちょっとしたKafkaに対する設定変更をロールアウトするために、Kafkaブローカーをローリング・リスタートしている最中、あるサービスの開発者から「私たちのサービスでKafkaへのメッセージのProduceが失敗し始めたんだけど、何か問題が起きていますか」との報告を受けました。
開発者から提供してもらったログから、Producerでリクエストがタイムアウトしてしまっているということがわかりました。これは設定されたタイムアウト内に、メッセージをKafkaに送信することができなかったことを意味します。ここで送信できなかったデータは失われてしまうため、これは大変クリティカルな事象です。私たちは、すぐさま調査を開始しました。
まず私たちは、Produceリクエストに対するブローカーのレスポンスタイムの上昇、これは例えばレプリケーションが遅延してしまっているなど、そういった理由で起こり得るため、これを疑ったんですが、ブローカーの示すメトリクスは、至って正常なままでした。
次に、なんらかの理由でProduceリクエストを継続的にリジェクトし続けてしまっている、例えばこれはレプリカがもう複数死んでしまっているような状況など、そういうのがあり得るのですが、これを疑いました。
しかしこれも、サーバー側でも特にエラーは出ていなかったし、Producer側でもリクエストタイムアウト以外のログは確認できず、該当しませんでした。
さらに調査を続けていくと、非常に奇妙な現象を観測しました。ブローカー側で計測しているレスポンスタイムは、先ほど見たとおり、いたって正常だったにもかかわらず、Producer側で計測されたリクエストレイテンシーは16秒以上といった、極めて異常な数値となっていました。
レイテンシーがこのレベルまで悪化しているとすると、先ほどリクエストタイムアウト起こっていましたが、これも自然に起こることが考えられます。
さらに奇妙な現象として、このような異常なレイテンシーの上昇が、Producerから特定のブローカーに対してProduceする際にのみ起こっていることもわかりました。そしてこのレイテンシーの悪化ですが、これはその特定のブローカーを、私たちがリスタートした直後から発生し始めたということもわかりました。
まとめると、私たちがそのあるブローカーをリスタートした直後から、Produceリクエストレイテンシーが顕著に悪化し始めた。しかしそのブローカーサイドのレスポンスタイムのメトリクスは、いたって正常なままという状況です。
この時点では、根本原因はまったく不明でしたが、私たちは一刻も早く、今まさに起こってしまっているデータロストに対処する必要がありました。
本来であれば、こういった現象に対しては、十分な調査の時間を取りたいところではありましたが、とりあえず問題のブローカーをもう1度リスタートしてみることにしました。そうすると、実際に現象がピタリとおさまってしまいました。
さて、ひとまず状況は落ち着いたものの、再発を防ぐためには根本原因を突き止めなければなりません。まずはProducerとブローカー双方におけるレスポンスタイムのメトリクスが非常に大きなギャップを示していたということ、これがなぜ起こっているのか、理解する必要がありました。
Kafkaのコードを確認してみたところ、リクエストの送信自体に時間がかかると、こういったギャップが起こり得るという仮説が得られました。
どういうこというかといいますと、まずこの図のとおり、リクエストの処理時間が、どのように計測されているのかを考えてみます。
Producer側ではリクエストを生成して、それをブローカーに送信し始めて、それがブローカー側に届いて、ブローカーで処理をされて、かつ、ブローカーがレスポンスをクライアントに送り返して、そのレスポンスを完全に受信しきったところまで、というのがレイテンシーとしてカウントされます。
一方、ブローカー側のレスポンスタイムメトリクスというのは、リクエストを受信しきったところから、計測が始まります。したがって、リクエストの送信自体がなんらかの理由で遅延してしまうと、ブローカーのメトリクスにはそれが現れないということになります。
このようなリクエスト送信自体の遅延、これを引き起こす原因として、まず私たちはProducerのSenderスレッドという、ノンブロッキングI/Oを行っているスレッドがあり、これがなんらかの理由でスタックしてしまっていた、という仮説を立てました。
この仮説を検証するためには、Producer側のデバックログや、ProducerはJavaで動いていますので、JVMのasync-profilerのプロファイルといった、クライアントサイドの詳細な情報が必要不可欠となります。
そこで私たちは、現象が発生したサービスの開発者に協力を要請し、本番環境で再現実験を試みることにしました。
具体的には、まずプロファイルを取る準備をしたうえで、再現してしまった際に素早く対応できるようにモニタリングをしておきます。そして問題が起こった同じブローカーをもう1度、もしくは再現するまで何度かリスタートしてみる、という実験です。
さて、実験を行ってみたものの、何度かブローカーをリスタートしてみたんですが、結局現象を再現することはできず、失敗に終わりました。
現在のところ、得られている材料が少ないうえに、このように再現性も低く、Producerがなんらかの理由でスタックしていたかどうか、検証するのは非常に難しいということが判明しました。
次に、もう1つリクエスト送信自体の遅延を引き起こし得る原因として、問題のあったProducerとブローカーという、特定のネットワークの経路において、ネットワークのパフォーマンスが悪化してしまっていたのでは、と仮説を立てました。
こちらについても、簡単にpingでレイテンシーを計測してみたんですが、こちらは至って正常でした。また、他にTCPのリトランスミッションが増えていたり、もしくは帯域がサチュレーションしてしまっていたりといった、ネットワークレベルの遅延を引き起こし得るような他の現象も観測されませんでした。
さてこのように、調査は非常に難航しました。そうこうしているうちに、残念ながら今度は前回最初に起きた時とは異なるサービス、また異なるブローカーにおいて、まったく同じ問題が再発してしまいました。
観測された現象は初回とまったく同じで、その問題が起きたブローカーをリスタートした直後から、Producer側で急激にリクエストが遅延し始める。しかしブローカー側で取っているレスポンスタイムのメトリクスおよび他のパフォーマンスのメトリクスは至って正常なまま。また、そのブローカーを再度リスタートすることで直ってしまう、というものでした。
さて、複数回の発生事例を得たことから、私たちはこれらの事例に共通するような、リクエスト送信の遅延に関連し得るなんらかのパターンを見つけられないか、と探ってみることにしました。
数多くあるメトリクスを注意深く一つひとつ観測していくと、ブローカーをリスタートした際に、nodeエクスポーターのSyncookiesSentというメトリクスがあるんですが、これが問題が発生する場合のみ、顕著なスパイクを示しているということがわかりました。
このSyncookiesSentメトリクスの上昇というのは、何を意味しているのでしょうか。これについて説明したいのですが、まずこのためにTCP SYN Cookiesというものについて簡単に紹介します。
TCP SYN Cookiesというのは、いわゆるSYN flood攻撃に対処するためのメカニズムです。まずTCPの3-way handshakeについて、簡単に復習します。
TCPでは、まずクライアントがコネクションを開きたい時、サーバーに対してSYNパケットを送信します。サーバーはそれをSYN Queueに追加した後、SYN ACKパケットをクライアントに対して返信します。最後にクライアントはそれを受けてACKをサーバーに送り返します。そしてそれを受けてサーバーはSYN Queueに取っておいたSYNエントリーを取り出して、コネクションを確立し、handshakeが完了となります。
SYN flood攻撃では、まず悪意のあるクライアントが、大量のSYNパケットをサーバーに対して送信します。ここでhandshakeの最後のステップとして、本来SYN ACKが送り返しされてきて、それに対してクライアントがACKをサーバーに送り返す必要があるんですが、これを悪意のあるクライアントは送らずに、サーバーからのSYN ACKを無視します。
こうなると、サーバーはずっと完了しないSYNをSYN Queueに放置し続けることになり、いずれSYN Queueがいっぱいになります。そして、悪意のない通常のクライアントからの接続も受け付けられなくなってしまうという、1種のDoS攻撃です。
TCP SYN Cookiesというのは、このようにSYN floodが起きてしまっている状態においても、クライアントからの接続を受け付けられるようにするための仕組みです。
ここではLinuxカーネルを前提として話を進めますが、このようにSYN floodが起きてSYN Queueがいっぱいになってしまうと、LinuxカーネルはSYNパケットをSYN Queueに格納する代わりに、SYNに含まれている情報をうまいことinitial sequence numberにエンコードして、SYN ACKパケットに埋め込んでクライアントに送り返します。そして、もうSYN Queueには入れないで、Reply and forgetしてしまいます。
そして、後でクライアントがSYN ACKを受けてACKを送り返してきた時に、sequence number、そこにSYN Cookiesがエンコードされているので、これをデコードして、もともと持っていたSYNの情報を取り出して、コネクションを確立して、handshakeを完了します。
SYN CookiesSentメトリクスのスパイクは、クライアントからSYNを受け取ったが、SYN Queueがいっぱいだったので、代わりにこのような、今見てきたフローのとおりSYN Cookiesを送って、通常とは異なる方法でhandshakeを行った数を示します。
さて、SYN CookiesSentメトリクスがスパイクしていたということは、つまりブローカーがSYN flood状態にあったということです。なぜSYN floodが起こってしまったのかを考えます。
まず問題が起こったブローカーを含めて、私たちのクラスターのブローカーは、たくさんのProducerやConsumerといったクライアントからの接続を受けています。
ここでブローカーをリスタートした時に何が起こるかというと、まずこのブローカーに接続していたクライアントのコネクションは、すべて1度他のブローカーにフェイルオーバーします。
そしてブローカーのリスタートが完了して戻ってくると、もともと接続していたクライアントたちが一気に元のブローカーに再接続を行います。これが実質的に攻撃のような状況を引き起こして、ブローカーがSYN flood状態になってしまったというわけです。
問題が起こってしまったブローカーは、18,000を超えるクライアントからのコネクションを持っていたため、こういったことが起こり得ることは、自然に考えられました。
(後半につづく)
LINE株式会社
2024.12.20
日本の約10倍がん患者が殺到し、病院はキャパオーバー ジャパンハートが描く医療の未来と、カンボジアに新病院を作る理由
2024.12.19
12万通りの「資格の組み合わせ」の中で厳選された60の項目 532の資格を持つ林雄次氏の新刊『資格のかけ算』の見所
2024.12.16
32歳で成績最下位から1年でトップ営業になれた理由 売るテクニックよりも大事な「あり方」
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
PR | 2024.12.20
モンスター化したExcelが、ある日突然崩壊 昭和のガス工事会社を生まれ変わらせた、起死回生のノーコード活用術
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2024.12.18
「社長以外みんな儲かる給与設計」にした理由 経営者たちが語る、優秀な人材集め・会社を発展させるためのヒント
2024.12.17
面接で「後輩を指導できなさそう」と思われる人の伝え方 歳を重ねるほど重視される経験の「ノウハウ化」
2024.12.13
ファシリテーターは「しゃべらないほうがいい」理由 入山章栄氏が語る、心理的安全性の高い場を作るポイント
2024.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
Climbers Startup JAPAN EXPO 2024 - 秋 -
2024.11.20 - 2024.11.21
『主体的なキャリア形成』を考える~資格のかけ算について〜
2024.12.07 - 2024.12.07
Startup CTO of the year 2024
2024.11.19 - 2024.11.19
社員の力を引き出す経営戦略〜ひとり一人が自ら成長する組織づくり〜
2024.11.20 - 2024.11.20
「確率思考」で未来を見通す 事業を成功に導く意思決定 ~エビデンス・ベースド・マーケティング思考の調査分析で事業に有効な予測手法とは~
2024.11.05 - 2024.11.05