kubernetes-1.35.3的基于Operator实现Elasticsearch高可用集群

 

 

 

 

案例:基于 Operator 实现 ECK( Elastic Cloud on Kubernetes )
注意:Worker 节点内存需要4G以上

官方安装说明

 

https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-quickstart.html
https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-deploy-eck.html
https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-deployelasticsearch.html

 

安装 Operator

#安装Operator

kubectl create -f https://download.elastic.co/downloads/eck/3.3.2/crds.yaml

#自动创建相关CRD

#安装operator相关RBAC 规则

kubectl apply -f https://download.elastic.co/downloads/eck/3.3.2/operator.yaml
kubectl apply -f operator.yaml

 

#在elastic-system 名称空间查看相关资源

kubectl get all -n elastic-system

[root@master1 stateful]# kubectl get pod -n elastic-system
NAME                 READY   STATUS    RESTARTS      AGE
elastic-operator-0   1/1     Running   5 (93s ago)   9m36s
[root@master1 stateful]# 
[root@master1 stateful]# kubectl get all -n elastic-system
NAME                     READY   STATUS    RESTARTS      AGE
pod/elastic-operator-0   1/1     Running   5 (96s ago)   9m39s

NAME                             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/elastic-webhook-server   ClusterIP   10.101.108.45   <none>        443/TCP   9m39s

NAME                                READY   AGE
statefulset.apps/elastic-operator   1/1     9m39s

 

部署 Elasticsearch

#准备业务的名称空间

kubectl create ns demo

 

#准备elasticsearch-cluster清单文件

[root@master1 stateful]# cat operator-elasticsearch-cluster.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: my-es-cluster
  #namespace: elastic-system
  namespace: demo
spec:
  #version: 8.13.2
  #version: 8.14.0
  version: 9.2.1
  nodeSets:
  - name: default
    count: 3                  #3个节点的集群
    config:
      node.store.allow_mmap: false
    volumeClaimTemplates:
    - metadata:
       name: elasticsearch-data
      spec:
        accessModes: ["ReadWriteOnce"] 
        resources:
          requests:
            storage: 2Gi
        storageClassName: sc-nfs     #需要提前准备sc-nfs的storageClass

#注意:节点内存需要4G以上

 kubectl apply -f operator-elasticsearch-cluster.yaml

#查看结果

下载镜像慢每个节点 docker pull docker.elastic.co/elasticsearch/elasticsearch:9.2.1

 

 

pvc pv自动创建,绑定了

root@master1 stateful]# kubectl get pvc -n demo 
NAME                                            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
elasticsearch-data-my-es-cluster-es-default-0   Bound    pvc-dd064d85-89ee-442c-9d59-b7969384ad66   2Gi        RWO            sc-nfs         <unset>                 5m43s
elasticsearch-data-my-es-cluster-es-default-1   Bound    pvc-f1807028-bf85-4ffc-9fbf-8e952dcd929e   2Gi        RWO            sc-nfs         <unset>                 5m43s
elasticsearch-data-my-es-cluster-es-default-2   Bound    pvc-4dcf51ea-a7d3-41df-8cf9-0f64196b7d72   2Gi        RWO            sc-nfs         <unset>                 5m43s

 

[root@master1 stateful]# 
[root@master1 stateful]# kubectl get pv -n demo  
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                                STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-4dcf51ea-a7d3-41df-8cf9-0f64196b7d72   2Gi        RWO            Delete           Bound    demo/elasticsearch-data-my-es-cluster-es-default-2   sc-nfs         <unset>                          5m58s
pvc-dd064d85-89ee-442c-9d59-b7969384ad66   2Gi        RWO            Delete           Bound    demo/elasticsearch-data-my-es-cluster-es-default-0   sc-nfs         <unset>                          5m59s
pvc-f1807028-bf85-4ffc-9fbf-8e952dcd929e   2Gi        RWO            Delete           Bound    demo/elasticsearch-data-my-es-cluster-es-default-1   sc-nfs         <unset>                          5m59s
[root@master1 stateful]#

 

[root@master1 stateful]# ls /data/sc-nfs/
demo-elasticsearch-data-my-es-cluster-es-default-0-pvc-dd064d85-89ee-442c-9d59-b7969384ad66
demo-elasticsearch-data-my-es-cluster-es-default-1-pvc-f1807028-bf85-4ffc-9fbf-8e952dcd929e
demo-elasticsearch-data-my-es-cluster-es-default-2-pvc-4dcf51ea-a7d3-41df-8cf9-0f64196b7d72

 

