https://www.qikqiak.com/post/install-efk-stack-on-k8s/
EFK简介
Kubernetes中比较流行的日志收集解决方案是Elasticsearch、Fluentd 和 Kibana(EFK)技术栈,也是官方现在比较推荐的一种方案。
Elasticsearch是一个实时的、分布式的可扩展的搜索引擎,允许进行全文、结构化搜索,它通常用于索引和搜索大量日志数据,也可用于搜索许多不同类型的文档。
Fluentd是一个流行的开源数据收集器,是CNCF的毕业项目。我们将在Kubernetes集群节点上以DaemonSet的方式安装Fluentd,通过获取容器日志文件、过滤和转换日志数据,然后将数据传递到 Elasticsearch集群,在该集群中对其进行索引和存储。
Kibana是Elasticsearch的一个功能强大的数据可视化 dashboard,Kibana提供了web 界面来浏览 Elasticsearch 日志数据。
本文我们先配置启动一个可扩展的Elasticsearch集群,然后在 Kubernetes集群中创建一个Kibana应用,最后通过DaemonSet 来运行Fluentd,以便它在每个Kubernetes工作节点上都可以运行一个Pod。
创建 Elasticsearch 集群
在创建Elasticsearch集群之前,我们先创建一个命名空间,用来部署所有日志相关的资源对象。
[root@k8s-87 efk]# cat logging-ns.yaml
apiVersion: v1
kind: Namespace
metadata:
name: logging
通过kubectl创建该资源清单,创建一个名为logging的 namespace:
[root@k8s-87 efk]# kubectl apply -f logging-ns.yaml
namespace/logging created
[root@k8s-87 efk]# kubectl get ns
NAME STATUS AGE
default Active 7d5h
kube-node-lease Active 7d5h
kube-public Active 7d5h
kube-system Active 7d5h
logging Active 5s
现在创建了一个命名空间来存放我们的日志相关资源,接下来可以部署 EFK 相关组件,首先开始部署一个3节点的 Elasticsearch 集群。
为了避免出现脑裂现象,需要设置参数discover.zen.minimum_master_nodes=N/2+1,其中N是Elasticsearch 集群中符合主节点的节点数,比如我们这里3个节点,意味着我们应该设置为2。这样,如果一个节点暂时与集群断开连接,则另外两个节点可以选择一个新的主节点,并且集群可以在最后一个节点尝试重新加入时继续运行。
首先创建一个名为es的headless service,新建文件elasticsearch-svc.yaml,文件内容如下:
kind: Service
apiVersion: v1
metadata:
name: elasticsearch
namespace: logging
labels:
app: elasticsearch
spec:
selector:
app: elasticsearch
clusterIP: None
ports:
- port: 9200
name: rest
- port: 9300
name: inter-node
定义了一个名为elasticsearch的Service,指定标签app=elasticsearch,当我们将 Elasticsearch StatefulSet 与此服务关联时,服务将返回带有标签app=elasticsearch Elasticsearch Pods的DNS A 记录,然后设置clusterIP=None,将该服务设置成无头服务。最后,我们分别定义端口9200、9300,分别用于与 REST API 交互,以及用于节点间通信。
使用 kubectl 直接创建上面的服务资源对象:
[root@k8s-87 efk]# kubectl apply -f elasticsearch-svc.yaml
service/elasticsearch created
[root@k8s-87 efk]# kubectl get svc --namespace=logging
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 22s
现在我们已经为Pod设置了headless service和一个稳定的域名.elasticsearch.logging.svc.cluster.local,接下来我们通过 StatefulSet 来创建具体的 Elasticsearch 的 Pod 应用。
Kubernetes StatefulSet 允许我们为Pod 分配一个稳定的标识和持久化存储,Elasticsearch 需要稳定的存储来保证 Pod 在重新调度或者重启后的数据依然不变,所以需要使用 StatefulSet 来管理 Pod。
我们先列出完整的资源清单文件elasticsearch-statefulset.yaml,然后在逐块分析。具体内容如下:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: es-cluster
namespace: logging
spec:
serviceName: elasticsearch
replicas: 3
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: 192.168.200.197:80/apm/elasticsearch-oss:6.7.0
resources:
limits:
cpu: 1000m
requests:
cpu: 100m
ports:
- containerPort: 9200
name: rest
protocol: TCP
- containerPort: 9300
name: inter-node
protocol: TCP
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
env:
- name: cluster.name
value: k8s-logs
- name: node.name
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: discovery.zen.ping.unicast.hosts
value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
- name: discovery.zen.minimum_master_nodes
value: "2"
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx512m"
initContainers:
- name: fix-permissions
image: 192.168.200.197:80/test-private/busybox:latest
command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
securityContext:
privileged: true
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
- name: increase-vm-max-map
image: 192.168.200.197:80/test-private/busybox:latest
command: ["sysctl", "-w", "vm.max_map_count=262144"]
securityContext:
privileged: true
- name: increase-fd-ulimit
image: 192.168.200.197:80/test-private/busybox:latest
command: ["sh", "-c", "ulimit -n 65536"]
securityContext:
privileged: true
volumeClaimTemplates:
- metadata:
name: data
labels:
app: elasticsearch
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: es-data-db
resources:
requests:
storage: 20Gi
- 首先我们定义了一个名为es-cluster的StatefulSet 对象,然后定义serviceName=elasticsearch和前面创建的 Service 相关联,这可以确保使用以下 DNS 地址访问 StatefulSet 中的每一个Pod:es-cluster-[0,1,2].elasticsearch.logging.svc.cluster.local,其中[0,1,2]对应于已分配的 Pod 序号。
然后指定3个副本,将matchLabels设置为app=elasticsearch,所以Pod 的模板部分.spec.template.metadata.lables也必须包含app=elasticsearch。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: es-cluster
namespace: logging
spec:
serviceName: elasticsearch
replicas: 3
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
- 然后定义Pod模板部分内容:
spec:
containers:
- name: es
image: 192.168.200.197:80/apm/elasticsearch-oss:6.7.0
resources:
limits:
cpu: 1000m
requests:
cpu: 100m
ports:
- containerPort: 9200
name: rest
protocol: TCP
- containerPort: 9300
name: inter-node
protocol: TCP
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
env:
- name: cluster.name
value: k8s-logs
- name: node.name
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: discovery.zen.ping.unicast.hosts
value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
- name: discovery.zen.minimum_master_nodes
value: "2"
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx512m"
该部分是定义StatefulSet中的Pod,我们这里使用一个-oss后缀的镜像,该镜像是Elasticsearch的开源版本,如果你想使用包含X-Pack之类的版本,可以去掉该后缀。然后