Skip to content

国内源部署高可用 Elasticsearch(ECK Operator)手册

  • 日期:2026-08-05
  • 集群:uat1(RKE2 v1.35.6,API 192.168.122.31:6443,Rancher 纳管)
  • 方案:ECK(Elastic Cloud on Kubernetes)Operator 管理 3 节点 Elasticsearch 集群
  • 存储:NFS 192.168.122.1:/mnt/xfs/rancher/es(专用 provisioner + StorageClass nfs-elasticsearch)
  • 镜像源:国内代理 elastic.m.daocloud.io → 同步至内网 Harbor 192.168.122.156:30000/elastic

1. 架构与版本

text
┌─ elastic-system ─────────────────────────┐
│ elastic-operator-0 (ECK Operator,1 副本) │  监听全集群 ES/Kibana CR
└──────────────────────────────────────────┘
                │ reconcile
┌─ elasticsearch ──────────────────────────┐
│ elasticsearch-es-master-data-0/1/2       │  3 节点,全角色(master+data+ingest)
│ 反亲和强制分散到不同 K8s 节点            │
│ PVC ×3 → nfs-elasticsearch → NFS 服务器  │
│ svc/elasticsearch-es-http:9200 (HTTPS)   │
│ elasticsearch-nfs-provisioner ×1         │
└──────────────────────────────────────────┘
组件版本镜像(Harbor 内网地址)
ECK Operator3.5.0192.168.122.156:30000/elastic/eck-operator:3.5.0
Elasticsearch9.4.4192.168.122.156:30000/elastic/elasticsearch:9.4.4
nfs-subdir-external-provisionerchart 4.0.18 / app v4.0.2192.168.122.156:30000/sig-storage/nfs-subdir-external-provisioner:v4.0.2
ECK helm chart3.5.0https://helm.elastic.co(国内可直连)

为什么选 ECK 而不是 elastic helm chart / StatefulSet 手写:ECK 是 Elastic 官方推荐的 K8s 部署方式,自动处理 TLS 证书、节点发现、滚动升级、扩缩容时的分片迁移与安全投票配置,HA 集群生命周期管理成本最低。

2. 前置条件

  • kubectl 能操作集群:export KUBECONFIG=~/.kube/config-122.31;
  • 一台能上公网的机器(本文档机):装 skopeo,能连 Harbor;
  • Harbor 管理员账号(项目需手工创建一次);
  • 集群节点 containerd 已配置 mirror "*" → http://192.168.122.156:30000,所有镜像必须先进 Harbor,否则拉取失败。

3. 部署步骤

全流程已固化为 deploy.sh,下文为逐步说明。

3.1 国内镜像源说明

地址用途
DaoCloud elastic 代理(本文使用)elastic.m.daocloud.io代理 docker.elastic.co,拉 ECK/ES/Kibana 镜像
DaoCloud dockerhub 代理docker.m.daocloud.io拉 docker.io 镜像
helm 官方 repohttps://helm.elastic.coECK chart,国内实测可直连

验证镜像可拉:

bash
skopeo inspect --tls-verify=false docker://elastic.m.daocloud.io/eck/eck-operator:3.5.0 --format '{{.Digest}}'
skopeo inspect --tls-verify=false docker://elastic.m.daocloud.io/elasticsearch/elasticsearch:9.4.4 --format '{{.Digest}}'

3.2 同步镜像到内网 Harbor

bash
# ① 创建项目(一次性)
curl -s -X POST -u 'admin:<HARBOR密码>' -H 'Content-Type: application/json' \
  -d '{"project_name":"elastic","metadata":{"public":"true"}}' \
  'http://192.168.122.156:30000/api/v2.0/projects'

# ② 登录并同步
skopeo login --tls-verify=false -u admin -p '<HARBOR密码>' 192.168.122.156:30000

skopeo copy --src-tls-verify=false --dest-tls-verify=false \
  docker://elastic.m.daocloud.io/eck/eck-operator:3.5.0 \
  docker://192.168.122.156:30000/elastic/eck-operator:3.5.0

skopeo copy --src-tls-verify=false --dest-tls-verify=false \
  docker://elastic.m.daocloud.io/elasticsearch/elasticsearch:9.4.4 \
  docker://192.168.122.156:30000/elastic/elasticsearch:9.4.4   # 约 890MB,内网几分钟

