容器內應用日誌採集方案介紹與實踐

本文主要分爲兩部分,第一部分介紹容器內日誌收集主要的解決方案和思路,並對各個解決方案的優劣進行比較。第二部分將分別詳細介紹基於Fluentd的Sidecar模式日誌採集部署方案和基於filebeat集成應用基礎鏡像的日誌採集方案。注意:本文重心在於介紹容器內應用日誌的收集方案,並不針對於對於容器引擎或者kubernetes本生系統日誌收集。

第一部分:容器內日誌收集主要的解決方案和比較

方案1.在宿主機上實現日誌採集(Logging at the node Level)
在宿主機上實現日誌採集(Logging at the node Level)

我們知道,容器內應用輸出到stdout和stderr的日誌會被容器引擎所處理,例如docker就可以配置其logging driver,默認的logging driver是json-file,通過在宿主機中查看對應容器的logs文件,就可以查看到應用容器輸出的日誌。同樣Kubernetes也是一樣的,pod內容器輸出到stdout和stderr的內容會被按照json格式存儲的文件系統。那麼基於這樣的原理,如果我們在宿主機上部署如fluentd和filebeat這樣的工具,我們就能夠實現容器內應用日誌的採集。那麼基於這樣方式的部署,優點在於部署簡單,日誌採集資源開銷小,但是會帶來以下幾個問題:

