深入 Elasticsearch(18):用 Docker 或 Podman 搭建 HTTP 实验集群
每次练习先从 Elastic 官方版本目录解析最新稳定版本,再拉取对应的官方镜像,启动一个只在本机开放 HTTP 的三节点集群。一次实验固定一个版本,结果同时记录镜像标识和服务器版本;下一次用新的空环境验证升级后的行为。
本章为前面的原理章节提供运行环境,直接复用《08 补充:查询为什么没命中》的 mapping、三份文档和查询附件。它不另写一套字段示例,也不把“请求返回 200”当成“命中结果正确”。
这是无认证、无 TLS 的本地实验环境,不是生产部署模板。 只有第一个节点的 HTTP 端口映射到 127.0.0.1:19200,节点间的 transport 通信留在容器网络中。默认关闭机器学习,插件、模型与安全实验需要另行准备。
先定义镜像、节点、集群与实验数据
镜像是启动程序和依赖的发行包。tag 是版本名称,image ID 与 digest 用于记录实际拉取的内容;容器是运行中的一个 Elasticsearch 节点。三个节点加入相同的 cluster,并通过容器网络发现彼此。
HTTP 的 9200 端口供 curl、Postman 或应用提交 REST 请求。transport 的 9300 端口供节点内部通信,不能拿它当 Search API 地址。映射后的本机端口是 19200,因此实验请求的完整地址是 http://127.0.0.1:19200/es-query-lab-v1/_search。
三节点不是三个独立集群,也不是每次查询都要请求三个地址。收到 HTTP 请求的节点可以协调其他节点完成操作。三个容器仍共享同一台主机,不能验证机房级高可用或真实生产性能。
flowchart TD
V["官方版本目录:选稳定 X.Y.Z"] --> P["每次 pull 官方镜像并记录标识"]
P --> N["独立容器网络"]
C["curl / Postman / 实验执行器"] --> H["127.0.0.1:19200"]
subgraph L["本机 Linux 容器环境"]
H --> A["节点 1:HTTP + transport"]
N --> A
N --> B["节点 2:transport"]
N --> D["节点 3:transport"]
A <--> B
B <--> D
D <--> A
end
F["08 补充的 mapping-lab.http"] --> C
C --> R["results.json:版本、预期、实际响应"]
准备 Docker 或 Podman
需要 Python 3.10 或以后版本,以及一个可用的 Docker 或 Podman Linux 容器环境。脚本只用 Python 标准库,不需要 Compose、第三方 Python 包或 ES 客户端库。
三个节点各设置 2 GiB 容器内存和 1 GiB JVM heap。建议给容器虚拟机预留至少 8 GiB 内存和 10 GiB 可用磁盘;这是本实验的资源预算,不是官方生产容量建议。模型推理、更多数据或 Kibana 会增加开销。
Docker Desktop 启动后先检查:
1 | |
Podman 在 Linux 上可以直接运行;macOS 和 Windows 的容器运行在 Linux 虚拟机中。已有 machine 时先启动它,没有 machine 时才初始化:
1 | |
不要在已有虚拟机上重复执行 init。脚本不会自动创建虚拟机、修改主机 sysctl 或使用 privileged 容器;它只消费已经可用的容器环境。
Elastic 的容器运行要求目前要求 Linux 的 vm.max_map_count 为 1048576。在原生 Linux 上可检查并临时设置:
1 | |
Podman machine 的设置发生在 Linux VM 内,而不是 macOS 内核:
1 | |
Docker Desktop 使用其 Linux VM;WSL 后端也应在对应 Linux 环境设置,按官方平台说明处理。重启后应重新检查。遇到 bootstrap check 失败时不能靠关闭检查来绕过环境要求。
模式提炼:把运行环境作为实验前提
程序版本 + 内核条件 + 资源预算 + 输入数据 → 可解释的观察结果。查询失败、节点启动失败和内存不足是不同问题,应分别收集查询响应、容器日志和资源信息。环境不满足时,不能把失败算成 Query DSL 的反例。
启动:每次解析最新稳定版本并 pull
下载 集群启动与实验执行脚本 和 配套测试。从《查询为什么没命中》下载 mapping-lab.http,将三个文件放在同一个实验目录中。
脚本的 up 每次访问 Elastic 官方 artifacts 版本接口,仅接受 X.Y.Z 形式的稳定版本,从中按数值选取最高版本,排除 SNAPSHOT、alpha、beta 和 rc。然后始终执行:
1 | |
官方Docker 安装教程同样使用版本化的官方镜像地址。本实验不依赖一个叫 latest 的 tag;“每次找最新”与“使用 latest tag”是两件事。2026-10-09 的接口查询解析为 9.5.5,这只是核对时的结果,代码没有写死它。
版本接口是这个启动器的发现入口,不是保证永久不变的公共兼容协议。接口不可达、返回结构改变或对应镜像不能 pull 时,脚本报错,不静默使用缓存或降低版本;可以在确认目标版本后用 --version X.Y.Z 复现实验。即使指定版本,仍会执行 pull。下载到的镜像内容可以被容器引擎复用,不等于每次重新下载全部层。
先使用 Podman:
1 | |
Docker 的命令只替换 engine:
1 | |
两条命令择一执行。启动器先检查容器引擎,再创建实验目录;已有目录会拒绝覆盖。容器名和网络名带有本次随机标识,数据留在容器的实验存储中,不复用旧版本的数据目录。
启动时三个节点都有 master、data、ingest 等实验所需角色。首次建立集群配置 cluster.initial_master_nodes;等到三节点加入、健康为 green 后,脚本从绑定的配置文件中移除这一引导设置,再写入 READY 状态。这遵循首次集群引导规则,后续重启不会重新配置初始投票节点。
lab-current/state.json 保存版本、容器引擎、网络、容器名、镜像 inspect、服务器信息和健康响应。镜像拉取成功还不够:脚本等待三个节点并核对实际服务器版本,默认等待上限为 240 秒;超时保留状态和容器,便于检查日志。
1 | |
根响应应显示 cluster_name: es-http-tutorial,节点数应为 3。具体版本以此次服务器返回为准。
模式提炼:发现版本与固定实验分开
发现最新版本 → pull → 记录实际内容 → 固定这一轮输入和环境。后续比较用 state 和结果记录,不用“当时应该是最新版”作证据。指定旧版本重跑与启动最新版本可以共用同一套脚本,只改变版本参数和实验目录。
实用《08 补充》的实验附件
原附件有 137 个请求,其中 environment 组 3 个、core 组 96 个。默认执行这 99 个请求,顺序仍由原文件决定:检查环境、创建 mapping、写入三份文档,再执行查询和观察操作。
1 | |
执行器只接受带端口的 http://127.0.0.1 地址,并检查集群名是 es-http-tutorial,避免把这套写入实验误指向别的集群。这个检查用于防止误操作,不是身份认证。
每个请求都保存到 results-current/results.json,包含案例编号、请求 body、原来的文字预期、HTTP 状态和完整响应。结果目录已存在时拒绝覆盖。发生网络错误也会记录;mapping 或文档写入失败时停止,不继续输出一串缺失索引的查询错误。其他查询失败会记录并继续,最后以非零退出码提示检查。
结果的几个状态含义不同:
| 状态 | 实际验证了什么 |
|---|---|
| HTTP_OK | 收到成功 HTTP 响应,仍需检查命中、排序、分数或聚合结果 |
| PASS_HITS | Q01、Q02、Q03、Q05、Q06 的命中文档集合与总数已通过明确断言 |
| EXPECTED_HTTP_ERROR | negative 组返回 4xx;还要核对实际错误原因是否对应原预期 |
| FAIL / FAIL_HITS | HTTP 状态异常,或有断言的命中集合不符 |
| TRANSPORT_ERROR | 请求未完成,不能解释为查询无命中 |
Q01 默认 OR 预期命中 1、2;Q02 的 AND 和 Q03 的短语查询预期仅命中 1;Q05 的 text 单词项查询预期无命中;Q06 的 keyword normalizer 查询预期命中 1。这几条正好把查询分析、词项匹配和完整值规范化联系起来。
HTTP_OK 没有自动变成 PASS。其余查询中的排序、数值精度、BM25、空间关系和聚合应对照响应逐项核实。脚本不改写原覆盖文件的 NOT_RUN;只有真正取得响应、完成对应断言的结果,才能成为这一版本的新证据。
主实验跑完后,可以单独重跑一组查询:
1 | |
故意失败的请求另跑:
1 | |
不要在同一索引上再次运行完整默认组:SETUP01 创建已存在索引会报错。重查已有文档用 --case;从建表开始重跑用一个新的空集群,或为原请求统一换一个实验索引后缀。脚本不自动删除已存在索引。
curl 与 Postman 使用同一个集群
原附件中的 POST /es-query-lab-v1/_search 是 Kibana Console 的相对地址形式,不能直接粘贴到普通终端。curl 要带完整地址:
1 | |
JSON 的 \u0027 表示单引号,避免和 shell 的外层单引号冲突。使用《08 补充》的 curl 导出器也可以保留原输入:
1 | |
若导入原 Postman collection,将集合变量 baseUrl 改为 http://127.0.0.1:19200,认证选择 No Auth,再按单个案例执行。已有 Kibana 时可在对应集群的 Dev Tools 使用相对请求;本章不会另外部署 Kibana。
扩展组不能跟着 core 全选运行
join、percolator、dynamic 可以在检查各自前提后单独运行;插件组需要带插件的同版本镜像,NEW 组要求目标版本支持全部配置,语义组要求真实推理端点、模型及许可。最新版本也不等于拥有全部插件、全部模型或全部订阅功能。
时间序列附件使用固定的样本时间和索引边界,升级重跑时应一起核对,而不是只把文档日期改成今天。原实验手填的向量仍然只演示运算,不会因为集群是真的就自动变成模型语义实验。
机器学习在本环境关闭,认证与 TLS 也关闭,因此第 15 章的用户、角色、API key 与加密实验不能在这个基线直接验证。需要安全实验时,应采用官方启用安全的部署流程,使用另一套环境,不在运行中的无认证集群上混用两种前提。
用同一环境练习系列中的其他知识
| 系列章节 | 在这个集群中能观察的操作 | 需要保留的边界 |
|---|---|---|
| 00、02、03、04、07、08、09 与 08 补充 | _analyze、_termvectors、_mapping、_explain、_segments、查询、评分与聚合 | 原字段和请求继续复用;分数可能随数据、分片和版本变化 |
| 01、10、11、12、16 | _cat/nodes、_cat/shards、_cluster/state、路由、副本及单节点故障恢复 | 三节点共享一台宿主机,不能证明跨主机高可用 |
| 05、06、14 | refresh/flush、profile、统计与监控 | 小样本可以观察机制,不支持生产吞吐和延迟结论 |
| 13 与经典架构模式 | ILM、alias、rollover、reindex;独立实验索引 | 冷热分层、快照仓库和跨集群检索还需额外拓扑或存储 |
| 15、17 | 安全前提对照与产品机制比较 | 本环境不验证认证加密,也不包含 Solr |
原实验索引设置一个主分片、零副本,适合比较查询,不能直接用它证明副本容错。另建一个小索引做第 10、11 章实验:
1 | |
先确认三个节点加入和副本分配完成,再从 state.json 取出第 3 个容器名,仅停止它:
1 | |
Docker 环境将两条 podman 命令替换为 docker。两个 master-eligible 节点仍在,通常可以继续选举与服务;状态可能在恢复过程中变化,不能承诺一定长期停在 yellow。检查 health、shards 和搜索响应,区分“节点少了”“副本待恢复”和“数据不可读”。不要连续停止第二个节点来套用单节点故障结论。
保留证据、清理和下次升级
完成实验后,先保留 state.json 与 results.json。下面的命令会删除本次容器、其匿名数据卷及网络,实验数据不可恢复;镜像、配置文件和结果记录保留:
1 | |
启动器通过状态文件定位确切资源,清理前核对所有现存资源的 owner 标签。标签不符就拒绝删除,不调用系统级 prune,也不删除其他网络或镜像。启动一半失败时也可用相同命令清理已创建的资源。
下一次使用新的目录重新解析最新版:
1 | |
这是重新建立空实验集群,不是生产滚动升级。旧数据目录不挂到新主版本容器,原请求和样本文档重新写入;比较两份响应时可以把版本差异与输入差异分开。
需要固定版本复现时:
1 | |
同一默认端口不能同时被两个集群使用。保留旧集群做并排对照时,第二个使用 --port 19210,其执行器也指定 --base-url http://127.0.0.1:19210。
模式速查
| 需求 | 操作 | 证据 |
|---|---|---|
| 跟随最新稳定版本 | 新目录执行 up,不指定 version | 官方版本目录、pull 结果、镜像 inspect、服务器版本 |
| 复现旧观察 | 指定 version,保持同一 mapping/doc/request | 固定版本及逐条实际响应 |
| 验证查询结果 | run 的请求记录加命中/排序/聚合断言 | PASS_HITS 或人工核对结果,不仅是 HTTP_OK |
| 清空实验 | down --discard-data | owner 校验与保留的状态、结果文件 |
故障定位与验证状态
启动失败时先查看 state.json 中的具体容器名:
1 | |
错误中的 vm.max_map_count 对应 Linux 环境配置,退出码 137 常需要检查内存或容器事件,address already in use 对应本机端口冲突。discovery 错误应检查容器网络的 DNS 与节点名;Podman 的自定义网络需要名称解析能力。通过端口发布规则将 HTTP 限制在 loopback;Docker 文档还指出旧于 28.0.0 的版本存在同二层网络访问 localhost 发布端口的历史问题,应使用已修复的引擎,不把实验端口暴露给不可信网络。
脚本测试可以独立运行:
1 | |
编写时核对了官方版本接口,离线测试覆盖稳定版本筛选、启动命令、bootstrap 设置移除、资源归属与请求解析,并使用临时本机 HTTP 服务验证了请求发送、单引号输入和结果记录。真实 Docker/Podman 三节点启动以及原附件在 ES 上执行仍为 NOT_RUN:验证机器没有 Docker,也没有已创建的 Podman machine。模拟服务通过不代表 ES 接受了 mapping;实际实验的结果文件才用于填补这个验证空缺。
