Kubernetes: Fluent Bitを使用してPodログをKafkaにリアルタイム転送する

# TL;DR

以下のアプリケーションをMinikube環境にデプロイし、
サンプルアプリケーションのPodに出力されるログをKafkaに転送してみました。

* [Kafka](https://kafka.apache.org/)
* [kafka-ui](https://github.com/provectus/kafka-ui) (Kafkaの管理画面ツール)
* [Fluent Bit](https://fluentbit.io/)
* 適当にログを出すサンプルアプリケーション

この記事では、
Kafkaにログを転送するためのFluent Bitのデプロイ方法について解説します。

また、デプロイのための設定ファイルやマニフェストファイルは以下のリポジトリから入手可能です。
https://github.com/cacapouh/fluentbit-kafka-example/

# Kafka周りのセットアップ

まず、Minikubeを使っている場合、メモリを4GB以上にして起動します。[^1]
[^1]: Minikubeはデフォルトですと2GBで起動しますが、私のPC環境ですとメモリ不足でKafkaがうまく動きませんでした。

```shell:Minikubeの起動
minikube start --memory=4096
```

次に以下のコマンドでKafka周りのツールをデプロイします。
Kafka自体のセットアップについては説明を省きますが、詳細は[こちらの記事](https://qiita.com/tobita_yoshiki/items/86585494d38f59b98359)で解説しています。

```shell:Kafka周りのアプリ起動
# ネームスペース作成
kubectl create namespace kafka

# Strimzi(Kafkaのデプロイツール)をデプロイ
kubectl create -f 'https://strimzi.io/install/latest?namespace=kafka' -n kafka

# Kafkaをデプロイ
kubectl apply -f https://strimzi.io/examples/latest/kafka/kafka-persistent-single.yaml -n kafka

# 起動するまで待つ 
kubectl wait kafka/my-cluster --for=condition=Ready --timeout=300s -n kafka

# kafka-ui(Kafkaの画面ツール)のデプロイ
kubectl apply -n kafka -f https://raw.githubusercontent.com/cacapouh/kafka-ui-k8s/main/kafka-ui.yml
```

デプロイ後、
kafka-ui(Kafkaの画面ツール)にて諸々の設定が上手くいっているか確認できます。
Minikube環境の場合、以下のコマンドでWeb UIへのアクセスリンクが手に入ります。

```shell
minikube service kafka-ui-service -n kafka
```

![kafka-ui起動チェック.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422592/c4901a4b-e016-d7fc-43fd-8f9814873582.png)

# サンプルアプリのデプロイ

サンプルアプリとして、1秒に1回`Count ${カウント値}`を標準出力するものをデプロイします。
以下はそのためのPythonスクリプト, Dockerfile, Deploymentのマニフェストファイルです。

```python:main.py
import time

if __name__ == '__main__':
    count = 0
    while True:
        count += 1
        print("Count {}".format(count))
        time.sleep(1)
```

```Dockerfile:Dockerfile
FROM python:3.9.18-slim

RUN mkdir /work
WORKDIR /work

COPY main.py /work
ENTRYPOINT python -u main.py
```

```yml:deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-app-deployment
  labels:
    app: simple-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-app
  template:
    metadata:
      labels:
        app: simple-app
    spec:
      containers:
        - name: simple-app
          image: "simple-app:latest"
          imagePullPolicy: IfNotPresent
```

こちらのアプリは、
docker buildして、マニフェストを適用するだけデプロイできます。

```shell:デプロイ方法
docker build -t simple-app .
minikube image load simple-app:latest # Minikube環境の場合
kubectl apply -f deployment.yml
```

以下はデプロイ後の挙動です。
1秒に1回カウント値がPodのログに出力されています。

```
$ kubectl get pods
NAME                                    READY   STATUS    RESTARTS   AGE
simple-app-deployment-dd44b5ff4-58nrb   1/1     Running   0          2m45s

$ kubectl logs simple-app-deployment-dd44b5ff4-58nrb | tail
Count 183
Count 184
Count 185
Count 186
Count 187
Count 188
Count 189
Count 190
Count 191
Count 192
```

# FluentBitのデプロイ

以下はFluent BitをKafkaに転送するための設定ファイルの一部です。
この設定ファイルをConfigMapとして扱い、他のマニフェストファイルと一緒に適用すれば、
KafkaにサンプルアプリのPodのログが転送されるようになります。

* INPUTセクションでは、入力プラグインの1種である[tail](https://docs.fluentbit.io/manual/pipeline/inputs/tail)を用いて、
Kubernetesクラスター内のコンテナログが格納される`/var/log/containers/*.log`をtailしています
* FILTERセクションでは、Pythonアプリが標準出力した内容だけを取得するために、ログに`Count`という文字列が含まれるかどうかでフィルタリングしています
* OUTPUTセクションでは、出力先のKafkaクラスタとトピックを指定しています
  * ブローカーに指定するアドレスとポートは、`${サービス名}.${ネームスペース名}.svc.cluster.local`という形式にしています[^2]
  * サービス名は`kubectl get services -n kafka`で判明します

[^2]: この名前解決については[こちら](https://kubernetes.io/ja/docs/concepts/services-networking/dns-pod-service/)で解説されています。

```text:fluent-bit.confの一部
[INPUT]
    Name tail
    Path /var/log/containers/*.log
    multiline.parser docker, cri
    Tag kube.*
    Mem_Buf_Limit 5MB
    Skip_Long_Lines On

[FILTER]
    Name    grep
    Match   *
    Regex   log Count

[OUTPUT]
    Name kafka
    Match kube.*
    Brokers my-cluster-kafka-bootstrap.kafka.svc.cluster.local:9092
    Topics my-topic
```

Fluent Bitの詳細はなデプロイ方法については以下の記事で解説しているため、
ここでは説明を割愛します。

https://qiita.com/tobita_yoshiki/items/cd6e540afdb41c77f5fd

# デプロイ後

以下は諸々のアプリケーションをデプロイした後に、kafka-uiでKafkaに転送されているログを確認した結果です。

スクリーンショットの通り、`Count ${カウント値}`という形式のログが存在し、
サンプルアプリのPodのログが転送できていることが確認できます。

![スクリーンショット 2024-01-16 21.18.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422592/4850362d-8a80-9090-89b8-ffaeea797232.png)

![スクリーンショット 2024-01-16 21.18.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422592/269d87e8-8d7d-809c-92d8-cc7783c29550.png)

# おわりに

Helm, Strimzi, 諸々のマニフェスト等を用いて、
Kubernetes環境に諸々のアプリケーションがデプロイできました :thumbsup: