---
title: Notes on Forwarding Kubernetes Logs to rsyslog with fluent-bit
tags: ["Kubernetes", "TKG", "Tanzu", "Fluent Bit", "syslog", "rsyslog"]
categories: ["Dev", "CaaS", "Kubernetes", "TKG", "Extensions", "FluentBit"]
date: 2024-10-08T01:13:09Z
updated: 2024-10-08T04:05:55Z
---

> ⚠️ This article was automatically translated by OpenAI (gpt-4o-mini).
> It may be edited eventually, but please be aware that it may contain incorrect information at this time.

I couldn't find a proper sample that includes structured data when forwarding K8s logs to rsyslog with fluent-bit, so here are my notes.

### rsyslog Configuration

rsyslog is often pre-installed on the OS. You can check the status of rsyslog with the following command.

```
$ sudo service rsyslog status
Redirecting to /bin/systemctl status rsyslog.service
● rsyslog.service - System Logging Service
   Loaded: loaded (/usr/lib/systemd/system/rsyslog.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2024-10-08 09:38:33 JST; 6min ago
     Docs: man:rsyslogd(8)
           https://www.rsyslog.com/doc/
 Main PID: 526 (rsyslogd)
    Tasks: 3 (limit: 128204)
   Memory: 1.9M
   CGroup: /system.slice/rsyslog.service
           └─526 /usr/sbin/rsyslogd -n

Oct 08 09:38:33 syslog systemd[1]: Starting System Logging Service...
Oct 08 09:38:33 syslog rsyslogd[526]: [origin software="rsyslogd" swVersion="8.2102.0-15.el8" x-pid="526" x-info="https://www.rsyslog.com"] start
Oct 08 09:38:33 syslog rsyslogd[526]: imjournal: No statefile exists, /var/lib/rsyslog/imjournal.state will be created (ignore if this is first run): No such file or direc
tory [v8.2102.0-15.el8 try https://www.rsyslog.com/e/2040 ]
Oct 08 09:38:33 syslog systemd[1]: Started System Logging Service.
Oct 08 09:38:33 syslog rsyslogd[526]: imjournal: journal files changed, reloading...  [v8.2102.0-15.el8 try https://www.rsyslog.com/e/0 ]
```

If it is not installed, you can install it with the following command.

```
sudo yum install rsyslog
```

I tried the following version of rsyslog.

```
$ rsyslogd -v
rsyslogd  8.2102.0-15.el8 (aka 2021.02) compiled with:
	PLATFORM:				aarch64-redhat-linux-gnu
	PLATFORM (lsb_release -d):		
	FEATURE_REGEXP:				Yes
	GSSAPI Kerberos 5 support:		Yes
	FEATURE_DEBUG (debug build, slow code):	No
	32bit Atomic operations supported:	Yes
	64bit Atomic operations supported:	Yes
	memory allocator:			system default
	Runtime Instrumentation (slow code):	No
	uuid support:				Yes
	systemd support:			Yes
	Config file:				/etc/rsyslog.conf
	PID file:				/var/run/rsyslogd.pid
	Number of Bits in RainerScript integers: 64

See https://www.rsyslog.com for more information.
```

Open `/etc/rsyslog.conf` and uncomment the following lines.

```
module(load="imtcp") # needs to be done just once
input(type="imtcp" port="514")
```

