Kubernetes 上のストレージをエンド・ツー・エンドに監視する – Cybozu Inside Out

この記事は、CYBOZU SUMMER BLOG FES ’25 の記事です。

こんにちは、クラウド基盤本部の伴野です。クラウド基盤本部では cybozu.com を稼働させるため、Neco と呼ばれる Kubernetes クラスタを当社で調達した機材上に構築しています。この記事では、そんなクラスタ上のストレージが正しく動作していることを確認するために開発した pie というソフトウェアについて紹介します。

背景

私はクラウド基盤本部の中でも CSA(Cloud Storage Agency)というチームに属しています。CSA は「cybozu.com にスケーラブルで信頼性のあるストレージを提供する」というミッションのもと、Neco 上のストレージに関わる開発や運用を担当するチームです。具体的には、Rook/Ceph や TopoLVM といったソフトウェアを Neco 上で運用し、cybozu.com に必要なストレージを提供しています。ストレージシステムの詳細に関しては、直近に筆者が書いた別の記事がありますので、そちらをご覧ください:

blog.cybozu.io

さて CSA が提供しているストレージは様々な原因で故障することが想定されます。例えばディスクやノードの故障といったハードウェア的な不具合はもちろん、ソフトウェアのバグによって使用できなくなることもあり得ます。CSA は、ストレージを利用してアプリケーションを運用するチームに対して、ひいてはお客様に対して、ストレージが(一定の範囲で)いつでも利用可能であることを約束しているため、このような不具合が発生した場合は速やかにその原因を特定し、適切に復旧しなければなりません。そのために CSA では、ストレージに関するメトリクスを監視するアラートルールを設定し、不具合に迅速に対応できるようにしています。

ただストレージを運用していく中で、大きく分けて二つの点から、既存のメトリクスだけでは不十分な面があることが分かってきました。まず第一に、Kubernetes におけるストレージの取り扱いは複雑で、多くの要素から構成されるという点です。その結果、ストレージを構成する各コンポーネントからメトリクスは出力されるものの、それらをバラバラに監視するだけではストレージ全体として正しく動いていることを保証することが難しくなっています。

理由の二つ目は、メトリクスの多くは、実際にストレージが使用されなければ出力が行われないという点です。例えばストレージから新規ボリュームを払い出す作業が行われる頻度は(ストレージの種類によっては)比較的低いため、その部分に障害が発生していてもすぐには気付けない場合があります。その結果、ストレージを使うアプリケーションがボリュームを払い出そうとしたタイミングで初めて障害に気づくことがあり得ます。しかしこれでは手遅れです。

以上の問題を解決するために、CSA では自ら開発した pie というソフトウェアを用いてストレージの監視を行っています。pie は OSS として公開されており、誰でも使用することができます。

github.com

pie は、ストレージが実際に動作していることをエンド・ツー・エンドに確認します。これにより、CSA が提供するストレージがアプリケーションから見て実効的に使用可能であることを保証しています。

仕組み

pie は Kubernetes のカスタムコントローラとして動作します。pie を利用するユーザー(ストレージの管理者が想定されます)は、まず監視したいストレージに関する情報を PieProbe リソースとして記述します。このリソースには、監視を行いたい StorageClass と監視を行うノードを指定します。このリソースを作ることで、pie は指定されたノード上で、指定された StorageClass から払い出されるボリュームが正常に動作しているかを監視することになります。

apiVersion: pie.topolvm.io/v1alpha1
kind: PieProbe
metadata:
  name: test-pie-probe
  namespace: pie
spec:
 
  monitoringStorageClass: topolvm-provisioner

 
  nodeSelector:
    nodeSelectorTerms:
    - matchExpressions:
      - key: node-role.kubernetes.io/role
        operator: In
        values:
        - compute

もう少し具体的に挙動を見てみます。pie コントローラは PieProbe リソースを見つけると、定期的にストレージの動作確認を行うための CronJob を二種類作成します。

pie の動作の概略

ひとつは provision probe と呼ばれる Pod を定期的に作成するための CronJob です(図中右)。この CronJob では、監視対象の StorageClass を用いた Generic Ephemeral Volume を使う Pod を、監視対象のノード上で定期的に立ち上げます。これによって、新規ボリュームの払い出しに問題がないかを確認します。払い出しに時間がかかり過ぎていたり、そもそも払い出しが行えないような場合は不具合とみなし、メトリクスに出力します。