3.3 安装 ECK Operator

bash
# chart 国内可直连下载;如失败也可先 helm pull 再传
curl -skLO 'https://helm.elastic.co/helm/eck-operator/eck-operator-3.5.0.tgz'

kubectl create namespace elastic-system
helm install elastic-operator ./eck-operator-3.5.0.tgz -n elastic-system \
  --set image.repository=192.168.122.156:30000/elastic/eck-operator

kubectl -n elastic-system rollout status sts/elastic-operator
kubectl get crd elasticsearches.elasticsearch.k8s.elastic.co   # CRD 就绪

3.4 准备 NFS 存储(专用目录 /mnt/xfs/rancher/es)

集群惯例:每个中间件一个独立 NFS provisioner(参考 mysql/redis),ES 同理。

bash
# ① 在任一能挂载 NFS 的机器上创建目录(一次性)
mount -t nfs 192.168.122.1:/mnt/xfs/rancher /tmp/nfs-mnt
mkdir -p /tmp/nfs-mnt/es && umount /tmp/nfs-mnt

# ② helm 安装 ES 专用 provisioner(chart 本地缓存或官方 repo)
helm install elasticsearch-nfs-provisioner \
  nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
  -n elasticsearch --create-namespace \
  --set nfs.server=192.168.122.1 \
  --set nfs.path=/mnt/xfs/rancher/es \
  --set image.repository=192.168.122.156:30000/sig-storage/nfs-subdir-external-provisioner \
  --set image.tag=v4.0.2 \
  --set storageClass.name=nfs-elasticsearch \
  --set storageClass.provisionerName=cluster.local/elasticsearch-nfs-provisioner \
  --set storageClass.archiveOnDelete=false

kubectl get sc nfs-elasticsearch

3.5 部署 3 节点 HA Elasticsearch

完整清单见 elasticsearch-cluster.yaml,核心点:

yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: elasticsearch
  namespace: elasticsearch
spec:
  version: 9.4.4
  image: 192.168.122.156:30000/elastic/elasticsearch:9.4.4   # 关键:指向 Harbor
  nodeSets:
  - name: master-data
    count: 3
    config:
      node.store.allow_mmap: false        # NFS 存储必须关 mmap
    podTemplate:
      spec:
        affinity:                          # 一机一拍,真 HA
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  elasticsearch.k8s.elastic.co/cluster-name: elasticsearch
              topologyKey: kubernetes.io/hostname
        containers:
        - name: elasticsearch
          resources:                       # ECK 自动按 limits 50% 配堆(2Gi→1G heap)
            requests: {memory: 2Gi, cpu: 500m}
            limits:   {memory: 2Gi, cpu: "2"}
    volumeClaimTemplates:
    - metadata: {name: elasticsearch-data}
      spec:
        accessModes: [ReadWriteOnce]
        storageClassName: nfs-elasticsearch
        resources: {requests: {storage: 20Gi}}
bash
kubectl apply -f elasticsearch-cluster.yaml
kubectl -n elasticsearch get elasticsearch -w   # 等 PHASE=Ready HEALTH=green

ECK 默认 Basic license(免费),安全(HTTPS + elastic 账号)开箱即用;vm.max_map_count 由 ECK initContainer 自动设置,无需人工调内核。

4. 验证

bash
# ① ECK 层
kubectl -n elasticsearch get elasticsearch
# NAME            HEALTH   NODES   VERSION   PHASE   AGE
# elasticsearch   green    3       9.4.4     Ready   9m

# ② 取 elastic 密码(ECK 自动生成)
PASS=$(kubectl -n elasticsearch get secret elasticsearch-es-elastic-user \
  -o jsonpath='{.data.elastic}' | base64 -d)

# ③ 集群健康与节点角色
kubectl -n elasticsearch port-forward svc/elasticsearch-es-http 19200:9200 &
curl -sk -u "elastic:$PASS" 'https://127.0.0.1:19200/_cluster/health?pretty'
# status: green, number_of_nodes: 3, unassigned_shards: 0
curl -sk -u "elastic:$PASS" 'https://127.0.0.1:19200/_cat/nodes?v&h=name,node.role,master,heap.max'
# 3 个节点均为 cdfhilmrstw 全角色,heap 1gb

