15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

俺たちは絶対にKubernetes上の分析ログを欠損させたくないんだっ...!!その対策としてのfluent-pvc-operatorを紹介🐥

Last updated at Posted at 2021-12-27

この記事はZOZO Advent Calendar 2021に出したかったけど既に枠が埋まっていたので野良で出した記事です。

はじめに

こんにちは。絶対にKubernetes上の分析ログを欠損させたくない@civitaspoです。今年は社内のログ収集基盤構築プロジェクトでfluent-pvc-operatorというKubernetes Operatorを作りました。この記事ではfluent-pvc-operatorが解決する課題、及び機能や使い方について紹介したいと思います。

なお、この記事ではfluent-pvc-operatorの説明に集中するため、ログ収集基盤構築プロジェクトの全体像等には触れません。プロジェクトの全体像や詳細に関しては弊社@shiozakiの資料をご参照ください。

また、Pod内のアプリケーションが出力する分析ログはSidecar Containerfluentdが回収しCloud Pub/Subへ送信する設計1としています。これから説明するfluent-pvc-operatorは、このPodからCloud Pub/Subへログを送信するログ収集フローの中でログ欠損を起こさないことを目的として設計・開発されました。そのため、fluent-pvc-operatorの機能を説明する時に度々このログ収集フローに言及します。このフローを念頭に置きつつ記事を読み進めて頂くと理解が進みやすいと思います。

fluent-pvc-operator とは

fluent-pvc-operatorはPodに対してPVC(Persistent Volume Claim)を動的にプロビジョニングするKubernetes Operatorです。このKubernetes Operatorを導入することで事前にPVCを発行することなく、Podに対して使い捨てのPVCをアタッチすることができます。ちなみに「fluent」という名称が付いているのは、当初fluentdに強く依存したPVC管理を実現するKubernetes Operatorとして設計されていたためです。実装を洗練するにつれfluentd関連の依存を完全に切り離すことができたので、現在は単に「なめらかな」PVC管理を実現するという意味しか持っていません2

fluent-pvc-operatorが解決する課題

fluent-pvc-operatorを導入することで解決したい課題は以下の通りです。

一つ一つ説明していきます。

データをPVへ永続化することでNodeの突然死からPod内のデータを保護可能にする

Kubernetesを運用していると予期しない理由でNodeが終了してしまうことがあります。こういったケースでは多くの場合Podは正しい終了処理を行う時間なく終了させられてしまいます。Podが正しい終了処理を行えない場合、処理内容によってはPod内に保持している一部のデータが欠損してしまう可能性があります。我々のログ収集フローもPodが出力したログは一定程度バッファリングしてCloud Pub/Subへ送信するため、Podが正しい終了処理を経ずに終了することでバッファリングしていたログが欠損することになります。

fluent-pvc-operatorを導入するとPod内のデータをPVへ永続化できるため、この課題は大きく軽減されます。我々のログ収集フローではアプリケーションがログを出力した時点で必ずPVへ永続化することでログ欠損の可能性をほぼ無くしています3。Sidecar ContainerのfluentdはPVに永続化されたログを回収してCloud Pub/Subへ送信しますが、Nodeが突然終了しても別のPodがそのPVを使用してログ回収を再開することで欠損無くログを回収することができます。

ファイルシステムに依存した終了処理を持つPodのLifecycleから終了処理を分離し、別のPodにその終了処理を委譲可能にする

我々のログ収集フローでは送信先のCloud Pub/Subが障害に陥ると障害復旧してログが送信完了するまでfluentdがログをバッファリングし続けることになります。このような状態で新規Podをデプロイすることを考えると、たとえば既存Pod終了時に全てのログをCloud Pub/Subへ送信しきらなければログが欠損するような仕組みであった場合は、Cloud Pub/Sub障害復旧まで旧Podの削除ができず新規Podがデプロイ出来ないことになります。

fluent-pvc-operatorはこういったケースに対応するため、発行したPVC毎に終了処理を行うPodをデプロイ出来る機能を持っています。この機能を利用することで我々のログ収集フローでは送信先の状態にかかわらず新規Podのデプロイが可能になっています。送信先のCloud Pub/Subが障害に陥っている状態で新規Podをデプロイする場合は、バッファリングしているログをPVへ永続化しPodを即終了します。そして、そのPVに永続化されたログに対して終了処理を行うPodがデプロイされ、Cloud Pub/Subの障害復旧まで送信のリトライを続けます。

