Kubernetes-日志采集
容器日志采集
基于 FluentBit + Kafka + ELK 的日志采集系统
创建namespace
kubectl create ns logging
消息队列
Zookeeper
StatefulSet
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: zookeeper
namespace: logging
spec:
selector:
matchLabels:
app: zookeeper
serviceName: zookeeper
template:
metadata:
labels:
app: zookeeper
spec:
containers:
- name: zookeeper
image: harbor.axzo.cn/ops/zookeeper:3.9.2
ports:
- containerPort: 2181
name: zookeeperclient
imagePullSecrets:
- name: harbor
EOF
Service
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: zookeeper # 与serviceName 同名
namespace: logging
spec:
clusterIP: None
ports:
- port: 2181
selector:
app: zookeeper
EOF
Kafka
StatefulSet
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
namespace: logging
spec:
selector:
matchLabels:
app: kafka
serviceName: kafka
template:
metadata:
labels:
app: kafka
spec:
imagePullSecrets:
- name: harbor
containers:
- name: kafka
image: harbor.axzo.cn/ops/kafka:3.3.2
ports:
- containerPort: 9092
name: service
env:
- name: ALLOW_ANONYMOUS_LOGIN
value: "yes"
- name: KAFKA_CFG_ZOOKEEPER_CONNECT
value: "zookeeper:2181"
- name: ALLOW_PLAINTEXT_LISTENER
value: "yes"
EOF
Service
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: kafka
namespace: logging
spec:
clusterIP: None
ports:
- port: 9092
selector:
app: kafka
EOF
配置
# 创建topic
kubectl exec -it kafka-0 -n logging -- /opt/bitnami/kafka/bin/kafka-topics.sh --create --bootstrap-server kafka:9092 --replication-factor 1 --partitions 1 --topic k8s-logs
# 列出topic
kubectl exec -it kafka-0 -n logging -- /opt/bitnami/kafka/bin/kafka-topics.sh --list --bootstrap-server kafka:9092
FluentBit
ConfigMap
kubectl apply -f - <<EOF
kind: ConfigMap
apiVersion: v1
metadata:
name: fluent-bit-config
namespace: logging
labels:
app: fluent-bit
data:
filter.conf: |-
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
K8S-Logging.Parser On
K8S-Logging.Exclude Off
Annotations Off
[FILTER]
Name grep
Match kube.*
regex \$kubernetes['labels']['logging'] true
[FILTER]
Name record_modifier
Match kube.*
Remove_key kubernetes.container*
Remove_key kubernetes.pod_id
Remove_key kubernetes.docker_id
Remove_key kubernetes.labels
Remove_key log
fluent-bit.conf: |-
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
@INCLUDE input.conf
@INCLUDE filter.conf
@INCLUDE output-kafka.conf
input.conf: |-
[INPUT]
Name tail
Tag kube.*
Path /var/log/containers/*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 1MB
Skip_Long_Lines On
Refresh_Interval 10
output-kafka.conf: |-
[OUTPUT]
Name kafka
Match kube.*
Brokers kafka:9092
Topics k8s-logs
Timestamp_Key @timestamp
Timestamp_Format iso8601
Retry_Limit false
# hides errors "Receive failed: Disconnected" when kafka kills idle connections
rdkafka.log.connection.close false
# producer buffer is not included in http://fluentbit.io/documentation/0.12/configuration/memory_usage.html#estimating
rdkafka.queue.buffering.max.kbytes 20240
# for logs you'll probably want this ot be 0 or 1, not more
rdkafka.request.required.acks 1
parsers.conf: |-
[PARSER]
Name axzo_json
Format json
[PARSER]
Name apache
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?\$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache2
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?\$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache_error
Format regex
Regex ^\[[^ ]* (?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])?( \[client (?<client>[^\]]*)\])? (?<message>.*)\$
[PARSER]
Name nginx
Format regex
Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?\$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name json
Format json
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
[PARSER]
Name syslog
Format regex
Regex ^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)\$
Time_Key time
Time_Format %b %d %H:%M:%S
[PARSER]
Name axzo-os-log
Format regex
Regex ^(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<process>[^ ]*)\: (?<message>.*)\$
Time_Key time
Time_Format %b %d %H:%M:%S
EOF
ServiceAccount
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluent-bit
namespace: logging
EOF
ClusterRole
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluent-bit-read
rules:
- apiGroups:
- ""
resources:
- namespaces
- pods
verbs:
- get
- list
- watch
EOF
ClusterRoleBinding
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: fluent-bit-read
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: fluent-bit-read
subjects:
- kind: ServiceAccount
name: fluent-bit
namespace: logging
EOF
DaemonSet
kubectl apply -f - <<EOF
kind: DaemonSet
apiVersion: apps/v1
metadata:
name: fluent-bit
namespace: logging
labels:
app: fluent-bit-logging
spec:
selector:
matchLabels:
app: fluent-bit-logging
template:
metadata:
labels:
app: fluent-bit-logging
spec:
volumes:
- name: varlog
hostPath:
path: /var/log
type: ''
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
type: ''
- name: fluent-bit-config
configMap:
name: fluent-bit-config
defaultMode: 420
containers:
- name: fluent-bit
image: 'harbor.axzo.cn/ops/fluent-bit:1.8.0'
ports:
- name: 2020tcp02
containerPort: 2020
protocol: TCP
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 256Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
readOnly: true
mountPath: /var/lib/docker/containers
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
livenessProbe:
httpGet:
path: /
port: 2020
scheme: HTTP
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/v1/metrics/prometheus
port: 2020
scheme: HTTP
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: Always
securityContext:
capabilities: {}
restartPolicy: Always
terminationGracePeriodSeconds: 11
dnsPolicy: ClusterFirst
serviceAccountName: fluent-bit
serviceAccount: fluent-bit
securityContext: {}
imagePullSecrets:
- name: harbor
schedulerName: default-scheduler
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
- operator: Exists
effect: NoExecute
- operator: Exists
effect: NoSchedule
dnsConfig:
options:
- name: single-request-reopen
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 3
revisionHistoryLimit: 10
EOF
ELK
ElasticSearch
Service
kubectl apply -f - <<EOF
---
apiVersion: v1
kind: Service
metadata:
name: elasticsearch
namespace: logging
spec:
clusterIP: None
ports:
- port: 9200
selector:
app: elasticsearch
EOF
ServiceAccount
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: elasticsearch
namespace: logging
labels:
app: elasticsearch
EOF
ClusterRole
kubectl apply -f - <<EOF
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: elasticsearch
labels:
app: elasticsearch
rules:
- apiGroups:
- ""
resources:
- "services"
- "namespaces"
- "endpoints"
verbs:
- "get"
ClusterRoleBinding
kubectl apply -f - <<EOF
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: logging
name: elasticsearch
labels:
app: elasticsearch
subjects:
- kind: ServiceAccount
name: elasticsearch
namespace: logging
apiGroup: ""
roleRef:
kind: ClusterRole
name: elasticsearch
apiGroup: ""
StatefulSet
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: StatefulSet #使用statefulset创建Pod
metadata:
name: elasticsearch #pod名称,使用statefulSet创建的Pod是有序号有顺序的
namespace: logging #命名空间
spec:
replicas: 1 #副本数量,单节点
selector:
matchLabels:
app: elasticsearch #和pod template配置的labels相匹配
template:
metadata:
labels:
app: elasticsearch
spec:
serviceAccountName: elasticsearch
imagePullSecrets:
- name: harbor
volumes:
- name: es-data
persistentVolumeClaim:
claimName: es-data
- name: localtime
hostPath:
path: /etc/localtime
containers:
- image: harbor.axzo.cn/ops/elasticsearch:8.14.1
name: elasticsearch
resources:
# need more cpu upon initialization, therefore burstable class
limits:
cpu: 1000m
memory: 2Gi
requests:
cpu: 100m
memory: 500Mi
ports:
- containerPort: 9200
protocol: TCP
volumeMounts:
- name: es-data
mountPath: /usr/share/elasticsearch/data/ #挂载点
- name: localtime
readOnly: true
mountPath: /etc/localtime
env:
- name: "NAMESPACE"
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: "discovery.type" #定义单节点类型
value: "single-node"
- name: ES_JAVA_OPTS #设置Java的内存参数,可以适当进行加大调整
value: "-Xms512m -Xmx2g"
- name: "ELASTIC_PASSWORD"
value: "TvTOidLL3R9xEDN@"
EOF
LogStash
ConfigMap
kubectl apply -f - <<EOF
kind: ConfigMap
apiVersion: v1
metadata:
name: logstash-os-configmap
namespace: logging
data:
logstash.conf: |
input {
kafka{
bootstrap_servers => "kafka:9092"
topics => "axzo-linux-os"
consumer_threads => 10
decorate_events => true
codec => json
auto_offset_reset => "latest"
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
user => "elastic"
password => "TvTOidLL3R9xEDN@"
index => "axzo-log-os-%{+YYYY.MM.dd}"
}
stdout {
codec => rubydebug
}
}
logstash.yml: |
http.host: "0.0.0.0"
path.config: /usr/share/logstash/pipeline
EOF
Deployment
kubectl apply -f - <<EOF
kind: Deployment
apiVersion: apps/v1
metadata:
name: logstash-os
namespace: logging
spec:
replicas: 1
selector:
matchLabels:
app: logstash-os
template:
metadata:
labels:
app: logstash-os
spec:
volumes:
- name: config-volume
configMap:
name: logstash-os-configmap
items:
- key: logstash.yml
path: logstash.yml
defaultMode: 420
- name: logstash-pipeline
configMap:
name: logstash-os-configmap
items:
- key: logstash.conf
path: logstash.conf
defaultMode: 420
- name: localtime
hostPath:
path: /etc/localtime
type: ''
containers:
- name: logstash
image: harbor.axzo.cn/ops/logstash:8.14.1
resources:
limits:
cpu: '1'
memory: 1Gi
requests:
cpu: 500m
memory: 1Gi
volumeMounts:
- name: config-volume
mountPath: /usr/share/logstash/config
- name: logstash-pipeline
mountPath: /usr/share/logstash/pipeline
- name: localtime
mountPath: /etc/localtime
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst
securityContext: {}
imagePullSecrets:
- name: harbor
schedulerName: default-scheduler
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
EOF
Kibana
Service
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: kibana
namespace: logging
spec:
type: NodePort #采用nodeport方式进行暴露,端口默认为25601
ports:
- port: 5601
nodePort: 32601
protocol: TCP
selector:
app: kibana
EOF
Deployment
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
namespace: logging
labels:
app: kibana
spec:
replicas: 1
selector:
matchLabels:
app: kibana
template:
metadata:
labels:
app: kibana
spec:
imagePullSecrets:
- name: harbor
volumes:
- name: localtime
hostPath:
path: /etc/localtime
containers:
- name: kibana
image: harbor.axzo.cn/ops/kibana:8.14.1
resources:
# need more cpu upon initialization, therefore burstable class
limits:
cpu: 1000m
requests:
cpu: 100m
env:
- name: ELASTICSEARCH_HOSTS
value: "http://elasticsearch:9200"
- name: ELASTICSEARCH_URL
value: "http://elasticsearch:9200"
- name: ELASTICSEARCH_USERNAME
value: "kibana"
- name: ELASTICSEARCH_PASSWORD
value: "TvTOidLL3R9xEDN@"
ports:
- containerPort: 5601
protocol: TCP
volumeMounts:
- name: localtime
readOnly: true
mountPath: /etc/localtime
EOF