第一種,在 Node 上部署 logging agent,將日誌檔案轉發到後端儲存裡儲存起來
如圖1.1
- 不難看到,這裡的核心就在於 logging agent ,它一般都會以 DaemonSet 的方式執行在節點上,然後將宿主機上的容器日誌目錄掛載進去,最後由 logging-agent 把日誌轉發出去。
- 舉個例子,我們可以通過+Fluentd+專案作為宿主機上的+logging-agent,然後把日誌轉發到遠端的+ElasticSearch+裡儲存起來供將來進行檢索。具體的操作過程官網很多kubernetes的部署裡,會自動為你啟用logrotate,在日誌檔案找過10MB的時候自動對日誌檔案進行roate操作。
- 可以看到在Node上部署logging agent最大的優點,在於只需要部署一個agent,並且不會對應用和pod有任何入侵性。所以這個方案在這區裡是最常用的一種。
- 但是這種方案的補助支出就在於,它要求應用輸出的日誌,都必須是直接輸出到容器的stdout和stderr裡
第二種,對特殊情況的一個處理
- 當容器的日誌只能輸出到某些檔案裡的時候,我們可以通過一個sidecar容器把這些日誌檔案重新輸出到sidecar的stdout和stderr、如圖1.2
- 現在我的應用pod只有一個容器,它會把日誌輸出到容器裡的/var/log/1.log和2.log這兩個檔案裡。這個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)" >> /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: {} 複製程式碼
- 在這種情況下,你用kubect命令是看不到應用的任何日誌的。而且最常用的方案一,也是沒辦法使用的。這時我們就可以為這個pod新增兩個sidecar容器,分別將上述兩個日誌檔案裡的內容重新以stout和stderr的方式輸出來,這個YAML檔案的寫法如下:
*由於sidecar跟主容器之間是共享Volume的,所以這裡的sidecar方案的二外效能損耗並不高,也就多佔用一點cpu和記憶體。但是,這時候宿主機上世紀會存在兩份相同的日誌檔案:一份是應用自己寫入的;另一份是sidecar的stdout和dtderr對應的json檔案。這對磁碟是很大的浪費。所以說,除非萬不得已或者應用容器完全不能費修改,否則還是建議你直接使用方案一,或者直接使用方案三。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: {} 複製程式碼
方案三,送過sidecar容器直接把日誌檔案傳送到遠端儲存
- 相當於把方案一里的logging agent,放在了應用pod裡。如圖1.3:
- 在這種方案裡,你的應用還可以直接把日誌輸出到固定的檔案裡而不是stdout,你的logging-agent還可以使用fluentd,後端儲存還可以是ElasticSearch。只不過,fluentd的輸入源變成了應用的日誌檔案。一般來說,我們會把fluentd的輸入源配置儲存在一個ConfigMap裡,如下所以:
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> 複製程式碼
- 我們在應用pod的定義裡,就可以生命一個Fluentd容器作為sidecar,專門負責將應用生成的1.log和2.log轉發到ElasticSearch中,這個配置,如下所示:
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 複製程式碼
- 如上所示,這個Fluentd容器使用的輸入源,就是通過引用我們前面寫的ConfigMap來指定的。這裡用到了Projected Volume來把ConfigMap掛在到Pod裡。
- 這種方案最燃部署簡單,並且最宿主機非常友好,但是這個sidecar容器很可能會消耗較多的資源,甚至拖垮應用容器。由於日誌沒有輸出到stdout上,所以你通過kubectl logs 是看不到日誌資訊的。
總結
以上,就是k8s最常用的三種收集日誌的手段了,綜合對比以上方案,比較建議你將應用日誌輸出到stdout和stderr,然後通過在蘇書記尚部署logging-agent的方式集中處理日誌、這種方案不但簡單,而且kubectl logs依然可以使用,也是官方推薦的一種。