Elasticsearch 部署运维
搜索集群的维护会改变分片所在节点,索引恢复会改变可查询的数据。节点重新加入以后,需要确认分片已经可用;索引恢复完成以后,还要补齐快照之后的写入和删除。两种操作最后都要回到应用实际查询与写入的结果。
维护前识别集群、分片与数据来源
Elasticsearch 主文提供安装、搜索模型、客户端和集群搭建。接手已有集群时,先确定这组对象:
集群身份:cluster UUID、各节点版本
├─ master-eligible:参与选举和集群状态维护
├─ data / 数据层:保存分片并执行读写
├─ ingest:处理写入流水线
└─ coordinating:接收请求并协调分片
索引
├─ primary:每个分片的主副本
├─ replica:该分片的其他副本
└─ alias / data stream:应用访问与写入入口
恢复依赖
├─ 快照仓库及其访问凭据
├─ 模板、ILM、pipeline、权限等配置
└─ 上游数据库、CDC 或可重放事件节点可以承担多个角色。单节点里取消副本以得到 green,与多节点副本跨故障域分布,是不同的可用性条件。green 表示当时所有主副本已分配,并不说明应用索引内容正确。
在 Linux 管理机准备 curl、jq 与已授权的监控 API Key。示例适用 Elasticsearch 9.x API;地址、CA 文件与 Key 由管理员提供,Key 权限只覆盖必要操作:
export ES_URL=https://search.example.internal:9200
export ES_CA=/etc/elasticsearch/http-ca.crt
read -r -s -p 'Elasticsearch API Key: ' ES_KEY; printf '\n'
esops() {
curl -q --noproxy '*' --fail-with-body --silent --show-error \
--cacert "$ES_CA" -H "Authorization: ApiKey $ES_KEY" \
-H 'Content-Type: application/json' "$ES_URL$1" "${@:2}"
}
esops / | jq '{cluster_uuid,version:.version.number}'
esops '/_cat/nodes?format=json&h=name,ip,version,node.role,master' | jq .
esops '/_cat/shards?format=json&h=index,shard,prirep,state,node' | jq .
esops '/_cluster/health' | jq .这里显式直连,适用于管理网可直达服务的情况;需要代理的企业路径单独验证。输出包含内部地址,只在受限位置保存。管理操作需要更高权限时按实际动作授权,不把只读监控 Key 扩成长期超级用户。
主分片不可用会影响对应数据;副本未分配则主要意味着冗余不足。先对具体未分配分片查询原因:
esops /_cluster/allocation/explain -X POST \
-d '{"index":"orders-search","shard":0,"primary":true}' | jq .
esops '/_cat/allocation?format=json' | jq .
esops '/_cluster/settings?include_defaults=true' | jq .替换为实际索引、分片号和主副本类型。重点看 decider:磁盘水位、数据层/过滤条件、同分片副本限制,以及是否存在有效 shard copy。API 解释见 分片分配诊断。没有有效副本时,调整分配开关也无法生成原数据。
先把一份快照恢复成可查询的索引
建立无外网的实验节点
使用 Linux Bash、Docker、jq,Elasticsearch 固定 9.5.2。节点没有发布端口且 network none;HTTP 只在容器内访问,保留账号认证,实验中不配置 HTTP TLS。共享环境使用完整 TLS,不复用演示口令。
docker volume create ops3-es-data
docker volume create ops3-es-snapshots
docker run --rm --user 0 --network none \
-v ops3-es-snapshots:/backup \
docker.elastic.co/elasticsearch/elasticsearch:9.5.2 \
sh -c 'chown 1000:0 /backup'
docker run -d --name ops3-es --network none --memory 2g \
-e discovery.type=single-node \
-e xpack.security.enabled=true \
-e xpack.security.http.ssl.enabled=false \
-e xpack.security.autoconfiguration.enabled=false \
-e ELASTIC_PASSWORD=LabOnly-Elastic-83 \
-e ES_JAVA_OPTS='-Xms512m -Xmx512m' \
-e node.store.allow_mmap=false \
-e path.repo=/usr/share/elasticsearch/backup \
-v ops3-es-data:/usr/share/elasticsearch/data \
-v ops3-es-snapshots:/usr/share/elasticsearch/backup \
docker.elastic.co/elasticsearch/elasticsearch:9.5.2卷名必须属于新的实验。初始化 root 只处理快照目录权限,服务按镜像 UID 1000 运行。node.store.allow_mmap=false 用于小实验避免修改宿主内核参数,不能据此给生产集群关闭 mmap;生产 bootstrap、内存与文件句柄按实际发行版要求准备。Docker 运行要求见 官方 Docker 安装说明。
定义容器内部 HTTP 工具:
eslab() {
docker exec ops3-es curl -q --noproxy '*' \
--fail-with-body --silent --show-error \
-u elastic:LabOnly-Elastic-83 -H 'Content-Type: application/json' \
"http://localhost:9200$1" "${@:2}"
}
eslab / | jq '{cluster_uuid,version:.version.number}'启动可能需要等待。只有返回预期版本与非空 UUID 后才继续;反复退出时检查容器日志及 OOM 状态。不要把失败后自动重建卷当成正常重试。
写入、注册仓库与保存快照
eslab /ops3-orders -X PUT -d '{
"settings":{"number_of_shards":1,"number_of_replicas":0},
"mappings":{"properties":{
"status":{"type":"keyword"},"amount":{"type":"double"}
}}
}'
eslab '/ops3-orders/_doc/102?refresh=wait_for' -X PUT \
-d '{"status":"NEW","amount":30}'
eslab /_snapshot/ops3-repo -X PUT -d '{
"type":"fs","settings":{"location":"/usr/share/elasticsearch/backup"}
}'
eslab /_snapshot/ops3-repo/_verify -X POST | jq .
eslab '/_snapshot/ops3-repo/before-change?wait_for_completion=true' \
-X PUT -d '{
"indices":"ops3-orders",
"include_global_state":false,
"feature_states":["none"]
}' | jq -e '.snapshot.state=="SUCCESS" and .snapshot.shards.failed==0'快照仓库注册成功之后仍需 verify 和实际快照。异步创建时只收到 accepted 还不够,应查询指定快照直到 SUCCESS,并检查失败分片。快照行为与兼容性见 Snapshot and restore。
这份快照只保存实验索引,没有打包全局状态和系统 feature state。数据卷与快照卷虽然分开,仍位于同一个 Docker 主机;它们适合演示机制,不能抵御主机或整个存储系统丢失。生产仓库应在独立故障域,同一仓库只允许一个集群写入,其他恢复集群以只读方式注册。
修改源数据,再恢复到另一个名称
eslab '/ops3-orders/_doc/102?refresh=wait_for' -X PUT \
-d '{"status":"PAID","amount":30}'
eslab /_snapshot/ops3-repo/before-change/_restore -X POST -d '{
"indices":"ops3-orders",
"include_global_state":false,
"feature_states":["none"],
"include_aliases":false,
"rename_pattern":"ops3-orders",
"rename_replacement":"restore-ops3-orders"
}'
eslab '/_cluster/health/restore-ops3-orders?wait_for_status=green&timeout=30s' \
| jq -e '.timed_out==false and .status=="green" and .active_primary_shards==1'
eslab /restore-ops3-orders/_doc/102 | jq -e '._source.status=="NEW"'
eslab /ops3-orders/_doc/102 | jq -e '._source.status=="PAID"'目标是备份时的 NEW,原索引保持 PAID。显式 include_aliases:false 防止实验恢复带入原有 alias,意外改变应用入口。green 条件来自这个已设置零副本的单节点实验,生产按设计的副本数等待,不能为消除 yellow 随意调为零。恢复参数见 Restore a snapshot。
_cat/recovery 没有活动任务时,也可能是任务未启动或早已失败。应联合核对目标索引、主分片、恢复结果与实际文档,而不是只看任务列表空了。
恢复数据仍受应用权限约束
给实验账号只授予目标索引读取权限:
eslab /_security/role/ops3_reader -X PUT \
-d '{"indices":[{"names":["restore-ops3-orders"],"privileges":["read"]}]}'
eslab /_security/user/ops3_reader -X POST \
-d '{"password":"LabOnly-Search-Reader-83","roles":["ops3_reader"]}'
docker exec ops3-es curl -q --noproxy '*' --fail-with-body \
--silent --show-error -u ops3_reader:LabOnly-Search-Reader-83 \
http://localhost:9200/restore-ops3-orders/_doc/102
docker exec ops3-es curl -q --noproxy '*' --silent --show-error \
-u ops3_reader:LabOnly-Search-Reader-83 \
-o /tmp/ops3-denied.json -w '%{http_code}\n' \
-X DELETE http://localhost:9200/restore-ops3-orders/_doc/102
docker exec ops3-es cat /tmp/ops3-denied.json读取成功,删除应是 HTTP 403 且错误为 security_exception。负例没有使用 fail 选项,以便分别检查 HTTP 状态和响应体;401、连接失败与 403 的原因不同。随后再次读取,确认文档未被删除。实验结束执行 docker stop ops3-es,两个卷保留。
安排一次节点维护窗口
进入窗口前的条件
先确认集群 UUID、节点与插件版本,读取近期磁盘、JVM、线程池、ILM 和快照结果:
esops /_nodes/stats/jvm,fs,thread_pool | jq .
esops /_cluster/pending_tasks | jq .
esops '/orders-search/_ilm/explain' | jq .
esops '/_cat/plugins?format=json' | jq .
esops /_migration/deprecations | jq .索引名换成实际受 ILM 管理的对象,不适用时不把其缺少策略判为失败。修复长期未分配分片、磁盘水位、持续增长的 pending task 和快照失败后再安排维护;保留能承接失去一台节点后的空间与处理能力。
内存预算分给堆、文件缓存、直接内存和系统开销,不把容器全部内存交给 JVM。文件句柄、swap、vm.max_map_count 和数据路径属主按具体节点配置核验。磁盘接近水位时,维护引发的复制和 merge 可能进一步耗尽空间;先扩容或减少允许削减的数据保留,再进入重启窗口。
一次只维护一个满足条件的节点
普通重启和跨版本升级使用不同准备。升级前固定 from/to,核对 breaking changes、插件、客户端、Kibana 及采集端兼容;目标版本不明确就不开始。官方升级顺序要求按数据层依次处理数据节点,再处理其他非 master 节点,master-eligible 节点最后,详见 Elasticsearch 升级说明。
节点可维护?
├─ 否:补副本、修磁盘/集群状态或恢复快照能力
└─ 是:保存分配设置 → 暂停副本分配 → 停一个节点
→ 维护/升级 → 节点重新加入且角色正确
→ 恢复原分配设置 → 等待副本与业务恢复
├─ 未恢复:停在当前节点,不继续扩散
└─ 已恢复:才进入下一节点临时限制副本分配可以避免短暂重启立刻触发不必要的副本复制。先在受限工作目录保存原设置,并保留原有 persistent 与 transient 值:
umask 077
esops '/_cluster/settings?flat_settings=true' > allocation-before.json
jq '{
persistent:{"cluster.routing.allocation.enable":
(.persistent["cluster.routing.allocation.enable"] // null)},
transient:{"cluster.routing.allocation.enable":
(.transient["cluster.routing.allocation.enable"] // null)}
}' allocation-before.json > allocation-restore.json
esops /_cluster/settings -X PUT -d '{
"persistent":{"cluster.routing.allocation.enable":"primaries"},
"transient":{"cluster.routing.allocation.enable":null}
}' | jq -e '.acknowledged==true'该操作需要集群管理权限。先检查两个文件非空、JSON 合法并读懂原设置;请求失败或响应丢失时重新查询实际设置,不直接停止节点。原设置本就限制分配时先查明原因,不能覆盖另一项进行中的维护。
之后在唯一批准的目标主机执行停止、安装已验证制品及匹配插件、启动。普通重启可使用以下两条命令;跨版本安装步骤按主文和官方版本说明插在中间:
sudo systemctl stop elasticsearch
sudo systemctl start elasticsearch用节点名称、版本和角色确认它重新加入原集群,而不是生成了新集群。恢复分配:
esops /_cluster/settings -X PUT --data-binary @allocation-restore.json \
| jq -e '.acknowledged==true'
esops '/_cluster/health?wait_for_status=green&timeout=60s' \
| jq -e '.timed_out==false and .status=="green" and .unassigned_shards==0'一分钟是一次等待上限,不是所有集群的恢复 SLA。超时后查询恢复进度和资源,不立即停下一台。应用端同时执行代表性查询与写入,确认错误率和延迟回到维护前可接受范围。
不要重新设置 cluster.initial_master_nodes 来帮助旧节点“加入集群”;它服务于初次引导新集群,已有集群使用原有身份和发现配置。版本升级后的数据目录也不能交给旧二进制直接降级。
从快照状态恢复到业务状态
快照之后的写入和删除需要补齐
搜索数据常由数据库或消息构建。快照记录的是各分片在快照期间取得的数据状态,不能假定所有分片都严格对应一个全局瞬间。需要与上游精确对账时,在可控制的停写或 checkpoint 流程下建立恢复基线,再保留后续事件。
恢复新索引或新集群时,核对以下关联:
| 对象 | 恢复后检查 | 遗漏后的表现 |
|---|---|---|
| 文档 | ID、字段内容、业务总量、代表查询 | 数量相同但状态错误,或删除对象重新出现 |
| mapping / analyzer | 字段类型、分词器、插件 | 写入失败或相同查询得到不同结果 |
| alias / data stream | 读写目标、filter、routing、write index | 应用继续访问旧索引或写入错误位置 |
| template / ILM / pipeline | 新索引设置、rollover、处理链 | 当前能写,下一次 rollover 后出错 |
| 用户与 API Key | 允许/拒绝样本、证书、有效期 | 管理员正常而应用 403 |
| 上游进度 | 恢复起点、终点、删除与失败事件 | 搜索长期缺数据或乱序覆盖 |
全局状态和 feature state 可能包含安全系统、Kibana 等内容。恢复它们会影响目标配置与身份,操作前明确需要哪些系统状态,不能把整个旧集群配置无差别覆盖到已有目标。
切换读写入口
先对隔离目标补齐后续 index/update/delete 事件,保持稳定文档 ID 与同一业务对象的顺序;检查消费延迟追到约定终点,再冻结最后一段写入并完成差异核对。
应用使用 alias 时,把旧索引移除和新索引加入放在同一次 _aliases 请求中,并保留原 filter、routing 和 write-index 属性。请求超时先查询实际 alias 状态,不依据超时判断一定没切换。切换后用应用账号核对写入实际落点、查询与删除。双索引迁移的完整操作见 Elasticsearch 主文。
索引内 alias 的原子切换不能替代跨集群连接配置发布。跨集群还要调整客户端连接池、证书、API Key 和采集端,保留旧集群只读。新目标已接收的写入需要回放到旧目标后才能回退;更换 endpoint 不会自动合并数据。
维护和恢复中的异常判断
yellow 与 red 分开处理
yellow 表示主分片可用、某些副本未分配;先查节点数量、分配约束、数据层和水位,恢复设计中的冗余后再继续维护。red 表示至少一个主分片不可用;找到具体分片并读取 allocation explain,优先恢复原节点或有效副本。
no_valid_shard_copy 时,需要快照或上游重建。allocate_empty_primary 和强制接受过期副本可能丢失数据,不能作为普通修复命令。没有批准的数据损失决策就停在保护原始数据的步骤。
水位触发写保护
查看数据卷的实际空间、_cat/allocation 和索引 block,识别业务增长、ILM 失败、分片倾斜与临时文件。先扩容、迁移合适分片或删除已经批准过期的数据;不要在磁盘仍满时反复清除 read_only_allow_delete。
空间降到恢复阈值后,再检查保护是否解除、分片是否恢复分配以及应用写入是否成功。自动解除与具体水位设置以 磁盘分配规则为准。
快照 SUCCESS,但恢复无法开始
查版本与索引兼容、目标同名索引、仓库权限和目标主分片数。恢复集群只读注册仓库;同一仓库多集群同时写入会危害其完整性。旧快照到新版本是否兼容还取决于索引创建版本,不能只比较集群 major。
恢复目标容量应覆盖数据、分片开销与恢复峰值。PARTIAL 或 FAILED 快照要检查具体失败对象,不能作为完整基线直接接流;仓库 verify 成功只检查访问与基础仓库操作,不代表某一快照里的数据已经恢复成功。
集群正常,业务仍然不正常
bulk 的 HTTP 200 可能带 errors:true,逐项检查失败文档;搜索响应还要看 timed_out 和失败分片。查询差异查 mapping、analyzer、alias filter、数据恢复点与刷新,不先删除重建整个索引。
问题解决后,让快照计划重新覆盖当前配置和数据,检查最近成功快照、失败通知及恢复耗时。这样下一次维护才有可使用的恢复基线。
权威资料与规范地址
部署与维护
- Docker 安装:https://www.elastic.co/docs/deploy-manage/deploy/self-managed/install-elasticsearch-docker-basic
- 分片分配诊断:https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-cluster-allocation-explain
- 集群升级:https://www.elastic.co/docs/deploy-manage/upgrade/deployment-or-cluster/elasticsearch
- 磁盘分配规则:https://www.elastic.co/docs/reference/elasticsearch/configuration-reference/cluster-level-shard-allocation-routing-settings
快照
- Snapshot and restore:https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore
- Restore a snapshot:https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/restore-snapshot