もう一つの CronJob は mount probe と呼ばれる Pod を定期的に作るためのもので、PieProbe が対象とするノード一台につき一つ作られます(図中左・中央)。この CronJob は、予め作成しておいた PV(C) を volumeMounts に持つような Pod を定期的に作ります。これによって、マウントの処理に問題がないかを確認します。その後 fio を用いて IO が正しく動作することも検証します。マウントの処理に時間がかかり過ぎている場合や、IO に失敗している場合は不具合とみなし、メトリクスとして出力します。

このように二種類の CronJob や Pod を使い分けているのは、それぞれで監視する観点とその実装が異なっているからです。provision probe の CronJob は一つの PieProbe に対して一つだけ作成され、監視するノードのどこかで Pod が立ち上がれば正常とみなす作りになっています。これは、cybozu.com のアプリケーションが新規にボリュームを必要とする場合、スケジューリング制約を満たすどこか一つのノードでそのボリュームが使用できればよく、仮に幾つかのノードで新規ボリュームが払い出せないような状況でも、その他の正常なノードが使えれば十分なことがほとんどであるためです。

一方で、既に払い出されたボリュームのマウントは、そのボリュームが割り当てられたノード(またはスケジューリング制約を満たすノード)上では常に成功しなければなりません。そこで mount probe の CronJob は、監視する各ノードを nodeAffinity に持つ形でノードの個数分作成され、担当するノード上でマウントを実行するように実装されています。

上記のような pie の動作は、Neco が提供しているストレージをエンド・ツー・エンドにテストしています。つまり、ストレージを構成する各コンポーネントを独立にテストするのではなく、ボリュームの払い出しからマウントまでを一気にテストします。これにより、ストレージを構成する各コンポーネントのメトリクスが特に異常を発していない場合でも、pie がアラートを出していれば、ストレージに関して何かしらの障害が発生していると想定して障害対応に当たれます。また pie は、アプリケーションが Neco 上のストレージを使用する際の処理そのものを監視します。そのため、いざアプリケーションがストレージを使おうとすると CSA が把握していない問題によって使用できない、といったケースを未然に防げます。

運用

pie は元々 2022 年のインターンにおいて開発がスタートしました。

blog.cybozu.io

インターン終了後に CSA では pie の運用を始め、現在では開発・ステージング・本番環境の全てで pie が導入されています。

以下では pie の実際の運用中に見つかった問題とその対応について紹介します。pie の本懐とも言えるストレージシステムに関する不具合の検知事例や、実際には障害が起こっていないにも関わらず provision probe・mount probe が失敗してしまうという誤検知への対応、さらに pie 開発当初には想定していなかったような、クラスタ全体に関わる不具合の発見についても紹介します。

TopoLVM の障害の検知

CSA では、自社の OSS である TopoLVM の開発・運用を行っています。TopoLVM は LVM を活用した CSI driver で、動的に LVM の LV(Logical Volume)を Kubernetes の PV(PersistentVolume)リソースとして切り出し、アプリケーションに提供できます。

ある時、そんな TopoLVM の新しいバージョンをリリースし開発環境に適用したところ、pie によるアラートが発報しました。確認したところ、多数のノード上で provision probe と mount probe が共に失敗していることが確認できました。一方で、TopoLVM から払い出された既存の PV を利用中のアプリケーションは正常に動作し続けていました。

そこで障害の原因を確認したところ、TopoLVM を構成する一要素である topolvm-node DaemonSet が作る Pod が、多くのノードでクラッシュして停止していることが分かりました。topolvm-node は TopoLVM が PV を払い出したりマウントしたりする際には必要な一方で、すでにマウントされている PV には(基本的には)介入しません。そのため、TopoLVM を使用しているソフトウェアであってもいきなりボリュームが使えなくなるといったことはなく、表面上は正常に動作していました。一方で、pie が作る provision probe や mount probe はボリュームの操作を行うため topolvm-node を必要とします。そのため、異常に気づくことができました。

