NIKKEI TECHNOLOGY AND CAREER

Kubernetes/Istio1.4環境において突如エラーレスポンスが増加した話

SRE チームの山崎です。 日経電子版では、Kubernetes と Istio を利用したアプリケーション基盤を構築しており、その上でサービスを提供しています。 先日、Kubernetes 基盤上のアプリケーションがエラーレスポンスである 503 の HTTP ステータスコードを頻繁に応答してしまう状況が発生し、サービス影響が出てしまいました。 今回はこの事象の原因調査や対応についての記録をまとめました。

概要

今回 Istio v1.4 の不具合を踏みました。 Istio v1.4 は比較的古いバージョンであり、アップデートすることで問題は解消するのですが、簡単にはいきませんでした。 今回は新しい基盤へアプリケーションごと移行することで対応を実施しました。

繰り返しになりますが、本記事では Istio v1.4 における事象について説明しています。 最新のバージョンでは不具合は解消されています。

日経電子版の Kubernetes 基盤について

日経電子版の一部のサービスは Kubernetes 上にデプロイされ、サービスを提供しています。 また、Kubernetes 上でサービスメッシュを実現する Istio[1] を導入しています。 日経電子版ではマイクロサービスアーキテクチャを採用していますが、そのサービスのトラフィックのルーティングやリクエストのロギングなどに Istio の機能を利用しています。 Kubernetes と Istio を利用した本基盤については、以下に詳細な解説があります。

GKE + Istio + GitOps で作る日経電子版新 Microservice 基盤

何が起きたのか

まずはじめに Kubernetes 基盤でどのような問題が発生したのかを振り返ってみます。

突然のエラー応答の増加

2021 年のはじめ、ある日突然サービスのエラーレスポンスの割合が急激に増加しました。 このエラーレスポンスの増加は少しすると解消しました。念のため確認してみると一時的にアクセスがスパイクしており、この時は「サービスのオートスケールが間に合わなかったのかな」くらいに思っていました。

しかし、このあと同様の事象が立て続けに発生するようになります。 「さすがにこれはおかしい」と思い、詳細にログを調査してみると、ユーザのアクセスが増加した際に Kubernetes 基盤上の全てのアプリケーションが、ユーザのリクエストに対して高い割合で 503 Service Unavailable を応答していることがわかりました。 これは異常なことです。

前述の通り、日経電子版はマイクロサービスアーキテクチャを採用しており、機能ごとにアプリケーションが分かれています。 サービスの性質上、記事ページなどはユーザのアクセスが非常に多いアプリケーションですが、一方でアクセスが少ないアプリケーションもあります。 ユーザのアクセスが増えたときに記事ページの負荷が高くなり 503 Service Unavailable が応答されることは想像しやすいのですが、今回はアクセスの少ないアプリケーションも含め Kubernetes 上の全てのアプリケーションで 503 が応答されていました。

アプリケーションごとの503レスポンス数

SRE チームが中心となってこの問題の調査を開始しました。

リリースしたアプリケーションがすべて 503 を応答

SRE が調査を行なっている中で新たな問題が発生してしまいます。

「新しいバージョンをリリースしたら、すべてエラー応答になっている」と日経電子版の開発チームから連絡を受けました。 機能追加のためにいつものようにリリースを実施したところ、ユーザアクセスに対して 503 を返している状況でした。 このとき、すぐにひとつ前のバージョンにロールバックしてもらうことでその場は対応しました。 (余談ですが、リリースフローに GitOps を採用しておりロールバックは GitHub の Pull Request を Revert するだけで自動で安全かつ迅速に完了します。)

今回の問題は、いつも実施している実績のあるリリースフローに従ってリリースを行なっている際に発生しました。 リリースはステージング環境での動作が確認されてから行われるのでアプリケーションの不具合である可能性は低いと考えられ (そもそもアプリケーションエラーであれば 503 ではなく 500 レスポンスになるでしょう)、こちらも SRE による調査が開始されました。

問題の原因

結論から言うと、今回の場合 Istio v1.4 の不具合を踏んでいました。 具体的には以下の GitHub の issue になります。

