日志收集

k8s 组件默认日志位置

kubernetes : /var/log/kubernetes
etcd : /var/log/messages

容器 : /var/log/containers/ -> /var/log/pods/ # 指向了这个位置

1
2
3
4
5
6
7
8
[root@k8s01 ~]# ll /var/log/kubernetes
total 86936
-rw-r--r--. 1 root root 9468454 Mar 23 15:09 kubelet.k8s01.root.log.ERROR.20210315-140844.24735
-rw-r--r--. 1 root root 59335295 Mar 23 15:09 kubelet.k8s01.root.log.INFO.20210315-140843.24735
-rw-r--r--. 1 root root 20200286 Mar 23 15:09 kubelet.k8s01.root.log.WARNING.20210315-140844.24735
lrwxrwxrwx. 1 root root 51 Mar 15 14:08 kubelet.ERROR -> kubelet.k8s01.root.log.ERROR.20210315-140844.24735
lrwxrwxrwx. 1 root root 50 Mar 15 14:08 kubelet.INFO -> kubelet.k8s01.root.log.INFO.20210315-140843.24735
lrwxrwxrwx. 1 root root 53 Mar 15 14:08 kubelet.WARNING -> kubelet.k8s01.root.log.WARNING.20210315-140844.24735

日志的需求

  1. 系统各组件的日志 : eg: kubelet、kube-proxy,docker 等日志
  2. 容器化运行的应用程序自身日志 : eg: Nginx 的运行日志
  3. k8s 内部各种 Event : eg: kubctl create pod 后,kubectl describe pod *** 的pod 的 Event信息

日志系统

Kubernetes 的日志系统在设计的时候,必须得独立于节点和 Pod 的生命周期,且保证日志数据可以实时采集到服务端,即完全独立于 Kubernetes 系统,使用自己的后端存储和查询工具

日志收集方式

一般有如下 四种方案来做日志收集:

log4

  1. 直接在应用程序中将日志信息推送到采集后端; (不推荐, 耦合严重。本身已有完善日志处理系统的公司可以采用)

log1

  1. 在节点上运行一个 Agent 来采集节点级别的日志; (使用 daemonset,比较常用,但只适合 容器内应用日志是标准输出场景,即应用把日志 输出的 stdout 和 stderr)

log2
3.当容器的日志只能输出到某些文件里的时候,我们可以通过一个 sidecar 容器把这些日志文件重新输出到 sidecarstdoutstderr 上,这样就能够继续使用第一种方案了; (Sidecar 容器读取日志文件,并重定向到自己的标准输出。会增加额外开销,cpu 内存,磁盘空间等,大规模集群,这些开销不可忽视,所以非特殊情况,一般推荐二和四)

log2

  1. 通过一个 sidecar 容器,直接把应用的日志文件发送到远程存储里面去。也就是相当于把方案二里的 logging agent,放在了应用 Pod 里。(应用还可以直接把日志输出到固定的文件里而不是 stdout,logging-agent 还可以使用 fluentd,后端存储还可以是 ElasticSearch。只不过, fluentd 的输入源,变成了应用的日志文件。一般来说,我们会把 fluentd 的输入源配置保存在一个 ConfigMap 里)

sidecar 指的就是我们可以在一个 Pod 中,启动一个辅助容器,来完成一些独立于主进程(主容器)之外的工作。

推荐方式

使用 EFK 进行日志收集和管理,通过 Fluentd 将日志导入到 Elasticsearch 中,然后通过 Kibana 查看所有的日志

使用 EFLK 收集容器内文件日志。 Elasticsearch + Filebeat + Logstash + Kinbana

yaml 文件

Pod 只有一个容器,它会把日志输出到容器里的 /var/log/1.log2.log 这两个文件里

log_to_file.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: busybox
args:
- /bin/sh
- -c
- >
i=0;
while true;
do
echo "$i: $(date)" >> /var/log/1.log;
echo "$(date) INFO $i" >> /var/log/2.log;
i=$((i+1));
sleep 1;
done
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
emptyDir: {}

因为非 stdoutstderr, kubectl logs 命令是看不到应用的任何日志的。所以方案二不能使用。
使用方案三 : Pod 添加两个 sidecar 容器,分别将上述两个日志文件里的内容重新以 stdoutstderr 的方式输出出来 tail -f

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: busybox
args:
- /bin/sh
- -c
- >
i=0;
while true;
do
echo "$i: $(date)" >> /var/log/1.log;
echo "$(date) INFO $i" >> /var/log/2.log;
i=$((i+1));
sleep 1;
done
volumeMounts:
- name: varlog
mountPath: /var/log
- name: count-log-1 # 此容器将主容器的 /var/log/1.log 通过 tail 输出到 stdout 和 stderr
image: busybox
args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log']
volumeMounts:
- name: varlog
mountPath: /var/log
- name: count-log-2
image: busybox
args: [/bin/sh, -c, 'tail -n+1 -f /var/log/2.log']
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
emptyDir: {}

方案四

fluentd-from-config.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
data:
fluentd.conf: |
<source>
type tail
format none
path /var/log/1.log
pos_file /var/log/1.log.pos
tag count.format1
</source>

<source>
type tail
format none
path /var/log/2.log
pos_file /var/log/2.log.pos
tag count.format2
</source>

<match **>
type google_cloud
</match>

声明一个 Fluentd 容器作为 sidecar,专门负责将应用生成的 1.log2.log 转发到 ElasticSearch 当中.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: busybox
args:
- /bin/sh
- -c
- >
i=0;
while true;
do
echo "$i: $(date)" >> /var/log/1.log;
echo "$(date) INFO $i" >> /var/log/2.log;
i=$((i+1));
sleep 1;
done
volumeMounts:
- name: varlog
mountPath: /var/log
- name: count-agent
image: k8s.gcr.io/fluentd-gcp:1.30
env:
- name: FLUENTD_ARGS
value: -c /etc/fluentd-config/fluentd.conf
volumeMounts:
- name: varlog
mountPath: /var/log
- name: config-volume
mountPath: /etc/fluentd-config
volumes:
- name: varlog
emptyDir: {}
- name: config-volume
configMap:
name: fluentd-config

这个 sidecar 容器很可能会消耗较多的资源,甚至拖垮应用容器。并且,由于日志还是没有输出到 stdout 上,所以你通过 kubectl logs 是看不到任何日志输出的。