4. ログの集約

本説明では Exastro IT Automation の各コンテナのログを集約する(Kubernetes環境における)手段の例を以下に記載します。

4.1. コンテナログについて

Exastroシステムでは、各コンテナのログ出力を標準出力で行っております。(The Twelve-Factor App の思想に基づく)
標準出力されたログは、Kubernetes環境の場合、コンテナがリスタートされたりすると標準出力されたログは削除されてしまいます、そのため、出力されたログを保存するには、その内容を別途保存する必要があります。
またKubernetes環境における、複数ノードで実行している場合は、その情報が各ノードに保存されているために、監視アプリケーションで監視する際に非常に扱いにくいものとなっております。
よって本説明では、監視アプリケーション等でも利用しやすいように、ノードごとに出力されたログを集約し、かつ、ファイル形式を JSON形式 で出力する方法の一例をあげたいと思います。

注釈

Kubernetesにおけるロギングのアーキテクチャについては、

4.2. ログの集約方法

本説明において、ログの集約方法に Fluent Bit を使用して、ログの集約を実施する例を説明します。

4.2.1. Fluent Bitインストール

Fluent Bitのインストールは、公式サイトに基づき、Helm charts を利用してインストールします。

4.2.2. 前提条件

本説明で使用する環境の前提条件は以下の通りとなります。
  • 使用する helm cli は事前にインストールされていること
  • 出力先のNFSサーバーとKubernetesが動作するサーバー間は、接続できる状態となっていること
  • ログの集約先となるNFSサーバーのディレクトリは /var/PersistentVolume/ha-conf-k8s/exastro-logs とし、アクセス権がフルアクセスとなっていること

4.2.3. 事前準備

ログを集約するために、今回はNFSを用いた方法とし、出力先にKubernetesの永続ボリューム(PersistentVolume)を利用します。

永続ボリューム(PersistentVolume)の準備

事前準備として、出力先の永続ボリューム(PV)とリソースの要求(PVC)のmanifestを作成します。
リスト 4.1 fluentbit-pv.yaml
 1apiVersion: v1
 2kind: PersistentVolumeClaim
 3metadata:
 4  name: pvc-exastro-logs
 5spec:
 6  accessModes:
 7    - "ReadWriteMany"
 8  resources:
 9    requests:
10      storage: "10Gi"
11  storageClassName: ""
12  # volumeName: pv-exastro-logs
13---
14apiVersion: v1
15kind: PersistentVolume
16metadata:
17  name: pv-exastro-logs
18spec:
19  capacity:
20    storage: 20Gi
21  accessModes:
22    - ReadWriteMany
23  persistentVolumeReclaimPolicy: Retain
24  storageClassName: ""
25  nfs:
26    server: 192.168.172.1
27    path: /var/PersistentVolume/ha-conf-k8s/exastro-logs
28  claimRef:
29    name: pvc-exastro-logs
30    namespace: default

Fluent Bit: helm chart values.yamlの準備

helm chartsでインストールする際に使用する values.yaml は、以下のコマンドで抽出して、編集する
リスト 4.2 コマンド
helm show values fluent/fluent-bit > fluentbit-values.yaml

Tip