[root@master1 stateful]# kubectl get svc -n demo  
NAME                             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
my-es-cluster-es-default         ClusterIP   None            <none>        9200/TCP   9m21s
my-es-cluster-es-http            ClusterIP   10.105.204.18   <none>        9200/TCP   9m22s
my-es-cluster-es-internal-http   ClusterIP   10.97.59.239    <none>        9200/TCP   9m22s
my-es-cluster-es-transport       ClusterIP   None            <none>        9300/TCP   9m22s

 

 

 

[root@master1 stateful]# kubectl get pod -n demo -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
my-es-cluster-es-default-0   1/1     Running   0          33m   10.244.3.72   node3.org   <none>           <none>
my-es-cluster-es-default-1   1/1     Running   0          33m   10.244.1.64   node1.org   <none>           <none>
my-es-cluster-es-default-2   1/1     Running   0          33m   10.244.2.51   node2.org   <none>           <none>

 

[root@master1 stateful]# kubectl get elasticsearch -n demo
NAME            HEALTH   NODES   VERSION   PHASE   AGE
my-es-cluster   green    3       9.2.1     Ready   35m

 

 

[root@master1 stateful]# kubectl get secret -n demo
NAME                                          TYPE     DATA   AGE
my-es-cluster-es-default-es-config            Opaque   1      13m
my-es-cluster-es-default-es-transport-certs   Opaque   1      13m
my-es-cluster-es-elastic-user                 Opaque   1      13m
my-es-cluster-es-file-settings                Opaque   1      13m
my-es-cluster-es-http-ca-internal             Opaque   2      13m
my-es-cluster-es-http-certs-internal          Opaque   3      13m
my-es-cluster-es-http-certs-public            Opaque   2      13m
my-es-cluster-es-internal-users               Opaque   5      13m
my-es-cluster-es-remote-ca                    Opaque   1      13m
my-es-cluster-es-transport-ca-internal        Opaque   2      13m
my-es-cluster-es-transport-certs-public       Opaque   1      13m
my-es-cluster-es-xpack-file-realm             Opaque   4      13m

 

 

#取出ES的访问密码

PASSWORD=$(kubectl get secret my-es-cluster-es-elastic-user -n demo -o go-template='{{.data.elastic | base64decode}}')

#或者 

PASSWORD=$(kubectl get secret my-es-cluster-es-elastic-user -n demo -o jsonpath={.data.elastic} -n demo|base64 -d)

root@master1 stateful]# PASSWORD=$(kubectl get secret my-es-cluster-es-elastic-user -n demo -o go-template='{{.data.elastic | base64decode}}')
[root@master1 stateful]# echo $PASSWORD
MUlbz7QQbT3tmEirYT6PVpNc

 

 #开启一个新的Pod测试访问ES Cluster

kubectl run --env="PASSWORD=$PASSWORD" client-$RANDOM --image registry.cn-beijing.aliyuncs.com/wangxiaochun/admin-box:v0.1 -it --rm --restart=Never --command -- /bin/bash

[root@master1 ~]# PASSWORD=$(kubectl get secret my-es-cluster-es-elastic-user -n 
> demo -o jsonpath={.data.elastic} -n demo|base64 -d)
error: flag needs an argument: 'n' in -n
See 'kubectl get --help' for usage.
-bash: demo: command not found
[root@master1 ~]# PASSWORD=$(kubectl get secret my-es-cluster-es-elastic-user -n demo -o go-template='{{.data.elastic | base64decode}}')
[root@master1 ~]# 
[root@master1 ~]# kubectl run --env="PASSWORD=$PASSWORD" client-$RANDOM --image registry.cn-beijing.aliyuncs.com/wangxiaochun/admin-box:v0.1 -it --rm --restart=Never --command -- /bin/bash
All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.
If you don't see a command prompt, try pressing enter.
root@client-7340 /# 
root@client-7340 /# 

 


curl -u "elastic:$PASSWORD" -k https://my-es-cluster-es-http.demo:9200

 

 访问成功