Kubernetes クラスタ上の Pod 数が多くなると、Istio の mixer というコンポーネントが高負荷となり、その影響で mixer のアプリケーションが再起動してしまうというものです。 mixer が停止してしまうとサービスメッシュを通るユーザのリクエストが 503 となって返ってしまうのです。

Istio v1.4 のアーキテクチャやコンポーネントについて詳しく知りたい方は以下の公式ドキュメントを読むとよいでしょう。 (Istio v1.4 はもはや古いアーキテクチャであることに注意してください。)

https://istio.io/v1.4/docs/ops/deployment/architecture/

それでは、mixer が高負荷となったことで、なぜ問題が発生してしまったのかを見てみましょう。

Istio における通信の概要

Istio mixier の役割は大きく 2 つあります。ポリシーとテレメトリです。

  • ポリシー: サービスメッシュの通信のアクセス制御
  • テレメトリ: ログやメトリクスの収集

今回関係あるのはポリシーの機能です。

Istio 環境における通信の概略を、ユーザが私たちのアプリケーションへアクセスする例で示します。 (今回の問題を説明するために必要最低限の図になっています。実際はもっと複雑です。)

istio-traffic-flow

Kubenetes で Istio を有効にすると、アプリケーションの Pod に Sidecar コンテナとして istio-proxy というコンテナが起動します。 istio-proxy の実体は Envoy Proxy[2] というオープンソースのサービスメッシュソリューションです。 Istio を有効にすると、アプリケーションへの通信は全て Sidecar の istio-proxy を経由するようになります。 通信が istio-proxy を通ることで、柔軟なルーティングやアクセス制御、ロギングといったサービスメッシュの様々な便利な機能が利用できるようになるわけです。 アプリケーションに手を加えることなく、このような付加機能が利用できるので、Istio は魅力的です。

さて、今回問題となったポリシー機能にフォーカスして、ユーザのリクエスト受信の流れを図に戻って見てみましょう。

  1. まず、ユーザのリクエストは、サービスメッシュの入口である istio-ingressgateway Pod (これも実体は Envoy Proxy です) へ到達します
  2. istio-ingressgateway においてリクエストが適切なアプリケーション Pod へルーティングされます。 このとき、アプリケーション Pod の istio-proxy コンテナがリクエストを受け取ります
  3. istio-proxy はポリシー機能により、アプリケーションがこのリクエストが受け入れ可能であるかどうかを istio-mixer へ問い合わせます
  4. istio-mixer はリクエストの認可の可否を istio-proxy へ応答します
  5. 認可されればリクエストはアプリケーションへ転送され、拒否されればアプリケーションへは転送されずに istio-proxy がすぐにレスポンスを返します

このように istio-proxy は、アプリケーションへの通信をインターセプトしてアクセス制御をしています。

istio-mixer 停止による全アプリケーションの 503 応答

すでに多くの方が察している通り、今回この istio-proxy と istio-mixer によるアクセス制御処理において問題が発生していました。 istio-proxy はリクエストのアクセス可否を都度 istio-mixer へ問い合わせます。 この istio-mixer への問い合わせ自体が失敗すると、istio-proxy はリクエストを 503 としてユーザへ応答することがわかりました。

Mixer停止による503レスポンス

先に挙げた Istio v1.4 の不具合によって、Kubernetes クラスタの Pod 数が増加した場合に istio-mixer が再起動を繰り返していました。 これは、実際は OOM により kill されるようです。 通常、istio-mixer も Kubernetes の Deployment および Pod として展開されており、複数の Pod が動作し可用性が保たれています。 しかしながら、今回の不具合では OOM によりすべての istio-mixer Pod が同時に停止、再起動を繰り返してしまっていました。 そうなると、Kubernetes クラスタ上のすべての istio-proxy におけるアクセス制御処理は失敗してしまいます。 最初の問題で特定のアプリケーションではなく全てのアプリケーションが 503 を応答してしまっていたのはこのためだったのです。

ちなみに、istio-proxy のログには upsream_timeout のエラーが記録されていました。 最初は upstream はアプリケーションのことだと思っており、調査が難航しました。。。

Pod の増加による istio-mixer の停止

今回 2 つのパターンで問題の事象が発生しました。

まず最初に、ユーザアクセスがスパイクした際の発生です。 これはユーザアクセスの増加によりサービスの Pod がスケールアウトすることで Pod 数が増えたことによるものと考えられます。

