k8s搭建EFK日志管理系統
efk就是目前比較受歡迎的日志管理系統。kubernetes可以實現efk的快速部署和使用,通過statefulset控制器部署elasticsearch組件,用來存儲日志數據,
還可通過volumenclaimtemplate動態生成pv實現es數據的持久化。通過deployment部署kibana組件,實現日志的可視化管理。
通過daemonset控制器部署fluentd組件,來收集各節點和k8s集群的日志。
https://www.nnv5.cn/index.php/archives/76/ 較新
EFK組件介紹
在K8s集群上運行多個服務和應用程序時,日志收集系統可以幫助你快速分類和分析由Pod生成的大量日志數據。K8s中比較流行的日志收集解決方案是Elasticsearch、Fluentd和Kibana(EFK)技術棧,也是官方推薦的一種方案。
Elasticsearch是一個實時的,分布式的,可擴展的搜索引擎,它允許進行全文本和結構化搜索以及對日志進行分析。它通常用于索引和搜索大量日志數據,也可以用于搜索許多不同種類的文檔。
Elasticsearch通常與Kibana一起部署,kibana是Elasticsearch 的功能強大的數據可視化的dashboard(儀表板)。Kibana允許你通過Web界面瀏覽Elasticsearch日志數據,也可自定義查詢條件快速檢索出elasticccsearch中的日志數據。
Fluentd是一個流行的開源數據收集器,我們將在Kubernetes 集群節點上安裝 Fluentd,通過獲取容器日志文件、過濾和轉換日志數據,然后將數據傳遞到 Elasticsearch 集群,在該集群中對其進行索引和存儲。
我們先來配置啟動一個可擴展的 Elasticsearch 集群,然后在Kubernetes集群中創建一個Kibana應用,最后通過DaemonSet來運行Fluentd,以便它在每個Kubernetes工作節點上都可以運行一個 Pod。
資料下載
1.****下文需要的yaml文件所在的github地址如下:
https://github.com/luckylucky421/efk
下面實驗用到yaml文件大家需要從上面的github上clone和下載到本地,解壓,然后把解壓后的yaml文件傳到k8s集群的master節點上,
如果直接復制粘貼格式可能會有問題。
2.****下文里提到的efk組件需要的鏡像獲取方式在百度網盤,鏈接如下:
鏈接:https://pan.baidu.com/s/1lsP2_NrXwOzGMIsVCUHtPw
提取碼:kpg2
3.實驗之前需要把鏡像上傳到k8s集群的各個節點,通過docker load -i 解壓,這樣可以保證下面的yaml文件可以正常執行,否則會存在鏡像拉取失敗問題:
[](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">docker load -i busybox.tar.gz
docker load -i elasticsearch_7_2_0.tar.gz
docker load -i fluentd.tar.gz
docker load -i kibana_7_2_0.tar.gz
docker load -i nfs-client-provisioner.tar.gz
docker load -i nginx.tar.gz</pre>
](javascript:void(0); "復制代碼")
正文-安裝efk組件
下面的步驟在k8s集群的master1節點操作
#****創建名稱空間
在安裝Elasticsearch集群之前,我們先創建一個名稱空間,在這個名稱空間下安裝日志收工具elasticsearch、fluentd、kibana。我們創建一個kube-logging名稱空間,將EFK組件安裝到該名稱空間中。
1.創建kube-logging名稱空間
cat kube-logging.yaml
|
kind: Namespace
apiVersion: v1
metadata:
name: kube-logging
|
kubectl apply -f kube-logging.yaml
2.查看kube-logging名稱空間是否創建成功
kubectl get namespaces | grep kube-logging
顯示如下,說明創建成功
kube-logging Active 1m
#安裝elasticsearch組件
通過上面步驟已經創建了一個名稱空間kube-logging,在這個名稱空間下去安裝日志收集組件efk,首先,我們將部署一個3節點的Elasticsearch集群。我們使用3個Elasticsearch Pods可以避免高可用中的多節點群集中發生的“裂腦”的問題。Elasticsearch腦裂可參考https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain
1.創建一個headless service(無頭服務)
創建一個headless service的Kubernetes服務,服務名稱是elasticsearch,這個服務將為3個Pod定義一個DNS域。headless service不具備負載均衡也沒有IP。要了解有關headless service的更多信息,可參考https://kubernetes.io/docs/concepts/services-networking/service/#headless-services。
cat elasticsearch_svc.yaml
kind: Service
apiVersion: v1
metadata:
name: elasticsearch
namespace``: kube-logging
labels:
app: elasticsearch
spec:
selector:
app: elasticsearch
clusterIP: None
ports:
- port: 9200
name: rest
- port: 9300
name: inter-node
|
在kube-logging名稱空間定義了一個名為 elasticsearch 的 Service服務,帶有app=elasticsearch標簽,當我們將 ElasticsearchStatefulSet 與此服務關聯時,服務將返回帶有標簽app=elasticsearch的 Elasticsearch Pods的DNS A記錄,然后設置clusterIP=None,將該服務設置成無頭服務。最后,我們分別定義端口9200、9300,分別用于與 REST API 交互,以及用于節點間通信。使用kubectl直接創建上面的服務資源對象:
kubectl apply -f elasticsearch_svc.yaml
查看elasticsearch的service是否創建成功
kubectl get services --namespace=kube-logging
看到如下,說明在kube-logging名稱空間下創建了一個名字是elasticsearch的headless service:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
現在我們已經為 Pod 設置了無頭服務和一個穩定的域名.elasticsearch.kube-logging.svc.cluster.local,接下來我們通過 StatefulSet來創建具體的 Elasticsearch的Pod 應用。
2.通過statefulset創建elasticsearch集群
Kubernetes statefulset可以為Pods分配一個穩定的標識,讓pod具有穩定的、持久的存儲。Elasticsearch需要穩定的存儲才能通過POD重新調度和重新啟動來持久化數據。更多關于kubernetes StatefulSet可參考
https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/。
1)下面將定義一個資源清單文件elasticsearch_statefulset.yaml,首先粘貼以下內容:
|
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: es-cluster
namespace``: kube-logging
spec:
serviceName: elasticsearch
replicas: 3
selector:
matchLabels:
app: elasticsearch
template``:
metadata:
labels:
app: elasticsearch
|
上面內容的解釋:在kube-logging的名稱空間中定義了一個es-cluster的StatefulSet。然后,我們使用serviceName 字段與我們之前創建的ElasticSearch服務相關聯。這樣可以確保可以使用以下DNS地址訪問StatefulSet中的每個Pod:,es-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local,其中[0,1,2]與Pod分配的序號數相對應。我們指定3個replicas(3個Pod副本),將matchLabels selector 設置為app: elasticseach,然后在該.spec.template.metadata中指定pod需要的鏡像。該.spec.selector.matchLabels和.spec.template.metadata.labels字段必須匹配。
2)statefulset中定義pod模板,內容如下:
. . .
spec:
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:7.2.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.seed_hosts
value:``"es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
- name: cluster.initial_master_nodes
value:``"es-cluster-0,es-cluster-1,es-cluster-2"
- name: ES_JAVA_OPTS
value:``"-Xms512m -Xmx512m"
|
上面內容解釋:在statefulset中定義了pod,容器的名字是elasticsearch,鏡像是docker.elastic.co/elasticsearch/elasticsearch:7.2.0。使用resources字段來指定容器需要保證至少有0.1個vCPU,并且容器最多可以使用1個vCPU(這在執行初始的大量提取或處理負載高峰時限制了Pod的資源使用)。了解有關資源請求和限制,可參考https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/。暴漏了9200和9300兩個端口,名稱要和上面定義的 Service 保持一致,通過volumeMount聲明了數據持久化目錄,定義了一個data數據卷,通過volumeMount把它掛載到容器里的/usr/share/elasticsearch/data目錄。我們將在以后的YAML塊中為此StatefulSet定義VolumeClaims。
最后,我們在容器中設置一些環境變量:
cluster.name
Elasticsearch 集群的名稱,我們這里是 k8s-logs。
|
node.name
節點的名稱,通過metadata.name來獲取。這將解析為 es-cluster-[0,1,2],取決于節點的指定順序。
****discovery.zen.ping.unicast.hosts****
|
此字段用于設置在Elasticsearch集群中節點相互連接的發現方法。
我們使用 unicastdiscovery方式,它為我們的集群指定了一個靜態主機列表。
由于我們之前配置的無頭服務,我們的 Pod 具有唯一的DNS域es-cluster-[0,1,2].elasticsearch.logging.svc.cluster.local,
因此我們相應地設置此變量。由于都在同一個``namespace
下面,所以我們可以將其縮短為es-cluster-[0,1,2].elasticsearch。
要了解有關 Elasticsearch 發現的更多信息,請參閱 Elasticsearch 官方文檔:https:``//www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html。
|
discovery.zen.minimum_master_nodes
我們將其設置為(N/2) + 1,N是我們的群集中符合主節點的節點的數量。
我們有3個Elasticsearch 節點,因此我們將此值設置為2(向下舍入到最接近的整數)。
要了解有關此參數的更多信息,請參閱官方 Elasticsearch 文檔:https:``//www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain。
|
ES_JAVA_OPTS
這里我們設置為-Xms512m -Xmx512m,告訴JVM使用512MB的最小和最大堆。
你應該根據群集的資源可用性和需求調整這些參數。
要了解更多信息,請參閱設置堆大小的相關文檔:https:``//www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html。
|
3****)initcontainer內容
|
. . .
initContainers:
- name: fix-permissions
image: busybox
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: busybox
command: [``"sysctl"``,``"-w"``,``"vm.max_map_count=262144"``]
securityContext:
privileged:``true
- name: increase-fd-ulimit
image: busybox
command: [``"sh"``,``"-c"``,``"ulimit -n 65536"``]
securityContext:
privileged:``true
|
這里我們定義了幾個在主應用程序之前運行的Init 容器,這些初始容器按照定義的順序依次執行,執行完成后才會啟動主應用容器。第一個名為 fix-permissions 的容器用來運行 chown 命令,將 Elasticsearch 數據目錄的用戶和組更改為1000:1000(Elasticsearch 用戶的 UID)。因為默認情況下,Kubernetes 用 root 用戶掛載數據目錄,這會使得 Elasticsearch 無法方法該數據目錄,可以參考 Elasticsearch 生產中的一些默認注意事項相關文檔說明:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#_notes_for_production_use_and_defaults。
第二個名為increase-vm-max-map 的容器用來增加操作系統對mmap計數的限制,默認情況下該值可能太低,導致內存不足的錯誤,要了解更多關于該設置的信息,可以查看 Elasticsearch 官方文檔說明:https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html。最后一個初始化容器是用來執行ulimit命令增加打開文件描述符的最大數量的。此外 Elastisearch Notes for Production Use 文檔還提到了由于性能原因最好禁用 swap,當然對于 Kubernetes 集群而言,最好也是禁用 swap 分區的。現在我們已經定義了主應用容器和它之前運行的Init Containers 來調整一些必要的系統參數,接下來我們可以添加數據目錄的持久化相關的配置。
****4****)在 StatefulSet 中,使用volumeClaimTemplates來定義volume 模板即可:****
. . .
volumeClaimTemplates:
- metadata:
name: data
labels:
app: elasticsearch
spec:
accessModes: [``"ReadWriteOnce"
]
storageClassName:``do``-block-storage
resources:
requests:
storage: 10Gi
|
我們這里使用 volumeClaimTemplates 來定義持久化模板,Kubernetes 會使用它為 Pod 創建 PersistentVolume,設置訪問模式為ReadWriteOnce,這意味著它只能被 mount到單個節點上進行讀寫,然后最重要的是使用了一個名為do-block-storage的 StorageClass 對象,所以我們需要提前創建該對象,我們這里使用的 NFS 作為存儲后端,所以需要安裝一個對應的 provisioner驅動。
5****)創建storageclass,實現nfs做存儲類的動態供給 #****安裝nfs服務,選擇k8s集群的master1節點,k8s****集群的master1節點的ip是192.168.0.6:
yum****安裝nfs
yum install nfs-utils -y
systemctl start nfs
chkconfig nfs on
在master1上創建一個nfs共享目錄
mkdir /data/v1 -p
cat /etc/exports
/data/v1 192.168.0.0/24(rw,no_root_squash)
exportfs -arv
使配置文件生效
systemctlrestart nfs
#****實現nfs做存儲類的動態供給
創建運行nfs-provisioner的sa賬號
cat serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
|
kubectl apply -f serviceaccount.yaml
對sa賬號做rbac授權
cat rbac.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-provisioner-runner
rules:
- apiGroups: [``""``]
resources: [``"persistentvolumes"``]
verbs: [``"get"``,``"list"``,``"watch"``,``"create"``,``"delete"``]
- apiGroups: [``""``]
resources: [``"persistentvolumeclaims"``]
verbs: [``"get"``,``"list"``,``"watch"``,``"update"``]
- apiGroups: [``"storage.k8s.io"``]
resources: [``"storageclasses"``]
verbs: [``"get"``,``"list"``,``"watch"``]
- apiGroups: [``""``]
resources: [``"events"``]
verbs: [``"create"``,``"update"``,``"patch"``]
- apiGroups: [``""``]
resources: [``"services"``,``"endpoints"``]
verbs: [``"get"``]
- apiGroups: [``"extensions"``]
resources: [``"podsecuritypolicies"``]
resourceNames: [``"nfs-provisioner"``]
verbs: [``"use"``]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace``:``default
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
rules:
- apiGroups: [``""``]
resources: [``"endpoints"``]
verbs: [``"get"``,``"list"``,``"watch"``,``"create"``,``"update"``,``"patch"``]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace``:``default
roleRef:
kind: Role
name: leader-locking-nfs-provisioner
apiGroup: rbac.authorization.k8s.io
|
kubectl apply -f rbac.yaml
通過deployment創建pod用來運行nfs-provisioner
cat deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-provisioner
spec:
selector:
matchLabels:
app: nfs-provisioner
replicas: 1
strategy:
type: Recreate
template``:
metadata:
labels:
app: nfs-provisioner
spec:
serviceAccount: nfs-provisioner
containers:
- name: nfs-provisioner
image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: example.com/nfs
- name: NFS_SERVER
value: 192.168.0.6
- name: NFS_PATH
value: /data/v1
volumes:
- name: nfs-client-root
nfs:
server: 192.168.0.6
path: /data/v1
|
kubectl apply -f deployment.yaml
kubectl get pods
看到如下,說明上面的yaml文件創建成功:
NAME READY STATUS RESTARTS AGE
nfs-provisioner-595dcd6b77-rkvjl 1/1 Running 0 6s
|
注:上面yaml文件說明:
- name: PROVISIONER_NAME
value: example.com/nfs
|
PROVISIONER_NAME是example.com/nfs,example.com/nfs需要跟后面的storageclass的provisinoer保持一致
- name: NFS_SERVER
value: 192.168.0.6
|
這個需要寫nfs服務端所在的ip地址,大家需要寫自己的nfs地址
- name: NFS_PATH
value: /data/v1
|
這個是nfs服務端共享的目錄
volumes:
- name: nfs-client-root
nfs:
server: 192.168.0.6
|
這個是nfs服務端的ip,大家需要寫自己的nfs地址
path: /data/v1 #這個是nfs服務端的共享目錄
創建storageclass
cat class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name:``do``-block-storage
provisioner: example.com/nfs
|
kubectl apply -f class.yaml
注:
provisioner:example.com/nfs #該值需要和provisioner配置的保持一致
|
6)最后,我們指定了每個 PersistentVolume 的大小為 10GB,我們可以根據自己的實際需要進行調整該值。最后,完整的elasticsaerch-statefulset.yaml資源清單文件內容如下:
****cat elasticsaerch-statefulset.yaml****
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: es-cluster
namespace``: kube-logging
spec:
serviceName: elasticsearch
replicas: 3
selector:
matchLabels:
app: elasticsearch
template``:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
imagePullPolicy: IfNotPresent
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.seed_hosts
value:``"es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
- name: cluster.initial_master_nodes
value:``"es-cluster-0,es-cluster-1,es-cluster-2"
- name: ES_JAVA_OPTS
value:``"-Xms512m -Xmx512m"
initContainers:
- name: fix-permissions
image: busybox
imagePullPolicy: IfNotPresent
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: busybox
imagePullPolicy: IfNotPresent
command: [``"sysctl"``,``"-w"``,``"vm.max_map_count=262144"``]
securityContext:
privileged:``true
- name: increase-fd-ulimit
image: busybox
imagePullPolicy: IfNotPresent
command: [``"sh"``,``"-c"``,``"ulimit -n 65536"``]
securityContext:
privileged:``true
volumeClaimTemplates:
- metadata:
name: data
labels:
app: elasticsearch
spec:
accessModes: [``"ReadWriteOnce"
]
storageClassName:``do``-block-storage
resources:
requests:
storage: 10Gi
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: es-cluster
namespace``: kube-logging
spec:
serviceName: elasticsearch
replicas: 3
selector:
matchLabels:
app: elasticsearch
template``:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
imagePullPolicy: IfNotPresent
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.seed_hosts
value:``"es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
- name: cluster.initial_master_nodes
value:``"es-cluster-0,es-cluster-1,es-cluster-2"
- name: ES_JAVA_OPTS
value:``"-Xms512m -Xmx512m"
initContainers:
- name: fix-permissions
image: busybox
imagePullPolicy: IfNotPresent
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: busybox
imagePullPolicy: IfNotPresent
command: [``"sysctl"``,``"-w"``,``"vm.max_map_count=262144"``]
securityContext:
privileged:``true
- name: increase-fd-ulimit
image: busybox
imagePullPolicy: IfNotPresent
command: [``"sh"``,``"-c"``,``"ulimit -n 65536"``]
securityContext:
privileged:``true
volumeClaimTemplates:
- metadata:
name: data
labels:
app: elasticsearch
spec:
accessModes: [``"ReadWriteOnce"
]
storageClassName:``do``-block-storage
resources:
requests:
storage: 10Gi
|
kubectl apply -f elasticsaerch-statefulset.yaml
kubectl get pods -n kube-logging
顯示如下,說明es創建成功了:
NAME READY STATUS RESTARTS AGE
es-cluster-0 1/1 Running 0 2m8s
es-cluster-1 1/1 Running 0 117s
es-cluster-2 1/1 Running 0 107s
|
kubectl get svc -n kube-logging
顯示如下
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 33m
|
pod部署完成之后,可以通過REST API檢查elasticsearch集群是否部署成功,使用下面的命令將本地端口9200轉發到 Elasticsearch 節點(如es-cluster-0)對應的端口:
kubectl port-forward es-cluster-0 9200:9200 --``namespace``=kube-logging
|
然后,在另外的終端窗口中,執行如下請求,新開一個master1終端:
|
1
|
curl http:``//localhost:9200/_cluster/state?pretty
|
輸出如下:
{
"cluster_name"
:``"k8s-logs"``,
"compressed_size_in_bytes"
: 348,
"cluster_uuid"
:``"QD06dK7CQgids-GQZooNVw"``,
"version"
: 3,
"state_uuid"
:``"mjNIWXAzQVuxNNOQ7xR-qg"``,
"master_node"
:``"IdM5B7cUQWqFgIHXBp0JDg"``,
"blocks"
: { },
"nodes"
: {
"u7DoTpMmSCixOoictzHItA"
: {
"name"
:``"es-cluster-1"``,
"ephemeral_id"
:``"ZlBflnXKRMC4RvEACHIVdg"``,
"transport_address"
:``"10.244.8.2:9300"``,
"attributes"
: { }
},
"IdM5B7cUQWqFgIHXBp0JDg"``: {
"name"
:``"es-cluster-0"``,
"ephemeral_id"
:``"JTk1FDdFQuWbSFAtBxdxAQ"``,
"transport_address"
:``"10.244.44.3:9300"``,
"attributes"
: { }
},
"R8E7xcSUSbGbgrhAdyAKmQ"
: {
"name"
:``"es-cluster-2"``,
"ephemeral_id"
:``"9wv6ke71Qqy9vk2LgJTqaA"``,
"transport_address"
:``"10.244.40.4:9300"``,
"attributes"
: { }
}
},
...
|
看到上面的信息就表明我們名為 k8s-logs的Elasticsearch 集群成功創建了3個節點:es-cluster-0,es-cluster-1,和es-cluster-2,當前主節點是 es-cluster-0。
#安裝kibana組件
elasticsearch安裝成功之后,開始部署kibana
cat kibana.yaml
apiVersion: v1
kind: Service
metadata:
name: kibana
namespace``: kube-logging
labels:
app: kibana
spec:
ports:
- port: 5601
selector:
app: kibana
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
namespace``: kube-logging
labels:
app: kibana
spec:
replicas: 1
selector:
matchLabels:
app: kibana
template``:
metadata:
labels:
app: kibana
spec:
containers:
- name: kibana
image: docker.elastic.co/kibana/kibana:7.2.0
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: 1000m
requests:
cpu: 100m
env:
- name: ELASTICSEARCH_URL
value: http:``//elasticsearch:9200
ports:
- containerPort: 5601
|
上面我們定義了兩個資源對象,一個Service和Deployment,為了測試方便,我們將 Service 設置為了 NodePort 類型,Kibana Pod 中配置都比較簡單,唯一需要注意的是我們使用ELASTICSEARCH_URL 這個環境變量來設置Elasticsearch 集群的端點和端口,直接使用 Kubernetes DNS 即可,此端點對應服務名稱為 elasticsearch,由于是一個 headless service,所以該域將解析為3個 Elasticsearch Pod 的 IP 地址列表。
配置完成后,直接使用 kubectl工具創建:
kubectl apply -f kibana.yaml
kubectl get pods -n kube-logging
顯示如下,說明kibana也已經部署成功了
NAME READY STATUS RESTARTS AGE
es-cluster-0 1/1 Running 0 170m
es-cluster-1 1/1 Running 0 170m
es-cluster-2 1/1 Running 0 170m
kibana-5749b5778b-c9djr 1/1 Running 0 4m3s
|
kubectl get svc -n kube-logging
顯示如下:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 3h28m
kibana ClusterIP 10.104.159.24 <none> 5601/TCP 11m
|
修改service的type類型為NodePort:
kubectl edit svc kibana -n kube-logging
把type:ClusterIP變成type: NodePort
保存退出之后
kubectlget svc -n kube-logging
顯示如下:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
elasticsearchClusterIP None <none> 9200/TCP,9300/TCP 3h50m
kibana NodePort 10.104.159.24 <none> 5601:32462/TCP 34m
|
在瀏覽器中打開http://<任意節點IP>:32462即可,如果看到如下歡迎界面證明 Kibana 已經成功部署到了Kubernetes集群之中。
#****安裝fluentd組件
我們使用daemonset控制器部署fluentd組件,這樣可以保證集群中的每個節點都可以運行同樣fluentd的pod副本,這樣就可以收集k8s集群中每個節點的日志,在k8s集群中,容器應用程序的輸入輸出日志會重定向到node節點里的json文件中,fluentd可以tail和過濾以及把日志轉換成指定的格式發送到elasticsearch集群中。
除了容器日志,fluentd也可以采集kubelet、kube-proxy、docker的日志。
cat fluentd.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd
namespace``: kube-logging
labels:
app: fluentd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluentd
labels:
app: fluentd
rules:
- apiGroups:
-``""
resources:
- pods
- namespaces
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: fluentd
roleRef:
kind: ClusterRole
name: fluentd
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: fluentd
namespace``: kube-logging
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace``: kube-logging
labels:
app: fluentd
spec:
selector:
matchLabels:
app: fluentd
template``:
metadata:
labels:
app: fluentd
spec:
serviceAccount: fluentd
serviceAccountName: fluentd
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
imagePullPolicy: IfNotPresent
env:
- name: FLUENT_ELASTICSEARCH_HOST
value:``"elasticsearch.kube-logging.svc.cluster.local"
- name: FLUENT_ELASTICSEARCH_PORT
value:``"9200"
- name: FLUENT_ELASTICSEARCH_SCHEME
value:``"http"
- name: FLUENTD_SYSTEMD_CONF
value: disable
resources:
limits:
memory: 512Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/``log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly:``true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/``log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
|
kubectl apply -f fluentd.yaml
查看是否部署成功
kubectl get pods -n kube-logging
顯示如下,看到status狀態是running,說明部署成功:
NAME READY STATUS RESTARTS AGE
es-cluster-0 1/1 Running 6 57m
es-cluster-1 1/1 Running 5 57m
es-cluster-2 1/1 Running 0 45m
fluentd-fs54n 1/1 Running 0 37m
fluentd-ghgqf 1/1 Running 0 37m
kibana-5749b5778b-zzgbc 1/1 Running 0 39m
|
Fluentd啟動成功后,我們可以前往 Kibana 的 Dashboard 頁面中,點擊左側的Discover
,可以看到如下配置頁面:
在這里可以配置我們需要的 Elasticsearch 索引,前面 Fluentd 配置文件中我們采集的日志使用的是 logstash 格式,這里只需要在文本框中輸入logstash-*即可匹配到 Elasticsearch集群中的所有日志數據,然后點擊下一步,進入以下頁面:
點擊next step,出現如下
選擇@timestamp,創建索引
點擊左側的discover,可看到如下:
#****測試容器日志
cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: busybox
imagePullPolicy: IfNotPresent
args: [/bin/sh, -c,``'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done'``]
|
kubectl apply -f pod.yaml
登錄到kibana的控制面板,在discover處的搜索欄中輸入kubernetes.pod_name:counter,這將過濾名為的Pod的日志數據counter
,如下所示:
通過上面幾個步驟,我們已經在k8s集群成功部署了elasticsearch,fluentd,kibana,這里使用的efk系統包括3個Elasticsearch Pod,一個Kibana Pod和一組作為DaemonSet部署的Fluentd Pod。
要了解更多關于elasticsearch可參考:https://www.elastic.co/cn/blog/small-medium-or-large-scaling-elasticsearch-and-evolving-the-elastic-stack-to-fit。
Kubernetes中還允許使用更復雜的日志系統,要了解更多信息,可參考https://kubernetes.io/docs/concepts/cluster-administration/logging/