必须用statefulset+headless service+pvc:statefulset提供固定pod名与启动顺序,headless service暴露9300端口实现节点发现,pvc确保数据持久化且storageclassname需匹配可用存储类。

直接上生产环境的 OpenSearch 集群,不能用 Deployment 简单起家 —— 节点发现会失败、数据重启就丢、扩容缩容不可控。必须用 StatefulSet + Headless Service + PVC 三件套打底,否则后续踩坑成本远高于初期多写几行 YAML。
为什么 StatefulSet 是硬性要求
OpenSearch 节点之间靠 discovery.seed_hosts 和稳定主机名通信,Deployment 的 Pod 名是随机的、IP 是不固定的,节点根本连不上彼此。而 StatefulSet 保证:
- Pod 名固定为
opensearch-cluster-0、opensearch-cluster-1… 可被 DNS 解析为opensearch-cluster-0.opensearch-discovery - 每个 Pod 挂载独立
PVC,重启或迁移后数据不丢失 - 启动顺序可控(
podManagementPolicy: OrderedReady),避免主节点还没起来,数据节点就抢着注册
如果强行用 Deployment,你会看到日志里反复刷 failed to join cluster 或 master_not_discovered_exception。
Headless Service 必须显式定义 discovery 端口
Headless Service 不只是让 Pod 能互相解析,关键是它把 9300(transport 端口)暴露给集群内节点发现用。漏掉这个,discovery.seed_hosts 列表里的域名就解析不到地址。
常见错误配置:
- 只暴露
9200(HTTP 端口),没配9300—— 节点间无法建立 transport 连接 - Service 的
selector标签和 Pod 的labels对不上,导致 DNS 解析为空 - Service 命名不是
opensearch-discovery,但opensearch.yml里还硬写这个域名
正确写法片段:
针对 Kubernetes 仪表板和 Web UI 的浏览器自动化。适用于与 Kubernetes Dashboard、Grafana、ArgoCD UI 或其他 Web 界面交互。需要设置 MCP_BROWSER_ENABLED=true。
apiVersion: v1
kind: Service
metadata:
name: opensearch-discovery
namespace: opensearch
spec:
clusterIP: None
ports:
- name: transport
port: 9300
- name: http
port: 9200
selector:
app: opensearch
ConfigMap 中的 discovery 配置必须与实际 DNS 对齐
discovery.seed_hosts 不是随便填几个 IP,它依赖 Kubernetes 内置 DNS 的命名规则:<statefulset-name>-<index>.<service-name></service-name></index></statefulset-name>。比如你的 StatefulSet 叫 opensearch-cluster,Headless Service 叫 opensearch-discovery,那就必须写成:
discovery.seed_hosts: ["opensearch-cluster-0.opensearch-discovery", "opensearch-cluster-1.opensearch-discovery", "opensearch-cluster-2.opensearch-discovery"]
容易忽略的点:
- 所有节点共用同一份
ConfigMap,所以不能写死node.name: opensearch-0,得用${HOSTNAME}动态取值 -
cluster.initial_master_nodes必须列出全部初始主节点的node.name(即HOSTNAME值),少一个,集群就卡在「等待 master 投票」状态 - 测试阶段可以关 TLS(
plugins.security.ssl.transport.enabled: false),但一旦启用了安全插件,transport 层必须配证书,否则节点拒绝握手
PVC 模板必须声明 storageClassName 且匹配可用 StorageClass
StatefulSet 的 volumeClaimTemplates 如果没指定 storageClassName,会走默认 StorageClass —— 很多集群默认是 standard,但在云厂商 AKS/EKS/GKE 上可能叫 gp3、premium-lrs 或压根没启用动态供给。结果就是 PVC 卡在 Pending,Pod 一直 ContainerCreating。
查证方式:
- 运行
kubectl get sc看可用 StorageClass 名称 - 确保 PVC 模板中
storageClassName字段值和它一致(注意大小写) - StorageClass 的
provisioner必须支持你选的卷类型(比如 AWS EBS 用kubernetes.io/aws-ebs,Azure Disk 用kubernetes.io/azure-disk)
另外,accessModes 必须是 ReadWriteOnce(RWO),OpenSearch 不支持共享存储(如 RWX),否则启动时会报 java.nio.file.AccessDeniedException。
真正麻烦的从来不是写完 YAML,而是每个组件之间的命名、端口、DNS、权限、存储策略必须严丝合缝对上 —— 少一个点,整个集群就停在「半启动」状态,日志里全是超时和拒绝连接,排查时得逐层验证:Pod 是否 Running → Service 是否能解析 → nslookup opensearch-cluster-0.opensearch-discovery 是否返回 IP → telnet opensearch-cluster-0.opensearch-discovery 9300 是否通。










