K8S Fluentd Mongo日志采集
项目最近需要对K8S集群的容器日志进行统计采集,再汇聚起来持久化。最近比较火的开源日志方案是EFK(Elasticsearch、Fluentd、Kibana),目前项目只需要采集、存储,所以仅对接Fluentd,没有上E和K。
最终确定的日志方案是Fluentd+MongoDB。
Fluentd踩坑
在K8S源代码k8s.io\kubernetes\cluster\addons\fluentd-elasticsearch
中有EFK的配置文件,由于我们只需要Fluentd,所以只需要关注fluentd-es-configmap.yaml
和fluentd-es-ds.yaml
。
fluentd-es-configmap.yaml
fluentd-es-configmap.yaml
就是Fluentd的配置文件,官方比较复杂,根据我们自己的需求,只需要采集容器日志,所有只需要/var/log/containers/*.log
,其他source
都可以删除。
output
存储方案采用Mongo,参考mongo。这里需要安装Mongo插件,由于我们这里讲flunetd容器化,所以不太好安装插件,在docker hub
找了全插件的fluentd容器docker pull theasp/fluentd-plugins
。
@type mongo
database k8s
collection containers
host mongo.mind-automl
port 27017
flush_mode interval
retry_type exponential_backoff
flush_thread_count 2
flush_interval 5s
retry_forever
retry_max_interval 30
chunk_limit_size 2M
queue_limit_length 8
overflow_action block
fluentd-ds.yaml
fluentd-ds.yaml
是运行Fluentd damonset的配置文件,基本不需要修改,可能需要修改image
和env
。
坑
pos_file
运行Fluentd失败,通过日志发现是pos_file
没权限写。先把文件夹权限变成777
,然后发现每个node
上的pos_file
文件权限很诡异,每个node
上文件的用户和用户组都不一样。通过id
命令发现uid
都是1000,这个问题遗留。
log不可读
Fluentd成功运行了,并且已经写入mongo,但是发现读不了容器的日志文件。
容器日志
-rw-r-----+ 1 root root 140 Sep 20 05:17 fd12af59e3c534aae556738ded6e412ebfb23933824b004982513177ef9411bd-json.log
只有root用户可读,结合上面pos_file
问题,应该是权限问题。通过阅读fluentd容器的Dockerfile,发现问题应该出在启动脚本上。
Dockerfile
# AUTOMATICALLY GENERATED
# DO NOT EDIT THIS FILE DIRECTLY, USE /Dockerfile.template.erb
FROM alpine:3.7
LABEL maintainer "TAGOMORI Satoshi "
LABEL Description="Fluentd docker image" Vendor="Fluent Organization" Version="1.1"
ENV DUMB_INIT_VERSION=1.2.0
ENV SU_EXEC_VERSION=0.2
ARG DEBIAN_FRONTEND=noninteractive
# Do not split this into multiple RUN!
# Docker creates a layer for every RUN-Statement
# therefore an 'apk delete' has no effect
RUN apk update \
&& apk upgrade \
&& apk add --no-cache \
ca-certificates \
ruby ruby-irb \
su-exec==${SU_EXEC_VERSION}-r0 \
dumb-init==${DUMB_INIT_VERSION}-r0 \
&& apk add --no-cache --virtual .build-deps \
build-base \
ruby-dev wget gnupg \
&& update-ca-certificates \
&& echo 'gem: --no-document' >> /etc/gemrc \
&& gem install oj -v 2.18.3 \
&& gem install json -v 2.1.0 \
&& gem install fluentd -v 0.12.43 \
&& apk del .build-deps \
&& rm -rf /var/cache/apk/* \
&& rm -rf /tmp/* /var/tmp/* /usr/lib/ruby/gems/*/cache/*.gem
# for log storage (maybe shared with host)
RUN mkdir -p /fluentd/log
# configuration/plugins path (default: copied from .)
RUN mkdir -p /fluentd/etc /fluentd/plugins
COPY fluent.conf /fluentd/etc/
COPY entrypoint.sh /bin/
RUN chmod +x /bin/entrypoint.sh
ENV FLUENTD_OPT=""
ENV FLUENTD_CONF="fluent.conf"
ENV LD_PRELOAD=""
ENV DUMB_INIT_SETSID 0
EXPOSE 24224 5140
ENTRYPOINT ["/bin/entrypoint.sh"]
CMD exec fluentd -c /fluentd/etc/${FLUENTD_CONF} -p /fluentd/plugins $FLUENTD_OPT
/bin/entrypoint.sh
#!/usr/bin/dumb-init /bin/sh
uid=${FLUENT_UID:-1000}
# check if a old fluent user exists and delete it
cat /etc/passwd | grep fluent
if [ $? -eq 0 ]; then
deluser fluent
fi
# (re)add the fluent user with $FLUENT_UID
useradd -u ${uid} -o -c "" -m fluent
export HOME=/home/fluent
# source vars if file exists
DEFAULT=/etc/default/fluentd
if [ -r $DEFAULT ]; then
set -o allexport
source $DEFAULT
set +o allexport
fi
chown home and data folder
chown -R fluent /home/fluent
chown -R fluent /fluentd
exec gosu fluent "$@"
很明显,fluentd容器创建了uid 1000的fluent用户,导致了一系列的权限问题。
解决之道
找到问题原因,解决方案有两个,一个是让1000用户有权限读容器日志,另一个是修改容器启动脚本。第一个方案需要改所有node,比较繁琐,所以采用第二种方法。
第二种方法直接暴力将脚本关于权限命令全部注释
#!/usr/bin/dumb-init /bin/sh
uid=${FLUENT_UID:-1000}
# check if a old fluent user exists and delete it
cat /etc/passwd | grep fluent
if [ $? -eq 0 ]; then
deluser fluent
fi
# (re)add the fluent user with $FLUENT_UID
# useradd -u ${uid} -o -c "" -m fluent
export HOME=/home/fluent
#source vars if file exists
DEFAULT=/etc/default/fluentd
if [ -r $DEFAULT ]; then
set -o allexport
source $DEFAULT
set +o allexport
fi
# chown home and data folder
# chown -R fluent /home/fluent
# chown -R fluent /fluentd
# exec gosu fluent "$@"
exec "$@"
修改好之后,重新commit新的容器镜像,经测试发现,可以用了。很棒。
fluent-plugin-k8s
虽然fluentd可以读取容器日志,并且存储到mongo里面了,但是日志内容不符合
{"log":"[info:2016-02-16T16:04:05.930-08:00] Some log text here\n","stream":"stdout","time":"2016-02-17T00:04:05.931087621Z"}
问题:
- 没有k8s相关信息;
- 由于采用
tail
,所以每次只读一行,根本无法聚合成完整的log文件。
想了很多方法,都没办法解决上面的问题(之前没接触过fluentd),只能回到原点,重头看k8s.io\kubernetes\cluster\addons\fluentd-elasticsearch\fluentd-es-configmap.yaml
(大赞老外的注释),由于之前我觉得这个配置文件内容太多,没必要,所以只截取了自己需要的部分。
在fluentd-es-configmap.yaml
上面大段的注释中,详细说道了我的问题,普通的json信息都非常缺失,所以K8S提供了Kubernetes fluentd plugin
解决这个问题,大赞。参考fluent-plugin-kubernetes_metadata_filter
kind: ConfigMap
apiVersion: v1
metadata:
name: fluentd
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: Reconcile
data:
k8s.conf: |-
@type kubernetes_metadata
ca_file /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file /var/run/secrets/kubernetes.io/serviceaccount/token
@type mongo
database k8s
collection containers
host mongo.mind-automl
port 27017
flush_mode interval
retry_type exponential_backoff
flush_thread_count 2
flush_interval 5s
retry_forever
retry_max_interval 30
chunk_limit_size 2M
queue_limit_length 8
overflow_action block
最终配置如上,这里采用serviceaccount
访问K8S集群,所以需要配置ca_file
以及bearer_token_file
,对serviceaccount
不清楚的同学,可以参考之前的文章Pod内进程访问k8s服务。