k8s搭建EFK日誌管理系統
efk就是目前比較受歡迎的日誌管理系統。kubernetes可以實現efk的快速部署和使用,通過statefulset控制器部署elasticsearch元件,用來儲存日誌資料,
還可通過volumenclaimtemplate動態生成pv實現es資料的持久化。通過deployment部署kibana元件,實現日誌的視覺化管理。
通過daemonset控制器部署fluentd元件,來收集各節點和k8s叢集的日誌。
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檔案可以正常執行,否則會存在映象拉取失敗問題:
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
正文-安裝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
elasticsearchClusterIPNone<none>9200/TCP,9300/TCP2m
現在我們已經為 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賬號
catserviceaccount.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終端:
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/
原文連結:
https://mp.weixin.qq.com/s/SoelIGIT5lQSgJlE4MYFjg