LINEが内製しているプライベートクラウドプラットフォーム「Verda」

坂本大将氏:それでは「Reliable Logs Aggregation System in Multi-Tenant Kubernetes cluster」というタイトルで発表し たいと思います。

まずスピーカーの自己紹介からします。私は坂本大将と申します。2021年1月にLINEに入社していて、以来VerdaというプライベートクラウドのSREを務めています。ミッションとしては、システムの信頼性だったり、信頼性の改善を担保していくようなロールです。Kubernetesや分散システムに興味があって、業務でもよく触れています。よろしくお願いいたします。

それでは本題に入る前に、まず背景を整理します。まずVerdaとは何かというところですが、VerdaはLINEが内製しているプライベートクラウドプラットフォームです。

OpenStackベースのIaaSだったり、ランチャーベースのKubernetes as a Serviceだったり、MySQLやElasticsearchなどのマネージドデータベースサービスだったり、KubernetesネイティブベースのFunction as a Service、LBやNATなどのネットワークサービスなどを多様に提供している状況です。

規模感としてはバーチャルマシンが7万4,000台、物理マシンが3万台、ハイパーバイザーで4,000台以上の規模を誇っています。その中でVerdaのSREチームはVerdaを横断して日々仕事をしています。

またこのSREチームは2つに分かれていて、Platform wide SREとInfra Managementという2つのユニットが存在しています。このうちPlatform wide SREユニットは、Verda内部で使うようなモニタリング基盤などのプラットフォームを開発・運用していくことで、Verdaサービスの信頼性向上やVerdaサービス開発者の運用負担を横断して減らすように努めています。

これに対してInfra Managementユニットは、Verda内の物理的なインフラリソースの調達だったり、管理や構築を行っているようなユニットで、より効率的なリソース活用に向けて、日々業務を行っている状況です。

今日お話しするのは、Platform wide SREとしてのお話です。そして今回の主題はVerda内でのログ集計でのお話になります。日々Verdaサービスの開発者が運用する上で、参照しているVerdaサービスそのもののアプリケーションログであったり、監査のために使用している監査ログの集計について言及していきたいと思います。

またこのプレゼンテーションのターゲットとしては、似たような構成で似たような課題に対処する必要のある方々、マルチテナントアーキテクチャについて具体的に検討しているような方々、それからシステム設計に意思決定権を持っているような方々をターゲットとしています。

テーマとしては、マルチテナントKubernetesにおける設定管理手法の事例紹介と、マルチテナントKubernetesにおけるFluentdによるログ収集について説明していきたいと思います。

また、この発表を聞く大きなメリットなんですが、マルチテナントKubernetesでconfig管理やログ管理をする上でのアイデアだったり、苦労ポイントというところを事前に知れたりするというのが大きなポイントになります。

特に設定管理については、Kubernetes Operatorを使った解決策を説明していく予定なのですが、これはマルチテナントで、複数チームで設定情報を扱うようなユースケースであれば、ログに閉じずに有効だと考えているので、ぜひ参考にしてもらえるとうれしいです。

Verda内でのログ集計にどのような課題があったのか

でははじめに、Verda内でのもともとのログ集計にどのような課題があったのかについて説明していきたいと思います。現在Verdaの多くのサービスは、このマルチテナントなKubernetesクラスターの上で稼働しています。このマルチテナントを提供している主な目的は、主に3つあります。

1つ目はインフラリソースの集約率を向上させること。2つ目はデプロイメントやモニタリングなどのオペレーションを標準化するということ。3つ目がKubernetes上に内部開発者向けのプラットフォームを開発・運用していくことによって、信頼性向上やオペレーションコストの削減ということを組織横断で行っていくため。この3つになります。

現在このクラスターは、Kubernetes as a ServiceチームとSREチームが協力して運用をしているような状況で、今回このようにマルチテナントのクラスターを運用している方々にとって、我々の解決手法や課題感が役に立てば幸いです。

では具体的に、もともとはどのようにログを集計していたのかについて説明したいと思います。もともとは、各VerdaサービスのPodの中に、サイドカーとしてFluentdとLogrotatedをデプロイしていました。そして、KubernetesのemptyDirという機能を利用して、ログファイルをPodの中のコンテナ間で共有していました。

例えば、IaaSコンポーネントであるOpenStackのNova APIコンポーネントが、emptyDirの中にログファイルを出力します。それを同じPodの中にいるFluentdが、常に監視をしていて、ログ行をテールしています。そしてそれを、Verda内でログストレージとして利用しているElasticsearchクラスターに送信します。

そしてこのemptyDirに書かれたログは、Logrotatedが一定の条件でローテーションしていくような仕組みになっていました。

3つの課題

では具体的に何が課題だったのかというと、課題は3つあると考えています。1つ目は1つのPodあたりのコンテナの数が増えすぎてしまうこと。2つ目はすべての開発者が知識の有無にかかわらず、必ずFluentdをメンテナンスしないといけないということ。3つ目はモニタリングやキャパシティ管理、パフォーマンス管理や信頼性やデータ永続性への配慮というところがまばらになっていたため、問題があるというところです。

これによって、例えば1つのPodのサイズがとても大きくなってしまうので、効率的なノードへのスケジューリングが難しくなってしまいます。これはもともと、マルチテナントKubernetesクラスターを提供していた目的の1つであった、インフラのリソース集約率を高めるような思想に反してしまいます。