root@client-7340 /# curl -u "elastic:$PASSWORD" -k https://my-es-cluster-es-http.demo:9200
{
  "name" : "my-es-cluster-es-default-0",
  "cluster_name" : "my-es-cluster",
  "cluster_uuid" : "McSD8kHlQmaBS5IhAXboGQ",
  "version" : {
    "number" : "9.2.1",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "4ad0ef0e98a2e72fafbd79a19fa5cae2f026117d",
    "build_date" : "2025-11-06T22:07:39.673130621Z",
    "build_snapshot" : false,
    "lucene_version" : "10.3.1",
    "minimum_wire_compatibility_version" : "8.19.0",
    "minimum_index_compatibility_version" : "8.0.0"
  },
  "tagline" : "You Know, for Search"
}
root@client-7340 /# 

 

#当前没有索引

curl -u "elastic:$PASSWORD" -k https://my-es-cluster-es-http.demo:9200/_cat/indices

 

部署 Kibana

#部署Kibana

[root@master1 stateful]# cat operator-elasticsearch-cluster-kibana.yaml
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: kibana
  namespace: demo
spec:
  version: 9.2.1          # ← 改为和 ES 一致
  count: 1
  elasticsearchRef:
    name: "my-es-cluster"
  http:
    tls:
      selfSignedCertificate:
        disabled: true
    service:
      spec:
        type: LoadBalancer

 

 

 

 

kubectl apply -f operator-elasticsearch-cluster-kibana.yaml

docker pull docker.elastic.co/kibana/kibana:8.14.0

#查看状态

 

[root@master1 stateful]# kubectl get kibana -n demo
NAME     HEALTH   NODES   VERSION   AGE
kibana   green    1       9.2.1     10m

 

 

[root@master1 stateful]# kubectl get pod -n demo                           
NAME                         READY   STATUS    RESTARTS   AGE
kibana-kb-5fc95fc47-zpmlt    1/1     Running   0          5m55s
my-es-cluster-es-default-0   1/1     Running   0          59m
my-es-cluster-es-default-1   1/1     Running   0          59m
my-es-cluster-es-default-2   1/1     Running   0          59m

 

#查看SVC

[root@master1 stateful]# kubectl get svc -n demo    
NAME                             TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)          AGE
kibana-kb-http                   LoadBalancer   10.105.110.152   192.168.3.11   5601:32304/TCP   2m39s
my-es-cluster-es-default         ClusterIP      None             <none>         9200/TCP         55m
my-es-cluster-es-http            ClusterIP      10.105.204.18    <none>         9200/TCP         55m
my-es-cluster-es-internal-http   ClusterIP      10.97.59.239     <none>         9200/TCP         55m
my-es-cluster-es-transport       ClusterIP      None             <none>         9300/TCP         55m

 

如何创建 TLS Secret

用自签名证书(测试环境)

mkdir ssl

cd ssl

# 生成私钥

openssl genrsa -out tls.key 2048

# 生成证书签名请求(CSR)

openssl req -new -key tls.key -out tls.csr -subj "/CN=kibana.ming.org"

 

# 生成自签名证书

openssl x509 -req -days 365 -in tls.csr -signkey tls.key -out tls.crt

 

# 创建 Secret

kubectl create secret tls tls-secret-name  --cert=tls.crt --key=tls.key  -n demo

 

  

[root@master1 stateful]# cd ssl/
[root@master1 ssl]# ll
total 0
[root@master1 ssl]# 
[root@master1 ssl]# openssl genrsa -out tls.key 2048
[root@master1 ssl]# 
[root@master1 ssl]# openssl req -new -key tls.key -out tls.csr -subj "/CN=kibana.ming.org"
[root@master1 ssl]# ll
total 8
-rw-r--r-- 1 root root  899 May 29 02:01 tls.csr
-rw------- 1 root root 1704 May 29 02:01 tls.key
[root@master1 ssl]# 
[root@master1 ssl]# openssl x509 -req -days 365 -in tls.csr -signkey tls.key -out tls.crt
Certificate request self-signature ok
subject=CN=kibana.ming.org

 

kubectl create secret tls tls-secret-name  --cert=tls.crt --key=tls.key  -n demo

 

[root@master1 stateful]# cat kibana-ingress.yaml              
---
# Ingress 启用 TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kibana
  namespace: demo
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - kibana.ming.org
    secretName: tls-secret-name    # ← Ingress 用的证书
  rules:
  - host: kibana.ming.org
    http:
      paths:
      - backend:
          service:
            name: kibana-kb-http
            port:
              number: 5601
        path: /
        pathType: Prefix

 

root@master1 stateful]# kubectl get ingress -n demo
NAME     CLASS   HOSTS             ADDRESS        PORTS     AGE
kibana   nginx   kibana.ming.org   192.168.3.10   80, 443   52s

 

部署 Filebeat