# ④ 读写测试(主+副本 2/2 成功即 HA 生效)
curl -sk -u "elastic:$PASS" -X POST 'https://127.0.0.1:19200/eck-test/_doc/1' \
  -H 'Content-Type: application/json' -d '{"msg":"hello eck"}'
# {"result":"created","_shards":{"total":2,"successful":2,"failed":0}}

# ⑤ 数据落盘确认(NFS 服务器侧可见 3 个 PVC 目录)
ls /mnt/xfs/rancher/es/
# elasticsearch-elasticsearch-data-elasticsearch-es-master-data-{0,1,2}-pvc-*

# ⑥ Pod 分散性
kubectl -n elasticsearch get pods -o wide | grep master-data   # 3 个不同 K8s 节点

5. 访问方式

场景地址
集群内应用https://elasticsearch-es-http.elasticsearch.svc:9200(HTTPS,自签 CA)
集群内跳过证书校验curl -sk(UAT 可接受;正规做法挂 elasticsearch-es-http-certs-public secret 里的 ca.crt)
集群外临时kubectl -n elasticsearch port-forward svc/elasticsearch-es-http 19200:9200
账号elastic / secret elasticsearch-es-elastic-user

需要经 Traefik 对外暴露时,参考《Traefik(RKE2 内置)HTTP/HTTPS 配置、原理与高并发生产落地手册》建 IngressRoute,注意后端是 HTTPS 需配 ServersTransport(scheme: https + insecureSkipVerify 或挂 CA)。

6. 生产注意事项

  1. NFS 仅适合 UAT/测试:Elastic 官方不推荐 ES 数据目录放 NFS(延迟与一致性风险),本文 node.store.allow_mmap=false 是 NFS 下的必要缓解。生产环境改用本地 SSD(local-path-provisioner / longhorn / TopoLVM),并拆分专用 master(3 小节点)与 data 节点;
  2. 堆内存:ECK 按容器 limits 的 50% 自动配堆,调内存只改 resources,不要手设 ES_JAVA_OPTS;
  3. 升级:改 CR 的 spec.version(如 9.4.4→9.5.x)并同步镜像到 Harbor,ECK 自动滚动升级(逐个节点,先迁分片);
  4. 扩缩容:改 count 即可;缩容时 ECK 自动迁移分片并维护投票配置,勿直接删 StatefulSet;
  5. 备份:生产配 snapshot 仓库(S3/MinIO 或 NFS path.repo),ECK 支持通过 keystore 注入仓库凭据;
  6. PDB:ECK 默认建 PodDisruptionBudget(maxUnavailable=1),节点维护 drain 时集群不中断。

7. 常见问题

现象原因与处理
Pod ImagePullBackOff,镜像名是 docker.elastic.co集群 mirror "*" 强制走 Harbor,镜像未同步或 CR 没改 spec.image
ECK status 显示 yellow 但 _cluster/health 是 greenECK 状态聚合有滞后,等 1~2 个 reconcile 周期;持续 yellow 看 operator 日志
ES 起不来,日志报 mmap/store 相关 I/O 错NFS 存储未加 node.store.allow_mmap: false
Pod 一直 Pending:pod has unbound immediate PersistentVolumeClaimsprovisioner 未就绪或 NFS 目录不存在,查 elasticsearch-nfs-provisioner Pod 日志
helm 拉 chart 失败(github EOF)用本机缓存 ~/.cache/helm/repository/nfs-subdir-external-provisioner-4.0.18.tgz,ECK chart 直连 helm.elastic.co 下载

8. 卸载

bash
kubectl -n elasticsearch delete elasticsearch elasticsearch   # ECK 级联删 StatefulSet/SVC/PVC(PVC 随 SC Delete 策略清 NFS 目录)
helm uninstall elasticsearch-nfs-provisioner -n elasticsearch
kubectl delete sc nfs-elasticsearch
helm uninstall elastic-operator -n elastic-system
kubectl delete ns elasticsearch elastic-system
kubectl get crd | grep elastic.co | awk '{print $1}' | xargs kubectl delete crd   # 彻底清 CRD