Elasticsearch 部署方式、架构选型与提效工具手册
从“能启动”走到“敢接入”
搜索接口在本机返回了结果,并不意味着 Elasticsearch 已经准备好进入项目。HTTPS 协议写错会让客户端误判为服务不可用,第一批脏数据会把字段映射固化,过多 shard 会持续消耗 heap 和文件句柄,磁盘水位又可能在进程仍存活时把索引切成只读。等这些问题到了共享环境再处理,代价通常已经从“重建一个 index”变成迁移、停写和恢复演练。
先把实验环境与真实环境隔开。下面的容器、账号和 your_project_dev_* 索引都只用于本机验证;共享实例应使用独立网络、最小权限账号和受控证书。Docker Desktop 至少预留 4GB 内存,机器学习类能力还需要更多资源;9200 留给 REST API,5601 留给 Kibana。端口冲突时改宿主映射,不能为了快速连通而把脚本指向生产地址。
开始前确认 Docker、Compose 和端口状态:
docker version
docker compose version
docker ps --format "table {{.Names}}\t{{.Ports}}\t{{.Status}}"Windows 可以查端口:
netstat -ano | findstr ":9200"
netstat -ano | findstr ":5601"Linux / macOS 可以查端口:
lsof -i :9200
lsof -i :5601如果端口已经被占用,不要为了省事把开发脚本指向共享或生产 Elasticsearch。先改本地宿主端口,例如把宿主 9202 映射到容器 9200。
Elasticsearch 的入口不是只有一种。个人验证、项目联调、共享开发环境的成本和风险完全不同。
镜像版本应从 Elasticsearch 下载页 或 Elastic 镜像仓库 选择团队批准的精确版本,Compose 中不要写 latest。升级前同时检查 Support Matrix、Product EOL 和 REST API compatibility:兼容模式只能跨一个 major 充当迁移桥,不能替代客户端升级和弃用项清理。
自建安装可选 Linux / macOS 的 tar.gz、Windows 的 .zip、Debian 系的 deb、RPM 系的 rpm 或官方容器镜像;具体操作系统和 JVM 组合以 Support Matrix 为准。发行版自带 OpenJDK,官方建议优先使用 bundled JVM;自行设置 ES_JAVA_HOME 会把 JVM 补丁、兼容性和故障支持责任带回团队,不能只因机器上已有 Java 就替换。Elastic Stack 组件还应保持同一版本线,不能把 Elasticsearch 与 Kibana 的 patch 版本随意混搭。
| 入口 | 适合 | 不适合 | 关键边界 |
|---|---|---|---|
| 本机安装 | 学习配置文件、长期使用 CLI、需要调试本机服务 | 新人快速接入、多项目版本并存 | 安装目录、服务用户、配置目录、卸载残留 |
官方 start-local | 个人快速验证 Elasticsearch + Kibana | 团队项目模板、共享环境、生产 | 先看脚本来源;仅本地开发;生成凭证要收好 |
| Docker 单容器 | 个人最小验证、快速复现协议 / 证书 / index 行为 | 多人共享、复杂初始化 | 固定版本、保存密码和 token、复制 CA、明确删除命令 |
| Docker Compose | 项目联调、新人一键启动、CI 冒烟 | 生产集群 | .env.example、端口绑定、volume、healthcheck、证书和清理脚本 |
| 共享开发实例 | 重型组件集中运行、多团队联调 | 个人随意试错、破坏性测试 | owner、角色权限、index prefix、证书轮换、清理窗口 |
| 自建多节点集群 | 核心业务、明确掌控硬件和运维体系 | 没有 DBA / SRE owner 的小团队 | 节点角色、shard、snapshot、升级、磁盘和告警 |
| Elastic Cloud / Serverless | 快速获得托管能力、减少自建运维 | 强监管离线环境、许可和数据边界未确认场景 | 费用、区域、网络、权限、快照和 SLA |
| ECK / Kubernetes | 已有成熟 K8S 平台、需要声明式管理 | 只为本地开发临时起服务 | CRD、operator、PVC、资源隔离、升级策略 |
选择路径可以按这个判断:
本机安装边界
本机安装适合理解 Elasticsearch 的目录结构和命令行工具,但不适合作为团队新人接入默认方案。原因很现实:不同操作系统安装路径不同,服务用户不同,配置文件、证书、数据目录和卸载残留也不同。
如果确实要本机安装,只做三件事:
从官方下载页选择当前团队批准版本。先区分安装包类型:Docker / archive 首次启动可能输出 elastic 密码和 Kibana enrollment token;RPM / Debian 这类包安装通常不会在服务启动时输出 elastic 密码,需要使用官方重置工具手动生成。用 curl --cacert 证明 HTTPS、账号和证书都可用。
本机服务不要默认开机启动,除非你明确知道它的端口、数据目录和资源占用。
官方 start-local
官方提供的本地快速入口:
curl -fsSL https://elastic.co/start-local | sh这条路径适合个人快速体验 Elasticsearch 和 Kibana,但它不应该直接进入团队项目模板。Windows 需要先进入 WSL;脚本依赖 Docker Compose,官方建议 Compose V2。团队模板要能审查版本、端口、volume、证书、密码注入和清理命令;一条远程脚本不容易承载这些治理要求。
官方 quickstart 当前把 Elasticsearch 暴露在 http://localhost:9200,Kibana 暴露在 http://localhost:5601,并启用一个月的全功能试用许可。试用能力到期后可能改变可用功能,因此验证记录要区分 Basic 能力与试用能力,不能把试用期跑通当成长期许可结论。这条路径和 Docker 单节点的 HTTPS / CA 链路不同,不能混写。
如果使用它,至少记录:
脚本来源、实际解析出的 Elasticsearch 版本和镜像 digest。生成的 .env 或凭证文件位置。Elasticsearch 和 Kibana 访问地址。
清理脚本或删除目录方式。明确这不是生产部署入口。
Docker 单容器
个人最小验证用单容器最直接。先创建网络:
docker network create te-search-dev启动容器。这里故意用交互模式,因为第一次启动输出的 elastic 密码和 Kibana enrollment token 只会显示在启动阶段,必须收好:
docker run --name te-es-single \
--net te-search-dev \
-p 127.0.0.1:9200:9200 \
-it \
-m 1GB \
docker.elastic.co/elasticsearch/elasticsearch:9.4.3如果团队固定的是其他版本,把 9.4.3 换成团队批准版本。不要写 latest。
把密码放到当前 shell 的临时环境变量里,不写进 Git:
export ELASTIC_PASSWORD="replace-with-first-start-password"复制 HTTP CA 证书:
docker cp te-es-single:/usr/share/elasticsearch/config/certs/http_ca.crt ./es-http_ca.crt验证服务:
curl --fail --cacert ./es-http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
https://127.0.0.1:9200看到 JSON 里有 cluster_name、version、tagline,才算 Elasticsearch REST API 可用。这里如果用 http://127.0.0.1:9200 失败,不要急着改配置;先确认是否因为当前版本默认启用了 HTTPS。
清理单容器:
docker rm -f te-es-single
docker network rm te-search-dev
rm -f ./es-http_ca.crtDocker Compose
项目模板建议把开发依赖放进一个明确目录:
dev-dependencies/
elasticsearch/
compose.yaml
.env.example
certs/
README.md
scripts/
copy-ca.sh
verify.sh
reset-local.sh.env.example 只放占位值:
ELASTIC_STACK_VERSION=9.4.3
ELASTIC_CONTAINER_NAME=te-es-dev
ELASTIC_HTTP_PORT=9200
ELASTIC_PASSWORD=replace-with-local-password
ELASTIC_JAVA_OPTS=-Xms512m -Xmx512m
ELASTIC_INDEX_PREFIX=your_project_dev
KIBANA_CONTAINER_NAME=te-kibana-dev
KIBANA_HTTP_PORT=5601项目落地时复制成 .env,真实密码只留在本机:
cp dev-dependencies/elasticsearch/.env.example dev-dependencies/elasticsearch/.env本地示例为了可读性把密码放进 .env。如果密码包含空格、引号、$、! 等 shell 特殊字符,Compose healthcheck 可能被 shell 解析影响;团队模板要么约束本地演示密码字符集,要么改用后文的 ELASTIC_PASSWORD_FILE / secret 方式。
compose.yaml 可以先从单节点开发形态开始:
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:${ELASTIC_STACK_VERSION}
container_name: ${ELASTIC_CONTAINER_NAME}
environment:
- node.name=te-es-dev
- cluster.name=te-es-dev
- discovery.type=single-node
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
- ES_JAVA_OPTS=${ELASTIC_JAVA_OPTS}
ports:
- "127.0.0.1:${ELASTIC_HTTP_PORT}:9200"
volumes:
- es-data:/usr/share/elasticsearch/data
mem_limit: 1g
healthcheck:
test:
[
"CMD-SHELL",
"curl --silent --fail --cacert /usr/share/elasticsearch/config/certs/http_ca.crt -u elastic:${ELASTIC_PASSWORD} https://127.0.0.1:9200 >/dev/null || exit 1"
]
interval: 15s
timeout: 10s
retries: 20
kibana:
image: docker.elastic.co/kibana/kibana:${ELASTIC_STACK_VERSION}
container_name: ${KIBANA_CONTAINER_NAME}
depends_on:
elasticsearch:
condition: service_healthy
ports:
- "127.0.0.1:${KIBANA_HTTP_PORT}:5601"
profiles:
- ui
volumes:
es-data:这个 Compose 有三个刻意选择:
宿主端口只绑定 127.0.0.1,避免开发机在局域网暴露 9200。ES_JAVA_OPTS 只给开发验证用;生产优先保留官方自动 heap sizing,确需自定义时保持 Xms = Xmx,且 heap 不超过节点可用内存 50%。Kibana 放到 ui profile,默认先把 Elasticsearch 跑通,需要 UI 时再启动。
启动 Elasticsearch:
docker compose --env-file dev-dependencies/elasticsearch/.env \
-f dev-dependencies/elasticsearch/compose.yaml \
up -d elasticsearch复制 CA 证书:
mkdir -p dev-dependencies/elasticsearch/certs
docker cp te-es-dev:/usr/share/elasticsearch/config/certs/http_ca.crt \
dev-dependencies/elasticsearch/certs/http_ca.crt验证:
export ELASTIC_PASSWORD="replace-with-local-password"
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
https://127.0.0.1:9200启动 Kibana:
docker compose --env-file dev-dependencies/elasticsearch/.env \
-f dev-dependencies/elasticsearch/compose.yaml \
--profile ui \
up -d kibana第一次接 Kibana 时要准备 enrollment token。可以从 Elasticsearch 容器生成:
docker exec -it te-es-dev \
/usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibanaKibana enrollment token 有有效期,官方自动安全设置文档当前说明为 30 分钟;过期后重新生成,不要把 token 当长期凭证保存。Kibana 登录使用 elastic 用户和当前 .env 里的本地密码。共享环境不要把 elastic 发给所有人;Kibana 登录账号和应用写入账号必须分开。
共享开发实例
共享开发实例不建议照搬个人 Compose。它至少要额外确认:
访问边界:VPN、内网域名、防火墙、安全组、只读 Dashboard。证书来源:团队 CA、证书有效期、分发位置、轮换窗口。角色权限:只允许项目自己的 index prefix。
命名规则:<team>_<project>_<env>_<purpose>_vN。清理策略:先 dry-run,再按 prefix 删除。owner:谁升级、谁轮换密码、谁处理重型查询、谁清理旧 index。
如果这些没有答案,共享开发实例很快会变成没人敢动的黑盒。
Elasticsearch 开发配置要把“能连上”和“能治理”拆开。最少要管住这些对象:
| 配置对象 | 建议 | 原因 |
|---|---|---|
| 版本 | ELASTIC_STACK_VERSION=9.4.3 或团队批准版本 | 避免镜像漂移 |
| 端口 | 宿主绑定 127.0.0.1:9200 | 防止局域网暴露 |
| 密码 | 本地 .env 或 secret 文件 | 不进 Git,不进截图 |
| CA 证书 | certs/http_ca.crt,标注来源 | 客户端需要信任链 |
| 数据卷 | named volume,例如 es-data | 可 inspect、可重置 |
| index prefix | your_project_dev | 支撑权限和清理 |
| Kibana | 默认 profile,不随服务强制启动 | UI 不是每次都需要 |
密码文件变体
Docker 配置文档支持通过 _FILE 后缀读取 ELASTIC_PASSWORD。共享开发环境或 CI 本地模拟可以改成:
services:
elasticsearch:
environment:
- ELASTIC_PASSWORD_FILE=/run/secrets/elastic_password
secrets:
- elastic_password
secrets:
elastic_password:
file: ./secrets/elastic_password.txt注意:./secrets/elastic_password.txt 不能提交。仓库里只保留 secrets/README.md,说明如何生成和轮换。
证书目录
http_ca.crt 是客户端验证 HTTPS 的 CA 证书。它不是密码,但它仍然属于环境材料,必须知道来源和环境。
建议写一个 certs/README.md:
# Elasticsearch local certificates
http_ca.crt comes from the local Elasticsearch container:
docker cp te-es-dev:/usr/share/elasticsearch/config/certs/http_ca.crt certs/http_ca.crt
This file is for local development only.
Do not copy production certificates into this directory.
Do not commit private keys or .p12 files.不要把 http.p12、transport.p12、私钥、生产 CA、客户现场证书放进项目仓库。
安全连接链路可以这样理解:
如果这条链路里缺了 CA、账号或权限边界,后面的 index 验证即使跑通,也不能算团队可落地。
角色和账号
本地单人可以临时用 elastic,但项目模板必须告诉团队:应用和 GUI 不能长期使用超级用户。
创建 setup 角色,用于初始化 index 和 mapping:
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
-X PUT "https://127.0.0.1:9200/_security/role/your_project_dev_search_setup" \
-H "Content-Type: application/json" \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["your_project_dev_*"],
"privileges": ["manage", "create_index", "write", "read", "view_index_metadata"]
}
]
}'创建应用读写角色:
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
-X PUT "https://127.0.0.1:9200/_security/role/your_project_dev_search_rw" \
-H "Content-Type: application/json" \
-d '{
"cluster": [],
"indices": [
{
"names": ["your_project_dev_*"],
"privileges": ["read", "write", "create_doc", "view_index_metadata"]
}
]
}'创建只读排障角色:
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
-X PUT "https://127.0.0.1:9200/_security/role/your_project_dev_search_ro" \
-H "Content-Type: application/json" \
-d '{
"cluster": [],
"indices": [
{
"names": ["your_project_dev_*"],
"privileges": ["read", "view_index_metadata"]
}
]
}'创建用户时,密码使用本机临时变量,不写死在文档和脚本里:
export ES_SETUP_PASSWORD="replace-with-setup-password"
export ES_APP_PASSWORD="replace-with-app-password"
export ES_READONLY_PASSWORD="replace-with-readonly-password"
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
-X POST "https://127.0.0.1:9200/_security/user/your_project_dev_search_setup" \
-H "Content-Type: application/json" \
-d "{
\"password\": \"${ES_SETUP_PASSWORD}\",
\"roles\": [\"your_project_dev_search_setup\"],
\"full_name\": \"your-project dev search setup user\"
}"
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
-X POST "https://127.0.0.1:9200/_security/user/your_project_dev_search_app" \
-H "Content-Type: application/json" \
-d "{
\"password\": \"${ES_APP_PASSWORD}\",
\"roles\": [\"your_project_dev_search_rw\"],
\"full_name\": \"your-project dev search app user\"
}"
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
-X POST "https://127.0.0.1:9200/_security/user/your_project_dev_search_readonly" \
-H "Content-Type: application/json" \
-d "{
\"password\": \"${ES_READONLY_PASSWORD}\",
\"roles\": [\"your_project_dev_search_ro\"],
\"full_name\": \"your-project dev search readonly user\"
}"团队共享环境建议用 API key 或受控用户,但原则不变:权限按 index prefix 收窄,不能全员共享 elastic。
最小验证必须完成五件事:连接、建 index、写入、查询、清理。只看到 You Know, for Search 不够。
先设置变量:
export ELASTIC_URL="https://127.0.0.1:9200"
export ELASTIC_CA="dev-dependencies/elasticsearch/certs/http_ca.crt"
export ELASTIC_PASSWORD="replace-with-local-password"
export ES_SETUP_PASSWORD="replace-with-setup-password"
export ES_APP_PASSWORD="replace-with-app-password"
export ES_READONLY_PASSWORD="replace-with-readonly-password"
export ELASTIC_INDEX="your_project_dev_article_v1"连接和版本
curl --fail --cacert "${ELASTIC_CA}" \
-u "elastic:${ELASTIC_PASSWORD}" \
"${ELASTIC_URL}"预期:
HTTP 状态为 200。JSON 中能看到 cluster_name。JSON 中能看到 version.number。
如果这里失败,先不要写 index。优先排查协议、证书、账号、端口和容器健康。
创建 index 和 mapping
开发验证也要显式 mapping。不要让第一条脏数据决定字段类型。
curl --fail --cacert "${ELASTIC_CA}" \
-u "your_project_dev_search_setup:${ES_SETUP_PASSWORD}" \
-X PUT "${ELASTIC_URL}/${ELASTIC_INDEX}" \
-H "Content-Type: application/json" \
-d '{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"dynamic": "strict",
"properties": {
"id": { "type": "keyword" },
"title": {
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 256 }
}
},
"tags": { "type": "keyword" },
"updated_at": { "type": "date" }
}
}
}'预期返回:
{"acknowledged":true,"shards_acknowledged":true,"index":"your_project_dev_article_v1"}检查 mapping:
curl --fail --cacert "${ELASTIC_CA}" \
-u "your_project_dev_search_setup:${ES_SETUP_PASSWORD}" \
"${ELASTIC_URL}/${ELASTIC_INDEX}/_mapping?pretty"应用账号写入一条文档
curl --fail --cacert "${ELASTIC_CA}" \
-u "your_project_dev_search_app:${ES_APP_PASSWORD}" \
-X POST "${ELASTIC_URL}/${ELASTIC_INDEX}/_doc/1?refresh=wait_for" \
-H "Content-Type: application/json" \
-d '{
"id": "1",
"title": "Elasticsearch 开发依赖验证",
"tags": ["tool-efficiency", "search"],
"updated_at": "<EVENT_TIMESTAMP>"
}'使用 refresh=wait_for 是为了让后续搜索在开发验证里更稳定。不要把这当成所有业务写入的默认建议,真实项目要结合写入吞吐和刷新策略判断。
应用账号查询验证
curl --fail --cacert "${ELASTIC_CA}" \
-u "your_project_dev_search_app:${ES_APP_PASSWORD}" \
-X GET "${ELASTIC_URL}/${ELASTIC_INDEX}/_search?pretty" \
-H "Content-Type: application/json" \
-d '{
"query": {
"match": {
"title": "开发依赖"
}
}
}'预期:
hits.total.value 至少为 1。_source.title 能看到刚写入的标题。没有证书、账号、index 不存在、mapping 报错。
低权限账号反向验证
只读账号应该能查询,不能写入:
curl --fail --cacert "${ELASTIC_CA}" \
-u "your_project_dev_search_readonly:${ES_READONLY_PASSWORD}" \
-X GET "${ELASTIC_URL}/${ELASTIC_INDEX}/_search?pretty" \
-H "Content-Type: application/json" \
-d '{"query":{"match_all":{}}}'写入应该被拒绝:
curl --cacert "${ELASTIC_CA}" \
-u "your_project_dev_search_readonly:${ES_READONLY_PASSWORD}" \
-X POST "${ELASTIC_URL}/${ELASTIC_INDEX}/_doc/2" \
-H "Content-Type: application/json" \
-d '{"id":"2","title":"should be rejected","tags":["deny"],"updated_at":"<EVENT_TIMESTAMP>"}'预期看到 403 或权限不足相关错误。这个失败是正确的验收结果。
清理测试 index
清理前先打印目标:
echo "will delete index: ${ELASTIC_INDEX}"删除:
curl --fail --cacert "${ELASTIC_CA}" \
-u "your_project_dev_search_setup:${ES_SETUP_PASSWORD}" \
-X DELETE "${ELASTIC_URL}/${ELASTIC_INDEX}"再次查询应该返回 404:
curl --cacert "${ELASTIC_CA}" \
-u "your_project_dev_search_setup:${ES_SETUP_PASSWORD}" \
"${ELASTIC_URL}/${ELASTIC_INDEX}"共享开发实例不要允许随手删除。清理脚本必须限制前缀,并要求显式确认变量。
项目接入不是把 URL 贴进配置文件,而是把连接、证书、账号、index 命名、初始化、验证和清理都纳入项目模板。
建议入库:
dev-dependencies/
elasticsearch/
compose.yaml
.env.example
certs/
README.md
scripts/
copy-ca.sh
verify.sh
init-index.sh
dry-run-delete-indices.sh
reset-local.sh
docs/
dev-dependencies.md应用环境变量
ELASTICSEARCH_URL=https://127.0.0.1:9200
ELASTICSEARCH_USERNAME=your_project_dev_search_app
ELASTICSEARCH_PASSWORD=replace-with-app-password
ELASTICSEARCH_CA_CERT=dev-dependencies/elasticsearch/certs/http_ca.crt
ELASTICSEARCH_INDEX_PREFIX=your_project_dev
ELASTICSEARCH_DEFAULT_INDEX=your_project_dev_article_v1连接信息必须同时包含协议、证书、账号、密码和 index prefix。只有 http://localhost:9200 的配置,到了新版 Elasticsearch 或共享开发实例里大概率会失败。
客户端连接边界
Node.js 官方客户端连接 self-managed 集群时,需要把 HTTPS 和 CA 证书配置进去。项目里可以用类似结构表达,不要把证书校验关掉:
import fs from "node:fs";
import { Client } from "@elastic/elasticsearch";
const client = new Client({
node: process.env.ELASTICSEARCH_URL,
auth: {
username: process.env.ELASTICSEARCH_USERNAME,
password: process.env.ELASTICSEARCH_PASSWORD,
},
tls: {
ca: fs.readFileSync(process.env.ELASTICSEARCH_CA_CERT),
rejectUnauthorized: true,
},
});
const info = await client.info();
console.log(info.cluster_name);Java、Python、Go 也是同一个原则:用官方客户端或团队封装,显式配置 HTTPS、CA、账号和 index prefix。不要把 rejectUnauthorized: false、ssl.verification_mode: none、-k 这类临时排查写进团队默认配置。
客户端版本还要和 Elasticsearch 升级节奏绑定:
团队模板要记录 server、Kibana 和客户端库的实际版本。升级 Elasticsearch 时,先看官方客户端兼容说明,不要只改镜像 tag。REST API compatibility 可以帮助跨 major 升级,但只作为过渡桥。它不能替代客户端升级,也不能长期掩盖 deprecated API。
升级后要看 deprecation log,确认没有请求长期依赖兼容模式。
初始化脚本
init-index.sh 应该做成幂等或显式失败:
#!/usr/bin/env bash
set -euo pipefail
: "${ELASTICSEARCH_URL:?missing ELASTICSEARCH_URL}"
: "${ELASTICSEARCH_USERNAME:?missing ELASTICSEARCH_USERNAME}"
: "${ELASTICSEARCH_PASSWORD:?missing ELASTICSEARCH_PASSWORD}"
: "${ELASTICSEARCH_CA_CERT:?missing ELASTICSEARCH_CA_CERT}"
: "${ELASTICSEARCH_DEFAULT_INDEX:?missing ELASTICSEARCH_DEFAULT_INDEX}"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
-X PUT "${ELASTICSEARCH_URL}/${ELASTICSEARCH_DEFAULT_INDEX}" \
-H "Content-Type: application/json" \
-d @dev-dependencies/elasticsearch/index/article-v1.json如果脚本需要管理 index,使用 setup 账号,不要使用运行时 app 账号。运行时 app 账号只负责写读业务文档。
清理脚本
清理脚本必须先 dry-run:
#!/usr/bin/env bash
set -euo pipefail
: "${ELASTICSEARCH_URL:?missing ELASTICSEARCH_URL}"
: "${ELASTICSEARCH_USERNAME:?missing ELASTICSEARCH_USERNAME}"
: "${ELASTICSEARCH_PASSWORD:?missing ELASTICSEARCH_PASSWORD}"
: "${ELASTICSEARCH_CA_CERT:?missing ELASTICSEARCH_CA_CERT}"
: "${ELASTICSEARCH_INDEX_PREFIX:?missing ELASTICSEARCH_INDEX_PREFIX}"
echo "target prefix: ${ELASTICSEARCH_INDEX_PREFIX}_"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cat/indices/${ELASTICSEARCH_INDEX_PREFIX}_*?h=index"真正删除放到单独脚本,并要求确认值必须等于当前前缀,不能只写 yes:
#!/usr/bin/env bash
set -euo pipefail
: "${ELASTICSEARCH_URL:?missing ELASTICSEARCH_URL}"
: "${ELASTICSEARCH_USERNAME:?missing ELASTICSEARCH_USERNAME}"
: "${ELASTICSEARCH_PASSWORD:?missing ELASTICSEARCH_PASSWORD}"
: "${ELASTICSEARCH_CA_CERT:?missing ELASTICSEARCH_CA_CERT}"
: "${ELASTICSEARCH_INDEX_PREFIX:?missing ELASTICSEARCH_INDEX_PREFIX}"
: "${CONFIRM_DELETE:?missing CONFIRM_DELETE}"
if [[ "${CONFIRM_DELETE}" != "${ELASTICSEARCH_INDEX_PREFIX}" ]]; then
echo "refuse to delete: CONFIRM_DELETE must equal ${ELASTICSEARCH_INDEX_PREFIX}" >&2
exit 1
fi
if [[ "${ELASTICSEARCH_INDEX_PREFIX}" == "*" || "${ELASTICSEARCH_INDEX_PREFIX}" == "_all" || ${#ELASTICSEARCH_INDEX_PREFIX} -lt 6 ]]; then
echo "refuse to delete: unsafe prefix ${ELASTICSEARCH_INDEX_PREFIX}" >&2
exit 1
fi
echo "delete indices with prefix: ${ELASTICSEARCH_INDEX_PREFIX}_"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
-X DELETE "${ELASTICSEARCH_URL}/${ELASTICSEARCH_INDEX_PREFIX}_*"共享环境还要额外限制执行账号:清理账号只允许项目 prefix,不能用 elastic 或全局 manage 权限做日常清理。
Elasticsearch 解决的核心架构问题
Elasticsearch 的价值不是“把数据再存一份”,而是把搜索、过滤、聚合、近实时分析和横向扩展能力从业务主库里拆出来。架构师看 Elasticsearch,至少要同时看六个问题:
| 架构问题 | Elasticsearch 提供的能力 | 必须接受的约束 |
|---|---|---|
| 高并发检索 | 倒排索引、分片并行、查询缓存、协调节点汇总 | 不能替代业务主库事务 |
| 近实时写入 | refresh、translog、bulk 写入、segment merge | 不是强实时消息队列 |
| 复杂查询和聚合 | 全文检索、过滤、排序、聚合、地理位置和向量能力入口 | 不保证所有复杂查询都低成本 |
| 横向扩展 | index 拆成 primary shard,再通过 replica 提供冗余和读扩展 | shard 数不是随时无痛改的 |
| 高可用 | 节点、角色、replica、master 选举和快照恢复 | 不等于自动容灾和跨地域恢复 |
| 团队效率 | 本地可跑、证书可配、index 可初始化、问题可诊断 | 不能靠手工 Dev Tools 维护生产变更 |
本地验证与生产架构并不是两套互不相干的世界。开发模板里固定的 mapping、shard、证书、账号和清理方式,会直接决定共享环境是否有可迁移、可恢复的出口。
主流生产架构
单节点开发或边缘生产
组成:一个 Elasticsearch 节点,同时承担 master、data、ingest、coordinating 等职责,通常配一个 Kibana。
适合:
个人本地验证。自动化测试环境。非核心、可重建、低并发的内部工具。
对数据丢失和短时不可用有明确接受度的边缘场景。
核心短板:
节点故障就是服务故障。没有副本承载节点级冗余。JVM、磁盘、segment merge 和查询峰值会相互抢资源。
number_of_replicas: 1 在单节点上会让集群长期 yellow。
判断标准:如果这个 index 无法在几分钟内从主库或快照重建,或者搜索不可用会影响核心交易,就不要把单节点当生产架构。
三节点基础高可用集群
组成:三个节点,至少三个具备 master 选举资格的节点,业务 index 配置 primary shard 和 replica,常见最小形态是三台规格接近的通用节点。
适合:
中小业务生产搜索。搜索数据可从业务主库重放,但服务需要较高可用。团队还没有能力维护复杂冷热分层和专用角色集群。
核心能力:
单节点故障后仍可选举 master。replica 可承接读请求和故障迁移。查询可在多个 shard 并行执行。
可以用 rolling upgrade 降低维护窗口影响。
核心问题:
三个节点不是无限容量,只是最小高可用底座。shard 过多会把 cluster state、heap 和调度成本拖垮。写入高峰、merge 高峰和查询高峰仍可能互相影响。
快照仓库、监控、告警和演练必须另外设计。
角色分离集群
组成:按职责拆出 dedicated master、hot data、warm data、ingest、coordinating、machine learning 或 transform 等角色。业务流量经由协调节点或网关入口进入,写入和查询不直接打到专用 master。
适合:
多业务共享 Elasticsearch。查询流量和写入流量都比较大。ingest pipeline、聚合查询或大屏分析会消耗明显 CPU。
希望把 master 稳定性、数据节点吞吐和入口层隔离开。
核心取舍:
专用 master 保护集群管理面,但不存业务数据。data node 负责真正的索引和查询压力。ingest node 适合做轻量字段处理,不适合承载复杂 ETL。
coordinating node 能隔离入口压力,但自身也可能成为瓶颈。角色越清晰,运维、监控、容量规划和故障定位成本越高。
数据分层与 ILM 架构
组成:hot、warm、cold、frozen 等数据层配合 Index Lifecycle Management。新写入数据先进 hot 层,随着时间或容量变化 rollover,再迁移到更便宜的存储层或进入删除阶段。
适合:
日志、审计、埋点、指标和时间序列数据。最近数据查询频繁,历史数据低频查询。成本压力明显,但又不能简单删除历史索引。
核心问题:
rollover 条件要按主 shard 大小、文档量、写入速率和查询窗口设计。ILM 不是清理脚本替代品,策略错误会让 index 堆积或过早删除。冷热分层需要配合 snapshot、searchable snapshot、磁盘水位和恢复时间目标。
业务查询必须知道哪些历史数据查询会变慢。
托管云、Serverless 与 ECK
云化部署包括 Elastic Cloud、Serverless Elasticsearch 和云厂商托管形态。Kubernetes 场景通常会看 ECK,由 operator 管理 Elasticsearch、Kibana、升级、证书和部分运维动作。
适合:
团队不想自建监控、升级、证书、节点替换和快照体系。业务弹性明显,容量变化快。平台团队已有云账号、网络、权限、预算和审计体系。
Kubernetes 是团队统一基础设施入口。
核心取舍:
托管服务降低日常运维成本,但价格、规格、网络、插件、快照和权限边界要按官方口径复核。ECK 不是“把 Docker Compose 放到 K8S”,它把问题换成了 CRD、operator、PVC、PodDisruptionBudget、节点亲和性和升级策略。Serverless 适合降低管理面复杂度,但容量、延迟、功能边界、成本上限和数据治理必须提前验证。
架构对比与选型标准
| 方案 | 典型场景 | 优点 | 风险 | 验收重点 |
|---|---|---|---|---|
| 单节点开发 | 本机、CI、demo | 启动快、成本低、便于重置 | 无高可用、旧 volume 干扰、yellow 误判 | curl --cacert、建 index、写入查询、清理 |
| 单节点边缘生产 | 可重建内部工具 | 成本低、简单 | 单点故障、容量和恢复能力弱 | 备份、重建脚本、告警和停机预案 |
| 三节点基础集群 | 中小生产搜索 | 最小高可用、易理解 | shard 规划和资源竞争仍然明显 | health、replica、allocation、snapshot |
| 角色分离集群 | 多业务共享或大流量 | 稳定性和扩展性更好 | 角色、容量、监控和升级复杂 | node role、入口隔离、master 稳定性 |
| 数据分层集群 | 日志、指标、审计 | 控成本、保留历史数据 | ILM 误配、历史查询慢、恢复窗口长 | rollover、tier、snapshot、删除策略 |
| 托管云 / Serverless | 快速生产化、弹性业务 | 降低运维负担 | 成本、网络、功能和合规边界 | 官方能力、预算、账号权限、退出方案 |
| ECK | Kubernetes 平台团队 | 云原生交付、声明式运维 | K8S 存储和 operator 复杂度 | PVC、升级、证书、PDB、节点调度 |
选型可以按这条路径判断:
底线是:开发环境可以用单节点降低摩擦,生产选型必须回答 RPO、RTO、容量、成本、权限、升级和回滚。回答不了这些问题,就不能只凭“能查询”验收。
核心底层架构
cluster、node 和 role
Elasticsearch cluster 是节点集合,node 是运行实例,role 决定节点职责。常见角色包括 master、data、ingest、coordinating、machine learning、transform 等。
架构判断:
专用 master 要少承载业务流量,避免查询和写入拖垮选举与 cluster state。data node 的磁盘、heap、IO 和 segment merge 决定真实吞吐。ingest node 只适合轻量处理,复杂清洗应放到上游数据管道。
coordinating node 汇总查询结果,如果聚合很重,也会吃 CPU 和 heap。所有节点都不是“越多越好”,节点数会增加通信、分片分配和运维复杂度。
shard、replica 和 segment
index 会被拆成 primary shard。replica 是 primary shard 的副本,用于冗余和读扩展。每个 shard 底层由 Lucene index 组成,Lucene 通过 segment 提供不可变段、搜索和 merge。
架构判断:
primary shard 数在 index 创建后不能像普通参数一样随手改,必须通过 reindex、split 或新 index 迁移来处理。shard 太大,恢复慢、迁移慢、故障窗口长。shard 太多,cluster state、heap、文件句柄和调度开销会变大。
replica 能提高可用性和读扩展,但会增加写入复制成本和存储成本。segment merge 会造成 IO 和 CPU 波动,写入高峰时要看 merge、refresh 和 bulk 背压。
官方 shard sizing 文档给出的常见经验目标是每个 shard 约 10GB 到 50GB、每个 shard 文档数低于 2 亿。它不是“所有集群都照抄”的硬参数,而是容量评审起点:日志型、搜索型、聚合型、长保留数据和高写入数据要分别用恢复时间、查询延迟、merge 压力和节点资源验证。
refresh、translog 和近实时
写入 Elasticsearch 后,数据不是强实时可搜索。refresh 会把新数据变成可搜索的 segment,translog 用于故障恢复,flush 会把内存状态持久化到磁盘并裁剪 translog。
架构判断:
搜索延迟通常来自 refresh 间隔、写入压力、merge 和查询缓存变化。把 refresh_interval 调得过低,会提高实时性但增加 segment 和 merge 压力。大批量导入可以临时调大 refresh 间隔,导入完成后恢复,再强制 refresh 或等待自然 refresh。
业务不能把 Elasticsearch 当作写后立即强一致读的主库。
routing、coordinator 和 thread pool
查询请求先到协调节点,再路由到相关 shard 执行,最后汇总结果。写入也要根据路由规则落到目标 primary shard,再复制到 replica。
架构判断:
routing key 设计会影响热点 shard。所有请求都落到一个 shard,就算集群很多节点也没用。大分页、宽聚合和高亮查询会放大协调节点压力。bulk 太大容易触发写入队列堆积、429 或内存压力。
thread pool rejected 不是简单“线程不够”,通常是上游流量、慢查询、热点 shard 或资源不足的信号。
JVM、文件缓存和磁盘水位
Elasticsearch 是 JVM 服务,但搜索和 segment 文件高度依赖操作系统文件缓存。heap 给得太小会频繁 GC,给得太大又会挤压文件缓存。
架构判断:
开发环境可以低配跑通,生产不能只按容器能启动验收。官方推荐大多数生产环境保留自动 heap sizing;如果自定义,Xms 和 Xmx 要相等,heap 不超过节点可用内存 50%。磁盘不是写满才危险。官方水位机制会在高水位迁移 shard,在 flood-stage 给相关 index 加只读保护。
文件系统复制数据目录不是可靠备份。跨版本、跨节点、跨集群恢复必须走 snapshot。
索引、mapping、template、alias 与 ILM 治理
index 不是表名
关系数据库里很多团队习惯先建表再改字段。Elasticsearch 里 index 设计更像“搜索契约”:mapping、analyzer、dynamic 策略、shard 数、replica 数、alias 和 ILM 会共同决定查询能力、写入成本和迁移方式。
建议规则:
业务代码只写 alias,不直接写物理 index。物理 index 带版本号,例如 your_project_dev_article_v1。核心字段显式 mapping,避免第一批脏数据污染类型。
动态字段只能用于明确可接受的扩展区,不要放任全局 dynamic。template 由脚本创建,不能靠 Kibana 手工点出来。index 创建参数和 mapping 要进入代码评审。
template 和 alias 示例
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
-X PUT "${ELASTICSEARCH_URL}/_index_template/your_project_article_template" \
-H "Content-Type: application/json" \
-d '{
"index_patterns": ["your_project_*_article_v*"],
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"refresh_interval": "1s"
},
"mappings": {
"dynamic": "strict",
"properties": {
"id": { "type": "keyword" },
"title": {
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 256 }
}
},
"created_at": { "type": "date" }
}
}
}
}'本地开发可以把 replica 设为 0,避免单节点 yellow。生产 index 不能照抄这个值,要按节点数、RPO、查询压力和写入成本重新设计。
alias 用来让应用从物理 index 迁移里解耦:
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
-X POST "${ELASTICSEARCH_URL}/_aliases" \
-H "Content-Type: application/json" \
-d '{
"actions": [
{ "add": {
"index": "your_project_dev_article_v1",
"alias": "your_project_dev_article_write"
} }
]
}'生产迁移时,常见路径是新建 v2 index,重放或 reindex 数据,验证查询结果,再切 alias。直接在旧 index 上手工改 mapping,往往会把风险藏到下一次搜索故障里。
ILM 的启用判断
ILM 适合 versioned Elastic Stack 里的时间序列、日志、审计和事件类数据,不适合所有业务索引;Elasticsearch Serverless 不使用 ILM,而是使用 data stream lifecycle。判断一个 index 是否要接 ILM,先问:
是否按时间写入,并且查询窗口明显偏新。是否有保留期限或成本上限。是否能接受 rollover 后物理 index 增多。
是否有 snapshot 和恢复演练。业务查询是否走 alias 或 data stream,而不是写死 index 名。
如果只是几个小型业务搜索 index,不要为了“显得高级”强上 ILM。先把 mapping、alias、权限、备份和诊断做好。
查询、写入与性能诊断体系
查询慢怎么判断
查询慢不要先猜参数。按顺序看:
查询是否命中了过多 shard。是否有大分页、宽聚合、高亮、脚本查询或 wildcard 前缀。mapping 是否把该做 keyword 的字段做成了 text。
是否排序在低基数字段或未优化字段上。协调节点、data node 的 CPU、heap、GC 和 thread pool 是否异常。是否处于 merge 或 snapshot 高峰。
常用入口:
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cat/nodes?v&h=name,roles,heap.percent,ram.percent,cpu,load_1m,disk.used_percent,node.role"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cat/thread_pool/search?v"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/your_project_dev_article_v1/_search?pretty" \
-H "Content-Type: application/json" \
-d '{ "profile": true, "query": { "match_all": {} }, "size": 1 }'profile 适合定位查询结构,不适合作为线上常态开关。线上诊断要结合 slow log、APM、节点指标和业务调用链。
写入慢怎么判断
写入慢通常不是单一原因。按顺序看:
bulk 批次是否过大或过小。是否所有写入都打到同一个 routing key。refresh 间隔是否过低。
replica 数是否导致复制成本过高。merge、translog、磁盘 IO 是否拥塞。write thread pool 是否 rejected。
上游是否没有限流和重试退避。
常用入口:
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cat/thread_pool/write?v"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_nodes/stats/indices,indexing,thread_pool,jvm,fs?pretty"开发环境最小验证只需要写入一条文档。团队共享环境必须额外做小规模 bulk 验证,确认 bulk 大小、超时、重试、失败明细和死信处理。
高可用、快照恢复与容量风险入口
健康状态不是红绿灯装饰
Elasticsearch 的 green、yellow、red 是 shard 分配状态。red 说明至少有 primary shard 未分配,相关数据不可用;yellow 通常说明 primary 可用但 replica 未分配。
诊断入口:
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cluster/health?filter_path=status,active_shards,unassigned_shards,active_primary_shards"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cat/shards?v"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
-X GET "${ELASTICSEARCH_URL}/_cluster/allocation/explain?pretty" \
-H "Content-Type: application/json" \
-d '{}'本地单节点的 yellow 可以通过开发 index 设置 number_of_replicas: 0 解决。生产 yellow 不能这样一降了之,要先判断节点、磁盘、分配规则、版本、数据层和副本策略。
快照才是可恢复备份
Elasticsearch 官方快照机制会保存集群可恢复状态和 index 数据。直接复制 data 目录不能作为可靠备份策略。
团队至少要定义:
快照仓库类型和位置。快照频率、保留周期和加密策略。恢复演练频率。
单 index 恢复、全集群恢复和误删恢复流程。快照失败时谁收到告警。恢复时间目标和恢复点目标。
本地和共享开发环境可以不搭完整快照仓库,但生产方案评审必须写清楚快照与恢复。没有恢复演练的备份,只是心理安慰。
磁盘水位会让写入突然失败
当磁盘达到 high watermark,Elasticsearch 会尝试迁移 shard。当达到 flood-stage,相关 index 可能被加上只读保护,写入会失败。
诊断入口:
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cat/allocation?v"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cluster/settings?include_defaults=true&pretty"不要把“清理几个旧 index”当作唯一治理。长期方案要看 shard 大小、ILM、冷热分层、磁盘扩容、snapshot 保留、日志归档和业务查询保留窗口。
架构演进与未来取舍
Elasticsearch 架构通常不是一步到位,而是随业务增长逐步演进。
| 阶段 | 典型形态 | 主要目标 | 主要风险 |
|---|---|---|---|
| 验证期 | 单节点、本地 Docker、少量 index | 跑通搜索、确认 mapping、接入客户端 | 旧教程、证书、密码、动态 mapping |
| 成长期 | 三节点集群、项目 alias、基础权限 | 读写稳定、具备节点故障承受能力 | shard 规划不足、bulk 背压、权限过大 |
| 多业务期 | 角色分离、共享入口、统一模板 | 隔离 master、data、ingest 和入口压力 | 角色不清、容量归属不清、手工变更 |
| 成本治理期 | ILM、冷热分层、快照恢复 | 控制历史数据成本和恢复能力 | ILM 误配、历史查询变慢、快照未演练 |
| 平台期 | 托管云、Serverless、ECK 或搜索平台 | 降低运维复杂度、统一治理 | 成本失控、供应商边界、K8S 存储复杂 |
架构师要避免两个极端:一个极端是永远停在单节点,把搜索服务当本地工具;另一个极端是刚开始就上复杂集群,把团队拖进运维泥潭。更务实的路径是先把本地和共享环境的模板打稳,再用真实数据量、查询模式、写入峰值、恢复目标、预算和团队能力推动升级。
Elasticsearch 和 OpenSearch、云原生搜索、向量检索、Serverless 搜索之间也不是简单替换关系。选型时先问:
数据能否从主库重建,还是搜索集群本身承载事实数据。查询是全文检索为主,还是聚合分析、日志检索、向量召回或混合搜索。团队能否长期维护自建集群的升级、快照、证书、容量和故障演练。
许可、价格、云区域、网络、插件和安全能力是否满足企业边界。退出方案是什么,是否能导出数据、重建 index、迁移客户端。
把这些问题写进选型记录并给出可验证证据。缺少数据重建方式、容量模型、恢复责任人或退出方案时,不应仅凭本地 demo 顺利就把搜索引擎纳入主链路。
查看容器状态
docker compose --env-file dev-dependencies/elasticsearch/.env \
-f dev-dependencies/elasticsearch/compose.yaml \
ps查看日志
docker compose --env-file dev-dependencies/elasticsearch/.env \
-f dev-dependencies/elasticsearch/compose.yaml \
logs --tail=200 elasticsearch查看集群健康
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
"https://127.0.0.1:9200/_cluster/health?pretty"本地单节点没有副本时,开发 index 建议 number_of_replicas: 0,否则健康状态可能因为副本无法分配而变黄。生产分片和副本规划见前面的架构选型、索引治理和容量风险小节,不能照搬本地参数。
查看节点、角色和资源
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
"https://127.0.0.1:9200/_cat/nodes?v&h=name,roles,heap.percent,ram.percent,cpu,disk.used_percent"本地只有一个节点时,这个命令用来建立判断习惯;共享环境和生产环境要把角色、资源和异常节点看出来。
查看 shard 分配
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
"https://127.0.0.1:9200/_cat/shards/your_project_dev_*?v"如果出现 unassigned shard,再看分配解释:
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
-X GET "https://127.0.0.1:9200/_cluster/allocation/explain?pretty" \
-H "Content-Type: application/json" \
-d '{}'查看 index 列表
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
"https://127.0.0.1:9200/_cat/indices/your_project_dev_*?v"查看 mapping
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
"https://127.0.0.1:9200/your_project_dev_article_v1/_mapping?pretty"查看线程池和写入背压
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
"https://127.0.0.1:9200/_cat/thread_pool/search,write?v"如果 rejected 增长,要回到查询结构、bulk 大小、routing、节点资源和热点 shard 排查,不要直接加线程数。
重置本地环境
普通停止:
docker compose --env-file dev-dependencies/elasticsearch/.env \
-f dev-dependencies/elasticsearch/compose.yaml \
down破坏性重置:
docker compose --env-file dev-dependencies/elasticsearch/.env \
-f dev-dependencies/elasticsearch/compose.yaml \
down -vdown -v 会删除 Compose 声明的 named volume 和匿名 volume。它适合个人本地重置,不适合共享开发实例。
查看真实 volume
docker volume ls
docker volume inspect elasticsearch_es-dataCompose 项目名会影响 volume 名称。排查旧数据时,不要只看 compose.yaml,要看真实 volume。
curl http://127.0.0.1:9200 失败
现象:浏览器或 curl 访问 http://127.0.0.1:9200,返回连接关闭、协议错误、空响应或无法解析的输出。
判断:
docker logs te-es-dev --tail=80
curl -v http://127.0.0.1:9200
curl -v --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
https://127.0.0.1:9200常见原因:当前 Elasticsearch 默认启用了 HTTPS 和认证,你却按旧教程用 HTTP 裸连。
修复:
改用 https://。带 --cacert http_ca.crt。带 -u elastic:${ELASTIC_PASSWORD}。
项目配置里补齐 CA 证书路径。
再验证:
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
https://127.0.0.1:9200self signed certificate 或证书不可信
现象:客户端报自签证书、不受信任 CA、certificate verify failed。
判断:
ls -l dev-dependencies/elasticsearch/certs/http_ca.crt
openssl x509 -in dev-dependencies/elasticsearch/certs/http_ca.crt -noout -subject -issuer -dates常见原因:
没有从当前容器复制 http_ca.crt。复制的是旧环境的 CA。客户端没有加载 CA。
团队把 -k 当成默认方案,掩盖了证书问题。
修复:
从当前 Elasticsearch 容器重新复制 CA。客户端显式配置 CA。共享环境由 owner 分发受控 CA。
临时 -k 只用于定位问题,不进入项目模板。
再验证:使用 curl --cacert 能稳定返回 200。
密码丢失或改了不生效
现象:不知道 elastic 密码;改了 .env 后仍然登录失败。
判断:
docker logs te-es-dev --tail=120
docker volume ls
docker volume inspect elasticsearch_es-data常见原因:
初次启动输出的密码没有保存。旧 volume 里已经有安全索引和用户数据。误以为修改 .env 会改掉已经初始化过的用户密码。
修复:
docker exec -it te-es-dev \
/usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic如果这是个人本地环境,且明确要重建:
docker compose --env-file dev-dependencies/elasticsearch/.env \
-f dev-dependencies/elasticsearch/compose.yaml \
down -v再验证:用新密码执行 curl --cacert。
容器启动后退出
现象:docker compose ps 里 Elasticsearch 反复重启或退出。
判断:
docker compose --env-file dev-dependencies/elasticsearch/.env \
-f dev-dependencies/elasticsearch/compose.yaml \
logs --tail=200 elasticsearch
docker stats --no-stream常见原因:
Docker Desktop 内存不足。JVM heap 设置过大。Linux 宿主 vm.max_map_count 不满足要求;ES 9 节点按当前系统配置文档检查,推荐值为 1048576。
bind mount 目录对容器内 elasticsearch 用户不可读写。磁盘空间不足。
修复:
Docker Desktop 给到足够内存。个人开发先用 -Xms512m -Xmx512m。Linux 宿主按官方文档调整 vm.max_map_count;生产 Docker 不沿用 OpenSearch 或旧 ES 教程里的 262144。
避免直接挂载权限不明的宿主目录;优先用 named volume。清理旧容器、日志和无用 volume。
再验证:docker compose ps 健康,curl --cacert 返回 200。
集群 yellow 或 red
现象:_cluster/health 显示 yellow 或 red,Kibana 提示集群状态异常,部分 index 不能正常查询或写入。
判断:
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cluster/health?pretty"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cat/shards?v"常见原因:
单节点开发 index 配了 replica。节点掉线导致 replica 或 primary 无法分配。磁盘水位、分配过滤、data tier 或版本问题阻止 shard 分配。
快照恢复、rolling upgrade 或节点替换过程中未完成迁移。
修复:
本地单节点开发 index 可设置 number_of_replicas: 0。生产先跑 _cluster/allocation/explain,不要盲目改副本。检查节点、磁盘水位、分配规则和 data tier。
red 状态优先恢复 primary shard 或从 snapshot 恢复。
再验证:目标 index 的 primary 和 replica 分配清楚,health 状态符合当前架构预期。
flood-stage 后 index 变只读
现象:写入报错,提示 index read-only 或 block;清理一点磁盘后仍然写不进去。
判断:
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cat/allocation?v"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/your_project_dev_article_v1/_settings?pretty"常见原因:磁盘达到 flood-stage 后,Elasticsearch 为保护集群给相关 index 加了只读 block。
修复:
先释放磁盘或扩容,确认水位回落。再移除只读 block。复盘 ILM、snapshot、日志保留和大 index 增长。
再验证:写入一条测试文档,确认写入成功且磁盘水位稳定。
bulk 写入变慢或出现 429
现象:批量导入耗时越来越长,客户端收到 429、rejected execution、timeout 或部分失败。
判断:
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_cat/thread_pool/write?v"
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_nodes/stats/indices,thread_pool,jvm,fs?pretty"常见原因:
bulk 单批太大。上游并发没有限流。routing 热点导致单 shard 过载。
refresh、replica、merge 和磁盘 IO 压力叠加。客户端没有记录 item 级失败。
修复:
降低 bulk 单批大小和并发。增加指数退避和失败重试。导入窗口临时调整 refresh 策略,完成后恢复。
检查 shard 分布和热点 routing。把失败文档落到死信或补偿队列。
再验证:bulk 失败率、rejected、延迟和节点资源回到可接受范围。
heap pressure 或 circuit breaking
现象:查询或聚合报 circuit_breaking_exception,节点 heap 长期高位,GC 频繁,Kibana 或应用偶发超时。
判断:
curl --fail --cacert "${ELASTICSEARCH_CA_CERT}" \
-u "${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}" \
"${ELASTICSEARCH_URL}/_nodes/stats/jvm,breaker,indices?pretty"常见原因:
聚合字段不适合当前 mapping。高基数字段聚合或大分页消耗过高。fielddata 被误用到 text 字段。
shard 数量过多或 segment 太多。heap 配置、节点规格和查询模型不匹配。
修复:
优先改查询和 mapping,不要第一步加 heap。为聚合字段使用 keyword 或合适的数据类型。控制分页、聚合桶数量和高亮字段。
治理 shard 与 segment。生产环境结合慢查询、APM 和节点指标复盘。
再验证:breaker 指标、heap pressure、GC 和慢查询都有回落。
mapping 被第一批脏数据污染
现象:写入能成功,但排序、范围查询、聚合或精确匹配不对;updated_at 像文本,user_id 有时是数字有时是字符串。
判断:
curl --fail --cacert "${ELASTIC_CA}" \
-u "elastic:${ELASTIC_PASSWORD}" \
"${ELASTIC_URL}/${ELASTIC_INDEX}/_mapping?pretty"常见原因:
没有显式 mapping。动态 mapping 接收了错误的第一批数据。共享开发实例中别人提前创建了同名 index。
修复:
删除开发 index。用显式 mapping 重建。核心字段使用稳定类型。
共享环境用版本化 index,例如 your_project_dev_article_v1,不要在同一个 index 上手工乱改。
再验证:重新写入一条文档,检查 mapping 和查询结果。
Kibana 连不上 Elasticsearch
现象:Kibana 页面要求 enrollment token、登录失败、提示无法连接 Elasticsearch。
判断:
docker logs te-kibana-dev --tail=120
curl --fail --cacert dev-dependencies/elasticsearch/certs/http_ca.crt \
-u "elastic:${ELASTIC_PASSWORD}" \
https://127.0.0.1:9200常见原因:
Elasticsearch 本身还没健康。enrollment token 过期或丢失。Kibana 和 Elasticsearch 版本不一致。
证书或内置用户密码不对。
修复:
docker exec -it te-es-dev \
/usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana必要时重置 elastic 密码:
docker exec -it te-es-dev \
/usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic再验证:Kibana 打开 http://127.0.0.1:5601,用 enrollment token 和 elastic 密码完成登录。
客户端升级后请求异常
现象:Elasticsearch 服务正常,curl 能查,但应用客户端升级或 ES 升级后部分 API 报错、返回字段变化、兼容 header 掩盖了 deprecation。
判断:
查看 Elasticsearch server 版本。查看应用使用的官方客户端 major / minor。检查是否启用了 REST API compatibility header。
查看 deprecation log 中是否有 compatible_api 相关记录。
常见原因:
只升级了 ES 镜像,没有升级客户端库。把 compatibility mode 当成长期配置。应用使用了旧 API 或旧请求体。
修复:
按官方客户端兼容口径制定升级顺序。升级客户端库并修正 API 调用。只在升级窗口临时启用 compatibility mode。
升级后复查 deprecation log。
再验证:项目自检、index 初始化、写入、查询和清理脚本都用新客户端跑通。
删除脚本删错目标
现象:本来只想清理本地 index,却删了共享开发环境或测试环境 index。
判断:
删除脚本是否打印了 ELASTICSEARCH_URL、cluster name 和 prefix。账号是否有全局 manage 或 all 权限。删除目标是否包含 *、_all 或没有项目前缀。
修复:
删除前先调用 _cluster/health 和 _cat/indices/<prefix>* 打印目标。删除脚本要求 CONFIRM_DELETE 等于当前 prefix。应用账号不给删除 index 权限。
共享环境只允许 owner 或 CI 受控任务删除。
再验证:只读账号执行删除应被拒绝;清理脚本 dry-run 只列出当前项目 index。
代理和镜像拉取
Elasticsearch 镜像来自 Elastic Docker registry。企业网络里拉取失败时,先区分是 Docker daemon、Shell、代理证书还是私有镜像仓库问题。
建议路径:
优先使用官方镜像名和官方文档命令。企业内网缓存官方镜像时,保留原始 tag、digest、同步日期和来源。离线环境用 docker save / docker load,同步记录镜像 digest。
不把来历不明的第三方镜像源写进项目唯一入口。
查看镜像:
docker pull docker.elastic.co/elasticsearch/elasticsearch:9.4.3
docker image inspect docker.elastic.co/elasticsearch/elasticsearch:9.4.3NO_PROXY 至少包含本机:
NO_PROXY=127.0.0.1,localhost如果应用通过企业代理访问 127.0.0.1:9200,错误会非常迷惑。排障时先打印进程环境变量。
权限分层
| 账号 | 用途 | 权限边界 |
|---|---|---|
elastic | 本地初始化、临时救援 | 不给普通开发长期使用 |
| setup 账号 | 创建 index、mapping、template | 只管理项目 prefix |
| app 账号 | 应用写读 | 只写读项目 prefix,不管理集群 |
| readonly 账号 | Kibana / GUI / 排障 | 只读项目 prefix |
| CI 账号 | 初始化、冒烟、清理 | 明确脚本和审计范围 |
团队共享环境里,权限不是“大家自觉别乱删”。要用角色把危险 API 拦住。
凭证落地规则
.env.example 只放占位值。.env 不提交。证书私钥、.p12、生产 CA、API key 不进仓库。
终端录屏、截图、Kibana 页面要脱敏。Kibana Docker 的敏感配置尽量放 keystore 或受控 secret;不要把长期密码堆进环境变量后再以为容器内就是安全边界。发现泄漏后轮换密码或 API key,不只是在 Git 里删除。
生产连接默认只读,写入和删除必须走受控任务。
敏感信息扫描可以加到团队自查里:
rg -n "elastic:|ELASTIC_PASSWORD|ApiKey|BEGIN .*PRIVATE KEY|http.p12|transport.p12|AKIA|ghp_|sk-" .命中变量名不一定是泄漏,命中真实值、私钥、生产域名和未脱敏连接串才是风险。自查脚本要区分占位符和真实凭证。
项目模板必须保留
至少保留这些文件:
dev-dependencies/elasticsearch/compose.yaml
dev-dependencies/elasticsearch/.env.example
dev-dependencies/elasticsearch/certs/README.md
dev-dependencies/elasticsearch/scripts/copy-ca.sh
dev-dependencies/elasticsearch/scripts/verify.sh
dev-dependencies/elasticsearch/scripts/init-index.sh
dev-dependencies/elasticsearch/scripts/dry-run-delete-indices.sh
dev-dependencies/elasticsearch/scripts/reset-local.sh
docs/dev-dependencies.md不要只在群里发一段 docker run。新人最需要的是可重复的入口、可判断的输出和可安全清理的脚本。
命名规则
推荐:
<team>_<project>_<env>_<purpose>_vN示例:
your_team_your_project_dev_article_v1
your_team_your_project_dev_search_log_v1
your_team_your_project_shared_dev_suggestion_v2index 名称要能看出团队、项目、环境、用途和版本。不要在共享实例里创建 test、article、logs 这种无法追责的名字。
共享实例职责
| 角色 | 职责 |
|---|---|
| owner | 维护版本、证书、账号、角色、升级窗口和清理策略 |
| 项目开发 | 提交 mapping、初始化脚本、清理脚本和最小验证 |
| 测试 / QA | 使用只读或受限账号查询,不手工改 mapping |
| CI | 使用独立账号执行初始化、冒烟、清理和权限验证 |
| 安全 / 平台 | 审查生产连接、证书、凭证轮换和访问边界 |
清理策略
本地个人环境允许 down -v,但必须知道会删什么。共享环境禁止无前缀删除。临时 index 带工单号、日期或 owner。
清理脚本先 dry-run 打印目标。删除必须要求显式确认 prefix。清理失败要报警或至少通知 owner,不能静默吞掉。
HTTP / HTTPS 误判会让团队排障绕一天
现象:旧项目文档只写 http://localhost:9200,新人按文档接入新版 Elasticsearch 后全部失败。
判断:
文档里是否写了 https://。是否保存并配置了 http_ca.crt。应用配置是否有账号和密码。
curl --cacert 是否能返回 200。
取舍:为了本地省事关闭安全或证书校验,看似能快一点,实际会把团队模板带回旧时代。个人临时排障可以跳过证书,团队默认模板必须保留证书验证。
旧 volume 会让密码、证书和 index 判断全部失真
现象:改了 .env 里的 ELASTIC_PASSWORD,登录仍然失败;改了 mapping,查询结果还是旧结构;删除 index 后又被旧脚本重建。
判断:
docker volume ls
docker volume inspect elasticsearch_es-data
docker compose --env-file dev-dependencies/elasticsearch/.env \
-f dev-dependencies/elasticsearch/compose.yaml \
logs --tail=80 elasticsearch取舍:个人本地可以 down -v 破坏性重置。共享环境不能这样做,必须先确认 index、账号、证书和数据归属,再走 owner 审批或维护窗口。
mapping 污染比启动失败更难发现
现象:服务能用,写入也成功,但某些字段不能排序、不能聚合、范围查询不准。
判断:
查看 _mapping。核心字段是否由显式 mapping 创建。是否有动态字段把日期、ID、金额写成了错误类型。
共享实例里是否有人提前创建过同名 index。
取舍:开发环境也要显式 mapping。对于已经污染的开发 index,不要靠手工补丁修一半,直接重建带版本号的新 index,再让脚本切换。
Kibana Dev Tools 不是只读浏览器
现象:某个同事用 Kibana Dev Tools 执行了 DELETE、PUT mapping 或批量 update,代码评审里完全看不到。
判断:
Kibana 登录账号是否是 elastic 或高权限账号。生产连接是否保存到个人浏览器或 GUI。是否能查到操作日志或至少能对应到用户。
Dashboard 是否默认只读。
治理:
生产 Kibana 普通用户默认只读。开发共享环境也按项目 prefix 授权。写入、删除、mapping 变更走脚本和评审。
截图和演示脱敏 host、index、文档内容和账号。
Docker Desktop 内存、mmap 和 ulimit 会伪装成版本问题
现象:团队以为“某个版本有 bug”,但其实是本机资源不足或宿主参数不满足。
判断:
docker stats --no-stream
docker logs te-es-dev --tail=200Linux 宿主再看:
sysctl vm.max_map_count
ulimit -nES 9 的 Docker 节点还要按 重要系统配置 检查 vm.max_map_count,推荐值为 1048576。进入生产评审时,把 swap、文件描述符、线程数、磁盘、JVM 和 bootstrap checks 一并变成主机基线和自动检查;开发机遇到启动退出时也先看资源与日志,不要靠反复换镜像碰运气。
shard 数量是容量契约,不是启动参数
现象:早期为了省事创建了 1 个 primary shard,数据量增长后查询、写入和恢复都卡在单 shard 上;或者反过来,团队按“多一点保险”的直觉创建几十上百个小 shard,结果 heap、cluster state 和文件句柄先崩。
判断:
index 创建脚本里是否明确写了 number_of_shards 和依据。单 shard 文档量、存储量、查询耗时和恢复耗时是否有记录。_cat/shards 是否出现大量很小的 shard 或少数异常巨大的 shard。
是否有 reindex、split、rollover 或 alias 切换预案。
治理:开发环境可以用 1 个 shard 降低成本,但生产评审必须把 shard 作为容量契约写清楚。没有数据量预估时,优先通过版本化 index 和 alias 保留迁移出口,不要把业务代码写死到物理 index。
快照恢复演练才是真正的备份
现象:团队说“有快照”,但没人做过恢复;误删 index 后才发现仓库权限、版本、加密、网络或恢复账号有问题。
判断:
快照仓库是否能列出最近一次成功快照。是否能在隔离环境恢复单个 index。恢复后 alias、权限、template 和 ILM 是否仍然正确。
恢复时间是否满足业务 RTO。快照失败是否有人收到告警。
治理:备份验收不能只看“任务成功”。至少按季度做一次恢复演练,记录恢复命令、耗时、数据校验、权限校验和回滚方案。正式生产变更仍要进入部署运维 SOP;开发团队不能把复制 data 目录误当成可恢复备份。
磁盘水位会把可写服务变成只读服务
现象:搜索服务没有宕机,查询还能用,但写入突然失败;清理磁盘后仍然写不进去。
判断:
_cat/allocation 是否显示磁盘使用率接近水位。index settings 是否存在 read-only block。最近是否有大批量导入、日志暴涨、snapshot 堆积或 ILM 未执行。
告警是否只看进程存活,没有看磁盘水位和写入错误率。
治理:先释放或扩容磁盘,再解除只读 block,最后复盘数据保留策略。长期方案是 ILM、snapshot 保留、容量告警、写入限流和 owner 责任一起做,不是等磁盘满了再手工删 index。
托管云和自建集群的客户端连接策略不同
现象:本地连接写的是 https://127.0.0.1:9200、CA 文件和账号密码;迁移到托管云后,应用仍然按本地 CA 和内网 host 连接,或者把云端 API key 直接塞进仓库。
判断:
项目配置是否区分 local、shared-dev、test、prod。托管云连接是否使用官方建议的 cloud id、API key 或受控凭证方式。API key 是否有最小权限、过期和轮换策略。
网络入口是否经过 VPC、PrivateLink、IP allowlist 或企业代理。
治理:连接封装要把 self-managed 和 cloud-managed 作为两个 profile,而不是用一堆 if 临时拼接。团队模板只放占位值和字段名,真实 cloud id、API key、endpoint 和证书都走 secret 系统。
搜索服务许可和产品边界不能口头带过
现象:团队把 Elasticsearch 镜像打进客户现场包、商业托管平台或内部 PaaS,却没有复核许可和功能边界。
判断:
是否只是个人开发验证。是否涉及商业托管、客户现场分发、竞争性服务或修改源码。是否启用了试用或订阅功能。
用途评审是否附有 Elastic licensing FAQ 的适用条款和法务结论。
官方默认 Elasticsearch / Kibana 发行版继续适用 Elastic License 2.0(ELv2);免费部分的源码可按 AGPLv3、SSPL 1.0 或 ELv2 选择,官方语言客户端则继续使用 Apache 2.0。三者不是同一个许可对象,也不能因为部分源码提供 AGPLv3 选项,就推断下载到的默认发行版改成了 AGPLv3。普通应用使用、商业托管、客户现场分发、竞争性服务和源码修改的义务不同;按实际制品和用途复核 Elastic licensing FAQ、订阅功能和交付模式,无法确认时由法务给出结论,不能把一次评审结果外推到所有场景。
客户端兼容模式不能变成永久拐杖
现象:升级到新 Elasticsearch 后,应用靠 REST API compatibility 继续跑,但没人处理 deprecation log,下一次升级再次踩坑。
判断:
请求头里是否长期带 compatible-with。环境变量里是否长期打开 ELASTIC_CLIENT_APIVERSIONING。deprecation log 是否持续出现兼容模式记录。
客户端库版本是否落后 server 一个 major。
治理:
compatibility mode 只在升级窗口使用。升级任务必须包含客户端库升级和 API 调整。验收时检查 deprecation log。
团队模板记录 server、client、Kibana 的版本矩阵。
误连生产是最高优先级风险
现象:本地 .env 复制了生产 URL,Kibana 历史连接指向生产,清理脚本删到了真实 index。
判断:
脚本执行前是否打印 ELASTICSEARCH_URL、cluster name、index prefix。生产账号是否默认只读。删除脚本是否需要显式确认 prefix。
GUI 连接名是否带环境和权限。
治理:
本地和生产配置文件物理隔离。生产连接不进入开发机默认 .env。Kibana / GUI 连接名必须包含 local、shared-dev、test、prod-readonly。
危险操作必须走受控脚本,不靠手工 Dev Tools。
安装后:
Elasticsearch 版本已固定,不使用 latest。实际 server、Kibana、客户端版本和镜像 digest 已记录。Docker Desktop 或宿主资源满足开发验证。
宿主端口只绑定 127.0.0.1,共享环境另有网络控制。http_ca.crt 已从当前容器复制,并标注来源。curl --cacert 能返回 200。
Kibana enrollment token 已在有效期内使用;过期后重新生成。Kibana enrollment token 和 elastic 密码没有进入仓库、截图或聊天记录。
接入前:
.env.example 只有占位值,真实 .env 未提交。应用配置包含 URL、账号、密码、CA 证书和 index prefix。应用账号不是 elastic。
index 使用显式 mapping。setup 账号能创建 index / mapping,app 账号能写入和查询。最小验证完成建 index、写入、查询、删除。
只读账号查询成功,写入被拒绝。
共享环境前:
owner、版本、证书、账号、角色和清理窗口已明确。权限按项目 index prefix 隔离。index template、mapping、alias 和 ILM 边界已评审。
shard / replica 规划有数据量、查询量和恢复窗口依据。集群角色、入口节点和 master 稳定性边界已说明。快照仓库、保留周期、恢复演练和责任人已记录。
磁盘水位、heap、GC、thread pool、slow log 和写入错误率有观测入口。共享环境完成小规模 bulk 冒烟,记录批大小、并发、失败明细和重试策略。Kibana / GUI 生产连接默认只读。
清理脚本先 dry-run,并要求显式确认 prefix。凭证轮换和证书轮换路径已记录。server、client、Kibana 版本矩阵已记录。
许可和产品边界按团队用途复核。
排障时:
先确认协议是 HTTP 还是 HTTPS。再确认 CA 证书、账号、密码和端口。再看旧 volume 是否影响密码、证书和 index。
再看 Docker 内存、vm.max_map_count、ulimit、磁盘和日志。再看 mapping 是否被动态字段污染。再看客户端版本和 REST API compatibility 是否只是临时过渡。
最后看项目脚本是否指向了错误环境或错误 index prefix。