Also, to output rfc5424 [STRUCTURED-DATA](https://datatracker.ietf.org/doc/html/rfc5424#section-6.3), remove the following configuration,

```
# Use default timestamp format
module(load="builtin:omfile" Template="RSYSLOG_TraditionalFileFormat")
```

And instead, add the following configuration.

```
module(load="builtin:omfile" Template="RSYSLOG_SyslogProtocol23Format")
```

Restart rsyslog.

```
sudo service rsyslog restart
```

Use the following command to tail the logs.

```
sudo tail -f /var/log/messages
```

Verify the operation. Access the rsyslog server from another terminal using telnet and send a log in rfc5424 format.

```
$ telnet <syslog server IP> 514
Trying *.*.*.*...
Connected to *.*.*.*.
Escape character is '^]'.
<14>1 2021-07-12T14:37:35.569848Z myhost myapp 1234 ID98 [uls@0 logtype="access" clustername="mycluster" namespace="mynamespace"] Sample app log message.
```

If the same log appears on the rsyslog side that you were tailing, it is OK.

```
<14>1 2021-07-12T14:37:35.569848Z myhost myapp 1234 ID98 [uls@0 logtype="access" clustername="mycluster" namespace="mynamespace"] Sample app log message.
```

If the configuration `module(load="builtin:omfile" Template="RSYSLOG_TraditionalFileFormat")` remains, the output will look like this.

```
Jul 12 14:37:35 myhost myapp[1234] Sample app log message.
```

### fluent-bit Configuration

Although not essential, fluent-bit is installed using the Tanzu (Carvel) Package. The key point in this article is the configuration of fluent-bit itself, and there is not much difference regardless of the installation method. Similar configurations should work even if installed via Helm or other methods.

> [!NOTE]
> For instructions on setting up the Tanzu Package, please refer to this article. This article assumes that the following repository is already configured.
>
> ```
> tanzu package repository add tanzu-standard \
> --url projects.registry.vmware.com/tkg/packages/standard/repo:v2024.8.21 \
> --namespace tanzu-install --create-namespace
> ```

Create the following configuration file. Please replace `<syslog server IP>` with the appropriate value.

```yaml
cat <<'EOF' > fluent-bit-values.yaml
---
namespace: tanzu-system-logging
fluent_bit:
  config:
    service: |
      [SERVICE]
        Daemon              Off
        Flush                1
        Log_Level            info
        Parsers_File         parsers.conf
        Health_Check         On
        HTTP_Server          On
        HTTP_Port            2020
        HTTP_Listen          0.0.0.0
    inputs: |
      [INPUT]
        Name                 tail
        Path                 /var/log/containers/*.log
        DB                   /var/log/flb_kube.db
        parser               cri
        Tag                  kube.*
        Mem_Buf_Limit        5MB
        Skip_Long_Lines      On
    parsers: |
      [PARSER]
        # http://rubular.com/r/tjUt3Awgg4
        Name                 cri
        Format               regex
        Regex                ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
        Time_Key             time
        Time_Format          %Y-%m-%dT%H:%M:%S.%L%z
    filters: |
      [FILTER]
        Name                 kubernetes
        Match                kube.*
        Kube_URL             https://kubernetes.default.svc.cluster.local:443
        Kube_CA_File         /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        Kube_Token_File      /var/run/secrets/kubernetes.io/serviceaccount/token
        Kube_Tag_Prefix      kube.var.log.containers.
        Merge_Log            On
        Merge_Log_Key        log_processed
        Keep_Log             Off
        K8S-Logging.Parser   On
        K8S-Logging.Exclude  On
      [FILTER]
        Name                lua
        Match               *
        call                add_structured_data
        code                function add_structured_data(tag, timestamp, record) new_record = record new_record["k8s@8"] = {} new_record["k8s@8"]["node"] = record["kubernetes"]["host"] new_record["k8s@8"]["app"] = record["kubernetes"]["labels"]["app"] new_record["k8s@8"]["container_image"] = record["kubernetes"]["container_image"] return 1, timestamp, new_record end
      [FILTER]
        Name                nest
        Match               *
        Operation           lift
        Nested_Under        kubernetes
    outputs: |
      [OUTPUT]
        Name                 syslog
        Match                *
        Host                 <syslog server IP>
        Port                 514
        Mode                 tcp
        Syslog_Format        rfc5424
        Syslog_Hostname_key  namespace_name
        Syslog_Appname_key   pod_name
        Syslog_Procid_key    container_name
        Syslog_Message_key   message
        Syslog_SD_key        k8s@8
---
EOF
```

Install fluent-bit with the following command.

```
tanzu package install -n tanzu-install fluent-bit -p fluent-bit.tanzu.vmware.com -v 2.1.6+vmware.2-tkg.1 --values-file fluent-bit-values.yaml
```

If the following logs appear on the rsyslog side, it is OK.

```
<14>1 2024-10-08T01:10:01.231783Z harbor harbor-portal-d6c99f896-2v8zm portal - [k8s@8 node="kind-worker2" container_image="docker.io/goharbor/harbor-portal:v2.11.1" app="harbor"] ﻿10.244.2.1 - - [08/Oct/2024:01:10:01 +0000] "GET / HTTP/1.1" 200 785 "-" "kube-probe/1.30"
<14>1 2024-10-08T01:10:02.539216Z harbor harbor-registry-59f445685-7ltfq registryctl - [k8s@8 node="kind-worker3" container_image="docker.io/goharbor/harbor-registryctl:v2.11.1" app="harbor"] ﻿10.244.1.1 - - [08/Oct/2024:01:10:02 +0000] "GET /api/health HTTP/1.1" 200 9
<14>1 2024-10-08T01:10:04.894222Z harbor harbor-registry-59f445685-7ltfq registry - [k8s@8 node="kind-worker3" container_image="docker.io/goharbor/registry-photon:v2.11.1" app="harbor"] ﻿10.244.1.1 - - [08/Oct/2024:01:10:04 +0000] "GET / HTTP/1.1" 200 0 "" "kube-probe/1.30"
<14>1 2024-10-08T01:10:05.741468Z kube-system kindnet-ltt8c kindnet-cni - [k8s@8 app="kindnet" node="kind-worker" container_image="docker.io/kindest/kindnetd:v20240813-c6f155d6"] ﻿I1008 01:10:05.741186       1 main.go:295] Handling node with IPs: map[192.168.228.4:{}]
<14>1 2024-10-08T01:10:05.741488Z kube-system kindnet-ltt8c kindnet-cni - [k8s@8 app="kindnet" node="kind-worker" container_image="docker.io/kindest/kindnetd:v20240813-c6f155d6"] ﻿I1008 01:10:05.741220       1 main.go:322] Node kind-control-plane has CIDR [10.244.0.0/24] 
```

Modify the `add_structured_data` code as necessary.

> [!TIP]
> If the configuration does not reflect when updating, restart fluent-bit with the following command.
> 
> ```
> kubectl rollout restart ds/fluent-bit -n tanzu-system-logging
> ```
