对于 K8S 内的容器日志收集,一般有两种常用的方式:
1、使用 DaemonSet 在每台 Node 上部署一个日志收集容器,用于收集当前 Node 上所有容器挂载到宿主机目录下的日志
2、使用 SideCar 模式将日志收集容器与业务容器部署在同一个 Pod 中,只收集对应容器的日志
这两种方式各有优缺点。使用 DaemonSet 方式部署日志收集服务,管理起来简单,但是如果一个 Node 中运行了过多的 Pod,那么日志收集会存在性能瓶颈。使用 SideCar 模式可以更有针对性的收集容器的日志,但是缺点是在运行了很多的业务时,SideCar 占用的资源也会增加。同时这种方式也会出现跟业务耦合的问题。
在我们实践 K8S 的过程中,结合当前业务的特点,最终选定了 SideCar 的方式,虽然占用的资源会增加,但是基于后期大规模日志收集稳定性的考虑,这些资源消耗是在承受范围之内的。
而日志收集的组件,经过长时间比对各种开源服务,选择了 Filebeat。一个是因为在业务未上容器之前,就是使用的 Filebeat 来收集的日志,在维护方面有很多的经验。还有就是 Filebeat 可以处理一些日志收集中出现的复杂情况,例如对于多行日志的处理(堆栈日志)。
下面对于整个实施步骤进行说明。
一、创建命名空间
首先创建一个命名空间,yaml 文件内容如下:
---
apiVersion: v1
kind: Namespace
metadata:
name: ns-smc-gateway
labels:
name: ns-smc-gateway
二、创建 Filebeat 配置文件
由于是使用容器的方式运行 FIlebeat,所以需要使用 Configmap 创建一个 Filebeat 的配置文件,然后通过卷挂载的方式挂载到 Filebeat 容器的指定目录下。
创建 Configmap 的 yaml 文件内容如下(注意命名空间的配置,否则后面 Filebeat 无法挂载 Configmap
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: ns-smc-gateway
labels:
k8s-app: filebeat
data:
filebeat.yml: |-
filebeat.idle_timeout: 2s
filebeat.inputs:
- type: log
paths:
- /opt/logs/app.log
fields:
type: app-log
enabled: true
backoff: 1s
backoff_factor: 2
close_inactive: 1h
encoding: plain
harvester_buffer_size: 262144
max_backoff: 10s
max_bytes: 10485760
scan_frequency: 10s
tail_lines: true
- type: log
paths:
- /opt/logs/app.err
fields:
type: app-err-log
enabled: true
backoff: 1s
backoff_factor: 2
close_inactive: 1h
encoding: plain
harvester_buffer_size: 262144
max_backoff: 10s
max_bytes: 10485760
scan_frequency: 10s
tail_lines: true
filebeat.name: filebeat-shiper
filebeat.spool_zie: 50000
output.elasticsearch:
bulk_max_size: 8192
hosts:
- 10.16.12.206:30187
- 10.16.12.207:30187
- 10.16.12.208:30187
- 10.16.13.214:30187
- 10.16.13.215:30187
index: smc-gateway-%{[fields.type]}-*
indices:
- index: smc-gateway-app-log-%{+yyyy.MM.dd}
when.equals:
fields.type: app-log
- index: smc-gateway-app-err-log-%{+yyyy.MM.dd}
when.equals:
fields.type: app-err-log
workers: 4
processors:
- drop_fields:
fields:
- agent.ephemeral_id
- agent.hostname
- agent.id
- agent.type
- agent.version
- ecs.version
- input.type
- log.offset
- version
- decode_json_fields:
fields:
- message
max_depth: 1
overwrite_keys: true
setup.ilm.enabled: false
setup.template.name: smc-gateway-log
setup.template.pattern: smc-gateway-*
setup.template.overwrite: true
setup.template.enabled: true
三、创建 Deployment
接下来使用 Deployment 控制器来部署 Pod,这个 Pod 中包含了业务容器和 Filebeat 服务容器。yaml 文件内容如下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: smc-gateway
namespace: ns-smc-gateway
labels:
app: smc-gateway
spec:
replicas: 1
selector:
matchLabels:
app: smc-gateway
template:
metadata:
name: smc-gateway
labels:
app: smc-gateway
spec:
imagePullSecrets:
- name: harbor-secret
containers:
- name: smc-gateway-for-test # 业务容器的名称
image: 10.16.12.204/smc-gateway/smc-gateway:1.1 # 业务容器镜像的私服下载地址
imagePullPolicy: Always
env: # 需要传入到业务容器中的环境变量,在服务启动的时候调用
- name: data_center
value: bx
- name: server_mode
value: test
volumeMounts: # 指定业务日志在容器中输出的目录
- name: logdata
mountPath: /opt/logs
- name: filebeat-for-smc-gateway # 指定 Filebeat 容器的名称
image: docker.elastic.co/beats/filebeat:7.3.0 # filebeat 镜像下载地址,这里使用的官方镜像仓库
args: [ # 指定服务启动时的参数
"-c", "/opt/filebeat/filebeat.yml", # 注意这里,文件的路径和名称要和 Configmap 传入的路径和名称一致
"-e",
]
env:
- name: POD_IP # 这里是将 pod 的 IP 地址赋值给这个变量传入容器中,便于后面 Filebeat 在日志中添加自定义的字段信息
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: pod_name # 这里是将 pod 的名称赋值给这个变量,便于后面 Filebeat 在日志中添加自定义的字段信息
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
securityContext: # 这里用于设置 Pod 的安全上下文
runAsUser: 0 # 指定容器内的服务以 ID 为 0 的用户运行(也就是root)
volumeMounts:
- name: config # 指定 filebeat 配置文件要挂载的路径
mountPath: /opt/filebeat/
- name: logdata # 指定卷的名称,这个名称要和前面业务容器指定的日志路径的卷名称一致,这样后面才会挂载同一个 emptyDir
mountPath: /opt/logs/ # 指定要将业务的日志挂载在 filebeat 容器中的路径,这个路径要和 Configmap 中指定的日志路径一致
volumes:
- name: logdata # 为前面声明名称为 logdata 的卷挂载一个 emptyDir 卷(filebeat 和业务容器都会挂载这个卷)
emptyDir: {}
- name: config # 为前面声明名称为 config 的卷(filebeat 配置文件)挂载一个 configmap,并指定configmap 的名称
configMap:
name: filebeat-config
items:
- key: filebeat.yml
path: filebeat.yml
需要注意的是,使用 SideCar 模式收集容器日志实现的本质,就是将业务容器内的日志路径存储到本地宿主机的一个目录下,然后这个目录也会同时挂载到日志收集服务容器中,这样日志收集服务才可以读取到业务的日志。
所以上面的 yaml 文件中,首先业务容器的日志目录声明为一个卷,并命名为 logdata。而下面的 Filebeat 容器中也声明了一个 logdata 的卷。由于这两个卷名称一样,所以这两个卷挂载是同一个 emptyDir。也就实现了在 Filebeat 容器中读取业务日志的目的。当然使用 emptyDir 将容器中的日志存储到宿主机本地并不是持久化存储,emptyDir 会随着容器的删除而销毁。
四、创建 Service
将业务容器的 8080 端口暴露出去,yaml 文件内容如下:
---
apiVersion: v1
kind: Service
metadata:
name: smc-service
labels:
app: smc-service
namespace: ns-smc-gateway
spec:
ports:
- port: 8080
targetPort: 8080
nodePort: 30378
selector:
app: smc-gateway
type: NodePort
五、验证
将以上 yaml 文件执行后,会在 ns-smc-gateway 命名空间下创建一个 Pod,这个 Pod 中包含两个容器。
[@k8s-master2 ~]# kubectl get pods -n ns-smc-gateway
NAME READY STATUS RESTARTS AGE
smc-gateway-fcf598c4b-wphhr 2/2 Running 0 150m
[@k8s-master2 ~]# kubectl describe pod smc-gateway-fcf598c4b-wphhr -n ns-smc-gateway | grep -B1 "Container ID"
smc-gateway-for-test:
Container ID: docker://3711e3a5bc8fafc94ea174578c0a79774f5b25c7eae6c7aa47759e513645f221
--
filebeat-for-smc-gateway:
Container ID: docker://1b87242cdf8632edc1bea2fe23910f68c2d5da0254163b123bfc414829f7bea7
此时到 Kibana 中的索引管理中,会看到已经新增了两个业务索引,配置对应的索引模式后,就可以在 Kibana 中看到对应的日志。
Filebeat 根据不同的日志设置不同的索引
平时在物理机上使用 Filebeat 收集日志时,会编写多个 filebeat 配置文件然后启动多个 filebeat 进程来收集不同路径下的日志并设置相对应的索引。那么如果将所有的日志路径都写到一个 filebeat 配置文件中,就需要根据不同的日志来设置索引了。
其实 logstash 也可以实现这个功能。但是此处只演示在 Filebeat 上实现。步骤和讲解如下:
例如现在有如下三个日志文件,需要输出到不同的索引:
access.log ----> 索引:web-nginx-access-log
error.log ----> 索引:web-nginx-error-log
blacklist.log ----> 索引:web-nginx-blacklist-log
所需要的 filebeat 配置文件如下:
filebeat.idle_timeout: 2s
filebeat.name: filebeat-shiper
filebeat.spool_zie: 50000
filebeat.inputs: # 从这里开始定义每个日志的路径、类型、收集方式等信息
- type: log # 指定收集的类型为 log
paths:
- /usr/local/nginx/logs/access.log # 设置 access.log 的路径
fields: # 设置一个 fields,用于标记这个日志
type: access-log # 为 fields 设置一个关键字 type,值为 access-log
enabled: true
backoff: 1s
backoff_factor: 2
close_inactive: 1h
encoding: plain
harvester_buffer_size: 262144
max_backoff: 10s
max_bytes: 10485760
scan_frequency: 10s
tail_lines: true
- type: log
paths:
- /usr/local/nginx/logs/error.log # 设置 error.log 的路径
fields: # 设置一个 fields,用于标记这个日志
type: error-log # 为 fields 设置一个关键字 type,值为 error-log
enabled: true
backoff: 1s
backoff_factor: 2
close_inactive: 1h
encoding: plain
harvester_buffer_size: 262144
max_backoff: 10s
max_bytes: 10485760
scan_frequency: 10s
tail_lines: true
- type: log
paths:
- /usr/local/nginx/logs/blacklist.log # 设置 blacklist.log 的路径
fields: # 设置一个 fields,用于标记这个日志
type: blacklist-log # 为 fields 设置一个关键字 type,值为 blacklist-log
enabled: true
backoff: 1s
backoff_factor: 2
close_inactive: 1h
encoding: plain
harvester_buffer_size: 262144
max_backoff: 10s
max_bytes: 10485760
scan_frequency: 10s
tail_lines: true
output.elasticsearch:
workers: 4
bulk_max_size: 8192
hosts: # 设置 elastic 的地址
- 10.16.12.206:30187
- 10.16.12.207:30187
- 10.16.12.208:30187
- 10.16.13.214:30187
- 10.16.13.215:30187
index: web-nginx-%{[fields.type]}-%{+yyyy.MM.dd} # 设置索引名称,后面引用的 fields.type 变量。此处的配置应该可以省略(不符合下面创建索引条件的日志,会使用该索引,后续会测试是否是这样)
indices: # 使用 indices 代表要创建多个索引
- index: web-nginx-access-log-%{+yyyy.MM.dd} # 设置 access.log 日志的索引,注意索引前面的 web-nginx 要与setup.template.pattern 的配置相匹配
when.equals: # 设置创建索引的条件:当 fields.type 的值等于 access-log 时才生效
fields.type: access-log
- index: web-nginx-error-log-%{+yyyy.MM.dd}
when.equals:
fields.type: error-log
- index: web-nginx-blacklist-log-%{+yyyy.MM.dd}
when.equals:
fields.type: blacklist-log
processors:
- drop_fields:
fields:
- agent.ephemeral_id
- agent.hostname
- agent.id
- agent.type
- agent.version
- ecs.version
- input.type
- log.offset
- version
- decode_json_fields:
fields:
- message
max_depth: 1
overwrite_keys: true
setup.ilm.enabled: false # 如果要创建多个索引,需要将此项设置为 false
setup.template.name: web-nginx-log # 设置模板的名称
setup.template.pattern: web-nginx-* # 设置模板的匹配方式,上面索引的前缀要和这里保持一致
setup.template.overwrite: true
setup.template.enabled: true
编辑完成后,启动 filebeat 进程。到 Kibana 中查看索引列表,可以发现已经有三个新创建的索引:
名称 运行状况 状态 主分片 副本分片 文档计数 存储大小
web-nginx-access-log-2020.10.28 green open 1 1 110 73.9kb
web-nginx-error-log-2020.10.28 green open 1 1 354 155kb
web-nginx-blacklist-log-2020.10.28 green open 1 1 460 219.5kb
针对这三个索引创建索引模式后,就可以在 Kibana 中对日志进行展示了。
【Kubernetes】Filebeat以sidecar模式实现容器日志收集
说明
Filebeat可以以sidecar模式来进行容器日志的收集,也就是filebeat和具体的服务容器部署在同一个pod内,指定收集日志的路径或文件,即可将日志发送到指定位置或Elasticsearch这类的搜索引擎。
每个pod内部署filebeat的模式,好处是和具体的应用服务低耦合,可扩展性强,不过需要在yaml进行额外配置。
yaml示例
# app服务
---
apiVersion: v1
kind: Service
metadata:
name: test-app
labels:
app: test-app
spec:
selector:
app: test-app
ports:
- protocol: TCP
port: 8080
targetPort: 8080
name: test-port
#定义日志收集相关配置的一个configmap
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test-filebeat-config
labels:
k8s-app: filebeat
data:
filebeat.yml: |-
filebeat.prospectors:
- type: log
paths:
- /logdata/*.log
tail_files: true
fields:
pod_name: '${pod_name}'
POD_IP: '${POD_IP}'
setup.template.name: "app-logs"
setup.template.pattern: "app-logs-*"
output.elasticsearch: # 日志输出到ES
hosts: ["192.168.1.xx:9200","192.168.1.xxx:9200"]
index: "app-logs-%{+yyyy.MM}"
# deployment, 也可通过daemonset方式
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-app
spec:
replicas: 1
minReadySeconds: 15 #滚动升级15s后标志pod准备就绪
strategy:
rollingUpdate: #replicas为2, 升级过程中pod个数在1-3个之间
maxSurge: 1 #滚动升级时会先启动1个pod
maxUnavailable: 1 #滚动升级时允许pod处于Unavailable的最大个数
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
spec:
terminationGracePeriodSeconds: 30 #30秒内优雅关闭程序
containers:
- image: hub.exmaple.com/publib/filebeat:6.1.3 #提前下载下来到私有镜像库的镜像(官方的可能会被墙)
name: filebeat
args: [
"-c", "/opt/filebeat/filebeat.yml",
"-e",
]
env:
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: pod_name
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
securityContext:
runAsUser: 0
resources:
limits:
memory: 200Mi
requests:
cpu: 200m
memory: 200Mi
volumeMounts:
- name: config #将configmap的内容放到容器本地目录
mountPath: /opt/filebeat/
- name: data
mountPath: /usr/share/filebeat/data
- name: logdata #同一个pod内的两个应用共享目录logdata, 一个写一个读
mountPath: /logdata
- name: test-app
image: hub.example.com/service/test-service:latest #提供具体服务的app镜像
ports:
- containerPort: 8080
volumeMounts:
- name: logdata #指定挂在目录到logdata
mountPath: /usr/local/tomcat/logs
volumes:
- name: data
emptyDir: {}
- name: logdata #定义logdata为EmptyDir类型挂载目录
emptyDir: {}
- name: config
configMap:
name: test-filebeat-config #使用前面定义的configmap
items:
- key: filebeat.yml
path: filebeat.yml
查看filebeat和app的运行日志
kubectl logs -f <pod名> filebeat
kubectl logs -f <pod名> test-app
这时可看到详细的运行日志情况,同时elasticsearch上也会新建一个app-logs-xxxx.xx
的索引。