問題1.應用容器的日誌輸出到stdout和stderr,同時被容器引擎處理後落到本地磁盤,隨着日誌的增多(如果應用容器不停止),會大量佔用本地空間。
解決辦法:引入滾動日誌工具,將日誌進行切割,並滾動生成,這樣落盤的日誌就不會一直消耗本地存儲空間。
問題2.我們在宿主機上進行日誌採集,會統一部署一套採集工具,那麼如何來標記日誌是屬於哪個應用以及日誌屬於哪個容器?
解決辦法:進行本地日誌採集工具的開發或者引入開源的日誌採集工具,例如:阿里的log-pilot (https://github.com/AliyunContainerService/log-pilot)
主體思路是日誌採集工具能夠通過容器引擎提供的API(如:dokcer)監聽到容器的啓動和停止,從而能夠找到容器與日誌的對應關係,從而在採集日誌的時候標識出日誌屬於哪個容器。當然如何標識日誌屬於哪個應用,應該還需要應用層打贏日誌的時候進行標識,日誌採集工具在採集日誌的時候,對日誌進行解析。
問題3.json-file格式的日誌,對於某些開發語言(例如JAVA)異常堆棧信息是被分行處理,日誌採集後還需要進行二次加工。
解決辦法:目前來看只能通過對日誌採集工具進行二次開發或者對日誌進行二次處理來解決。
可以看出,爲了解決上述問題,我們不得不將進行一些二次開發和處理工作,這增加了工作量和難度,所以除非有充足資源的情況下,可以採取該方案。

方案2.將日誌採集工具打包到應用基礎鏡像之中(Logging at the App Level)
方案2.將日誌採集工具打包到應用基礎鏡像之中(Logging at the App Level)

爲了解決宿主機日誌收集存在的問題,同時不增加工作量和難度,我們可以考慮將日誌採集軟件,例如:filebeat和fluentd集成到應用的基礎鏡像之中,應用基於基礎鏡像來打包應用鏡像,在鏡像啓動的時候會把日誌採集工具以後臺進程的方式拉起來,蒐集到日誌後,日誌採集工具可以將日誌送到消息中間件,如Kafka,再由Logstash解析入ElasticSearch,最終用戶可以通過Kibana進行日誌檢索。基於上述方式的部署,我們將採集工具和應用部署到一起,這樣我們就能輕鬆的在日誌採集工程中標識出日誌所屬的應用,同時因爲日誌是按照文件格式落到容器內部,一般的日誌採集軟件都能夠通過multiline的方式來處理異常堆棧信息。那麼對於日誌增多的問題,還是需要應用引入滾動日誌工具來解決。
該方案優點在於實現難度小,不增加工作量,同時能夠準確的標識出日誌所述的應用和所屬的文件,使得應用方能夠更加精確的管理日誌。缺點在於每個應用鏡像中都會引入額外的日誌採集工具,增加了應用容器額外的資源開銷,同時日誌採集工具和應用基礎鏡像緊耦合。

方案3.基於Sidecar日誌採集方式(Logging base on Sidecar)

方案3.基於Sidecar日誌採集方式(Logging base on Sidecar)
在Kubernetes中,Pod中可以存在多個容器,這些容器之間可以通過emptyDir的方式共享文件,基於上述的原理,我們能夠將日誌採集工具從應用基礎鏡像中剝離處理,單獨部署於一個容器中,這個日誌容器與應用容器存在於一個Pod之中,他們之間共享應用容器的日誌文件,從而實現應用容器將日誌寫入本地容器內部文件,日誌採集容器讀取文件內容並進行解析和傳輸。
該方案是第二個方案的優化版本,其減小了日誌採集工具與應用基礎鏡像的耦合,缺點在於每個應用Pod內都需要啓動一個Sidecar日誌採集容器,不過通過實際觀察來看,該Sidecar日誌採集容器只需要很小的資源,即可完成日誌採集。

第二部分:詳細方案介紹
1.方案2詳細部署介紹
1.1 部署環境
應用基礎鏡像:Centos7.3+Openjdk1.8+Tomcat8.5.20+filebeat6.2.4
容器引擎:Docker
日誌輸出位置:Kafka

1.2 應用基礎鏡像介紹
首先看一下基礎鏡像的Dockerfile,可以看到在應用的基礎鏡像中添加了filebeat-6.2.4-x86_64.rpm,並進行了安裝,最後通過start.sh將Tomcat進行啓動,我們繼續看看start.sh做了什麼。

     FROM 192.168.1.2:5000/centos:7.3.1611

USER root
WORKDIR /home/tomcat
RUN mkdir -p /usr/java
ADD openjdk-8u40.tar /usr/java
ENV JAVA_HOME /usr/java/java-se-8u40-ri
ENV CATALINA_HOME /home/tomcat
ENV PATH=$JAVA_HOME/bin:$CATALINA_HOME/bin:$PATH
ENV LANG en_US.UTF-8
ENV FILEBEAT_OUTPUT kafka
ADD start.sh /home/tomcat/
ADD config_filebeat.sh /home/tomcat/
ADD filebeat-6.2.4-x86_64.rpm /home/
RUN rpm -ivh /home/filebeat-6.2.4-x86_64.rpm
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime;\
        /bin/echo -e "ZONE="Asia/Shanghai"/nUTC=false/nRTC=false" > /etc/sysconfig/clock;\
        rm bin/*.bat;\
        groupadd -g 8000 xkx;\
        useradd --create-home --no-log-init --password mypasswd1 -g xkx -u 8000 --shell /home/tomcat xkx;\
        chown -Rf xkx.xkx /home/tomcat;
user xkx
EXPOSE 8080
CMD ["sh","/home/tomcat/start.sh"]

在start.sh中我們首先通過環境變量來生成filebeat的配置文件,環境變量主要包含日誌的位置、應用標識、Kafka brokers的地址和Kafka Topic名稱,接下來通過後臺方式啓動filebeat,完成啓動後首先檢查filebeat是否啓動成功,如果啓動成功,則以前臺方式啓動Tomcat。(注意:一定要以前臺方式啓動Tomcat,不然可能出現應用已經退出,而容器還存在)

獲取容器中的環境變量並執行腳本配置filebeat,生成配置文件
sh /home/tomcat/config_filebeat.sh
#啓動filebeat
nohup /usr/bin/filebeat -c /etc/filebeat/filebeat.yml >/dev/null 2>&1 &
#檢查filebeat啓動是否成功
ps -ef | grep filebeat |grep -v grep
PROCESS_1_STATUS=$?
echo $PROCESS_1_STATUS
if [ $PROCESS_1_STATUS -ne 0 ]; then
    echo "Failed to start filebeat: $PROCESS_1_STATUS"
    exit $PROCESS_1_STATUS
fi
sleep 5
#最後通過前臺方式啓動Tomcat
sh /home/tomcat/bin/catalina.sh run

接下來我們看下config_filebeat.sh腳本的片段,可以看到,我們主要通過環境變量來配置filebeat.yml文件

cat > $FILEBEAT_CONFIG << EOF
logging.level: error
logging.to_syslog: false
logging.to_files: false
logging.metrics.enabled: false
filebeat.prospectors:
- type: log
  enabled: true
  paths:
    - ${TOMCATLOG}*
  fields:
    type: tomcat_catalina_log
    PORT0: ${PORT0}
    host: ${HOST}
    app: ${_APP}
  fields_under_root: true
  multiline.pattern: ^[0-9]
  multiline.negate: true
  multiline.match: after
EOF
}

kafka() {
assert_not_empty "$KAFKA_BROKERS" "KAFKA_BROKERS required"
KAFKA_BROKERS=$(/usr/bin/echo $KAFKA_BROKERS|awk -F, '{for(i=1;i<=NF;i++){printf "\"%s\",", $i}}')
KAFKA_BROKERS=${KAFKA_BROKERS%,}

cat >> $FILEBEAT_CONFIG << EOF
$(base)
output.kafka:
    hosts: [$KAFKA_BROKERS]
    topic: '%{[type]}'
EOF
}

最終生成的filebeat.yml效果如下圖所示:
在這裏插入圖片描述
最後我們看下tomcat前臺啓動,我們知道catalina.sh的參數,如果參數爲"run"的化,tomcat會以前臺方式啓動,並將日誌輸出到標準輸出,那麼爲了日誌採集,我們需要將前臺啓動下的日誌輸出到本地文件,這裏我們只需要簡單修改下catalina.sh腳本,修改如下,首先將CATALINA_OUT文件名稱添加上HOST和PORTS,以標識日誌所屬容器,這樣filebeat在日誌採集的時候,就可以將日誌文件名稱獲取,接下來在以run參數啓動的位置添加日誌輸出位置。

在這裏插入圖片描述
在這裏插入圖片描述
完成上述工作後,我們的基礎鏡像就製作好了,這時候應用基於這個基礎鏡像進行製作,並將日誌輸出到指定的位置,日誌就可以通過filebeat採集到kafka並消費到ES
在這裏插入圖片描述

1.3 總結
以上簡單介紹了方案2的一個實際案例,通過在基礎鏡像中集成filebeat實現應用tomcat日誌的採集

2.方案3詳細部署介紹
2.1 部署環境
容器編排器:Kubernetes
應用基礎鏡像:alpine linux3.8+openjdk1.8+tomcat8.5.38
日誌採集基礎鏡像:alpine linux3.8+fluentd+kafka plugin
2.2 日誌採集基礎鏡像介紹
日誌採集鏡像是基於fluentd+kafka plugin來製作的,起Dockerfile由於篇幅原因,這裏貼出部分內容,在dockerfile中我們通過entrypoint.sh來實現權限的變更和fluentd配置文件的生成。
在這裏插入圖片描述
以下是entrypoint.sh內容,我們通過環境變量來替換fluent.conf中的配置信息,並用fluent用戶啓動fluentd.
在這裏插入圖片描述
以下是fluent.conf,這裏面主要定義了爲文件tail類型,並同過變量指定了path和tag,在tag中我們包含了應用和POD IP的信息。接下來在filter和match中都指定了topic和brokers信息。

2.3 kubernetes 應用deployment yaml文件介紹
首先我們POD裏面啓動了兩個容器,分別爲應用容器logtest和日誌採集fluentd-plugin-kafka,並且定義了一個emptyDir類型的卷,通過emptyDir實現了兩個容器的日誌文件共享,同時定義環境環境變量將fluentd的配置信息傳入到日誌採集鏡像當中,用於配置文件生成。

    kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: logtest-app
  namespace: xkx
  labels:
    app:xkx
    version: v1
spec:
#replicas: 2
  template:
    metadata:
      labels:
        app: logtest-app
    spec:
      nodeSelector:
        node-mode: app1        
      containers:
      - name: logtest-app
        image: logtest:3.0
        volumeMounts:
         - name: logdir
           mountPath: /usr/local/tomcat/logs/
        ports:
         - containerPort: 8080
        resources:
           limits:
            cpu: 800m
            memory: 2Gi
        requests:
            cpu: 400m
            memory: 1Gi
        imagePullPolicy: IfNotPresent
        readinessProbe:
            tcpSocket:
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
        livenessProbe:
            tcpSocket:
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
      - name: fluentd-log-collector
        image: fluentd-plugin-kafka:v1.6
        volumeMounts:
          - name: logdir
            mountPath: /usr/local/tomcat/logs/
        resources:
            limits:
              cpu: 300m
              memory: 256M
        requests:
              cpu: 150m
              memory: 128M
        env:
          - name: LOGPATH
            value: "/usr/local/tomcat/logs/*" 
          - name: _SYS
            value: "xkx" 
          - name: _SAPP
            value: "logtest"   
          - name: BROKERS
            value: "192.168.100.2:6667,192.168.100.3:6667,192.168.100.4:6667,192.168.100.5:6667,192.168.100.6:6667" 
          - name: _APP
            value: "logtest"
          - name: KAFKA_TOPIC
            value: "testt"
          - name: HOST
            valueFrom: 
               fieldRef:
                  apiVersion: v1
                  fieldPath: status.podIP
      imagePullSecrets:
        - name: xkx-rep
      volumes:
        - name: logdir
          emptyDir: {}

2.4 總結
上面通過一個實際的案例演示了方案3的部署,可以看到藉助於kubernetes pod的概念,我們能夠以sidecar的方式部署日誌採集程序,並且能夠實現對日誌標上應用以及POD的一些信息,從而方便應用方進行日誌查詢和檢索。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章