參考來源: https://kubernetes.io/docs/concepts/cluster-administration/logging/
這個章節會來研究關於 k8s 的 Log 架構
應用程式的 log 能夠幫助理解應用程式的運行狀態
對於容器化的應用程式最基礎的 log 方式就是透過 stream 的方式把 standard output 與 standard error 導出容器之外
然而只有應用程式層面的 log 對於容器狀態的管控還是不夠, 再跑容器時還想紀錄關於容器的失敗跑起來的紀錄
在 k8s 會想紀錄 Pod 的狀態在 Pod 要關閉時, 在結點死掉時
而要這樣做需要讓 log 的生命周期獨立於 nodes, pods, 還有 containers 並且具有額外的儲存空間
這種概念稱作 Cluster-level logging
Cluster-level logging 架構需要具有一個獨立於 Cluster 之外的後端去儲存, 分析, 跟查詢 logs.
k8s 本身不具有原生提供的儲存方案來處理 log 資料
目前有許多第三方 logging 方案有跟 k8s 整合
下面將依序說明這些方案
以下用一個範例來示範
設定 counter-pod.yaml 如下:
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)"; i=$((i+1)); sleep 1; done']
使用以下指令建立 Pod
kubectl apply -f counter-pod.yaml
察看 Pod 的 log 使用以下指令:
kubectl logs counter
container 引擎會把容器化應用的 stdout 跟 stderr 透過 stream 的方式導出
舉例來說, Docker Engine 會透過 logger driver 來處理 stdout, stderr 到 JSON format 的 file
在 k8s 中, 如果一個 container 重新啟動, kubelet 預設會保存該 container 的 log. 而如果一個 Pod 被從結點移除, 則所有相關的 Container 以及 log 都會被移除
在 node level 很重要的一個概念是要做 log rotation, 否則 log 將會隨著運行佔據太多空間
k8s 本身並不做這 log rotation 機制, 而是 Deployment 內部在 container 自行去設定各自的 log rotation
當使用一個 CRI container runtime, kubelet 會負責處理 log rotation 還有管理 logging 資料夾結構.
以下有兩個參數可以設定在 kubelet config:
containerLogMaxSize 這個設定用來決定每個 log 檔案大小
containerLogMaxFiles 這個設定用來決定最多幾個檔案
使用 kubelet logs 指令時, kubelet 會直接讀取 log 檔案來顯示
k8s 的系統元件分為兩種: 跑在容器內與不跑在容器內的
舉例來說:
k8s scheduler 還有 kube-proxy 跑在一個容器內
k8s kubelet 還有 container runtime 不跑在容器內
在有 systemd 的機器上, kubelet 跟 container runtime 會把 log 寫入 journald
假設沒有 systemd, 則kubelet 跟 container runtime 會把 log 寫入 /var/log 下的 .log 黨內
而跑在容器內的元件則固定寫 log 到 /var/log 資料夾下
系統元件使用 klog 來當作 log 工具
系統元件有實作 log rotation 機制
以下有幾種選項是可行的方案
建立一個 node-level logging agent 在每個 node
這個 logging agent 本身就是負責發送 log 到後端
一般來說, 這個 logging agent 會是一個 container 舉有一個資料架包含所有應用的log
因為這個 agent 要跑在所有 node, 建議使用 DaemonSet 來做佈署
這種做不需要影響到其他的應用
sidecar container 可以有兩種作法:
1 sidecar container 直接串流應用程式的log 到自己的 stdout
使用 sidecar container 來建立 stdout, stderr stream, 就可以利用原本的 kubelet 以及 logging agent 功能
如此一來可以建立兩個 stream 分別不同 format 格式分開
舉例來說:
假設一個 Pod 跑在一個節點內要寫入兩個檔案已兩種不同的格式如下
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: {}
因為有兩個檔案 所以不建議都用 stdout來顯示
這時候可以透過建立兩個 sidecar Container 來做處理
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
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: {}
可以分別針對不同格式做 log
2 sidecar container 跑一個 logging agent, logging agent 負責把 應用程式的 log 串接回來
當 node-level 的 logging agent 不夠彈性時, 就可以使用一個 sidecar container 來跑 logging agent
直接從應用推送 log 到後端