Fluent Bitのhelmリポジトリ登録は以下のコマンドで実施できます。 helm show values を実行する前に1度だけ必要となります。
リスト 4.3 コマンド
# Exastro システムの Helm リポジトリを登録
helm repo add fluent https://fluent.github.io/helm-charts
# リポジトリ情報の更新
helm repo update
出力された fluentbit-values.yaml を以下のように修正します。
リスト 4.4 fluentbit-values.yaml
  1--- /home/runner/work/exastro-it-automation-docs/exastro-it-automation-docs/workspace/src/ja/2.4/share/ja/manuals/maintenance/literal_includes/fluentbit-values-org.yaml
  2+++ /home/runner/work/exastro-it-automation-docs/exastro-it-automation-docs/workspace/src/ja/2.4/share/ja/manuals/maintenance/literal_includes/fluentbit-values.yaml
  3@@ -298,9 +298,12 @@
  4 
  5 priorityClassName: ""
  6 
  7-env: []
  8+# env: []
  9 #  - name: FOO
 10 #    value: "bar"
 11+env:
 12+  - name: TZ
 13+    value: "Asia/Tokyo"
 14 
 15 # The envWithTpl array below has the same usage as "env", but is using the tpl function to support templatable string.
 16 # This can be useful when you want to pass dynamic values to the Chart using the helm argument "--set <variable>=<value>"
 17@@ -329,9 +332,14 @@
 18 #     name: tcp
 19 #     nodePort: 30517
 20 
 21-extraVolumes: []
 22-
 23-extraVolumeMounts: []
 24+extraVolumes:
 25+  - name: volume-exastro-logs
 26+    persistentVolumeClaim:
 27+      claimName: pvc-exastro-logs
 28+
 29+extraVolumeMounts:
 30+  - name: volume-exastro-logs
 31+    mountPath: /var/logs/fluentbit
 32 
 33 updateStrategy: {}
 34 #   type: RollingUpdate
 35@@ -346,7 +354,14 @@
 36 #   ingress:
 37 #     from: []
 38 
 39-luaScripts: {}
 40+# luaScripts: {}
 41+luaScripts:
 42+  record_date.lua: |
 43+    function record_date(tag, timestamp, record)
 44+            new_record = record
 45+            new_record["record_date"] = os.date("%Y%m%d",timestamp)
 46+            return 1, timestamp, new_record
 47+    end  
 48 
 49 ## https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/classic-mode/configuration-file
 50 config:
 51@@ -365,45 +380,45 @@
 52   ## https://docs.fluentbit.io/manual/pipeline/inputs
 53   inputs: |
 54     [INPUT]
 55-        Name tail
 56-        Path /var/log/containers/*.log
 57-        multiline.parser docker, cri
 58-        Tag kube.*
 59-        Mem_Buf_Limit 5MB
 60-        Skip_Long_Lines On
 61-
 62-    [INPUT]
 63-        Name systemd
 64-        Tag host.*
 65-        Systemd_Filter _SYSTEMD_UNIT=kubelet.service
 66-        Read_From_Tail On
 67+        Name             tail
 68+        Path             /var/log/containers/*.log
 69+        Parser           docker_no_time
 70+        Tag              <namespace_name>.<container_name>
 71+        Tag_Regex        (?<pod_name>[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)_(?<namespace_name>[^_]+)_(?<container_name>.+)-
 72+        Refresh_Interval 5
 73+        Mem_Buf_Limit    5MB
 74+        Skip_Long_Lines  On
 75 
 76   ## https://docs.fluentbit.io/manual/pipeline/filters
 77   filters: |
 78     [FILTER]
 79-        Name kubernetes
 80-        Match kube.*
 81-        Merge_Log On
 82-        Keep_Log Off
 83-        K8S-Logging.Parser On
 84+        Name                kubernetes
 85+        Match               exastro.*
 86+        Merge_Log           On
 87+        Keep_Log            Off
 88+        K8S-Logging.Parser  On
 89         K8S-Logging.Exclude On
 90+
 91+    [FILTER]
 92+        Name                lua
 93+        Match               exastro.*
 94+        # full path to the script
 95+        script              /fluent-bit/scripts/record_date.lua
 96+        call                record_date
 97+
 98+    [FILTER]
 99+        Name                rewrite_tag
100+        Match               exastro.*
101+        Rule                $record_date   ^(.*)$  exastro-suite.$TAG.$record_date.log false
102+        Emitter_Name        re_emitted
103 
104   ## https://docs.fluentbit.io/manual/pipeline/outputs
105   outputs: |
106     [OUTPUT]
107-        Name es
108-        Match kube.*
109-        Host elasticsearch-master
110-        Logstash_Format On
111-        Retry_Limit False
112-
113-    [OUTPUT]
114-        Name es
115-        Match host.*
116-        Host elasticsearch-master
117-        Logstash_Format On
118-        Logstash_Prefix node
119-        Retry_Limit False
120+        Name    file
121+        Format  plain
122+        Match   exastro-suite.exastro.*
123+        Path    /var/logs/fluentbit/
124 
125   ## https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/classic-mode/upstream-servers
126   ## This configuration is deprecated, please use `extraFiles` instead.
127@@ -412,11 +427,16 @@
128   ## https://docs.fluentbit.io/manual/pipeline/parsers
129   customParsers: |
130     [PARSER]
131-        Name docker_no_time
132-        Format json
133-        Time_Keep Off
134-        Time_Key time
135-        Time_Format %Y-%m-%dT%H:%M:%S.%L
136+        Name        docker_no_time
137+        # Format      json
138+        # Time_Keep   Off
139+        # Time_Key    time
140+        # Time_Format %Y-%m-%dT%H:%M:%S.%L
141+        Format      regex
142+        Regex       ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<log>.*)$
143+        Time_Keep   Off
144+        Time_Key    time
145+        Time_Format %Y-%m-%dT%H:%M:%S.%L%z
146 
147   # This allows adding more files with arbitary filenames to /fluent-bit/etc/conf by providing key/value pairs.
148   # The key becomes the filename, the value becomes the file content.

注釈

helm show values で出力された内容をそのまま使用しているため、コメント化された内容もそのまま記載しています。

注釈

ログファイルは、json形式 で出力されるように設定しています。
ログファイル名は、ログローテーションを考慮して、日付+コンテナ名(namespace名含む)で出力されるように設定しています。

危険

rewrite_tag で、rewrite先が同じタグを継承すると永久ループとなり、サーバーが不安定になるので、rewriteのタグは違う内容となるように設定してください。

4.2.4. インストール

helmによるインストール手順は公式の手順に従ってインストールします。

注釈

公式の手順のまま実施しているため、namespaceは指定しておりません。
  • PV, PVCのapply
    リスト 4.5 コマンド
    kubectl apply -f fluentbit-pv.yaml
    
  • Fluent Bit: インストール
    リスト 4.6 コマンド
    helm upgrade --install fluent-bit fluent/fluent-bit -f fluentbit-values.yaml
    
  • インストールされた Fluent Bit の確認
    リスト 4.7 コマンド
    kubectl get po
    
    リスト 4.8 実行結果
    NAME               READY   STATUS              RESTARTS   AGE
    fluent-bit-gx2c5   1/1     Running             0          22s
    fluent-bit-tgbls   0/1     ContainerCreating   0          22s
    fluent-bit-xhm6w   0/1     ContainerCreating   0          22s
    
    リスト 4.9 コマンド
    kubectl get daemonset
    
    リスト 4.10 実行結果
    NAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
    fluent-bit   3         3         3       3            3           <none>          88m
    

4.2.5. 出力された内容を確認

インストール完了後、すぐに設定された内容で、NFSサーバーにログファイルが集約されます。
  • 集約したログファイル一覧(例)
    リスト 4.11 実行結果
    -rw-r--r-- 1 root root   156434 Mar  5 14:53 exastro-suite.exastro.mongo.20240305.log
    -rw-r--r-- 1 root root   514599 Mar  5 14:53 exastro-suite.exastro.platform-api.20240305.log
    -rw-r--r-- 1 root root     6931 Mar  5 14:51 exastro-suite.exastro.platform-job.20240305.log
    
  • 集約したログファイルの内容(例)
    リスト 4.12 集約したログファイルの内容
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"2024/03/01 16:40:09.098836 INFO (dumm) /app/controllers/internal_maintenance_mode_setting_controller.py(37) ### func:internal_get_maintenance_mode_setting"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:09 +0900] \"GET /internal-api/platform/maintenance-mode-setting HTTP/1.1\" 200 135 \"-\" \"python-requests/2.31.0\""}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:09 +0900] \"GET /internal-api/platform/maintenance-mode-setting HTTP/1.1\" 200 135"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"2024/03/01 16:40:10.666816 INFO (dumm) /app/controllers/internal_maintenance_mode_setting_controller.py(37) ### func:internal_get_maintenance_mode_setting"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:10 +0900] \"GET /internal-api/platform/maintenance-mode-setting HTTP/1.1\" 200 135 \"-\" \"python-requests/2.31.0\""}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:10 +0900] \"GET /internal-api/platform/maintenance-mode-setting HTTP/1.1\" 200 135"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"2024/03/01 16:40:12.326828 INFO (dumm) /app/controllers/internal_maintenance_mode_setting_controller.py(37) ### func:internal_get_maintenance_mode_setting"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:12 +0900] \"GET /internal-api/platform/maintenance-mode-setting HTTP/1.1\" 200 135 \"-\" \"python-requests/2.31.0\""}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:12 +0900] \"GET /internal-api/platform/maintenance-mode-setting HTTP/1.1\" 200 135"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"2024/03/01 16:40:12.807144 INFO (dumm) /app/controllers/internal_maintenance_mode_setting_controller.py(37) ### func:internal_get_maintenance_mode_setting"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:12 +0900] \"GET /internal-api/platform/maintenance-mode-setting HTTP/1.1\" 200 135 \"-\" \"python-requests/2.31.0\""}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:12 +0900] \"GET /internal-api/platform/maintenance-mode-setting HTTP/1.1\" 200 135"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"2024/03/01 16:40:12.836383 INFO (dumm) /app/controllers/internal_common_service_controller.py(153) ### func:internal_settings_system_config_list"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:12 +0900] \"GET /internal-api/platform/settings/common HTTP/1.1\" 200 223 \"-\" \"python-requests/2.31.0\""}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:12 +0900] \"GET /internal-api/platform/settings/common HTTP/1.1\" 200 223"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"2024/03/01 16:40:12.858139 INFO (dumm) /app/controllers/internal_plan_service_controller.py(62) ### func:limits_get"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:12 +0900] \"GET /internal-api/platform/limits HTTP/1.1\" 200 239 \"-\" \"python-requests/2.31.0\""}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"192.168.172.2 - - [01/Mar/2024:16:40:12 +0900] \"GET /internal-api/platform/limits HTTP/1.1\" 200 239"}
    {"stream":"stdout","logtag":"F","record_date":"20240301","log":"2024/03/01 16:40:15.921191 INFO (dumm) /app/controllers/internal_maintenance_mode_setting_controller.py(37) ### func:internal_get_maintenance_mode_setting"}
    

注釈

本説明では、ログファイルの削除に関する説明は行っておりません。
出力先の容量(ディスクサイズ)に合わせて、ログファイルの削除を行うことをお勧めします。