2 つ目はアプリケーションのリリースの際に発生しました。 私たちのリリースフローでは、新しいバージョンの Pod を一式デプロイした後にユーザリクエストのルーティング先を新しいものに切り変えることでアプリケーションのバージョンアップを実施しています。 このとき、一時的に現在のバージョンの Pod と新しいバージョンの Pod の両方が混在するタイミングがあります。 このタイミングでは普段よりもクラスタ上の Pod 数が多くなってしまうため、問題の事象が発生したと考えられます。

問題への対応

問題の原因がわかりました。 しかしながら、アプリケーションのリリースが安全にできない状態であることから、早急に対応を実施する必要がありました。

対応案は以下の 3 つが考えられました。

  1. Istio のバージョンアップの実施
  2. Istio のアクセス制御機能の無効化
  3. 新 Kubernetes 基盤へのアプリケーション移行

Istio のバージョンアップの実施 (断念)

今回の問題は Istio のバージョンは 1.4 で発生しましたが、これはバージョン 1.5 以降では発生しません。 そのため、Istio のバージョンアップを実施することが対応として考えられました。

しかしながら、早急な対応が求められている中で Istio のバージョンアップは非常にリスキーであると判断せざるをえませんでした。 特に、Istio v1.4 から v1.5 では、istiod というコンポーネントの導入により、コントロールプレーンに大幅な変更が入っています。 十分に検証を重ねた上で実施したい所ですが、今回そのような時間はありません。 検証なしに Istio のバージョンを上げることは危険と判断し、この方法は見送りました。

Istio のアクセス制御機能の無効化 (断念)

今回の問題は Istio のアクセス制御処理に不具合がありました。 しかしながら、私たちの Kubernetes クラスタではアクセス制御の機能は利用していなかったので (つまり、アクセス制御処理は実行されていましたが、結果はすべて Allow という状態)、それを無効化することで問題を回避することができると考えられました。

Istio のアクセス制御処理は、ConfigMap を編集することで無効化できそうです。 具体的には istio-system/istio ConfigMap の global.disablePolicyCheckstrue とします。

$ kubectl edit configmap -n istio-system istio
apiVersion: v1
data:
  mesh: |-
    # Set the following variable to true to disable policy checks by Mixer.
    # Note that metrics will still be reported to Mixer.
    disablePolicyChecks: true  # false -> true に変更
    ...

この設定によりistio-proxy が通信のたびに istio-mixer へ問い合わせを行なうことがなくなります。 そのため、今回の問題は発生しなくなるはずでした。

しかし、不幸にもここで別の Istio の不具合を踏んでしまいます。 global.disablePolicyChecks の設定変更が反映されないというものです[3]。 こちらも解消のためには Istio のバージョンアップが必要となることがわかったため、見送ることになります。

新 Kubernetes 基盤へのアプリケーション移行

問題を解消するには Istio のバージョンを上げるほかありませんでしたが、それも簡単ではありません。 最終的に私たちが取った対応は、新しいバージョンの Istio がインストールされた新 Kubernetes 基盤へアプリケーションを移行することでした。

ここまで一切触れていませんでしたが、これまで話してきた Kubernetes 基盤とは別の新しい Kubernetes 基盤を SRE で構築しています。便宜上、それぞれを現行基盤、新基盤と呼びます。 ちょうど現行基盤でこの問題が発生する 1 ヶ月ほど前に SRE として新基盤をリリースしていました。

新基盤開発の目的はいくつかありますが、その 1 つに「Kubernetes のアップグレードを安全にかつ簡単に行なえるようにすること」が挙げられます。 そしてこれは、まさに今回感じた「Istio のバージョンアップの敷居を下げる」こともスコープに含まれていて、バージョンアップに悩まされることは無くなります。 このように新基盤は、現行基盤の運用上の課題を解決するために構築されたものです。 新基盤については別の機会にご紹介できればと思います。

新基盤と現行基盤は基本的なコンセプトは同じです。 サービスのリリースフローには GitOps を取り入れ、Argo CD[4] を採用しています。 Argo CD を利用することで Git レポジトリ上の Kubernetes マニフェストファイルを自動でクラスタに同期できます。 そのため、日々の開発環境デプロイやリリース作業は GitHub 上での操作で完結し、人為的なミスが混入するリスクを極めて少なくできます。