また、メンテナンスするチームによって、Fluentdによるログ集計のクオリティが変わってきてしまうというのも問題です。さらにはVerdaでは、監査ログを同じ仕組みで集計していたのですが、このようにモニタリングや永続性への配慮が、チームによってまばらになってしまうと、監査ログが監査の目的として機能しなくなってしまいます。

監査ログというのは、いつ・誰が・何をしたのかというところを正確に記録していて、いつでもそれが遡れることが求められると思いますが、モニタリングの不足やデータ永続性への配慮の欠如によって消えたのか消えていないのかがわからなかったり、そもそも監査ログが消えてしまったりするのが、かなり問題になってしまいます。こうした問題があったため、我々はログ集計方法を根本的に見直すことにしました。

2つの解決策

では次に、これらの課題に対してどのような解決策を実施していったのかについて紹介したいと思います。解決策としては、主に2つ実施しました。SREマネージドなFluentdクラスターの提供と、Fluentdのconfigを自動的にバリデーションして適用してくれるFluentd Config Operatorの導入です。まずは、このManaged Fluentd Clusterについて説明したいと思います。

我々は、この図のような構成でManaged Fluentd Clusterを提供しました。Fluentdをサイドカーから切り離して、ログは基本的に標準出力に出力するようにしています。

マネージドなのは、FluentdのForwarderとAggregatorという部分です。Fluentdは、主にこの2つのロールに分かれてデプロイされています。

まずForwarderとは何かというところですが、Forwarderとはログを集めてAggregatorに送信する役割を担っています。そして、デーモンセットとしてデプロイされているので、各ノードに必ず1つのFluentd Forwarder Podが存在することになります。この形式で自分たちのFluentdをデプロイしているような方々は、これを聞いているみなさまの中にもかなり多いのではないかと考えています。

これに対してAggregatorは何かというと、Forwarderからログを受け取って加工やフィルタリング処理を行って、その結果を最終的なログのデータストアに送信するような役割を担っています。PersistentVolumeを扱うので、ステートフルセットとしてデプロイされています。

また、このAggregatorとForwarderの2つのロールに、わざわざ分割してデプロイしているということは、メリットが3つほどあると考えています。1つ目はデーモンセットであるForwarderに対して、Forwarderはたいしてリソースが必要ないというところです。Forwarderはログを単純にAggregatorに転送するだけで良いので、かなり処理を軽くできます。

2つ目はスケーラビリティの向上です。加工処理や転送処理というのが往々にして高負荷になりがちなんですが、ロールをこのように分割したことによって、Aggregatorを水平スケールすることで簡単に負荷分散が可能になります。

また3つ目は、変更の範囲を小さくできるというところになります。ログの取得に関しては、基本的に標準出力に出力するだけなのであまり変更が入らないですが、しかし加工処理や送信処理に関しては頻繁に変更が入るということが今後予想されます。その度に、すべてのノードに対してFluentdに変更をかけるのは非効率であって、リスクもそこそこに大きいと考えています。そのため、分割することでこの変更点を最小にできるところが大きいメリットとして考えています。

どのようにログが流れていくか

では実際に、どのようにログが流れていくのかを順々に説明していきたいと思います。まず、Podは標準出力にログを出力します。コンテナの標準出力というのはDockerのログドライバによって、ホスト側にJSONログファイルとして出力されています。よってFluentdのPodはホスト側にある該当ディレクトリをマウントしておけば、各コンテナの標準出力のログが集計可能です。

そしてForwarderは集計したログをAggregatorに送信します。この時1つのAggregatorに負荷が偏らないようにロードバランシングしています。Forwarderから受け取ったログを加工、フィルタリングして、最終的にElasticsearchに送信します。これでFluentdは完全にクラスター内での共有リソースになり、各サービス開発者は自分たちでFluentdを扱う必要がなくなりました。

このマネージドなFluentdは、データを失わないように、さまざまな工夫をしています。まずForwarder側なんですが、Podがデプロイされているホスト上のパスにログをバッファリングしてAggregatorの障害に備えています。

また、Podの再スケジューリングやOOM Killerによるプロセスの再起動でログを失わなくても済むように、シャットダウン時にバッファをフラッシュしたり、すでに読み終わったログという位置をファイルとして記録しておくことで、ログ読み取りをいつでも復旧できるようにしています。

また、確実に1回はログが到達していることを保証するために、AggregatorにAckレスポンスを要求して、Ackが返ってこない場合はリトライをするようなことを実施しています。

次に、AggregatorではログがマウントされているPersistentVolumeにバッファリングします。これでデータストア、例えばElasticsearchが障害時であってもログを失わずにバッファリング、つまり溜めておくことが可能です。

また、Podの再スケジューリングでForwarderと同じようにログを失わないように、プロセス終了時はバッファリングされたログをフラッシュします。また単一のノード障害で複数Aggregatorが同時にダウンしないように、分散して配置していたりします。

Managed Fluentd Cluster構成のメリット

最後にまとめると、このManaged Fluentd Clusterの構成には、メリットがいくつかあると考えています。Verdaサービスの開発者は完全にFluentdをメンテナンスしなくてよくなり、Aggregatorやデータストアの障害でも耐えられるように十分にバッファリングできるボリュームを用意しています。

また、Aggregatorのスケールも容易です。さらにこの環境は、完全にSREチームによってモニタリングされているので、データ永続性、信頼性、パフォーマンスなども、SREによって管理されています。

ではこのFluentdで、自分たちサービスチームのロギングの設定を入れるためにはどのようにしたら良いでしょうか? そこで次に、導入したFluentd Config Operatorについてのお話をしたいと思います。

後半につづく