在生产环境中,ES 通常是不会在 k8s 集群中存在的,一般 MySQL 和 Elasticsearch 都是独立在 k8s 之外。

那么无论哪种 pod,要甩日志到 ES,最轻量的方案肯定是用 filebeat 甩过去了。

当然,如果是阿里的 ACK,logtail 和 logstore 配搭已经非常不错了,根本用不到 filebeat 和 ES。

可但是,我们不想为阿里 sls、logstore 出钱买单,就只能用 filebeat + ES 了

说一下 filebeat 的 sidecar 边车(僚机)用法:

image-20211110192939845

如上图所示,简单说就是起一个 filebeat 的 logging-agent 边车(僚机),边车和主应用之间共享某个文件夹(mountPath),达到收集主应用日志并发送到 ES,而不用动 app-container 分毫。

我们以部署一个 Tomcat 应用为例来说明:

一、打造 filebeat 边车镜像

首先准备 Dockerfile

FROM alpine:3.12  

ARG VERSION=7.15.1  

COPY docker-entrypoint.sh /  

RUN set -x \  
    && cd /tmp \  
    && wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-${VERSION}-linux-x86_64.tar.gz \  
    && tar xzvf filebeat-${VERSION}-linux-x86_64.tar.gz \  
    && mv filebeat-${VERSION}-linux-x86_64 /opt \  
    && rm /tmp/* \  
    && chmod +x /docker-entrypoint.sh  


ENV PATH $PATH:/opt/filebeat-${VERSION}-linux-x86_64  

WORKDIR /opt/filebeat-${VERSION}-linux-x86_64  

ENTRYPOINT ["/docker-entrypoint.sh"]  

我们以 alphine:3.12 为底版,然后下载 filebeat 7.15.1的二进制包并释放到 /opt 下,最后指定入口文件 /docker-entrypoint.sh

奥妙全在这个 docker-entrypoint.sh 中了

#!/bin/bash 
  
cat > /etc/filebeat.yaml << EOF 
filebeat.config.modules:
  path: /opt/filebeat-7.15.1-linux-x86_64/modules.d/*.yml
  reload.enabled: true

# 加入自定义的字段
fields_under_root: true
fields:
  project: kuaijian-tomcat
  pod_ip: ${POD_IP}
  pod_name: ${POD_NAME}
  node_name: ${NODE_NAME}
  pod_namespace: ${POD_NAMESPACE}
  
# 收集云厂商的数据和docker的变量
processors:
  - add_cloud_metadata: ~
  - add_docker_metadata: ~

filebeat.modules:
  - module: apache
    access:
      enabled: true
      var.paths:
      - "/usr/local/tomcat/logs/localhost_access_log.*.txt"
    error:
      enabled: true
      var.paths:
      - "/usr/local/tomcat/logs/application.log*"
      - "/usr/local/tomcat/logs/catalina.*.log"
      - "/usr/local/tomcat/logs/host-manager.*.log"
      - "/usr/local/tomcat/logs/localhost.*.log"
      - "/usr/local/tomcat/logs/manager.*.log"
      
setup.template.name: "tomcat-logs"
setup.template.pattern: "tomcat-logs-*"
output.elasticsearch:
  hosts: ["172.19.20.xxx:9200","172.19.20.xxx:9200"]
  index: "tomcat-logs-%{+yyyy.MM}"
EOF 
 
set -xe 

# If user don't provide any command 
# Run filebeat 
if [[ "$1" == "" ]]; then 
     exec /opt/filebeat-7.15.1-linux-x86_64/filebeat -c /etc/filebeat.yaml  
else 
    # Else allow the user to run arbitrarily commands like bash 
    exec "$@" 
fi 

我们为什么不在 k8s 里用 configmap 来配置 filebeat.yml 呢?

理由是收集日志文件多且路径、类型各不相同,这么一堆的配置都放在 configmap 里会让人癫狂的。所以干脆放到镜像里,便于调试也便于修改。

上面我们也充分利用了 filebeat 的 module,有 module 可用就必须用 module,而不是手动指定 filebeat.inputs ,可用的 mudole 实在太多了,一定要善用!!!另外 tomcat 和 apache 的日志格式是一样的。

我们在最后执行的时候,也加了 exec $@ 便于调试,如果没有指定 CMD,就启动 filebeat,如果指定了比如 /bin/bash,就进入调试状态。

我们打好镜像就 push 到 harbor 里待用

附录:https://www.elastic.co/guide/en/beats/filebeat/current/configuration-general-options.html filebeat的配置列表

二、sidecar部署

我们写一个 k8s 的 tomcat deployment文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat
  labels:
    app: tomcat
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tomcat
  template:
    metadata:
      labels:
        app: tomcat
    spec:
      containers:
      - name: filebeat-sidecar
        image: xxxx.xxxx.xxx/filebeat:xxx
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName
        - name: POD_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name            
        volumeMounts:
        - name: logs-volume
          mountPath: /usr/local/tomcat/logs
      - name: tomcat
        image: tomcat
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: logs-volume
          mountPath: /usr/local/tomcat/logs
      volumes:
      - name: logs-volume
        emptyDir: {}

可以看到我们在这个 deployment 里定义了 pod 是单副本,里面跑了两个 container,一个是 filebeat,一个是 tocmat,两者通过同一个 volume 连接在一起,这样就可以做到不修改 tomcat container 而拿到里面的日志了。

这样就把 tomcat 应用的日志收到 ES 去了。