このようにfluent-pvc-operatorを導入することで、アタッチされたPVCを利用してPodの終了処理を別のPodに委譲することができます。

DeploymentやDaemonSetなどPod Templateから生成されるPodに対してもAccess ModeがRWOまたはRWOPであるPVCを利用可能にする

ログ収集時の永続化層としてPVを使用するため、NFS(Network File System)を実体とするようなAccess ModeがRWX(ReadWriteMany)であるPVの利用は想定していませんでした4AWS(Amazon Web Services)EBS(Amazon Elastic Block Store)GCP(Google Cloud Platform)PD(Persistent Disk)といったブロックストレージをPVとして利用する場合はAccess ModeがRWOまたはRWOPとなります。つまり、PVCはPodに対して1対1となるようにしかアタッチできません。1つのPVCを複数のPodで共有することはできません。このとき、DeploymentやDaemonSetといったPod Templateを利用してPodをデプロイするリソースで問題が出ます。それは定義したPVCが最初にデプロイされたPodでしか利用できず、2つ目以降のPodを立ち上げられなくなる問題です。StatefulSetには.spec.volumeClaimTemplatesを使ってPVCを動的に発行する機能が備わっていますが、DeploymentやDaemonSetにはそれに相当する機能はありません。つまり、DeploymentやDaemonSetではPVCを実質利用することができません。

DeploymentやDaemonSetで利用できないとなると、既存の多くのワークロードをStatefulSetに置き換える必要があり現実的な選択肢ではありませんでした。またKubernetesクラスタ運用者として、ログ収集に関する設定を意識しながらクラスタ利用者がManifestを書かなければならない状態は避ける必要がありました5

fluent-pvc-operatorはこの問題を解決するため、PVCを定義するためのテンプレートを別のリソースとして事前定義しておき、PodがデプロイされるタイミングでテンプレートからPVC発行してPodのManifestに注入する機能を持っています。ログ収集に関する詳細な設定はKubernetesクラスタ運用者が定義しておき、クラスタ利用者はその定義を利用するためにアノテーションを1行追加するだけで済むような仕組みにしています。

fluent-pvc-operatorが持つ具体的な機能

ここまでの内容でfluent-pvc-operatorの持つ機能はある程度伝わっていると思いますが、具体的にfluent-pvc-operatorが持つ機能を一つ一つ説明していきます。

image.png

Dynamic PVC Provisioning

Podが作成されるタイミングでPVCを動的に作成し、PodのManifestへPVCがアタッチされるような設定を注入しますAdmission WebhookというKubernetesの機能を使って実現しています。

Sidecar Container Injection

Podが作成されるタイミングでManifestへ事前に定義されたSidecar Containerの設定を注入します。この機能もAdmission Webhookを使って実現しています。

Unhealthy Pod Auto Deletion

Pod起動中にSidecar Containerが再起動を繰り返すなど異常状態を検知したらそのPodの終了処理を行います

PVC Auto Finalization

Podが削除されたあと、アタッチされていたPVCの終了処理を行うJobをデプロイしますJob
が成功ステータスになるとPVCは自動で削除されます。

Sidecar Container Auto Termination

JobでfluentdのようなデーモンプロセスをSidecar Containerとして利用する場合に使用する機能です。主となるContainerの終了を検知したらSidecar Containerは終了して良いと見なし終了処理を行います。この機能6はまだ実装されておらず将来実装予定です。

使い方

fluent-pvc-operatorによって解決する課題や機能を説明したので、最後にfluent-pvc-operatorをデプロイして使う方法について説明します。

CRD(Custom Resource Definition)Custom Controllerをデプロイする

fleunt-pvc-operatorで使用するCRDとCustom Controllerをデプロイします。必要なマニフェストを1つにまとめたファイルを用意しているのでこのファイルを使ってデプロイをしてください。Admission Webhookを定義するため事前にCert Managerがデプロイされている必要があります。

$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.yaml
$ kubectl apply -f ./deploy/fluent-pvc-operator-aio.yaml

fluent-pvc-operatorは2つのCRDを定義します。

定義したCRDのうちFluentPVCBindingはfluent-pvc-operatorが内部的に使用するものです。利用者がCR(Custom Resource)として定義することはありません。FluentPVCBindingは動的に生成されたPVCとPodや終了処理を行うJobを内部的に関連付けるために使用されています。

FluentPVCを定義する

利用者がCRとして定義する必要のあるFluentPVCは以下の設定項目を持っています。PVCやPodに注入するSidecar Containerの定義、終了処理を行うJobの定義などです。

