Collecting Kubernetes Logs
Details on using the Seq Helm chart
The easiest way for applications running in a Kubernetes cluster to log to Seq is to use a native logging library and HTTP ingestion with the Seq instance's cluster internal DNS record, like http://seq.logging.svc.cluster.local
.
A standard way to ingest logs in Kubernetes and other container orchestrators is to log directly to an output stream and let the ambient infrastructure collect the data and ship it off to the log server. This can be done using Fluent Bit to collect container logs and forward them to a Seq instance deployed from the Helm chart using GELF as the protocol.
Enabling GELF
The Seq Helm chart supports ingestion via GELF out-of-the-box. It can be enabled on deployment:
helm install -f config.yaml my-seq stable/seq
# config.yaml
gelf:
enabled: true
Using Fluent Bit to collect logs
With Seq ready to ingest GELF events, the next step is to deploy Fluent Bit. It will be responsible for watching container logs on each node in the cluster and forwarding them as GELF events to Seq. Fluent Bit will take care of buffering for us in case Seq becomes unavailable.
See the Fluent Bit Kubernetes docs for more details.
Setting up a service account
First, create a namespace and a service account for Fluent Bit to use:
kubectl create namespace logging
kubectl apply -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-service-account.yaml
kubectl apply -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role.yaml
kubectl apply -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role-binding.yaml
Creating a config map
Next, Fluent Bit needs some configuration to tell it where to find logs and how to filter and output them. This will be stored in a config map:
kubectl apply -f config.yaml
# config.yaml
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging
apiVersion: v1
data:
fluent-bit.conf: |
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
@INCLUDE input-kubernetes.conf
@INCLUDE filter-kubernetes.conf
@INCLUDE output-gelf.conf
input-kubernetes.conf: |
[INPUT]
Name tail
Tag kube.*
Path /var/log/containers/*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
filter-kubernetes.conf: |
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc: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
K8S-Logging.Parser On
K8S-Logging.Exclude Off
output-gelf.conf: |
[OUTPUT]
Name gelf
Match kube.*
Host ${FLUENT_GELF_HOST}
Port ${FLUENT_GELF_PORT}
Mode ${FLUENT_GELF_PROTOCOL}
Gelf_Short_Message_Key log
parsers.conf: |
[PARSER]
Name apache
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache2
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache_error
Format regex
Regex ^\[[^ ]* (?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])?( \[client (?<client>[^\]]*)\])? (?<message>.*)$
[PARSER]
Name nginx
Format regex
Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name json
Format json
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
[PARSER]
Name syslog
Format regex
Regex ^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
Time_Key time
Time_Format %b %d %H:%M:%S
See the Fluent Bit docs for more details on how its configuration is structured.
Deploy Fluent Bit
Fluent Bit is deployed as a daemon set to make sure it gets run on every node in the cluster:
kubectl apply -f daemon.yaml
# daemon.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
k8s-app: fluent-bit-logging
version: v1
template:
metadata:
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:1.3.11
imagePullPolicy: Always
env:
- name: FLUENT_GELF_HOST
value: "seq.logging.svc.cluster.local"
- name: FLUENT_GELF_PORT
value: "12201"
- name: FLUENT_GELF_PROTOCOL
value: "tcp"
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
terminationGracePeriodSeconds: 10
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: fluent-bit-config
configMap:
name: fluent-bit-config
serviceAccountName: fluent-bit
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
- operator: "Exists"
effect: "NoExecute"
- operator: "Exists"
effect: "NoSchedule"
Note the environment variables:
FLUENT_GELF_HOST
: The internal domain your Seq instance is reachable on. This example usesseq.logging.svc.cluster.local
.FLUENT_GELF_PORT
: The port GELF is listening on in the Seq instance. The default is12201
.FLUENT_GELF_PROTOCOL
: The protocol GELF is using. The default is TCP.
Ingest events
With Fluent Bit configured, anything an application writes to its output streams will be logged as an event in Seq.
Updated over 4 years ago