k8s容器日誌收集方案

Humble在掘金發表於2019-04-19

第一種,在 Node 上部署 logging agent,將日誌檔案轉發到後端儲存裡儲存起來

如圖1.1

k8s容器日誌收集方案

  • 不難看到,這裡的核心就在於 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

k8s容器日誌收集方案

  • 現在我的應用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檔案的寫法如下:
    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跟主容器之間是共享Volume的,所以這裡的sidecar方案的二外效能損耗並不高,也就多佔用一點cpu和記憶體。但是,這時候宿主機上世紀會存在兩份相同的日誌檔案:一份是應用自己寫入的;另一份是sidecar的stdout和dtderr對應的json檔案。這對磁碟是很大的浪費。所以說,除非萬不得已或者應用容器完全不能費修改,否則還是建議你直接使用方案一,或者直接使用方案三。

方案三,送過sidecar容器直接把日誌檔案傳送到遠端儲存

  • 相當於把方案一里的logging agent,放在了應用pod裡。如圖1.3:

k8s容器日誌收集方案

  • 在這種方案裡,你的應用還可以直接把日誌輸出到固定的檔案裡而不是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依然可以使用,也是官方推薦的一種。

相關文章