名前 必須? デフォルト値 説明
pvcSpecTemplate PersistentVolumeClaimSpec true 発行するPVCのテンプレート
pvcFinalizerJobSpecTemplate JobSpec true PVCの終了処理を行うJobのテンプレート
pvcVolumeName string true PodにVolumeとして定義を注入するときの名称。この値はRFC 1123に定義されたDNSラベル名の仕様を満たした上でPod内でユニークである必要があります。
pvcVolumeMountPath string true Pod内のコンテナにマウントするときのPath。':'が含まれてはいけません。
sidecarContainerTemplate Container true Podに注入するSidecar Containerのテンプレート
commonEnvs []EnvVar false [] Pod内の全てのコンテナに注入される共通の環境変数
commonVolumes []Volume false [] デプロイされるPodや終了処理を行うJobに注入される共通のVolume定義
commonVolumeMounts []VolumeMount false [] Pod内のコンテナに共通で注入されるVolumeMount定義
deletePodIfSidecarContainerTerminationDetected boolean false true Sidecar Containerの異常を検知したときにPodを終了するか否か
設定例

簡略化のためspec.pvcFinalizerJobSpecTemplate.template.spec.containersspec.sidecarContainerTemplateの定義は単に文字列をechoするだけにしています。より詳細な定義は後述のに含まれるManifestを参照してください。

apiVersion: fluent-pvc-operator.tech.zozo.com/v1alpha1
kind: FluentPVC
metadata:
  name: fluent-pvc-sample
spec:
  pvcSpecTemplate:
    accessModes: [ "ReadWriteOnce" ]
    storageClassName: standard
    resources:
      requests:
        storage: 1Gi
  pvcFinalizerJobSpecTemplate:
    template:
      spec:
        restartPolicy: Never
        containers:
          - name: sidecar
            image: alpine:latest
            imagePullPolicy: Always
            command: [echo, finalizer]
            resources:
              limits:
                cpu: '1'
                memory: 1Gi
  pvcVolumeName: fluent-pvc
  pvcVolumeMountPath: /mnt/fluent-pvc
  sidecarContainerTemplate:
    name: sidecar
    image: alpine:latest
    imagePullPolicy: Always
    command: [echo sidecar]
    resources:
      limits:
        cpu: '1'
        memory: 1Gi
  deletePodIfSidecarContainerTerminationDetected: true
  commonEnvs:
    - name: FLUENT_PVC_MOUNT_DIR
      value: /mnt/fluent-pvc
  commonVolumes:
    - name: SOME_SECRET
      secret:
        secretName: some-secret
  commonVolumeMounts:
    - name: SOME_SECRET
      mountPath: /path/to/secret

fluent-pvc-operatorの動きを理解しやすくするためkindを使って構築したKubernetesにfluent-pvc-operatorをデプロイする例を用意しています。以下のコマンドを実行することでKubernetesクラスタがローカルに構築され、例がデプロイされます。

$ make examples/log-collection/clean-deploy

実際にデプロイされるManifest群はexamplesディレクトリにまとめているので、合わせて参照ください。

おわりに

この記事ではfluent-pvc-operatorの解きたかった課題や使い方について説明をしました。fluent-pvc-operatorが他者でも利用され、ログ欠損が発生しない幸せな世界になれば良いなと切に願っています。

  1. Logging Architectureで紹介されているとおり、他にもstdout/stderrを回収する方法やDaemonSetでログを回収する方法などがあります。ただ前者に関してはCRI: Log management for container stdout/stderr streamsで語られているとおりログ関連の要件を完全に満たすCRIは存在しないため欠損の可能性があり、後者に関してもDaemonSetのfluentdが他のPodよりも先に終了するとログが欠損するなど、要件を満たせるのはSidecar Containerがログを回収する方法のみでした。

  2. 開発当初に「fluentd」という名前を使わずに「fluent」に留めたのは先見の明があったなと勝手に思っています。

  3. クラウドベンダーのディスク障害などPVへの書き込みが不可になるケースは救えません。

  4. NFSはSSDを使用したブロックストレージと比較してネットワークのスループットに影響されるなどパフォーマンス要求を満たさないため。またflock取得時に書込モードでopenする必要があるなど通常のファイルシステムとは異なる部分があるため早期に選択肢から外しました。

  5. 基盤提供者と利用者で責任分界点が曖昧になると運用コストが非常に高くなるため。

  6. この機能はKEP 753 Sidecar Containersの機能群がKubernetesで実装されたら不要になります。

15
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?