
За основу возьмем официальный Docker образ fluntd
Установим необходимые gem'ы для GELF протокола и Kubernetes'а, а так же укажем, что fluentd будет запускаться от пользователя root, иначе будет получать ошибку доступа.
Dockerfile:
FROM fluent/fluentd:latest
USER root
WORKDIR /home/fluent
RUN set -ex \
&& apk add --no-cache --virtual .build-deps \
build-base \
ruby-dev \
&& echo 'gem: --no-document' >> /etc/gemrc \
&& gem install fluent-plugin-secure-forward \
&& gem install fluent-plugin-record-reformer \
&& gem install fluent-plugin-gelf-hs \
&& gem install fluent-plugin-kubernetes_metadata_filter \
&& gem install fluent-plugin-rewrite-tag-filter \
&& apk del .build-deps \
&& rm -rf /tmp/* /var/tmp/* /usr/lib/ruby/gems/*/cache/*.gem
# Copy plugins
COPY plugins /fluentd/plugins/
# Environment variables
ENV FLUENTD_OPT=""
ENV FLUENTD_CONF="fluent.conf"
# For Kubernetes. Start Fluentd by user root
ENV FLUENT_UID="0"
# Run Fluentd
CMD fluentd -c /fluentd/etc/$FLUENTD_CONF -p /fluentd/plugins $FLUENTD_OPT
plugins/parser_kubernetes.rb:
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# The following Fluentd parser plugin, aims to simplify the parsing of multiline
# logs found in Kubernetes nodes. Since many log files shared the same format and
# in order to simplify the configuration, this plugin provides a 'kubernetes' format
# parser (built on top of MultilineParser).
#
# When tailing files, this 'kubernetes' format should be applied to the following
# log file sources:
#
# - /var/log/kubelet.log
# - /var/log/kube-proxy.log
# - /var/log/kube-apiserver.log
# - /var/log/kube-controller-manager.log
# - /var/log/kube-scheduler.log
# - /var/log/rescheduler.log
# - /var/log/glbc.log
# - /var/log/cluster-autoscaler.log
#
# Usage:
#
# ---- fluentd.conf ----
#
# <source>
# type tail
# format kubernetes
# path ./kubelet.log
# read_from_head yes
# tag kubelet
# </source>
#
# ---- EOF ---
require 'fluent/parser'
module Fluent
class KubernetesParser < Fluent::TextParser::MultilineParser
Fluent::Plugin.register_parser("kubernetes", self)
CONF_FORMAT_FIRSTLINE = %q{/^\w\d{4}/}
CONF_FORMAT1 = %q{/^(?<severity>\w)(?<time>\d{4} [^\s]*)\s+(?<pid>\d+)\s+(?<source>[^ \]]+)\] (?<message>.*)/}
CONF_TIME_FORMAT = "%m%d %H:%M:%S.%N"
def configure(conf)
conf['format_firstline'] = CONF_FORMAT_FIRSTLINE
conf['format1'] = CONF_FORMAT1
conf['time_format'] = CONF_TIME_FORMAT
super
end
end
end
После сборки кастомного образа, переходим к настройке Kubernetes'а
rbac.yaml:
apiVersion: v1
kind: ServiceAccount
metadata:
name: kube-gelf
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
name: kube-gelf-metadata-filter
rules:
- apiGroups:
- ""
resources:
- pods
- pods/exec
- namespaces
verbs: ["get", "list", "watch", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: kube-gelf
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kube-gelf-metadata-filter
subjects:
- kind: ServiceAccount
name: kube-gelf
namespace: kube-system
configmap.yml:
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-gelf
namespace: kube-system
labels:
app: kube-gelf
data:
GELF_HOST: "graylog-svc.staging.svc.cluster.local"
GELF_PORT: "12201"
GELF_PROTOCOL: "udp"
fluent.conf: |
<match fluent.**>
@type null
</match>
<source>
@type tail
path /var/log/containers/*.log
pos_file /pos/containers.pos
time_key time
time_format %Y-%m-%dT%H:%M:%S.%NZ
tag kubernetes.*
format json
read_from_head true
</source>
<filter kubernetes.**>
@type kubernetes_metadata
ca_file /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file /var/run/secrets/kubernetes.io/serviceaccount/token
</filter>
<filter access>
@type record_transformer
<record>
hostname "#{ENV['NODENAME']}"
</record>
</filter>
### NOT COLLECT LOGS FROM ALL EXCEPT NAMESPACE STAGING ###
<match **fluentd**>
@type null
</match>
<match **kube-system**>
@type null
</match>
<match **monitoring**>
@type null
</match>
<match **cert-manager**>
@type null
</match>
<match **efs-staging**>
@type null
</match>
##########################################################
<match kubernetes.**>
@type rewrite_tag_filter
<rule>
key $['kubernetes']['labels']['app']
pattern ^(.+)$
tag $1
</rule>
</match>
<match **>
@type copy
<store>
@type gelf
include_tag_key true
host "#{ENV['GELF_HOST']}"
port "#{ENV['GELF_PORT']}"
protocol "#{ENV['GELF_PROTOCOL']}"
flush_interval 10s
num_threads 2
use_record_host true
buffer_chunk_limit 4096K
buffer_queue_limit 512
max_retry_wait 300
</store>
</match>
# Valid log_level's are: fatal, error, warn, info, debug, trace
<system>
log_level warn
</system>
В файле "fluent.conf" я исключил все, кроме namespace staging, в котором у меня крутятся все контейнеры.
manifest.yml:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-gelf
namespace: kube-system
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: kube-gelf
template:
metadata:
labels:
app: kube-gelf
spec:
serviceAccountName: kube-gelf
dnsPolicy: ClusterFirst
containers:
- name: agent
image: my-image-repo:kube-gelf
imagePullPolicy: "IfNotPresent"
env:
- name: GELF_HOST
valueFrom:
configMapKeyRef:
name: kube-gelf
key: GELF_HOST
- name: GELF_PORT
valueFrom:
configMapKeyRef:
name: kube-gelf
key: GELF_PORT
- name: GELF_PROTOCOL
valueFrom:
configMapKeyRef:
name: kube-gelf
key: GELF_PROTOCOL
- name: NODENAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
- name: gelf-pos
mountPath: /pos
readOnly: false
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: kube-gelf-config
mountPath: /fluentd/etc/fluent.conf
subPath: fluent.conf
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: kube-gelf-config
configMap:
name: kube-gelf
items:
- key: fluent.conf
path: fluent.conf
- name: gelf-pos
hostPath:
path: /var/log/pos
Не забываем указать свой путь с собраному образу.