一、概述
1.统一日志管理的整体方案
Kubernetes本身并没有为日志数据提供原生的存储解决方案,但可以将许多现有的日志记录解决方案集成到Kubernetes集群中。在Kubernetes中,有三个层次的日志:
- 基础日志
- Node级别的日志
- 群集级别的日志架构
1.1.基础日志
kubernetes基础日志即将日志数据输出到标准输出流,可以使用kubectl logs命令获取容器日志信息。
如果Pod中有多个容器,可以通过将容器名称附加到命令来指定要访问哪个容器的日志。
例如,在Kubernetes集群中的devops命名空间下有一个名称为nexus3-f5b7fc55c-hq5v7的Pod,就可以通过如下的命令获取日志:
1
$ kubectl logs nexus3-f5b7fc55c-hq5v7 --namespace=devops
1.2.Node级别的日志
容器化应用写入到stdout和stderr的所有内容都是由容器引擎处理和重定向的。例如,docker容器引擎会将这两个流重定向到日志记录驱动,在Kubernetes中该日志驱动被配置为以json格式写入文件。docker json日志记录驱动将每一行视为单独的消息。当使用docker日志记录驱动时,并不支持多行消息,因此需要在日志代理级别或更高级别上处理多行消息。 默认情况下,如果容器重新启动,kubectl将会保留一个已终止的容器及其日志。如果从Node中驱逐Pod,那么Pod中所有相应的容器也会连同它们的日志一起被驱逐。Node级别的日志中的一个重要考虑是实现日志旋转,这样日志不会消耗Node上的所有可用存储。Kubernetes目前不负责旋转日志,部署工具应该建立一个解决方案来解决这个问题。
1.3.集群级别的日志架构
Kubernetes本身没有为群集级别日志记录提供原生解决方案,但有几种常见的方法可以采用:
- 使用运行在每个Node上的Node级别的日志记录代理
- 在应用Pod中包含一个用于日志记录的sidecar
- 将日志直接从应用内推到后端
2.kubernetes日志收集方案
2.1.在Node上部署logging agent
这种架构的核心点就在logging-agent,通常情况下它会以DS的方式运行在节点上,然后将宿主机的容器日志目录挂载进去,然后由logging-agent将日志收集转发出去。常用的方案有EFK,即用fluentd作为上面的logging-agent,然后将日志转发到远端的ElasticSearch中,然后由kibana进行展示。
这种方案的优点是只需要在节点部署一个logging-agent,而且不会对应用和Pod有任何侵入性,但是其缺点就是必须要应用的日志输出必须输出到stdout和stderr中去。
2.2.sidecar收集日志
由于第一种方式无法收集一些没有标准输出到stdout和stderr中,所以就有了第二种方式对这种特殊情况进行处理:当容器的日志只能输出到某些文件的时候,就可以通过一个sidecar容器把这些日志重新输出到sidecar的stdout和stderr中去,这样就可以继续使用第一种方案进行日志再处理。
由于sidecar跟主容器是共享volume的,所以这里的sidecar方案其实并不会占用太多的资源。但是这种办法实际上是会存在两份文件,一份是系统自己写的文件,第二份是通过sidecar输出到stdout和stderr中所对应的JSON文件,多以对磁盘来说是一个大的开销。
2.3.以sidecar运行logging-agent
这种方案就是直接在sidecar中运行logging-agent,直接将日志转发到后端存储,也就是相当于在方案一中将logging-agent放到一个Pod中了。
在这个方案中,应用可以直接把日志输出到一个文件中,我们的logging-agent可以使用fluentd收集日志转发到后端ElasticSearch中,不过这里的输入源变成了日志文件。
这样虽然更加灵活,但是在 sidecar 容器中运行日志采集代理程序会导致大量资源消耗,因为你有多少个要采集的 Pod,就需要运行多少个采集代理程序,另外还无法使用 kubectl logs 命令来访问这些日志,因为它们不受 kubelet 控制。
2.4.日志采集方式
K8S日志采集方式:
- 原生方式:使用
kubectl logs
直接在查看本地保留的日志,或者通过docker engine的log driver
把日志重定向到文件、syslog、fluentd等系统中。 - DaemonSet方式:在K8S的每个node上部署日志agent,由agent采集所有容器的日志到服务端
- Sidecar方式:一个POD中运行一个sidecar的日志agent容器,用于采集该POD主容器产生的日志
每种采集方式都有一定的优劣势,这里我们进行简单的对比:
原生方式 | DaemonSet方式 | Sidecar方式 | |
---|---|---|---|
采集日志类型 | 标准输出 | 标准输出+部分文件 | 文件 |
部署运维 | 低,原生支持 | 一般,需维护DaemonSet | 较高,每个需要采集日志的POD都需要部署sidecar容器 |
日志分类存储 | 无法实现 | 一般,可通过容器/路径等映射 | 每个POD可单独配置,灵活性高 |
多租户隔离 | 弱 | 一般,只能通过配置间隔离 | 强,通过容器进行隔离,可单独分配资源 |
支持集群规模 | 本地存储无限制,若使用syslog、fluentd会有单点限制 | 中小型规模,业务数最多支持百级别 | 无限制 |
资源占用 | 低,docker engine提供 | 较低,每个节点运行一个容器 | 较高,每个POD运行一个容器 |
查询便捷性 | 低 | 较高,可进行自定义的查询、统计 | 高,可根据业务特点进行定制 |
可定制性 | 低 | 低 | 高,每个POD单独配置 |
适用场景 | 测试、POC等非生产场景 | 功能单一型的集群 | 大型、混合型、PAAS型集群 |
从上述表格中可以看出:
- 原生方式相对功能太弱,一般不建议在生产系统中使用,否则问题调查、数据统计等工作很难完成
- DaemonSet方式在每个节点只允许一个日志agent,相对资源占用要小很多,但扩展性、租户隔离性受限,比较适用于功能单一或业务不是很多的集群
- Sidecar方式为每个POD单独部署日志agent,相对资源占用较多,但灵活性以及多租户隔离性较强,建议大型的K8S集群或作为PAAS平台为多个业务方服务的集群使用该方式
3.官方推荐方案-EFK
采用使用Node日志记录代理的方面进行Kubernetes的统一日志管理,相关的工具采用:
- 日志记录代理(logging-agent):日志记录代理用于从容器中获取日志信息,使用Fluentd
- 日志记录后台(Logging-Backend):日志记录后台用于处理日志记录代理推送过来的日志,使用Elasticsearch
- 日志记录展示:日志记录展示用于向用户显示统一的日志信息,使用Kibana
在Kubernetes中通过了Elasticsearch 附加组件,此组件包括Elasticsearch、Fluentd和Kibana。Elasticsearch是一种负责存储日志并允许查询的搜索引擎。Fluentd从Kubernetes中获取日志消息,并发送到Elasticsearch;而Kibana是一个图形界面,用于查看和查询存储在Elasticsearch中的日志。
1
2
3
4
5
6
7
8
9
10
11
12
# 官方参考
https://github.com/kubernetes/kubernetes/tree/master/cluster/addons
https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch
https://codeload.github.com/kubernetes/kubernetes/tar.gz/refs/tags/v1.20.6
# ES
https://www.elastic.co/cn/
# fluentd
https://www.fluentd.org/
https://github.com/fluent/fluentd
4.EFK组件介绍
Kubernetes中比较流行的日志收集解决方案是Elasticsearch、Fluentd和Kibana(EFK)技术栈,也是官方推荐的一种方案。
- Elasticsearch
Elasticsearch是一个实时的,分布式的,可扩展的搜索引擎,它允许进行全文本和结构化搜索以及对日志进行分析。它通常用于索引和搜索大量日志数据,也可以用于搜索许多不同种类的文档。
- Kibana
Elasticsearch通常与Kibana一起部署,kibana是Elasticsearch 的功能强大的数据可视化的dashboard(仪表板)。Kibana允许你通过Web界面浏览Elasticsearch日志数据,也可自定义查询条件快速检索出elasticccsearch中的日志数据。
- Fluentd
Fluentd是一个流行的开源数据收集器,我们将在Kubernetes 集群节点上安装 Fluentd,通过获取容器日志文件、过滤和转换日志数据,然后将数据传递到 Elasticsearch 集群,在该集群中对其进行索引和存储。
5.限制日志
1
2
3
4
5
6
7
8
9
10
# 限制docker日志大小
https://docs.docker.com/config/containers/logging/json-file/
https://docs.docker.com/config/containers/logging/configure/
# 日志路径
[root@k8s-node1 ~]# cd /var/log/containers/
# 新版本路径
[root@k8s-master containers]# cd /var/log/pods/
二、基础
1.EFK
1
2
3
4
5
6
7
8
9
10
11
12
13
# 官方地址
# kubernetes
https://github.com/kubernetes/kubernetes/tree/master/cluster/addons
# fluentd-elasticsearch
https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch
# 下载源码(与k8s版本对应)
https://codeload.github.com/kubernetes/kubernetes/tar.gz/refs/tags/v1.20.6
https://kubernetes.io/docs/concepts/cluster-administration/logging/
2.EFK部署方案
Elasticsearch、Kibana可以部署在K8S内部或外部,部署在K8S内部需要提供分布式是存储,生产环境建议部署在K8S外部。
两种部署方案:
- 方案一:Fluentd以DaemonSet方式部署,Elasticsearch、Kibana部署在K8S外部
- 方案一:Fluentd以DaemonSet方式部署,Elasticsearch、Kibana部署在K8S内部
3.下载源码
注意:EFK版本要与Kubernetes版本一致
1
2
3
4
5
6
# fluentd-elasticsearch
https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch
# 下载源码(与k8s版本对应)
https://codeload.github.com/kubernetes/kubernetes/tar.gz/refs/tags/v1.20.6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@k8s-master efk]# ll
total 48
drwxr-xr-x 3 root root 169 Nov 19 16:41 es-image
-rw-r--r-- 1 root root 580 Nov 19 16:41 es-service.yaml
-rw-r--r-- 1 root root 3186 Nov 19 16:41 es-statefulset.yaml
-rw-r--r-- 1 root root 16125 Nov 19 16:41 fluentd-es-configmap.yaml
-rw-r--r-- 1 root root 2581 Nov 19 16:41 fluentd-es-ds.yaml
drwxr-xr-x 2 root root 112 Nov 19 16:41 fluentd-es-image
-rw-r--r-- 1 root root 1519 Nov 19 16:41 kibana-deployment.yaml
-rw-r--r-- 1 root root 354 Nov 19 16:41 kibana-service.yaml
-rw-r--r-- 1 root root 189 Nov 19 16:41 OWNERS
drwxr-xr-x 2 root root 33 Nov 19 16:41 podsecuritypolicies
-rw-r--r-- 1 root root 4550 Nov 19 16:41 README.md
[root@k8s-master efk]# ll
total 48
# es
-rw-r--r-- 1 root root 580 Nov 19 16:41 es-service.yaml
-rw-r--r-- 1 root root 3186 Nov 19 16:41 es-statefulset.yaml
# fluentd
-rw-r--r-- 1 root root 16125 Nov 19 16:41 fluentd-es-configmap.yaml
-rw-r--r-- 1 root root 2581 Nov 19 16:41 fluentd-es-ds.yaml
# kibana
-rw-r--r-- 1 root root 1519 Nov 19 16:41 kibana-deployment.yaml
-rw-r--r-- 1 root root 354 Nov 19 16:41 kibana-service.yaml
4.Docker部署Elasticsearch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
***************************************************************
# elasticsearch
# 下载镜像 查看镜像
docker pull elasticsearch:7.4.2
# 运行 elasticsearch
docker run -d --name elasticsearch --network host --restart=always \
-e "discovery.type=single-node" elasticsearch:7.4.2
# 检测 elasticsearch 是否启动成功
curl 127.0.0.1:9200
***************************************************************
# kibana
# 下载镜像 查看镜像
docker pull kibana:7.4.2
# 运行 Kibana
docker run -d --name kibana --network host --restart=always kibana:7.4.2
# 修改配置文件
docker exec -it kibana /bin/bash
/usr/share/kibana/config/kibana.yml
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://172.51.216.98:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true
# 重启docker
# Kibana地址:
http://172.51.216.98:5601
# ES地址:
http://172.51.216.98:9200
三、实践
1.EFK安装部署
1.1.安装Elasticsearch
1
2
3
4
5
# 直接安装,不修改配置,不配置存储
kubectl apply -f es-statefulset.yaml
kubectl apply -f es-service.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看
[root@k8s-master efk]# kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE
......
pod/elasticsearch-logging-0 1/1 Running 0 2d16h
pod/elasticsearch-logging-1 1/1 Running 0 2d16h
......
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/elasticsearch-logging ClusterIP None <none> 9200/TCP,9300/TCP 2d17h
NAME READY AGE
statefulset.apps/elasticsearch-logging 2/2 2d16h
1.2.安装Fluentd
1
2
3
4
5
# 直接安装,不配置存储
kubectl apply -f fluentd-es-configmap.yaml
kubectl apply -f fluentd-es-ds.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 查看
[root@k8s-master efk]# kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE
pod/elasticsearch-logging-0 1/1 Running 0 2d16h
pod/elasticsearch-logging-1 1/1 Running 0 2d16h
pod/fluentd-es-v3.1.0-8j9g8 1/1 Running 6 2d17h
pod/fluentd-es-v3.1.0-dgkcc 1/1 Running 8 2d17h
pod/fluentd-es-v3.1.0-ft7pn 1/1 Running 7 2d17h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/elasticsearch-logging ClusterIP None <none> 9200/TCP,9300/TCP 2d17h
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/fluentd-es-v3.1.0 3 3 3 3 3 <none> 2d17h
NAME READY AGE
statefulset.apps/elasticsearch-logging 2/2 2d16h
# 每个Node一个DaemonSet
[root@k8s-master efk]# kubectl get pod -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
elasticsearch-logging-0 1/1 Running 0 2d17h 10.244.169.180 k8s-node2 <none> <none>
elasticsearch-logging-1 1/1 Running 0 2d17h 10.244.169.166 k8s-node2 <none> <none>
etcd-k8s-master 1/1 Running 10 96d 172.51.216.81 k8s-master <none> <none>
fluentd-es-v3.1.0-8j9g8 1/1 Running 6 2d17h 10.244.36.97 k8s-node1 <none> <none>
fluentd-es-v3.1.0-dgkcc 1/1 Running 8 2d17h 10.244.107.228 k8s-node3 <none> <none>
fluentd-es-v3.1.0-ft7pn 1/1 Running 7 2d17h 10.244.169.171 k8s-node2 <none> <none>
1.3.安装Kibana
1
2
3
4
# 修改配置安装
kubectl apply -f kibana-deployment.yaml
kubectl apply -f kibana-service.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# kibana-deployment.yaml
# 修改 replicas: 3
# NodePort映射端口需要注释
#- name: SERVER_BASEPATH
# value: /api/v1/namespaces/kube-system/services/kibana-logging/proxy
[root@k8s-master efk]# vim kibana-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana-logging
namespace: kube-system
labels:
k8s-app: kibana-logging
addonmanager.kubernetes.io/mode: Reconcile
spec:
replicas: 3
selector:
matchLabels:
k8s-app: kibana-logging
template:
metadata:
labels:
k8s-app: kibana-logging
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: kibana-logging
image: docker.elastic.co/kibana/kibana-oss:7.4.2
resources:
# need more cpu upon initialization, therefore burstable class
limits:
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana-logging
namespace: kube-system
labels:
k8s-app: kibana-logging
addonmanager.kubernetes.io/mode: Reconcile
spec:
replicas: 3
selector:
matchLabels:
k8s-app: kibana-logging
template:
metadata:
labels:
k8s-app: kibana-logging
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: kibana-logging
image: docker.elastic.co/kibana/kibana-oss:7.4.2
resources:
# need more cpu upon initialization, therefore burstable class
limits:
cpu: 1000m
requests:
cpu: 100m
env:
- name: ELASTICSEARCH_HOSTS
value: http://elasticsearch-logging:9200
- name: SERVER_NAME
value: kibana-logging
#- name: SERVER_BASEPATH
# value: /api/v1/namespaces/kube-system/services/kibana-logging/proxy
- name: SERVER_REWRITEBASEPATH
value: "false"
ports:
- containerPort: 5601
name: ui
protocol: TCP
livenessProbe:
httpGet:
path: /api/status
port: ui
initialDelaySeconds: 5
timeoutSeconds: 10
readinessProbe:
httpGet:
path: /api/status
port: ui
initialDelaySeconds: 5
timeoutSeconds: 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# kibana-service.yaml
# 修改 type: NodePort
# 修改nodePort: 30601
[root@k8s-master efk]# vim kibana-service.yaml
apiVersion: v1
kind: Service
metadata:
name: kibana-logging
namespace: kube-system
labels:
k8s-app: kibana-logging
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "Kibana"
spec:
type: NodePort
ports:
- port: 5601
protocol: TCP
targetPort: ui
nodePort: 30601
selector:
k8s-app: kibana-logging
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 查看
[root@k8s-master efk]# kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE
pod/elasticsearch-logging-0 1/1 Running 0 2d16h
pod/elasticsearch-logging-1 1/1 Running 0 2d16h
pod/fluentd-es-v3.1.0-8j9g8 1/1 Running 6 2d17h
pod/fluentd-es-v3.1.0-dgkcc 1/1 Running 8 2d17h
pod/fluentd-es-v3.1.0-ft7pn 1/1 Running 7 2d17h
pod/kibana-logging-7b5d6867cd-nvdk8 1/1 Running 0 14m
pod/kibana-logging-7b5d6867cd-vzs4h 1/1 Running 0 14m
pod/kibana-logging-7b5d6867cd-z7nqg 1/1 Running 0 76m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/elasticsearch-logging ClusterIP None <none> 9200/TCP,9300/TCP 2d17h
service/kibana-logging NodePort 10.103.188.206 <none> 5601:30601/TCP 2d17h
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/fluentd-es-v3.1.0 3 3 3 3 3 <none> 2d17h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/kibana-logging 3/3 3 3 2d17h
NAME DESIRED CURRENT READY AGE
replicaset.apps/kibana-logging-7b5d6867cd 3 3 3 76m
NAME READY AGE
statefulset.apps/elasticsearch-logging 2/2 2d16h
1.4.测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 查看
[root@k8s-master efk]# kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE
pod/elasticsearch-logging-0 1/1 Running 0 2d17h
pod/elasticsearch-logging-1 1/1 Running 0 2d17h
pod/etcd-k8s-master 1/1 Running 10 96d
pod/fluentd-es-v3.1.0-8j9g8 1/1 Running 6 2d17h
pod/fluentd-es-v3.1.0-dgkcc 1/1 Running 8 2d17h
pod/fluentd-es-v3.1.0-ft7pn 1/1 Running 7 2d17h
pod/kibana-logging-7b5d6867cd-m7z5t 1/1 Running 0 6m41s
pod/kibana-logging-7b5d6867cd-mm5gl 1/1 Running 0 6m41s
pod/kibana-logging-7b5d6867cd-w92sf 1/1 Running 0 6m41s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/elasticsearch-logging ClusterIP None <none> 9200/TCP,9300/TCP 2d17h
service/kibana-logging NodePort 10.103.188.206 <none> 5601:30601/TCP 2d17h
service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 96d
service/metrics-server ClusterIP 10.106.104.139 <none> 443/TCP 31d
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/fluentd-es-v3.1.0 3 3 3 3 3 <none> 2d17h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/kibana-logging 3/3 3 3 6m41s
NAME DESIRED CURRENT READY AGE
replicaset.apps/kibana-logging-7b5d6867cd 3 3 3 6m41s
NAME READY AGE
statefulset.apps/elasticsearch-logging 2/2 2d17h
1
2
3
# Kibana地址
http://172.51.216.81:30601/
2.K8S存储方式部署
把安装的ES删除,重新安装,副本改成3个,配置ceph的存储方式。
ES部署方式是StatefulSet,选择有状态服务的存储方式,块存储(RBD)
2.1.修改配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# es-statefulset.yaml
# 1.修改:replicas: 3
# 2.注释:
#volumes:
# - name: elasticsearch-logging
# emptyDir: {}
# 3.增加配置
volumeClaimTemplates:
- metadata:
name: elasticsearch-logging
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "rook-ceph-block"
resources:
requests:
storage: 4Gi
# ES完整配置
[root@k8s-master efk]# vim es-statefulset.yaml
......
---
# Elasticsearch deployment itself
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: elasticsearch-logging
namespace: kube-system
labels:
k8s-app: elasticsearch-logging
version: v7.4.3
addonmanager.kubernetes.io/mode: Reconcile
spec:
serviceName: elasticsearch-logging
replicas: 2
selector:
matchLabels:
k8s-app: elasticsearch-logging
version: v7.4.3
template:
metadata:
labels:
k8s-app: elasticsearch-logging
version: v7.4.3
spec:
serviceAccountName: elasticsearch-logging
containers:
- image: quay.io/fluentd_elasticsearch/elasticsearch:v7.4.3
name: elasticsearch-logging
imagePullPolicy: Always
resources:
# need more cpu upon initialization, therefore burstable class
limits:
cpu: 1000m
memory: 3Gi
requests:
cpu: 100m
memory: 3Gi
ports:
- containerPort: 9200
name: db
protocol: TCP
- containerPort: 9300
name: transport
protocol: TCP
livenessProbe:
tcpSocket:
port: transport
initialDelaySeconds: 5
timeoutSeconds: 10
readinessProbe:
tcpSocket:
port: transport
initialDelaySeconds: 5
timeoutSeconds: 10
volumeMounts:
- name: elasticsearch-logging
mountPath: /data
env:
- name: "NAMESPACE"
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: "MINIMUM_MASTER_NODES"
value: "1"
#volumes:
# - name: elasticsearch-logging
# emptyDir: {}
# Elasticsearch requires vm.max_map_count to be at least 262144.
# If your OS already sets up this number to a higher value, feel free
# to remove this init container.
initContainers:
- image: alpine:3.6
command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"]
name: elasticsearch-logging-init
securityContext:
privileged: true
volumeClaimTemplates:
- metadata:
name: elasticsearch-logging
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "rook-ceph-block"
resources:
requests:
storage: 4Gi
2.2.重新部署
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 删除es、kibana
[root@k8s-master efk]# kubectl delete -f kibana-deployment.yaml
[root@k8s-master efk]# kubectl delete es-statefulset.yaml
# 创建ES
[root@k8s-master efk]# kubectl apply -f es-statefulset.yaml
# ES安装成功后再安装Kibana
[root@k8s-master efk]# kubectl apply -f kibana-deployment.yaml
# 查看
[root@k8s-master test]# kubectl get pvc -n kube-system
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
elasticsearch-logging-elasticsearch-logging-0 Bound pvc-ef784e9d-39d8-45d5-b5b3-ef52b6a18b70 4Gi RWO rook-ceph-block 12m
elasticsearch-logging-elasticsearch-logging-1 Bound pvc-4c55fa22-a7db-4693-af15-23fa5ee9b328 4Gi RWO rook-ceph-block 11m
elasticsearch-logging-elasticsearch-logging-2 Bound pvc-e26a2ccf-dd36-4ab3-acfd-332fe7b3c4a9 4Gi RWO rook-ceph-block 10m
[root@k8s-master test]# kubectl get pv -n kube-system
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-4c55fa22-a7db-4693-af15-23fa5ee9b328 4Gi RWO Delete Bound kube-system/elasticsearch-logging-elasticsearch-logging-1 rook-ceph-block 11m
pvc-e26a2ccf-dd36-4ab3-acfd-332fe7b3c4a9 4Gi RWO Delete Bound kube-system/elasticsearch-logging-elasticsearch-logging-2 rook-ceph-block 10m
pvc-ef784e9d-39d8-45d5-b5b3-ef52b6a18b70 4Gi RWO Delete Bound kube-system/elasticsearch-logging-elasticsearch-logging-0 rook-ceph-block 12m
3.外部存储方式部署
3.1.部署方式
在K8S内部部署Fluentd,Elasticsearch、Kibana部署在K8S集群外部。
Fluentd配置连接外部集群。
3.2.部署Elasticsearch - Kibana
参考:二.4.Docker部署Elasticsearch
版本:elasticsearch:7.4.2
3.3.创建Elasticsearch的Endpoint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# elasticsearch-ext.yml
apiVersion: v1
kind: Service
metadata:
name: elasticsearch-ext
namespace: kube-system
spec:
ports:
- port: 9200
targetPort: 9200
protocol: TCP
---
apiVersion: v1
kind: Endpoints
metadata:
name: elasticsearch-ext
namespace: kube-system
subsets:
- addresses:
- ip: 172.51.216.98
ports:
- port: 9200
1
2
3
4
5
6
7
8
9
10
11
# 创建
kubectl apply -f elasticsearch-ext.yml
kubectl describe ep elasticsearch-ext -n kube-system
kubectl describe svc elasticsearch-ext -n kube-system
# K8S 中的容器使用访问
svcname.namespace.svc.cluster.local:port
elasticsearch-ext.kube-system.svc.cluster.local:9200
3.4.安装Fluentd
1.修改配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# fluentd-es-configmap-ext.yaml
[root@k8s-master efk]# vim fluentd-es-configmap-ext.yaml
output.conf: |-
<match **>
@id elasticsearch
@type elasticsearch
@log_level info
type_name _doc
include_tag_key true
# 外部ES端点配置
host elasticsearch-ext.kube-system.svc.cluster.local
port 9200
logstash_format true
<buffer>
@type file
path /var/log/fluentd-buffers/kubernetes.system.buffer
flush_mode interval
retry_type exponential_backoff
flush_thread_count 2
flush_interval 5s
retry_forever
retry_max_interval 30
chunk_limit_size 2M
total_limit_size 500M
overflow_action block
</buffer>
</match>
2.安装Fluentd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 安装
[root@k8s-master efk]# kubectl apply -f fluentd-es-configmap-ext.yaml
configmap/fluentd-es-config-v0.2.0 created
[root@k8s-master efk]# kubectl apply -f fluentd-es-ds.yaml
serviceaccount/fluentd-es created
clusterrole.rbac.authorization.k8s.io/fluentd-es created
clusterrolebinding.rbac.authorization.k8s.io/fluentd-es created
daemonset.apps/fluentd-es-v3.1.0 created
# 查看
[root@k8s-master efk]# kubectl get pod -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
fluentd-es-v3.1.0-2vjrv 1/1 Running 0 2m14s 10.244.169.131 k8s-node2 <none> <none>
fluentd-es-v3.1.0-fq9pv 1/1 Running 0 2m14s 10.244.107.231 k8s-node3 <none> <none>
fluentd-es-v3.1.0-hlb2w 1/1 Running 0 2m14s 10.244.36.107 k8s-node1 <none> <none>
3.5.测试
1
2
3
# 访问地址:
http://172.51.216.98:5601/
4. 微服务测试
4.1.运行微服务
微服务:msa-ext-elk
创建镜像,上传镜像仓库,运行微服务。
注意:配置私有镜像密钥Secret
4.2.测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看
[root@k8s-master ~]# kubectl get all -n dev
NAME READY STATUS RESTARTS AGE
pod/msa-ext-elk-6974d77cb7-fqnrd 1/1 Running 0 5m19s
pod/msa-ext-elk-6974d77cb7-gshjr 1/1 Running 0 5m19s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/msa-ext-elk ClusterIP 10.100.30.131 <none> 8114/TCP 5m19s
service/rabbitmq-svc ClusterIP 10.101.192.176 <none> 5672/TCP 8m38s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/msa-ext-elk 2/2 2 2 5m19s
NAME DESIRED CURRENT READY AGE
replicaset.apps/msa-ext-elk-6974d77cb7 2 2 2 5m19s
[root@k8s-master ~]# curl 10.100.30.131:8114/hello
hello,this is elk messge! ### Mon Nov 22 17:08:26 CST 2021
4.3.总结
1
2
3
logback.xml配置同时向mq和控制台输出日志,在ES中会有两份日志,但格式不同:
1.K8S收集的Pod的标准输出日志(通过Pod名称过滤查看)
2.微服务发送到MQ写到的ES的日志
5.SideCar 模式部署 Filebeat 收集日志
使用 SideCar 模式将日志收集容器与业务容器部署在同一个 Pod 中,只收集对应容器的日志。
业务容器(Nginx)、SideCar(Filebeat)。
5.1.Filebeat配置
1.filebeat.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[root@k8s-master ~/software]# cat filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /log/access.log
json.keys_under_root: true
json.overwrite_keys: true
tags: ["access"]
- type: log
enabled: true
paths:
- /log/error.log
tags: ["error"]
output.elasticsearch:
hosts: ["elasticsearch-logging.kube-system.svc.cluster.local:9200"]
indices:
- index: "nginx_access-%{[beat.version]}-%{+yyyy.MM}"
when.contains:
tags: "access"
- index: "nginx_error-%{[beat.version]}-%{+yyyy.MM}"
when.contains:
tags: "error"
setup.template.name: "nginx"
setup.template.pattern: "nginx_*"
setup.template.enabled: false
setup.template.overwrite: true
2.创建configmap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#kubectl create configmap filebeat-cm --from-file=filebeat.yml --namespace=dev
[root@k8s-master efk]# kubectl create configmap filebeat-cm --from-file=filebeat.yml --namespace=dev
configmap/filebeat-cm created
# 查看
[root@k8s-master efk]# kubectl get cm -n dev
NAME DATA AGE
filebeat-cm 1 52s
[root@k8s-master efk]# kubectl describe cm filebeat-cm -n dev
Name: filebeat-cm
Namespace: dev
Labels: <none>
Annotations: <none>
Data
====
filebeat.yml:
----
filebeat.inputs:
- type: log
enabled: true
paths:
- /log/access.log
json.keys_under_root: true
json.overwrite_keys: true
tags: ["access"]
- type: log
enabled: true
paths:
- /log/error.log
tags: ["error"]
output.elasticsearch:
hosts: ["10.0.0.200:9200"]
indices:
- index: "nginx_access-%{[beat.version]}-%{+yyyy.MM}"
when.contains:
tags: "access"
- index: "nginx_error-%{[beat.version]}-%{+yyyy.MM}"
when.contains:
tags: "error"
setup.template.name: "nginx"
setup.template.pattern: "nginx_*"
setup.template.enabled: false
setup.template.overwrite: true
Events: <none>
5.2.创建服务
1.nginx-filebeat-sidecar.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# nginx-filebeat-sidecar.yaml
[root@k8s-master ~/software]# cat nginx-filebeat-sidecar.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-filebeat
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: nginx-filebeat
template:
metadata:
namespace: dev
labels:
app: nginx-filebeat
spec:
containers:
- image: nginx:alpine
name: nginx
ports:
- containerPort: 80
volumeMounts:
- name: applog
mountPath: /var/log/nginx
- image: elastic/filebeat:7.4.2
name: filebeat
name: filebeat
volumeMounts:
- name: applog
mountPath: /log
- name: filebeat-config
mountPath: /usr/share/filebeat
volumes:
- name: applog
emptyDir: {}
- name: filebeat-config
configMap:
name: filebeat-cm
[root@k8s-master efk]# kubectl exec -it nginx-filebeat-8674b4d8d4-9xhs7 -c filebeat -n dev -- bash
bash-4.2$ ls
LICENSE.txt README.md fields.yml filebeat.reference.yml kibana module
NOTICE.txt data filebeat filebeat.yml logs modules.d
bash-4.2$ pwd
/usr/share/filebeat
mountPath: /etc/filebeat/
# 参考
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-filebeat
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: nginx-filebeat
template:
metadata:
namespace: dev
labels:
app: nginx-filebeat
spec:
containers:
- image: nginx:alpine
name: nginx
ports:
- containerPort: 80
volumeMounts:
- name: applog
mountPath: /var/log/nginx
- image: elastic/filebeat:7.4.2
name: filebeat
volumeMounts:
- name: applog
mountPath: /log
- name: filebeat-config
mountPath: /usr/local/filebeat/config/
volumes:
- name: applog
emptyDir: {}
- name: filebeat-config
configMap:
name: filebeat-cm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@k8s-master efk]# kubectl apply -f nginx-filebeat-sidecar.yaml
deployment.apps/nginx-filebeat created
[root@k8s-master efk]# kubectl get all -n dev
NAME READY STATUS RESTARTS AGE
pod/nginx-filebeat-6d76d8cddd-zpxk5 2/2 Running 0 111s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-filebeat 1/1 1 1 111s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-filebeat-6d76d8cddd 1 1 1 111s
2.service-nginx.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# service-nginx.yaml
[root@k8s-master ~/software]# cat service-nginx.yaml
apiVersion: v1
kind: Service
metadata:
name: service-nginx
namespace: dev
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30781
selector:
app: nginx-filebeat
1
2
3
4
5
6
7
[root@k8s-master efk]# kubectl apply -f service-nginx.yaml
service/service-nginx created
[root@k8s-master efk]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-nginx NodePort 10.106.197.74 <none> 80:30781/TCP 33s