HighlyAvailable and Scalable Elasticsearch on Kubernetes
Kubernetes上部署高可用和可擴充套件的Elasticsearch
ofollow,noindex" target="_blank">原文
在上一篇文章中,我們通過擴充套件MongoDB副本集來了解有StatefulSets。 在這篇文章中,我們將與ES-HQ和Kibana一起使用HA Elasticsearch叢集(具有不同的Master,Data和Client節點)。
先決條件
- Elasticsearch的基本知識,其Node型別及角色
- 執行至少有3個節點的Kubernetes叢集(至少4Cores 4GB)
- Kibana的相關知識
部署架構圖
- Elasticsearch Data Node的Pod被部署為具有Headless Service的StatefulSets,以提供穩定的網路ID
- Elasticsearch Master Node的Pod被部署為具有Headless Service的副本集,這將有助於自動發現
- Elasticsearch Client Node的Pod部署為具有內部服務的副本集,允許訪問R/W請求的Data Node
- Kibana和ElasticHQ Pod被部署為副本集,其服務可在Kubernetes叢集外部訪問,但仍在您的子網內部(除非另有要求,否則不公開)
- 為Client Node部署HPA(Horizonal Pod Auto-scaler)以在高負載下實現自動伸縮
要記住的重要事項:
- 設定ES_JAVA_OPT環境變數
- 設定CLUSTER_NAME環境變數
- 為Master Node的部署設定NUMBER_OF_MASTERS環境變數(防止腦裂問題)。如果有3個Masters,我們必須設定為2。
- 在類似的pod中設定正確的Pod-AntiAffinity策略,以便在工作節點發生故障時確保HA。
讓我們直接將這些服務部署到我們的GKE叢集。
Master節點部署
apiVersion: v1 kind: Namespace metadata: name: elasticsearch --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: es-master namespace: elasticsearch labels: component: elasticsearch role: master spec: replicas: 3 template: metadata: labels: component: elasticsearch role: master spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: role operator: In values: - master topologyKey: kubernetes.io/hostname initContainers: - name: init-sysctl image: busybox:1.27.2 command: - sysctl - -w - vm.max_map_count=262144 securityContext: privileged: true containers: - name: es-master image: quay.io/pires/docker-elasticsearch-kubernetes:6.2.4 env: - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: NODE_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: CLUSTER_NAME value: my-es - name: NUMBER_OF_MASTERS value: "2" - name: NODE_MASTER value: "true" - name: NODE_INGEST value: "false" - name: NODE_DATA value: "false" - name: HTTP_ENABLE value: "false" - name: ES_JAVA_OPTS value: -Xms256m -Xmx256m - name: PROCESSORS valueFrom: resourceFieldRef: resource: limits.cpu resources: limits: cpu: 2 ports: - containerPort: 9300 name: transport volumeMounts: - name: storage mountPath: /data volumes: - emptyDir: medium: "" name: "storage" --- apiVersion: v1 kind: Service metadata: name: elasticsearch-discovery namespace: elasticsearch labels: component: elasticsearch role: master spec: selector: component: elasticsearch role: master ports: - name: transport port: 9300 protocol: TCP clusterIP: None
root$ kubectl apply -f es-master.yml root$ kubectl -n elasticsearch get all NAMEDESIREDCURRENTUP-TO-DATEAVAILABLEAGE deploy/es-master333332s NAMEDESIREDCURRENTREADYAGE rs/es-master-594b58b86c33331s NAMEREADYSTATUSRESTARTSAGE po/es-master-594b58b86c-9jkj21/1Running031s po/es-master-594b58b86c-bj7g71/1Running031s po/es-master-594b58b86c-lfpps1/1Running031s NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE svc/elasticsearch-discoveryClusterIPNone<none>9300/TCP31s
有趣的是,可以從任何主節點pod的日誌來見證它們之間的master選舉,然後何時新增新的data和client節點。
root$ kubectl -n elasticsearch logs -f po/es-master-594b58b86c-9jkj2 | grep ClusterApplierService [2018-10-21T07:41:54,958][INFO ][o.e.c.s.ClusterApplierService] [es-master-594b58b86c-9jkj2] detected_master {es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300}, added {{es-master-594b58b86c-lfpps}{wZQmXr5fSfWisCpOHBhaMg}{50jGPeKLSpO9RU_HhnVJCA}{10.9.124.81}{10.9.124.81:9300},{es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300},}, reason: apply cluster state (from master [master {es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300} committed version [3]])
可以看出,名為es-master-594b58b86c-bj7g7的es-master pod被選為master節點,其他2個pod被新增到這個叢集。
名為elasticsearch-discovery的Headless Service預設設定為docker映象中的env變數,用於在節點之間進行發現。 當然這是可以被改寫的。
同樣,我們可以部署Data和Client節點。 配置如下:
Data節點部署:
apiVersion: v1 kind: Namespace metadata: name: elasticsearch --- apiVersion: storage.k8s.io/v1beta1 kind: StorageClass metadata: name: fast provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd fsType: xfs allowVolumeExpansion: true --- apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: es-data namespace: elasticsearch labels: component: elasticsearch role: data spec: serviceName: elasticsearch-data replicas: 3 template: metadata: labels: component: elasticsearch role: data spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: role operator: In values: - data topologyKey: kubernetes.io/hostname initContainers: - name: init-sysctl image: busybox:1.27.2 command: - sysctl - -w - vm.max_map_count=262144 securityContext: privileged: true containers: - name: es-data image: quay.io/pires/docker-elasticsearch-kubernetes:6.2.4 env: - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: NODE_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: CLUSTER_NAME value: my-es - name: NODE_MASTER value: "false" - name: NODE_INGEST value: "false" - name: HTTP_ENABLE value: "false" - name: ES_JAVA_OPTS value: -Xms256m -Xmx256m - name: PROCESSORS valueFrom: resourceFieldRef: resource: limits.cpu resources: limits: cpu: 2 ports: - containerPort: 9300 name: transport volumeMounts: - name: storage mountPath: /data volumeClaimTemplates: - metadata: name: storage annotations: volume.beta.kubernetes.io/storage-class: "fast" spec: accessModes: [ "ReadWriteOnce" ] storageClassName: fast resources: requests: storage: 10Gi --- apiVersion: v1 kind: Service metadata: name: elasticsearch-data namespace: elasticsearch labels: component: elasticsearch role: data spec: ports: - port: 9300 name: transport clusterIP: None selector: component: elasticsearch role: data
Headless Service為Data節點提供穩定的網路ID,有助於它們之間的資料傳輸。
在將持久卷附加到pod之前格式化它是很重要的。 這可以通過在建立storage class時指定卷型別來完成。 我們還可以設定標誌以允許動態擴充套件。 這裡 可以閱讀更多內容。
... parameters: type: pd-ssd fsType: xfs allowVolumeExpansion: true ...
Client節點部署
apiVersion: v1 kind: Namespace metadata: name: elasticsearch --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: es-client namespace: elasticsearch labels: component: elasticsearch role: client spec: replicas: 2 template: metadata: labels: component: elasticsearch role: client spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: role operator: In values: - client topologyKey: kubernetes.io/hostname initContainers: - name: init-sysctl image: busybox:1.27.2 command: - sysctl - -w - vm.max_map_count=262144 securityContext: privileged: true containers: - name: es-client image: quay.io/pires/docker-elasticsearch-kubernetes:6.2.4 env: - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: NODE_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: CLUSTER_NAME value: my-es - name: NODE_MASTER value: "false" - name: NODE_DATA value: "false" - name: HTTP_ENABLE value: "true" - name: ES_JAVA_OPTS value: -Xms256m -Xmx256m - name: NETWORK_HOST value: _site_,_lo_ - name: PROCESSORS valueFrom: resourceFieldRef: resource: limits.cpu resources: limits: cpu: 1 ports: - containerPort: 9200 name: http - containerPort: 9300 name: transport volumeMounts: - name: storage mountPath: /data volumes: - emptyDir: medium: "" name: storage --- apiVersion: v1 kind: Service metadata: name: elasticsearch namespace: elasticsearch annotations: cloud.google.com/load-balancer-type: Internal labels: component: elasticsearch role: client spec: selector: component: elasticsearch role: client ports: - name: http port: 9200 type: LoadBalancer
此處部署的服務是從Kubernetes叢集外部訪問ES群集,但仍在我們的子網內部。 註釋掉 cloud.google.com/load-balancer-type:Internal
可確保這一點。
但是,如果我們的ES叢集中的應用程式部署在叢集中,則可以通過 http://elasticsearch.elasticsearch:9200 來訪問ElasticSearch服務。
建立這兩個deployments後,新建立的client和data節點將自動新增到叢集中。(觀察master pod的日誌)
root$ kubectl apply -f es-data.yml root$ kubectl -n elasticsearch get pods -l role=data NAMEREADYSTATUSRESTARTSAGE es-data-01/1Running048s es-data-11/1Running028s -------------------------------------------------------------------- root$ kubectl apply -f es-client.yml root$ kubectl -n elasticsearch get pods -l role=client NAMEREADYSTATUSRESTARTSAGE es-client-69b84b46d8-kr7j41/1Running047s es-client-69b84b46d8-v5pj21/1Running047s -------------------------------------------------------------------- root$ kubectl -n elasticsearch get all NAMEDESIREDCURRENTUP-TO-DATEAVAILABLEAGE deploy/es-client22221m deploy/es-master33339m NAMEDESIREDCURRENTREADYAGE rs/es-client-69b84b46d82221m rs/es-master-594b58b86c3339m NAMEDESIREDCURRENTAGE statefulsets/es-data223m NAMEREADYSTATUSRESTARTSAGE po/es-client-69b84b46d8-kr7j41/1Running01m po/es-client-69b84b46d8-v5pj21/1Running01m po/es-data-01/1Running03m po/es-data-11/1Running03m po/es-master-594b58b86c-9jkj21/1Running09m po/es-master-594b58b86c-bj7g71/1Running09m po/es-master-594b58b86c-lfpps1/1Running09m NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE svc/elasticsearchLoadBalancer10.9.121.160 10.9.120.89200:32310/TCP1m svc/elasticsearch-dataClusterIPNone<none>9300/TCP3m svc/elasticsearch-discoveryClusterIPNone<none>9300/TCP9m -------------------------------------------------------------------- #Check logs of es-master leader pod root$ kubectl -n elasticsearch logs po/es-master-594b58b86c-bj7g7 | grep ClusterApplierService [2018-10-21T07:41:53,731][INFO ][o.e.c.s.ClusterApplierService] [es-master-594b58b86c-bj7g7] new_master {es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300}, added {{es-master-594b58b86c-lfpps}{wZQmXr5fSfWisCpOHBhaMg}{50jGPeKLSpO9RU_HhnVJCA}{10.9.124.81}{10.9.124.81:9300},}, reason: apply cluster state (from master [master {es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300} committed version [1] source [zen-disco-elected-as-master ([1] nodes joined)[{es-master-594b58b86c-lfpps}{wZQmXr5fSfWisCpOHBhaMg}{50jGPeKLSpO9RU_HhnVJCA}{10.9.124.81}{10.9.124.81:9300}]]]) [2018-10-21T07:41:55,162][INFO ][o.e.c.s.ClusterApplierService] [es-master-594b58b86c-bj7g7] added {{es-master-594b58b86c-9jkj2}{x9Prp1VbTq6_kALQVNwIWg}{7NHUSVpuS0mFDTXzAeKRcg}{10.9.125.81}{10.9.125.81:9300},}, reason: apply cluster state (from master [master {es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300} committed version [3] source [zen-disco-node-join[{es-master-594b58b86c-9jkj2}{x9Prp1VbTq6_kALQVNwIWg}{7NHUSVpuS0mFDTXzAeKRcg}{10.9.125.81}{10.9.125.81:9300}]]]) [2018-10-21T07:48:02,485][INFO ][o.e.c.s.ClusterApplierService] [es-master-594b58b86c-bj7g7] added {{es-data-0}{SAOhUiLiRkazskZ_TC6EBQ}{qirmfVJBTjSBQtHZnz-QZw}{10.9.126.88}{10.9.126.88:9300},}, reason: apply cluster state (from master [master {es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300} committed version [4] source [zen-disco-node-join[{es-data-0}{SAOhUiLiRkazskZ_TC6EBQ}{qirmfVJBTjSBQtHZnz-QZw}{10.9.126.88}{10.9.126.88:9300}]]]) [2018-10-21T07:48:21,984][INFO ][o.e.c.s.ClusterApplierService] [es-master-594b58b86c-bj7g7] added {{es-data-1}{fiv5Wh29TRWGPumm5ypJfA}{EXqKGSzIQquRyWRzxIOWhQ}{10.9.125.82}{10.9.125.82:9300},}, reason: apply cluster state (from master [master {es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300} committed version [5] source [zen-disco-node-join[{es-data-1}{fiv5Wh29TRWGPumm5ypJfA}{EXqKGSzIQquRyWRzxIOWhQ}{10.9.125.82}{10.9.125.82:9300}]]]) [2018-10-21T07:50:51,245][INFO ][o.e.c.s.ClusterApplierService] [es-master-594b58b86c-bj7g7] added {{es-client-69b84b46d8-v5pj2}{MMjA_tlTS7ux-UW44i0osg}{rOE4nB_jSmaIQVDZCjP8Rg}{10.9.125.83}{10.9.125.83:9300},}, reason: apply cluster state (from master [master {es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300} committed version [6] source [zen-disco-node-join[{es-client-69b84b46d8-v5pj2}{MMjA_tlTS7ux-UW44i0osg}{rOE4nB_jSmaIQVDZCjP8Rg}{10.9.125.83}{10.9.125.83:9300}]]]) [2018-10-21T07:50:58,964][INFO ][o.e.c.s.ClusterApplierService] [es-master-594b58b86c-bj7g7] added {{es-client-69b84b46d8-kr7j4}{gGC7F4diRWy2oM1TLTvNsg}{IgI6g3iZT5Sa0HsFVMpvvw}{10.9.124.82}{10.9.124.82:9300},}, reason: apply cluster state (from master [master {es-master-594b58b86c-bj7g7}{1aFT97hQQ7yiaBc2CYShBA}{Q3QzlaG3QGazOwtUl7N75Q}{10.9.126.87}{10.9.126.87:9300} committed version [7] source [zen-disco-node-join[{es-client-69b84b46d8-kr7j4}{gGC7F4diRWy2oM1TLTvNsg}{IgI6g3iZT5Sa0HsFVMpvvw}{10.9.124.82}{10.9.124.82:9300}]]])
leading master pod的日誌清楚地描述了每個節點何時新增到叢集。 這在除錯問題時非常有用。
部署完所有元件後,我們應驗證以下內容:
- 在kubernetes叢集內部使用ubuntu容器進行Elasticsearch部署的驗證。
root$ kubectl run my-shell --rm -i --tty --image ubuntu -- bash root@my-shell-68974bb7f7-pj9x6:/# curl http://elasticsearch.elasticsearch:9200/_cluster/health?pretty { "cluster_name" : "my-es", "status" : "green", "timed_out" : false, "number_of_nodes" : 7, "number_of_data_nodes" : 2, "active_primary_shards" : 0, "active_shards" : 0, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 }
- 在kubernetes叢集外部使用GCP內部LoadBalancer IP(這裡是10.9.120.8)進行Elasticsearch部署的驗證。
root$ curl http://10.9.120.8:9200/_cluster/health?pretty { "cluster_name" : "my-es", "status" : "green", "timed_out" : false, "number_of_nodes" : 7, "number_of_data_nodes" : 2, "active_primary_shards" : 0, "active_shards" : 0, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 }
- ES-Pods的Anti-Affinity規則驗證。
root$ kubectl -n elasticsearch get pods -o wide NAMEREADYSTATUSRESTARTSAGEIPNODE es-client-69b84b46d8-kr7j41/1Running010m10.8.14.52gke-cluster1-pool1-d2ef2b34-t6h9 es-client-69b84b46d8-v5pj21/1Running010m10.8.15.53gke-cluster1-pool1-42b4fbc4-cncn es-data-01/1Running012m10.8.16.58gke-cluster1-pool1-4cfd808c-kpx1 es-data-11/1Running012m10.8.15.52gke-cluster1-pool1-42b4fbc4-cncn es-master-594b58b86c-9jkj21/1Running018m10.8.15.51gke-cluster1-pool1-42b4fbc4-cncn es-master-594b58b86c-bj7g71/1Running018m10.8.16.57gke-cluster1-pool1-4cfd808c-kpx1 es-master-594b58b86c-lfpps1/1Running018m10.8.14.51gke-cluster1-pool1-d2ef2b34-t6h9
請注意,同一節點上沒有2個類似的pod。 這可以在節點發生故障時確保HA。
Scaling相關注意事項
我們可以根據CPU閾值為client節點部署autoscalers。 Client節點的HPA示例可能如下所示:
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: es-client namespace: elasticsearch spec: maxReplicas: 5 minReplicas: 2 scaleTargetRef: apiVersion: extensions/v1beta1 kind: Deployment name: es-client targetCPUUtilizationPercentage: 80
每當autoscaler啟動時,我們都可以通過觀察任何master pod的日誌來觀察新增到叢集中的新client節點pod。
對於Data Node Pod,我們必須使用K8 Dashboard或GKE控制檯增加副本數量。 新建立的data節點將自動新增到叢集中,並開始從其他節點複製資料。
Master Node Pod不需要自動擴充套件,因為它們只儲存叢集狀態資訊,但是如果要新增更多data節點,請確保叢集中沒有偶數個master節點,同時環境變數NUMBER_OF_MASTERS也需要相應調整。
部署Kibana和ES-HQ
Kibana是一個視覺化ES資料的簡單工具,ES-HQ有助於管理和監控Elasticsearch叢集。 對於我們的Kibana和ES-HQ部署,我們記住以下事項:
- 我們提供ES-Cluster的名稱作為docker映象的環境變數
- 訪問Kibana/ES-HQ部署的服務僅在我們組織內部,即不建立公共IP。 我們使用GCP內部負載均衡。
Kibana部署
apiVersion: v1 kind: Namespace metadata: name: elasticsearch --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: es-kibana namespace: elasticsearch labels: component: elasticsearch role: kibana spec: replicas: 1 template: metadata: labels: component: elasticsearch role: kibana spec: containers: - name: es-kibana image: docker.elastic.co/kibana/kibana-oss:6.2.2 env: - name: CLUSTER_NAME value: my-es - name: ELASTICSEARCH_URL value: http://elasticsearch:9200 resources: limits: cpu: 0.5 ports: - containerPort: 5601 name: http --- apiVersion: v1 kind: Service metadata: name: kibana annotations: cloud.google.com/load-balancer-type: "Internal" namespace: elasticsearch labels: component: elasticsearch role: kibana spec: selector: component: elasticsearch role: kibana ports: - name: http port: 80 targetPort: 5601 protocol: TCP type: LoadBalancer
ES-HQ部署
apiVersion: v1 kind: Namespace metadata: name: elasticsearch --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: es-hq namespace: elasticsearch labels: component: elasticsearch role: hq spec: replicas: 1 template: metadata: labels: component: elasticsearch role: hq spec: containers: - name: es-hq image: elastichq/elasticsearch-hq:release-v3.4.0 env: - name: HQ_DEFAULT_URL value: http://elasticsearch:9200 resources: limits: cpu: 0.5 ports: - containerPort: 5000 name: http --- apiVersion: v1 kind: Service metadata: name: hq annotations: cloud.google.com/load-balancer-type: "Internal" namespace: elasticsearch labels: component: elasticsearch role: hq spec: selector: component: elasticsearch role: hq ports: - name: http port: 80 targetPort: 5000 protocol: TCP type: LoadBalancer
我們可以使用新建立的Internal LoadBalancers訪問這兩個服務。
root$ kubectl -n elasticsearch get svc -l role=kibana NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE kibanaLoadBalancer10.9.121.24610.9.120.1080:31400/TCP1m root$ kubectl -n elasticsearch get svc -l role=hq NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE hqLoadBalancer10.9.121.15010.9.120.980:31499/TCP1m
Kibana Dashboard http://<External-Ip-Kibana-Service>/app/kibana#/home?_g=()
ElasticHQ Dasboard http://<External-Ip-ES-Hq-Service>/#!/clusters/my-es
ES是最廣泛使用的分散式搜尋和分析系統之一,當與Kubernetes結合使用時,將消除有關擴充套件和HA的關鍵問題。 此外,使用Kubernetes部署新的ES群集需要時間。 我希望這個部落格對你有用,我真的很期待改進的建議。 隨意評論或聯絡 LinkedIn 。