#部署Filebeat

[root@master1 stateful]# cat operator-elasticsearch-cluster-filebeat.yaml
apiVersion: beat.k8s.elastic.co/v1beta1
kind: Beat
metadata:
  name: filebeat
  namespace: demo
spec:
  type: filebeat
  version: 9.2.1
  elasticsearchRef:
    name: "my-es-cluster"
  kibanaRef:
    name: "kibana"
  config:
    filebeat:
      autodiscover:
        providers:
        - type: kubernetes
          node: ${NODE_NAME}
          hints:
            enabled: true
            default_config:
              type: container
              paths:
              - /var/log/containers/*${data.kubernetes.container.id}.log
      processors:
      - add_kubernetes_metadata:
          host: ${NODE_NAME}
          matchers:
          - logs_path:
              logs_path: "/var/log/containers/"
      - drop_event.when:
          or:
          - equals:
              kubernetes.namespace: "kube-system"
          - equals:
              kubernetes.namespace: "logging"
          - equals:
              kubernetes.namespace: "ingress-nginx"
          - equals:
              kubernetes.namespace: "kube-node-lease"
          - equals:
              kubernetes.namespace: "elastic-system"
    output:
      elasticsearch:
        hosts: ['https://my-es-cluster-es-http.demo.svc.cluster.local:9200']
        ssl:
          verification_mode: none
  daemonSet:
    podTemplate:
      spec:
        serviceAccountName: filebeat
        automountServiceAccountToken: true
        terminationGracePeriodSeconds: 30
        dnsPolicy: ClusterFirstWithHostNet
        hostNetwork: true
        containers:
        - name: filebeat
          securityContext:
            runAsUser: 0
          volumeMounts:
          - name: varlogcontainers
            mountPath: /var/log/containers
          - name: varlogpods
            mountPath: /var/log/pods
          - name: varlibdockercontainers
            mountPath: /var/lib/docker/containers
          env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
        volumes:
        - name: varlogcontainers
          hostPath:
            path: /var/log/containers
        - name: varlogpods
          hostPath:
            path: /var/log/pods
        - name: varlibdockercontainers
          hostPath:
            path: /var/lib/docker/containers
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: filebeat
rules:
- apiGroups: [""]
  resources:
  - namespaces
  - pods
  - nodes
  verbs:
  - get
  - watch
  - list
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: filebeat
  namespace: demo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: filebeat
subjects:
- kind: ServiceAccount
  name: filebeat
  namespace: demo
roleRef:
  kind: ClusterRole
  name: filebeat
  apiGroup: rbac.authorization.k8s.io

 

 

 

kubectl apply -f operator-elasticsearch-cluster-filebeat.yaml

 

[root@master1 stateful]# kubectl get pod -n demo -o wide
NAME                           READY   STATUS    RESTARTS   AGE     IP             NODE        NOMINATED NODE   READINESS GATES
filebeat-beat-filebeat-8l5j2   1/1     Running   0          6m40s   192.168.3.64   node2.org   <none>           <none>
filebeat-beat-filebeat-f7fnw   1/1     Running   0          6m41s   192.168.3.63   node1.org   <none>           <none>
filebeat-beat-filebeat-mnkc8   1/1     Running   0          6m40s   192.168.3.65   node3.org   <none>           <none>
kibana-kb-679c8b8d4c-tqsf9     1/1     Running   0          12m     10.244.3.77    node3.org   <none>           <none>
my-es-cluster-es-default-0     1/1     Running   0          101m    10.244.3.72    node3.org   <none>           <none>
my-es-cluster-es-default-1     1/1     Running   0          101m    10.244.1.64    node1.org   <none>           <none>
my-es-cluster-es-default-2     1/1     Running   0          101m    10.244.2.51    node2.org   <none>           <none>

 

[root@master1 stateful]# kubectl get beats -n demo
NAME       HEALTH   AVAILABLE   EXPECTED   TYPE       VERSION   AGE
filebeat   green    3           3          filebeat   9.2.1     7m17s

 

测试访问 Kibana

#使用ElasticSearch部署时生成的用户elastic及其密码即可登录service的LoadBalancer
#用前面的命令取出密码

[root@master1 ~]# PASSWORD=$(kubectl get secret my-es-cluster-es-elastic-user -n demo -o jsonpath={.data.elastic} -n demo|base64 -d)
[root@master1 ~]# echo $PASSWORD
MUlbz7QQbT3tmEirYT6PVpNc

 

通过Kibana的LoadBalancer SVC的IP直接访问

[root@master1 ~]# kubectl get svc -n demo
NAME                             TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)          AGE
kibana-kb-http                   LoadBalancer   10.111.212.32   192.168.3.11   5601:32685/TCP   15m
my-es-cluster-es-default         ClusterIP      None            <none>         9200/TCP         105m
my-es-cluster-es-http            ClusterIP      10.105.204.18   <none>         9200/TCP         105m
my-es-cluster-es-internal-http   ClusterIP      10.97.59.239    <none>         9200/TCP         105m
my-es-cluster-es-transport       ClusterIP      None            <none>         9300/TCP         105m

 

http://192.168.3.11:5601/

9ebdf7cda6e2ee0763b3d66d2e915e07

 

用ingress nginx LoadBalancer 80 443访问

192.168.3.10 kibana.ming.org

https://kibana.ming.org/

5c9376084fa1a398f7975d9d2f992448

 

53e062685de21238d51c16bacb018fae

 

 

Kibana会自动加载由filebeat生成的index pattern

 

9d5314322542a9afc358b005e2608bf6

 

114fef52990ae62cd1378b829adde000

 

 

因此直接进行Discover页面即可查看到数据

 

728e2746-27f0-4f9d-84a5-7afb6ef6e793

 

#查看到索引

PASSWORD=$(kubectl get secret my-es-cluster-es-elastic-user -n demo -o jsonpath={.data.elastic} -n demo|base64 -d)

 

kubectl run --env="PASSWORD=$PASSWORD" client-$RANDOM --image wangxiaochun/admin-box:v0.1 -it --rm --restart=Never --command -- /bin/bash

curl -u "elastic:$PASSWORD" -k https://my-es-cluster-es-http.demo:9200/_cat/indices

root@client-15351 /#  curl -u "elastic:$PASSWORD" -k https://my-es-cluster-es-http.demo:9200/_cat/indices
green open .internal.alerts-transform.health.alerts-default-000001            LisipK3qQRWorAIDPutpxQ 1 1 0 0 498b 249b 249b
green open .internal.alerts-observability.logs.alerts-default-000001          rC2l1mx2QfqNh9eEePogvQ 1 1 0 0 498b 249b 249b
green open .internal.alerts-observability.uptime.alerts-default-000001        lFX1l9dWQxuuiXw46YB_Iw 1 1 0 0 498b 249b 249b
green open .internal.alerts-ml.anomaly-detection.alerts-default-000001        1C6hhwhlQS--bKez_D6CPA 1 1 0 0 498b 249b 249b
green open .internal.alerts-observability.slo.alerts-default-000001           LhEBqyYQT260H6NicnACTw 1 1 0 0 498b 249b 249b
green open .internal.alerts-default.alerts-default-000001                     uxW_Kzu7R2G5utoGzDBb5w 1 1 0 0 498b 249b 249b
green open .internal.alerts-streams.alerts-default-000001                     wqhTlFNDRaagMvaxrx8qgg 1 1 0 0 498b 249b 249b
green open .internal.alerts-observability.apm.alerts-default-000001           dDkomNgfQlyg6NZTUlxejw 1 1 0 0 498b 249b 249b
green open .internal.alerts-security.attack.discovery.alerts-default-000001   bRNBXUlCTnianu4rUMmdYA 1 1 0 0 498b 249b 249b
green open .internal.alerts-observability.metrics.alerts-default-000001       LDOoAnzIRFe-JAsizHysTw 1 1 0 0 498b 249b 249b
green open .internal.alerts-ml.anomaly-detection-health.alerts-default-000001 jVTf5Pp_Tz2LMisUWlYEGA 1 1 0 0 498b 249b 249b
green open .internal.alerts-observability.threshold.alerts-default-000001     UPpq9fQGRB2o0wM5QDeHtQ 1 1 0 0 498b 249b 249b
green open .internal.alerts-security.alerts-default-000001                    z3SoAFPLSGyUTBQaA5ZCww 1 1 0 0 498b 249b 249b
green open .internal.alerts-dataset.quality.alerts-default-000001             XitRxpKNRYioNBYd95hbNA 1 1 0 0 498b 249b 249b
green open .internal.alerts-stack.alerts-default-000001                       Zm-YRokWRB-XIcO3kT4EQw 1 1 0 0 498b 249b 249b

 

posted @ 2026-05-29 05:05  minger_lcm  阅读(7)  评论(0)    收藏  举报