その後詳細な調査を行った結果、topolvm-node のサイドカーコンテナの一つを更新した際に混入したバグが、Pod が停止した根本的な原因であることが分かりました。このサイドカーコンテナは Kubernetes コミュニティによって OSS として管理されているものであったため、CSA から upstream へこのバグを報告し、その後 upstream によって修正が行われました。

github.com

ノードの再起動による誤検知

pie によって正しく検知される障害がある一方で、障害を誤検知してしまうこともあります。運用中に特に問題になるのは、ノードの再起動中に provision probe・mount probe が動作するケースです。Neco ではノードの全台再起動を定期的に行なっているため、ノードが再起動している時にこれらの probe がそのノード上で立ちあがろうとする場合があります。当然、ノードの再起動中は Pod の立ち上げは行えないため、probe は失敗します。その結果、実際にはダウンしていないストレージを誤ってダウンしているとみなしてしまいます。

他にも、ノードに taint がついて pie の Pod がスケジュールできなくなることもあります。特に、ノードがクラスタに追加されたごく初期の場合や、逆にクラスタからノードが外されようとしている場合には CiliumCKE(Cybozu Kubernetes Engine)によって taint がつけられるケースがあります。このような場合も、そのノードで probe が動作することができず、同じような誤検知の問題を引き起こしてしまいます。

現状、これらの誤検知を pie 側で防ぐことは難しいため、代わりにアラートの発報条件を指定する PromQL の式を工夫することで対処しています。具体的には以下のような式を用いて、pie の監視対象とすべきノードを抽出しています:

# ノードの status が Ready=True のものであって、かつ
max by (node) (kube_node_status_condition{condition="Ready", status="true"} == 1)
  unless on (node)
# ノードが unschedulable でなく、かつ
max by (node) (kube_node_spec_unschedulable == 1)
  unless on (node)
# CKE によってノードの drain や再起動が行われておらず、かつ
max by (node) (cke_node_reboot_status{status=~"draining|rebooting"} == 1)
  unless on (node)
# CKE によってクラスタから外されたノードではなく、かつ
max by (node) (kube_node_spec_taint{key="cke.cybozu.com/state"} == 1)
  unless on (node)
# Cilium の taint がついていない。
max by (node) (kube_node_spec_taint{key="node.cilium.io/agent-not-ready"} == 1)

通信のレートリミット

pie は多数のノードで定期的にワークロードを実行します。この pie の動作そのものによって、ストレージと直接は関係のない、クラスタ全体に関わる問題を引き当てることもあります。特によくあるのは、ソフトウェアが行う通信に設定されているレートリミットに引っかかるケースです。以下では代表的な例を一つ紹介します。

Neco で pie による監視を始めた当初、PVC を使う Pod を立ち上げようとすると pie 以外も含め全て Pending となってしまい、なかなか立ち上がらないという現象に遭遇したことがありました。調査したところ、pie が作成する CronJob が頻繁にボリュームの操作を行なった結果、kube-controller-manager から kube-apiserver への通信がレートリミットに引っかかってしまっていることが分かりました。

この件に関しては、過去に以下のスライドでも紹介しています。P.35 の「他のコントローラー」(左下)がまさに pie でした。

speakerdeck.com

スライドにもあるように、この際は対策として kube-controller-manager の --kube-api-qps オプションの値を増加させました。これによりレートリミットが緩和され、pie も正常に動作するようになりました。

このケースのようにレートリミットを緩和できる場合は良いのですが、様々な要因からレートリミットを変更できないこともあります。そのような場合は pie が作る provision probe・mount probe の作成頻度を落とすことで対応しています。pie では PieProbe リソースの .spec.probePeriod によってこれらの probe が作成される頻度を分単位で指定できます:

apiVersion: pie.topolvm.io/v1alpha1
kind: PieProbe
metadata:
  name: test-pie-probe
  namespace: pie
spec:
  probePeriod: 5
 

まとめ

この記事では、Neco 上のストレージをエンド・ツー・エンドに監視するためのソフトウェアである pie について紹介しました。pie がどのように動作しているかについて説明した上で、実際に pie を運用している際に遭遇した事象について解説しました。

本日 11/5 は CYBOZU SUMMER BLOG FES ’25 の最終日でした。ここまでお読みいただきありがとうございました!




Source link

関連記事

コメント

この記事へのコメントはありません。