現行基盤上のアプリケーションを新基盤へ移行する場合は、新基盤の Argo CD がアプリケーションのレポジトリを同期対象として見てくれるように変更すればよいわけです。 そうすると後は Argo CD が自動で現行基盤と同じ構成のアプリケーションを新基盤にデプロイしてくれます。 SRE が共通化された CI コマンドを提供しているため、この変更は基本的にアプリケーションのレポジトリに CI 設定を 1 つ追加するだけで済みます。 そのため、今回、現行基盤上で動作するアプリケーションを新基盤に移行することは容易に完了できました。

Argo CDによる自動デプロイ

新基盤で問題が発生しないことの確認や負荷試験なども含め、2 日ほどで移行は完了しました。 移行後は問題が発生することなく安定してサービスを提供しています。 今回、 Kubernetes によるポータビリティの高さや GitOps による恩恵をとても実感しました。

振りかえり

SRE の一環として、障害時はポストモーテム (振りかえり) の実施を推進しています。 今回の問題について、簡単に振りかえってみましょう。

うまくいったこと

一部アプリケーションをすでに現行基盤から新基盤へ移行を進めていたこともあり、今回のアプリケーション移行をスムーズに実施することができ、問題を解消することができました。

また、多少話が逸れますが、日経電子版では CDN を積極的に活用したサービス設計をしています。 今回の問題で比較的多くのリクエストがオリジンでエラーとなっていました。 しかしながら、CDN におけるキャッシュが多く効いており、ユーザ影響は最小限に抑えることができていました。

うまくいかなかったこと

根本原因を特定するのに苦労しました。 当初 Istio の問題だろうと当たりを付けたものの、すべての日経電子版マイクロサービスで 503 が発生していたことから、クラスタへの入口である istio-ingressgateway というコンポーネントの性能が足りていないことが原因ではないかと推測していました。 そのため、istio-ingressgateway の Pod 数を増やすなどの試行錯誤を行なっていました。 しかしながら、Pod を増やすということは今回の問題をさらに深刻化させてしまいます。 実際のところはわかりませんが、解決のための試行錯誤において、意図せず問題を悪化させていたのかもしれません。

幸運だったこと

新基盤が少し前にリリースされていたことは幸運でした。 新基盤があったため、問題のある現行基盤をからアプリケーションを移行させるという、ある意味思い切った対応ができました。 もし、新基盤が存在しなかった場合、対応は困難を極めたことと思います。

教訓

isito への理解が不足している面があり、Istio について再入門する気運が高まりました。 SRE で Istio の勉強会なども開催します。

まとめ

運用中の Kubernetes/Istio クラスタにおいて突然 503 エラーレスポンスが増加するという事象に遭遇し、調査と対応を実施しました。 原因は Istio v1.4 における不具合であることを突き止めました。 現在の Istio と比べ、Istio v1.4 の頃は不具合も多く運用にはそれなりの覚悟が必要です。 特に、Istio はクラスタ上の通信を司るコンポーネントであることから、アップグレードを実施することは容易ではないでしょう。 今回は、不具合が修正されている新しいバージョンの Istio が動作する新 Kubernetes 基盤へアプリケーションを移行することで対応することができました。

日経では今回経験したような Kubernetes クラスタ運用のつらみを解消するための新 Kubernetes 基盤を構築しています。 現在 SRE は、新基盤の運用はもちろん、現行基盤に残っているアプリケーションを新基盤へ移行するための開発チームのサポートも行なっています。 新基盤についても別の機会にご紹介したいと思います。

ただいま日経では SRE を絶賛募集しております。 SRE 経験のある方はもちろん、今回の記事を読んでいただいて興味を持たれた方など、カジュアル面談も実施しておりますので、ぜひお気軽にご連絡いただけたらと思います。

[1] https://istio.io/
[2] https://www.envoyproxy.io
[3] https://github.com/istio/istio/issues/20456
[4] https://argo-cd.readthedocs.io/en/stable/

山崎一樹
ENGINEER山崎一樹

Entry

各種エントリーはこちらから

キャリア採用
Entry
新卒採用
Entry
短期インターン
Entry