參考文獻(xiàn):
https://blog.csdn.net/dkfajsldfsdfsd/article/details/80970179
日志方案的關(guān)注點(diǎn)
- 日志
來源
和存放位置
- 日志
采集
和上報(bào)
- 日志
存儲(chǔ)
- 日志
分析
- 日志
查詢
日志
主要分成兩種,
- 一種是
應(yīng)用日志
颗祝, - 一種是
系統(tǒng)日志
蹄梢。
日志對(duì)于理解集群內(nèi)部
的工作原理非常有幫助们豌,另外日志對(duì)于調(diào)試
、排錯(cuò)
障癌、監(jiān)控
集群活動(dòng)特別有用涛浙。
大部分的現(xiàn)在應(yīng)用都支持某種類型的日志機(jī)制摄欲,相似的胸墙,大部分的容器引擎也被設(shè)計(jì)成支持此種類型的日志機(jī)制。
標(biāo)準(zhǔn)輸出
及標(biāo)準(zhǔn)錯(cuò)誤輸出
是最簡(jiǎn)單的也是大部容器內(nèi)嵌的日志機(jī)制
仓手。
然而嗽冒,
容器引擎dockerd
支持的原生日志系統(tǒng)
往往不是
有關(guān)日志的完全解決方案补履。
例如箫锤,容器銷毀
、pod驅(qū)除阳准、節(jié)點(diǎn)死亡等野蝇,發(fā)生這種情況后绕沈,用戶往往仍然
需要查看
日志,但是日志此時(shí)已經(jīng)伴隨容器赠摇、pod蝉稳、節(jié)點(diǎn)被同步銷毀掘鄙。
因些操漠,日志應(yīng)該有獨(dú)立
于節(jié)點(diǎn)
浊伙、pod
长捧、容器
的存儲(chǔ)空間及生命周期串结,這一概念稱之為集群級(jí)日志
(cluster-level-logging)肌割。
集群級(jí)日志
要求一個(gè)獨(dú)立
的后端存儲(chǔ)
、分析
弥奸、查詢?nèi)罩?/code>盛霎。
kubernetes沒有
為日志數(shù)據(jù)的存儲(chǔ)
提供原生
的解決方案,但是用戶可以很容易將已存在
的日志解決方案集成
到kubernetes集群中期揪。
集群級(jí)日志假定用戶有一個(gè)位于集群內(nèi)部
或者外部
横侦,專門用于處理日志的后端枉侧。
如果對(duì)集群級(jí)日志
不感興趣狂芋,仍然能夠從本文中學(xué)到有關(guān)日志的存儲(chǔ)帜矾、處理等非常有用的知識(shí)點(diǎn)屡萤。
1、介紹
關(guān)于kubernetes的日志分幾種招拙,針對(duì)kubernetes本身而言有三種:
- 資源運(yùn)行時(shí)的
event事件
别凤。比如在k8s集群中創(chuàng)建pod之后领虹,可以通過kubectl describe pod 命令查看pod的詳細(xì)信息塌衰。 - 容器中運(yùn)行的
應(yīng)用程序
自身產(chǎn)生的日志
最疆,比如tomcat肚菠、nginx、php的運(yùn)行日志层扶。 - k8s
各組件
的服務(wù)日志
镜会,比如systemctl status kubelet戳表。
容器日志收集的方式通常有以下幾種:
宿主機(jī)Node層面統(tǒng)一收集
:將宿主機(jī)的目錄掛載為容器的日志目錄,然后在宿主機(jī)上收集镣屹。pod中以
sidecar容器
的方式收集:這個(gè)sidecar容器收集應(yīng)用日志之后女蜈,可以輸出到宿主機(jī)或者直接發(fā)送到遠(yuǎn)端的日志收集服務(wù)伪窖。網(wǎng)絡(luò)收集
: 容器內(nèi)應(yīng)用將日志直接發(fā)送到日志中心居兆,比如java程序可以使用log4j2轉(zhuǎn)換日志格式并發(fā)送到遠(yuǎn)端泥栖×木螅或者通過修改docker的–log-driver耙蔑」氯伲可以利用不同的driver把日志輸出到不同地方盐股,將log-driver設(shè)置為syslog疯汁、fluentd、splunk等日志收集服務(wù)谤碳,然后發(fā)送到遠(yuǎn)端
2蜒简、kubernetes中標(biāo)準(zhǔn)輸出中的輸出日志
通過一個(gè)例子了解kubernetes中標(biāo)準(zhǔn)輸出中的輸出日志搓茬。容器中執(zhí)行shell, 定時(shí)輸出統(tǒng)計(jì)數(shù)卷仑。
debug/counter-pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: 192.168.200.197:80/test-private/busybox:latest
args: [/bin/sh, -c,
'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']
通過下面的方式運(yùn)行:
kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml
pod/counter created
查看日志方式:
[root@kube-196 ~]# kubectl logs -f counter
1126: Tue Jun 18 09:27:07 UTC 2019
1127: Tue Jun 18 09:27:08 UTC 2019
1128: Tue Jun 18 09:27:09 UTC 2019
1129: Tue Jun 18 09:27:10 UTC 2019
1130: Tue Jun 18 09:27:11 UTC 2019
1131: Tue Jun 18 09:27:12 UTC 2019
這些日志文件系枪,在宿主機(jī)上的什么位置呢私爷?
[root@kube-196 ~]# less /var/log/pods/default_counter_9ce0c974-91a8-11e9-9cd8-fa163e1e5fde/count/0.log
{"log":"1081: Tue Jun 18 09:26:22 UTC 2019\n","stream":"stdout","time":"2019-06-18T09:26:22.866910994Z"}
{"log":"1082: Tue Jun 18 09:26:23 UTC 2019\n","stream":"stdout","time":"2019-06-18T09:26:23.868754044Z"}
{"log":"1083: Tue Jun 18 09:26:24 UTC 2019\n","stream":"stdout","time":"2019-06-18T09:26:24.870266589Z"}
{"log":"1084: Tue Jun 18 09:26:25 UTC 2019\n","stream":"stdout","time":"2019-06-18T09:26:25.871929216Z"}
[root@kube-197 kube-proxy]# cd /var/log/containers/
[root@kube-197 containers]# ll
total 112
lrwxrwxrwx 1 root root 101 Jun 25 10:47 apm-els-elasticsearch-master-2_default_chown-2a0eea1ac1a4c4a8fe66fa5ab16bbdcf896fde9c5e752abdc5bbe6340fd00a17.log -> /var/log/pods/default_apm-els-elasticsearch-master-2_ac94ba63-96f2-11e9-9cd8-fa163e1e5fde/chown/0.log
lrwxrwxrwx 1 root root 109 Jun 25 10:47 apm-els-elasticsearch-master-2_default_elasticsearch-e6727337b8d1146868f480ce829a13100629aa78f61786c1aa8823850bfdfbc5.log -> /var/log/pods/default_apm-els-elasticsearch-master-2_ac94ba63-96f2-11e9-9cd8-fa163e1e5fde/elasticsearch/0.log
3捌浩、Node層面
每個(gè)容器通過stdout
和stderr
將日志輸出到宿主機(jī)的日志文件中尸饺。
通過在每個(gè)節(jié)點(diǎn)上
運(yùn)行一個(gè)日志收集的agent來采集日志數(shù)據(jù)浪听,日志采集agent是一種專用工具迹栓,用于將日志數(shù)據(jù)推送到統(tǒng)一的后端俭缓。
一般來說华坦,這種agent用一個(gè)容器來運(yùn)行惜姐,可以訪問該節(jié)點(diǎn)上所有應(yīng)用程序容器的日志文件所在目錄。
由于這種agent必須在每個(gè)節(jié)點(diǎn)上運(yùn)行耘拇,所以直接使用DaemonSet控制器
運(yùn)行該應(yīng)用程序即可惫叛。
在節(jié)點(diǎn)上運(yùn)行一個(gè)日志收集的agent這種方式是最常見的一直方法嘉涌,因?yàn)樗恍枰诿總€(gè)節(jié)點(diǎn)上運(yùn)行一個(gè)代理程序仑最,并不需要
對(duì)節(jié)點(diǎn)上運(yùn)行的應(yīng)用程序
進(jìn)行更改,對(duì)應(yīng)用程序沒有任何侵入性
亿胸,但是這種方法也僅僅適用于收集輸出到stdout
和 stderr 的
應(yīng)用程序日志`侈玄。
Note: Docker json日志驅(qū)動(dòng)將每一行當(dāng)做一條獨(dú)立的
消息序仙。如果想支持多行消息潘悼,需要在agent或者更高的層面去自行處理爬橡。
Note: 如果使用了logrotate糙申,那么只能獲取最新的文件郭宝,比如有個(gè)15MB的文件粘室,通過logrotate分割成一個(gè)10MB和一個(gè)5MB的文件衔统,那么kubectl logs只能查看5MB的文件。
4舱殿、系統(tǒng)組件日志
系統(tǒng)組件
有兩種類型:
- 在
容器中運(yùn)行
的系統(tǒng)組件 -
不在容器中
運(yùn)行的系統(tǒng)組件沪袭。
例如:
Kubernetes的scheduler和kube-proxy在容器中運(yùn)行冈绊。
kubelet和容器運(yùn)行時(shí)(例如Docker)不在容器中運(yùn)行死宣。
對(duì)于系統(tǒng)級(jí)的日志文件毅该,也是存放在/var/log目錄下眶掌,也需要配置logrotate分割罢绽。
對(duì)于具備systemd功能的主機(jī)良价,kubelet與容器運(yùn)行時(shí)將日志寫入到j(luò)ournald中明垢,否則痊银,日志寫入到/var/log目錄下的.log文件中溯革。
對(duì)于運(yùn)行在容器內(nèi)部的系統(tǒng)組件,通過使用glog日志處理庫冈闭,將日志寫入到/var/log目錄下萎攒,繞開默認(rèn)的日志處理機(jī)制。
總之
耍休,
系統(tǒng)組件
的日志處理機(jī)制獨(dú)立于
容器處理機(jī)制刃永。
對(duì)于以systemd服務(wù)
等式運(yùn)行的組件,日志由journald處理羊精。
以容器方式運(yùn)行
的組件斯够,日志最終到輸出到宿主機(jī)的/var/log目錄下,這種情況下需要手動(dòng)處理日志的滾動(dòng)喧锦,大多數(shù)情況通過安裝腳本自動(dòng)完成雳刺。
5裸违、集群級(jí)日志采集架構(gòu)方案
首先需要明確的是掖桦,Kubernetes里面對(duì)容器日志的處理方式,都叫作 cluster-level-logging供汛,即:這是個(gè)日志處理系統(tǒng)枪汪,與容器、Pod以及Node的生命周期都是完全無關(guān)的怔昨。這種設(shè)計(jì)當(dāng)然是為了保證雀久,無論是容器掛了、Pod被刪除趁舀,甚至節(jié)點(diǎn)宕機(jī)的時(shí)候赖捌,應(yīng)用的日志依然可以被正常獲取到。
而對(duì)于一個(gè)容器來說矮烹,當(dāng)應(yīng)用把日志輸出到 stdout 和 stderr 之后越庇,容器引擎
在默認(rèn)情況下就會(huì)把這些日志輸出到宿主機(jī)上的一個(gè) JSON 文件里。
這樣奉狈,你通過 kubectl logs 命令就可以看到這些容器的日志了卤唉。
上述機(jī)制,就是我們今天要講解的容器日志
收集的基礎(chǔ)假設(shè)仁期。
而如果你的應(yīng)用
是把文件輸出
到其他地方桑驱,比如直接輸出
到了容器里的某個(gè)文件里
,或者輸出到了遠(yuǎn)程存儲(chǔ)里
跛蛋,那就屬于特殊情況了熬的。
當(dāng)然,我在文章里也會(huì)對(duì)這些特殊情況的處理方法進(jìn)行講述赊级。
而 Kubernetes 本身押框,實(shí)際上是不會(huì)為你做容器日志收集工作的,所以為了實(shí)現(xiàn)上述cluster-level-logging此衅,你需要在部署集群的時(shí)候强戴,提前對(duì)具體的日志方案進(jìn)行規(guī)劃。而 Kubernetes 項(xiàng)目本身挡鞍,主要為你推薦了三種日志方案骑歹。
每個(gè)node部署一個(gè)node級(jí)別的代理
每個(gè)應(yīng)用的pod中集成一個(gè)sidecar容器
從應(yīng)用內(nèi)部直接推送日志到后端
以上三種方法,可以總結(jié)成節(jié)點(diǎn)級(jí)
墨微、Pod級(jí)
道媚、容器級(jí)
集群級(jí)日志采集架構(gòu)解決方案。
5.1翘县、每個(gè)node部署一個(gè)node級(jí)別的代理
第一種最域,在Node上部署logging agent,將日志文件轉(zhuǎn)發(fā)
到后端存儲(chǔ)里保存起來锈麸。這個(gè)方案的架構(gòu)圖如下所示镀脂。
不難看到,這里的核心就在于 logging agent 忘伞,它一般都會(huì)以 DaemonSet 的方式運(yùn)行在節(jié)點(diǎn)上薄翅,然后將
宿主機(jī)
上的容器日志目錄掛載進(jìn)去
,最后由 logging-agent 把日志轉(zhuǎn)發(fā)
出去氓奈。
舉個(gè)例子翘魄,我們可以通過 Fluentd 項(xiàng)目作為宿主機(jī)上的 logging-agent,然后把日志轉(zhuǎn)發(fā)到遠(yuǎn)端的 ElasticSearch 里保存起來供將來進(jìn)行檢索舀奶。
因?yàn)閗8s集群已經(jīng)將pod日志在集群的/var/log/pods/xxxxxx/xxx/xxx.log 目錄下做了軟連接(xxxx.log)到pod所在 /var/lib/docker/containers/xxxxx/-json.log 文件中暑竟,只需要將fluentd監(jiān)控相關(guān)/var/log/pods/ 目錄下的.log 文件,即可達(dá)到搜集日志的效果育勺,同時(shí)由于在 docker 層設(shè)置了日志分割方式但荤,將日志占用空間做了限制,基本上不會(huì)出現(xiàn)日志過期不能清理的問題涧至。
"log-opts": {
"max-size": "100m"
},
另外纱兑,在很多 Kubernetes 的部署里,會(huì)自動(dòng)為你啟用 logrotate化借,在日志文件超過10MB的時(shí)候自動(dòng)對(duì)日志文件進(jìn)行 rotate 操作潜慎。
可以看到,在Node上部署logging agent 最大的優(yōu)點(diǎn)
蓖康,在于一個(gè)節(jié)點(diǎn)只需要部署一個(gè)agent铐炫,并且不會(huì)對(duì)應(yīng)用和 Pod有任何侵入性。
所以蒜焊,這個(gè)方案倒信,在社區(qū)里是最常用的一種。
但是也不難看到泳梆,這種方案的不足之處
就在于鳖悠,它要求應(yīng)用輸出
的日志榜掌,都必須是直接輸出到容器的stdout和stderr 里。
具體參考官網(wǎng)的方案配置:https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch
5.2乘综、每個(gè)應(yīng)用的pod中集成一個(gè)sidecar容器
使用sidecar容器方式憎账,有兩種方式:
- sidecar將
應(yīng)用日志
輸出到stdout
- sidecar運(yùn)行
日志代理
5.2.1、sidecar將應(yīng)用日志輸出到stdout
所以卡辰,Kubernetes容器日志方案的第二種胞皱,就是對(duì)這種特殊情況的一個(gè)處理,
即:當(dāng)容器的日志只能輸出到某些文件里的時(shí)候九妈,我們可以通過一個(gè)sidecar容器把這些日志文件重新輸出到 sidecar的stdout和stderr上反砌,這樣就能夠繼續(xù)使用第一種方案了。這個(gè)方案的具體工作原理萌朱,如下圖所示:
比如宴树,現(xiàn)在我的應(yīng)用Pod只有一個(gè)容器,它會(huì)把日志輸出到容器里的 /var/log/1.log 和 2.log 這兩個(gè)文件里晶疼。
這個(gè) Pod 的 YAML 文件如下所示:
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: 192.168.200.197:80/test-private/busybox:latest
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: {}
在這種情況下森渐,你用kubectl logs 命令是看不到
應(yīng)用的任何日志的;/var/log/pods也沒有
日志文件冒晰。而且我們前面講解的同衣、最常用的方案一,也是沒辦法使用的壶运。
那么這個(gè)時(shí)候耐齐,我們就可以為這個(gè) Pod 添加兩個(gè)sidecar 容器,分別將上述兩個(gè)日志文件里的內(nèi)容重新以 stdout 和 stderr 的方式輸出出來蒋情,這個(gè) YAML 文件的寫法如下所示:
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: 192.168.200.197:80/test-private/busybox:latest
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
image: 192.168.200.197:80/test-private/busybox:latest
args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log']
volumeMounts:
- name: varlog
mountPath: /var/log
- name: count-log-2
image: 192.168.200.197:80/test-private/busybox:latest
args: [/bin/sh, -c, 'tail -n+1 -f /var/log/2.log']
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
emptyDir: {}
這時(shí)候埠况,你就可以通過 kubectl logs 命令查看這兩個(gè) sidecar 容器的日志,間接看到應(yīng)用的日志內(nèi)容了棵癣,如下所示:
$ kubectl logs counter count-log-1
0: Mon Jan 1 00:00:00 UTC 2001
1: Mon Jan 1 00:00:01 UTC 2001
2: Mon Jan 1 00:00:02 UTC 2001
...
$ kubectl logs counter count-log-2
Mon Jan 1 00:00:00 UTC 2001 INFO 0
Mon Jan 1 00:00:01 UTC 2001 INFO 1
Mon Jan 1 00:00:02 UTC 2001 INFO 2
...
由于 sidecar 跟主容器之間是共享Volume
的辕翰,所以這里的sidecar方案的額外性能損耗并不高,也就是多占用一點(diǎn) CPU 和內(nèi)存罷了狈谊。
注意:這種方式宿主機(jī)上
實(shí)際上
會(huì)存在兩份相同的日志文件:
一份是應(yīng)用自己寫入的喜命;
另一份則是sidecar 的stdout和stderr對(duì)應(yīng)的JSON文件。
這對(duì)磁盤是很大的浪費(fèi)河劝。所以說壁榕,除非萬不得已或者應(yīng)用容器完全不可能被修改,
5.2.2赎瞎、sidecar運(yùn)行日志代理
上圖中牌里,日志代理運(yùn)行在pod內(nèi)部,每個(gè)pod一個(gè)日志代理务甥,而不是每個(gè)節(jié)點(diǎn)一個(gè)日志代理牡辽。
如果
節(jié)點(diǎn)級(jí)
日志代理不夠靈活的話喳篇,可以創(chuàng)建一個(gè)運(yùn)行日志代理
的挎斗容器,配置后使它與指定的容器一起工作态辛。注意
:靈活性帶來開銷的增加麸澜,并且kubectl logs不再生效,因?yàn)槿罩静⒎怯蒶ubelet負(fù)責(zé)收集因妙。
作為一個(gè)例子,可以使用Stackdriver票髓,它使用fluentd作為日志代理攀涵,由下邊的兩個(gè)配置文件實(shí)現(xiàn)這種方法。第一個(gè)文件是一個(gè)ConfigMap用來配置fluentd洽沟。
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>
上面的配置文件是配置收集原文件 /var/log/1.log 和 /var/log/2.log 的日志數(shù)據(jù)以故,然后通過 google_cloud 這個(gè)插件將數(shù)據(jù)推送到 Stackdriver 后端去。
下面是我們使用上面的配置文件在應(yīng)用程序中運(yùn)行一個(gè) fluentd 的容器來讀取日志數(shù)據(jù):
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: 192.168.200.197:80/test-private/busybox:latest
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
上面的Pod創(chuàng)建完成后裆操,count-agent容器就會(huì)將 count 容器中的日志進(jìn)行收集然后上傳怒详。當(dāng)然,這只是一個(gè)簡(jiǎn)單的示例踪区,我們也完全可以使用其他的任何日志采集工具來替換 fluentd昆烁,比如 logstash、fluent-bit 等等缎岗。
5.2.3静尼、從應(yīng)用內(nèi)部直接推送日志到后端
除了容器應(yīng)用
的日志之外,還有一個(gè)重要的部分就是传泊,程序自身的日志鼠渺。
一般情況下,會(huì)將程序日志寫到固定目錄文件眷细、或者寫入到中間件保存和讀取拦盹。
這部分日志的處理,可以直接通過日志文件收集溪椎、或者是讓開發(fā)程序的同學(xué)修改程序把日志輸出到中間件或者是直接輸出到elk上
6普舆、總結(jié)
在本篇文章中,我們?cè)敿?xì)講解了Kubernetes項(xiàng)目對(duì)容器應(yīng)用日志的收集方式校读。
綜合對(duì)比以上三種方案奔害,個(gè)人傾向于使用第一種方式,將應(yīng)用日志輸出到stdout和stderr地熄,然后通過在宿主機(jī)上部署logging-agent的方式來集中處理日志华临。
這種方案不僅管理簡(jiǎn)單,kubectl logs 也可以用端考,而且可靠性高雅潭,并且宿主機(jī)本身揭厚,很可能就自帶了rsyslogd等非常成熟的日志收集組件來供你使用。
最后需要指出的是扶供,無論是哪種方案筛圆,你都必須要及時(shí)將這些日志文件從宿主機(jī)上清理掉,或者給日志目錄專門掛載一些容量巨大的遠(yuǎn)程盤椿浓。否則太援,一旦主磁盤分區(qū)被打滿,整個(gè)系統(tǒng)就可能會(huì)陷入奔潰狀態(tài)扳碍,這是非常麻煩的提岔。