OpenSearch 部署方式、架构选型与提效工具手册
从旧教程切换到可治理的搜索节点
最常见的失败并不是容器拉不下来,而是团队把 OpenSearch 当成“换了名字的 Elasticsearch”:继续使用 admin/admin,让应用持有管理员账号,把 demo 证书带进共享环境,再假设旧客户端和插件可以无条件兼容。OpenSearch 有自己的 Security plugin、OpenSearch Dashboards、Index State Management、快照机制和版本节奏,这些差异会直接影响启动、权限、迁移和恢复。
先在隔离的本地环境证明协议、账号、mapping 和清理链路。Docker Desktop 至少预留 4GB 内存;Linux 宿主确认 vm.max_map_count 不低于 262144。容器内仍使用 9200、9600 和 5601,下面把宿主端口映射为 9201、9601 和 5602,这样可以与本机 Elasticsearch 并存。真实 endpoint、管理员密码、证书私钥和 API key 不进入仓库、截图或终端录屏。
先确认 Docker、Compose、端口和 Linux 内核参数:
docker version
docker compose version
docker ps --format "table {{.Names}}\t{{.Ports}}\t{{.Status}}"Windows:
netstat -ano | findstr ":9201"
netstat -ano | findstr ":5602"Linux / macOS:
lsof -i :9201
lsof -i :5602
sysctl vm.max_map_count如果端口被占用,不要把本地开发脚本指向共享或生产 OpenSearch。先改宿主端口。
OpenSearch 的入口要按使用场景分开:
示例固定使用 3.7.0,OpenSearch 与 Dashboards 的镜像版本必须一致。新项目应从 下载页 和 版本维护策略 选择团队批准版本,再把精确 tag 或 digest 写入锁定文件;不要让 latest 在不同成员机器上解析成不同构建。OpenSearch 2.12 及以上的新安装还必须提供强 OPENSEARCH_INITIAL_ADMIN_PASSWORD,这项要求也适用于 3.x 的 demo security 初始化。
| 入口 | 适合 | 不适合 | 关键边界 |
|---|---|---|---|
| Docker 单容器 | 个人最小验证、快速复现安全插件和 REST API | 团队长期模板、Dashboard 联调 | 自定义 admin 密码、demo 证书、清理命令 |
| Docker Compose | 项目联调、新人一键启动、Dashboards 验证 | 生产集群 | 版本、端口、volume、security demo、healthcheck、清理脚本 |
| 共享开发实例 | 多服务联调、重型组件集中运行 | 个人随意试错、破坏性测试 | owner、角色权限、index prefix、证书轮换、清理窗口 |
| 自建多节点 | 核心业务、强自控、客户现场 | 无 SRE / DBA owner 的小团队 | 节点角色、shard、snapshot、滚动升级、磁盘和告警 |
| 托管 OpenSearch | 快速生产化、减少自建运维 | 数据边界和成本未确认场景 | 账号权限、网络、版本、插件、备份、退出方案 |
| Kubernetes Operator | 成熟 K8S 平台和声明式管理 | 只为本地起服务 | CRD、PVC、PDB、节点亲和、证书和升级 |
Docker 单容器
单容器用于个人验证协议、账号和 index 行为:
docker network create te-opensearch-dev
docker run -d \
--name te-os-single \
--net te-opensearch-dev \
-p 127.0.0.1:9201:9200 \
-p 127.0.0.1:9601:9600 \
-e "discovery.type=single-node" \
-e "OPENSEARCH_INITIAL_ADMIN_PASSWORD=YOUR_STRONG_ADMIN_PASSWORD" \
-e "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" \
opensearchproject/opensearch:3.7.0验证:
export OPENSEARCH_ADMIN_PASSWORD="YOUR_STRONG_ADMIN_PASSWORD"
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
https://127.0.0.1:9201-k 只用于本地 demo 证书快速验证。团队模板要收敛为受控 CA 或证书分发,不把跳过 TLS 校验写成默认客户端配置。
清理:
docker rm -f te-os-single
docker network rm te-opensearch-devDocker Compose
项目模板建议放到明确目录:
dev-dependencies/
opensearch/
compose.yaml
.env.example
index/article-v1.json
scripts/verify.sh
scripts/init-index.sh
scripts/clean-index-dry-run.sh
scripts/clean-index-confirmed.sh.env.example:
OPENSEARCH_VERSION=3.7.0
OPENSEARCH_INITIAL_ADMIN_PASSWORD=YOUR_STRONG_ADMIN_PASSWORD
OPENSEARCH_HTTP_PORT=9201
OPENSEARCH_DASHBOARDS_PORT=5602
OPENSEARCH_INDEX_PREFIX=your_project_dev
OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512mcompose.yaml:
services:
opensearch:
image: opensearchproject/opensearch:${OPENSEARCH_VERSION}
container_name: your-project-opensearch
environment:
- cluster.name=your-project-opensearch
- node.name=your-project-opensearch-1
- discovery.type=single-node
- bootstrap.memory_lock=true
- OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD}
- OPENSEARCH_JAVA_OPTS=${OPENSEARCH_JAVA_OPTS}
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- opensearch-data:/usr/share/opensearch/data
ports:
- "127.0.0.1:${OPENSEARCH_HTTP_PORT:-9201}:9200"
- "127.0.0.1:9601:9600"
healthcheck:
test:
[
"CMD-SHELL",
"curl --silent --fail -k -u admin:$$OPENSEARCH_INITIAL_ADMIN_PASSWORD https://127.0.0.1:9200 >/dev/null || exit 1"
]
interval: 15s
timeout: 10s
retries: 30
dashboards:
image: opensearchproject/opensearch-dashboards:${OPENSEARCH_VERSION}
container_name: your-project-opensearch-dashboards
depends_on:
opensearch:
condition: service_healthy
environment:
- OPENSEARCH_HOSTS=["https://opensearch:9200"]
ports:
- "127.0.0.1:${OPENSEARCH_DASHBOARDS_PORT:-5602}:5601"
profiles:
- ui
volumes:
opensearch-data:启动:
docker compose --env-file dev-dependencies/opensearch/.env \
-f dev-dependencies/opensearch/compose.yaml \
up -d opensearch启动 Dashboards:
docker compose --env-file dev-dependencies/opensearch/.env \
-f dev-dependencies/opensearch/compose.yaml \
--profile ui \
up -d dashboards几个边界必须写清:
端口只绑定 127.0.0.1。demo 证书和 -k 只服务本地验证。OPENSEARCH_INITIAL_ADMIN_PASSWORD 需要强密码,真实 .env 不提交。
OPENSEARCH_JAVA_OPTS 是开发验证入口,生产资源规划要单独评审。Dashboards 登录使用本地 admin 密码;共享环境不能全员共享 admin。
共享开发实例
共享开发实例不能照搬个人 Compose。它至少要确认:
owner 是谁,谁能重启、升级、清理 index、轮换证书和密码。TLS 证书来源、有效期、分发位置和轮换窗口。每个项目是否只能访问自己的 index prefix。
Dashboards 是否默认只读,Dev Tools 是否受控。清理脚本是否先 dry-run,再显式确认 prefix。snapshot repository、保留周期和恢复演练是否有人负责。
OpenSearch 开发配置要管住这些对象:
| 配置对象 | 建议 | 原因 |
|---|---|---|
| 版本 | OPENSEARCH_VERSION=3.7.0 或团队批准版本 | 避免镜像漂移 |
| 端口 | 宿主绑定 127.0.0.1:9201 | 防止局域网暴露 |
| 管理员密码 | .env 或 secret,占位值不提交 | 2.12+ 新安装必需 |
| TLS / 证书 | 本地 demo 可 -k;共享环境要受控 CA | 避免把跳过校验写成默认 |
| 数据卷 | named volume | 可 inspect、可安全重置 |
| index prefix | your_project_dev | 支撑权限和清理 |
| Dashboards | profile 启动,账号分层 | UI 不是每次都需要 |
角色和权限
本地单人可以临时用 admin 初始化,但项目模板必须分层:
| 账号 | 用途 | 权限边界 |
|---|---|---|
admin | 本地初始化、紧急救援 | 不给普通开发长期使用 |
| setup 账号 | 创建 index、mapping、template、ISM policy | 只管理项目 prefix |
| app 账号 | 应用写读 | 只写读项目 prefix |
| readonly 账号 | Dashboards / GUI / 排障 | 只读项目 prefix |
| CI 账号 | 初始化、冒烟、清理 | 明确脚本和审计范围 |
权限靠角色拦截,不靠“大家自觉别乱删”。
共享环境至少要做三类低权限验收:
setup 账号能创建 your_project_dev_* 的 template、index、alias 和 ISM policy,但不能操作其他项目前缀。app 账号能对 your_project_dev_* 写入、查询和 bulk,但不能改 cluster settings、snapshot repository、security index。readonly 账号只能查询和进入 Dashboards 只读视图,执行写入、删除、改 mapping、Dev Tools 高危 API 时必须返回 403。
权限运行证据不要只写“已配置”。要留下账号类型、index pattern、允许动作、拒绝动作、返回码、测试时间和负责人。Security plugin 的角色、用户和映射 API 以当期官方文档为准,不把 demo 用户复制到团队模板。
index 命名
建议:
<team>_<project>_<env>_<domain>_vN例如:
arch_blog_dev_article_v1
arch_blog_test_article_v2共享环境里短名字会让权限、清理和恢复都变得危险。
配置与插件变更
配置文件、集群设置、索引设置和插件不是同一种变更。opensearch.yml、JVM 参数、证书路径和节点角色通常随进程启动读取,修改后要滚动重启;Cluster Settings API 写入的 persistent 设置保存在 cluster state,重启后仍然生效,临时排障结束后要显式撤销;refresh interval、replica 等索引设置可以动态调整,但 mapping 中已经存在的字段类型不能原地改写,只能新建 index、reindex 并切 alias。每次变更前先保存当前值,验收后再删除临时设置,避免一次排障永久改变集群行为。
标准镜像已经带有 Security、Index Management、SQL 等插件。先记录每个节点实际加载的插件,不能只看一台机器:
docker exec your-project-opensearch \
/usr/share/opensearch/bin/opensearch-plugin list
: "${OPENSEARCH_URL:?missing OPENSEARCH_URL}"
: "${OPENSEARCH_ADMIN_PASSWORD:?missing OPENSEARCH_ADMIN_PASSWORD}"
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
"${OPENSEARCH_URL}/_cat/plugins?v"容器里的额外插件应在派生镜像构建阶段安装,不要进入运行中的容器手工修改。OpenSearch 官方核心插件的版本必须与发行版版本匹配,第三方插件还要核对其 descriptor 声明、来源、许可和所请求的 JVM 权限;所有节点必须得到同一插件集合,安装或移除后需要重启。可把强依赖插件写入静态 plugin.mandatory,让缺少插件的节点拒绝启动,而不是带着不完整能力加入集群。
ARG OPENSEARCH_VERSION=3.7.0
FROM opensearchproject/opensearch:${OPENSEARCH_VERSION}
RUN /usr/share/opensearch/bin/opensearch-plugin install --batch analysis-icu升级时先用新 tag 构建并扫描派生镜像,在隔离集群验证插件列表、索引打开、snapshot 恢复、Security role、ISM 和查询回归,再逐节点滚动。回退不是把旧 tag 直接指向已经被新版本写过的数据目录;应保留旧镜像、旧配置、插件清单和升级前 snapshot,失败时停止继续滚动,并按已演练路径恢复到隔离的旧版本集群后切回流量。
最小验证必须完成:连接、建 index、写入、查询、Dashboards、清理。
设置变量:
export OPENSEARCH_URL="https://127.0.0.1:9201"
export OPENSEARCH_ADMIN_PASSWORD="YOUR_STRONG_ADMIN_PASSWORD"
export OPENSEARCH_INDEX="your_project_dev_article_v1"
export EVENT_TIMESTAMP="$(date -u +%Y-%m-%dT%H:%M:%SZ)"连接和版本
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
"${OPENSEARCH_URL}"预期看到 cluster_name、version.number 和 OpenSearch tagline。这里失败时先看协议、密码、端口、容器健康和旧 volume,不要直接改应用代码。
创建 index 和 mapping
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
-X PUT "${OPENSEARCH_URL}/${OPENSEARCH_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" }
}
}
}'写入和查询
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
-X POST "${OPENSEARCH_URL}/${OPENSEARCH_INDEX}/_doc/1?refresh=wait_for" \
-H "Content-Type: application/json" \
-d "{
\"id\": \"1\",
\"title\": \"OpenSearch 开发依赖验证\",
\"tags\": [\"tool-efficiency\", \"search\"],
\"updated_at\": \"${EVENT_TIMESTAMP}\"
}"
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
-X GET "${OPENSEARCH_URL}/${OPENSEARCH_INDEX}/_search?pretty" \
-H "Content-Type: application/json" \
-d '{"query":{"match":{"title":"开发依赖"}}}'预期 hits.total.value 至少为 1。
接着用一组正反实验确认字段设计真的生效。title 的 text 主字段用于分词检索,title.keyword 用于精确聚合;tags 是 keyword,不会把 tool-efficiency 拆成多个 token;updated_at 是 date,可以执行范围查询。
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
-X GET "${OPENSEARCH_URL}/${OPENSEARCH_INDEX}/_search?pretty" \
-H "Content-Type: application/json" \
-d '{
"size": 0,
"aggs": {
"titles": { "terms": { "field": "title.keyword" } },
"tags": { "terms": { "field": "tags" } }
}
}'预期两个聚合都返回 bucket。若把 title.keyword 改为 title,请求通常会因 text 字段默认未启用 fielddata 而失败,这正是 mapping 对排序和聚合能力的直接影响。
再故意写入一个 mapping 中不存在的字段:
curl -sS -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
-X POST "${OPENSEARCH_URL}/${OPENSEARCH_INDEX}/_doc/bad?refresh=wait_for" \
-H "Content-Type: application/json" \
-o /tmp/opensearch-negative-test.json \
-w "HTTP %{http_code}\n" \
-d "{
\"id\": \"bad\",
\"title\": \"不应写入\",
\"tags\": [\"negative-test\"],
\"updated_at\": \"${EVENT_TIMESTAMP}\",
\"unexpected_field\": true
}"
cat /tmp/opensearch-negative-test.json预期 HTTP 400,错误中出现 strict_dynamic_mapping_exception。这个失败证明 dynamic: strict 能在入口处阻止脏字段扩散;如果返回 201,应立即检查目标 index 和实际 mapping,不能继续执行后续初始化。
Dashboards 验证
启动 Dashboards 后访问:
http://127.0.0.1:5602至少确认:
Dashboards 能连接 OpenSearch。Dev Tools 可以查询当前本地 index。页面截图和终端录屏不包含真实生产地址和密码。
清理测试 index
echo "will delete index: ${OPENSEARCH_INDEX}"
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
-X DELETE "${OPENSEARCH_URL}/${OPENSEARCH_INDEX}"
curl -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
"${OPENSEARCH_URL}/${OPENSEARCH_INDEX}"再次查询应返回 404。共享实例不能靠手工 Dev Tools 删除,必须走带 prefix 保护的脚本。
.env.example:
OPENSEARCH_URL=https://127.0.0.1:9201
OPENSEARCH_USERNAME=your_project_dev_search_app
OPENSEARCH_PASSWORD=YOUR_OPENSEARCH_APP_PASSWORD
OPENSEARCH_INDEX_PREFIX=your_project_dev
OPENSEARCH_CA_CERT=dev-dependencies/opensearch/certs/root-ca.pem
OPENSEARCH_SKIP_TLS_VERIFY=false本地 demo 可临时 -k,但应用默认配置不能长期跳过 TLS 校验。共享环境要由 owner 分发 CA 或配置正式证书。
初始化脚本
#!/usr/bin/env bash
set -euo pipefail
: "${OPENSEARCH_URL:?missing OPENSEARCH_URL}"
: "${OPENSEARCH_USERNAME:?missing OPENSEARCH_USERNAME}"
: "${OPENSEARCH_PASSWORD:?missing OPENSEARCH_PASSWORD}"
: "${OPENSEARCH_INDEX:?missing OPENSEARCH_INDEX}"
curl --fail -k \
-u "${OPENSEARCH_USERNAME}:${OPENSEARCH_PASSWORD}" \
-X PUT "${OPENSEARCH_URL}/${OPENSEARCH_INDEX}" \
-H "Content-Type: application/json" \
-d @dev-dependencies/opensearch/index/article-v1.json如果脚本需要管理 index、template 或 ISM policy,用 setup 账号;运行时 app 账号只写读业务文档。
受控清理脚本
dry-run:
#!/usr/bin/env bash
set -euo pipefail
: "${OPENSEARCH_URL:?missing OPENSEARCH_URL}"
: "${OPENSEARCH_USERNAME:?missing OPENSEARCH_USERNAME}"
: "${OPENSEARCH_PASSWORD:?missing OPENSEARCH_PASSWORD}"
: "${OPENSEARCH_INDEX_PREFIX:?missing OPENSEARCH_INDEX_PREFIX}"
echo "target prefix: ${OPENSEARCH_INDEX_PREFIX}_"
curl --fail -k \
-u "${OPENSEARCH_USERNAME}:${OPENSEARCH_PASSWORD}" \
"${OPENSEARCH_URL}/_cat/indices/${OPENSEARCH_INDEX_PREFIX}_*?h=index"确认删除:
#!/usr/bin/env bash
set -euo pipefail
: "${OPENSEARCH_URL:?missing OPENSEARCH_URL}"
: "${OPENSEARCH_USERNAME:?missing OPENSEARCH_USERNAME}"
: "${OPENSEARCH_PASSWORD:?missing OPENSEARCH_PASSWORD}"
: "${OPENSEARCH_INDEX_PREFIX:?missing OPENSEARCH_INDEX_PREFIX}"
: "${CONFIRM_DELETE:?missing CONFIRM_DELETE}"
if [[ "${CONFIRM_DELETE}" != "${OPENSEARCH_INDEX_PREFIX}" ]]; then
echo "refuse to delete: CONFIRM_DELETE must equal ${OPENSEARCH_INDEX_PREFIX}" >&2
exit 1
fi
if [[ "${OPENSEARCH_INDEX_PREFIX}" == "*" || "${OPENSEARCH_INDEX_PREFIX}" == "_all" || ${#OPENSEARCH_INDEX_PREFIX} -lt 6 ]]; then
echo "refuse to delete: unsafe prefix ${OPENSEARCH_INDEX_PREFIX}" >&2
exit 1
fi
curl --fail -k \
-u "${OPENSEARCH_USERNAME}:${OPENSEARCH_PASSWORD}" \
-X DELETE "${OPENSEARCH_URL}/${OPENSEARCH_INDEX_PREFIX}_*"OpenSearch 解决的核心架构问题
OpenSearch 解决的是搜索和分析入口问题,不是关系数据库替代品。
| 诉求 | OpenSearch 提供 | 架构代价 |
|---|---|---|
| 全文搜索 | inverted index、scoring、query DSL | mapping 和 analyzer 要提前设计 |
| 近实时查询 | refresh 后可搜 | 不是写后强一致主库 |
| 横向扩展 | shard / replica / node | shard 规划和恢复窗口要治理 |
| 可视化探索 | OpenSearch Dashboards | Dashboard 权限和 Dev Tools 风险 |
| 生命周期治理 | ISM、rollover、delete、snapshot | 策略错误会过早删除或堆积数据 |
| 开源许可 | Apache 2.0 | 仍需看托管、商标、客户交付和合规 |
架构师要回答:它是开发依赖还是业务搜索主链路;数据能否从主库重建;查询是轻搜索、重聚合、日志分析还是向量检索;index 生命周期、snapshot、清理和恢复责任人是谁;是否真的需要 OpenSearch,还是数据库索引、Elasticsearch 或托管服务更合适。
主流生产架构
| 架构 | 适合 | 风险 | 验收重点 |
|---|---|---|---|
| 单节点开发 | 本地、CI、mapping 验证 | 无高可用,replica 会 yellow | 连接、index、清理 |
| 三节点基础集群 | 中小生产搜索 | shard 规划和恢复仍需治理 | health、replica、snapshot |
| 角色分离集群 | 写入、查询、Dashboard 压力变大 | 网络、证书、升级复杂 | cluster manager、data、ingest、coordinating |
| ISM 数据分层 | 日志、审计、事件 | 策略误删或堆积 | rollover、delete、snapshot |
| 托管服务 | 快速生产化 | 成本、插件、网络和版本边界 | 账号、SLA、退出方案 |
角色和节点
OpenSearch cluster 由多个 node 组成。cluster manager 负责 cluster state、选主和 shard 分配;data node 执行存储和查询;ingest node 处理 pipeline;coordinating node 汇总查询结果。节点不是越多越好,节点数会增加通信、分配、证书和升级复杂度。
shard、replica 和 segment
index 被拆成 primary shard,replica 提供冗余和读扩展。shard 底层由 Lucene segment 组成。shard 太大恢复慢,shard 太多拖累 cluster state、heap、文件句柄和调度。replica 提升可用性,也增加写入复制和存储成本。
refresh、translog 和近实时
OpenSearch 是近实时搜索。refresh=wait_for 适合开发验证,不是所有业务写入默认策略。业务如果要求写后强一致读,应先读主库或做业务补偿。
JVM、文件缓存和磁盘水位
heap 太大挤压文件缓存,太小会 GC 频繁或触发 circuit breaker。磁盘达到水位会影响 shard 分配,严重时触发写入 block。复制 data 目录不是可靠备份。
索引、mapping、template、alias 与 ISM
index 是搜索契约,不只是表名。
团队规则:
核心字段必须显式 mapping。动态字段只用于非核心实验。业务读写走 alias,不写死物理 index。
mapping 变更通过 v2 index、reindex、验证和 alias 切换完成。ISM policy 进入共享环境前必须评审删除和 rollover 条件。
alias 切换前同时记录旧 index、目标 index 和校验结果。切换失败就保持旧 alias;切换后出现查询或字段回归,则把 alias 原子切回旧 index。旧 index 至少保留到回滚窗口结束,不能在切换成功的同一任务里立即删除。
ISM 不是“保存后立刻执行”的定时器。策略按 index age、size 或文档数判断状态迁移,后台任务按间隔检查;达到阈值与真正 rollover 之间存在时间差。上线前至少用 Explain API 证明策略已经附着到预期 index:
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
"${OPENSEARCH_URL}/_plugins/_ism/explain/${OPENSEARCH_INDEX}?show_policy=true&pretty"证据要同时包含 policy_id、当前 state、action、失败信息和目标 rollover alias。状态为 red 时 ISM job 不运行;alias 不是 write index、索引名不符合递增序号规则或策略权限不足,也会让 rollover 停住。删除状态进入共享环境前,先在临时前缀缩短阈值演练,并确认快照成功早于删除动作。
查询、写入与性能诊断
查询慢先看 query 是否触发大分页、宽聚合、高亮或脚本;mapping 是否让 text 字段参与聚合;shard 是否热点;heap、breaker、thread pool 是否高压;是否处于 snapshot、merge 或 bulk 写入窗口。
写入慢先看 bulk 批大小和并发、refresh、replica、merge、磁盘 IO、routing 热点和客户端 item 级失败。
诊断入口:
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_cluster/health?pretty"
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_cat/nodes?v"
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_cat/shards?v"
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_nodes/stats/jvm,indices,thread_pool,fs?pretty"高可用、快照恢复与容量风险
| 状态 | 含义 | 开发环境判断 | 生产环境判断 |
|---|---|---|---|
| green | primary 和 replica 都分配 | 正常 | 正常但仍看资源 |
| yellow | primary 分配,replica 未分配 | 单节点常见 | 查副本、节点、分配规则 |
| red | primary 未分配 | 不应忽略 | 优先恢复数据或 snapshot |
Snapshot 才是备份。它从 primary shard 复制数据,进行中的新写入不一定属于同一个一致时间点;快照又是增量的,多个快照可能共享底层数据,所以删除仓库文件会破坏其他快照。创建、列出和删除都应走 Snapshot API。
在隔离集群准备好 repository 后,可以用“拍快照、改名恢复、校验文档”的闭环证明恢复能力。OPENSEARCH_SNAPSHOT_REPOSITORY 必须是 owner 已注册并授权的仓库,不能临时把生产仓库挂到开发机:
export OPENSEARCH_SNAPSHOT_REPOSITORY="your_project_dev_repo"
export OPENSEARCH_SNAPSHOT="article-v1-smoke"
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
-X PUT "${OPENSEARCH_URL}/_snapshot/${OPENSEARCH_SNAPSHOT_REPOSITORY}/${OPENSEARCH_SNAPSHOT}?wait_for_completion=true" \
-H "Content-Type: application/json" \
-d "{\"indices\":\"${OPENSEARCH_INDEX}\",\"include_global_state\":false,\"partial\":false}"
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
"${OPENSEARCH_URL}/_snapshot/${OPENSEARCH_SNAPSHOT_REPOSITORY}/${OPENSEARCH_SNAPSHOT}?pretty"
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
-X POST "${OPENSEARCH_URL}/_snapshot/${OPENSEARCH_SNAPSHOT_REPOSITORY}/${OPENSEARCH_SNAPSHOT}/_restore?wait_for_completion=true" \
-H "Content-Type: application/json" \
-d "{\"indices\":\"${OPENSEARCH_INDEX}\",\"include_global_state\":false,\"include_aliases\":false,\"rename_pattern\":\"(.+)\",\"rename_replacement\":\"restored-\$1\"}"
curl --fail -k \
-u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
"${OPENSEARCH_URL}/restored-${OPENSEARCH_INDEX}/_count?pretty"快照状态必须是 SUCCESS,恢复后的 _count、抽样文档、mapping 和业务校验值要与快照前一致。然后检查 alias、template、Security role 和 ISM policy,因为 include_global_state: false 不会替你重建所有集群级配置。恢复验证结束后只删除 restored-* 测试索引;快照保留与删除由仓库策略处理。
容量评审至少拆成四笔账:primary 数据量、replica 倍数、merge 与水位预留、快照仓库。粗略起点可以写成“可用数据盘 > primary 预计峰值 x (1 + replica 数) / 目标最高利用率”,再用真实压缩率、segment merge 峰值和恢复窗口修正。提高 replica 会增加存储与写入复制成本;减少 shard 能降低 heap 和文件句柄开销,却会放大单 shard 的恢复时间;托管服务还要把跨区流量、快照、热温节点、专用 cluster manager 和保留周期计入月度成本。没有采样数据时,不要拿单一经验值直接定生产 shard 数。
docker compose --env-file dev-dependencies/opensearch/.env \
-f dev-dependencies/opensearch/compose.yaml ps
docker compose --env-file dev-dependencies/opensearch/.env \
-f dev-dependencies/opensearch/compose.yaml logs --tail=120 opensearch
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_cluster/health?pretty"
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_cat/nodes?v"
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_cat/indices?v"重置个人本地:
docker compose --env-file dev-dependencies/opensearch/.env \
-f dev-dependencies/opensearch/compose.yaml down -v共享环境不能这么做。共享环境清理由 owner 走受控脚本、备份恢复或变更窗口。
新版容器不接受 admin/admin
现象:容器启动失败或 Dashboards 登录失败。
判断:
docker logs your-project-opensearch --tail=120原因:OpenSearch 2.12+ 新安装要求设置 OPENSEARCH_INITIAL_ADMIN_PASSWORD。
修复:在 .env 设置强密码;个人本地可删除旧 volume 重建;共享环境按 owner 的密码轮换流程处理。
再验证:
curl --fail -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" https://127.0.0.1:9201HTTP / HTTPS 误判
现象:curl http://127.0.0.1:9201 失败。
判断:
curl -v http://127.0.0.1:9201
curl -vk -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" https://127.0.0.1:9201原因:Security demo 默认 HTTPS。修复:开发验证用 https:// 和 -k,共享环境用受控 CA。
Dashboards 连不上 OpenSearch
判断:
docker logs your-project-opensearch-dashboards --tail=120
docker logs your-project-opensearch --tail=120常见原因:OPENSEARCH_HOSTS 协议不对、OpenSearch 未健康、密码或证书不匹配、Dashboards 和 OpenSearch 版本不一致。修复后打开 http://127.0.0.1:5602 验证。
旧 volume 让新密码不生效
现象:改了 .env 的 OPENSEARCH_INITIAL_ADMIN_PASSWORD,仍然登录失败。
判断:
docker volume ls
docker volume inspect your-project_opensearch-data原因:安全配置已经写入数据目录。个人环境可以 down -v,共享环境必须走密码轮换或 securityadmin / API 流程。
mapping 被脏数据污染
判断:
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" \
"${OPENSEARCH_URL}/${OPENSEARCH_INDEX}/_mapping?pretty"修复:开发环境删除 index 重建;共享环境新建 v2 index、reindex、验证、切 alias。
集群 yellow 或 red
判断:
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_cluster/health?pretty"
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_cat/shards?v"开发单节点可设 number_of_replicas: 0;生产先查分配原因,再决定扩容、恢复节点或从 snapshot 恢复。
bulk 写入出现 429
判断:
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_cat/thread_pool/write?v"
curl -k -u "admin:${OPENSEARCH_ADMIN_PASSWORD}" "${OPENSEARCH_URL}/_nodes/stats/jvm,indices,thread_pool,fs?pretty"修复:降低 bulk 批大小和并发,增加退避重试,记录 item 级失败,必要时调整 refresh 策略。
误连生产
判断:脚本是否打印 URL、cluster name、index prefix;账号是否默认只读;删除脚本是否需要 CONFIRM_DELETE;Dashboards 连接名是否包含环境和权限。
治理:生产连接不进入开发机默认 .env;生产普通账号默认只读;危险操作走受控脚本和变更流程。
OpenSearch 镜像拉取依赖 Docker Hub 或 Amazon ECR。企业网络里要确认代理、镜像仓库和证书:
docker pull opensearchproject/opensearch:3.7.0
docker pull opensearchproject/opensearch-dashboards:3.7.0NO_PROXY 至少包含本机:
NO_PROXY=127.0.0.1,localhost凭证规则:
.env.example 只放占位值,.env 不提交。admin 密码、API key、证书私钥、生产 CA 不进仓库。Dashboards 截图、终端录屏和日志粘贴要脱敏。
发现泄漏后轮换,不只是在 Git 里删除。生产连接默认只读,写入和删除走受控任务。
敏感信息自查:
rg -n "OPENSEARCH_INITIAL_ADMIN_PASSWORD|Authorization:|ApiKey|BEGIN .*PRIVATE KEY|admin:" .命中变量名不一定是泄漏,命中真实值、私钥、生产域名和未脱敏连接串才是风险。
项目模板必须保留:
dev-dependencies/opensearch/compose.yaml
dev-dependencies/opensearch/.env.example
dev-dependencies/opensearch/index/
dev-dependencies/opensearch/scripts/init-index.sh
dev-dependencies/opensearch/scripts/clean-index-dry-run.sh
dev-dependencies/opensearch/scripts/clean-index-confirmed.sh
docs/dependency-setup.md| 角色 | 负责 |
|---|---|
| 搜索 owner | 版本、证书、账号、snapshot、容量和升级 |
| 项目开发 | mapping、初始化脚本、最小验证和清理脚本 |
| 测试 | 共享环境数据准备和回归 |
| 运维 / 平台 | 生产部署、监控、告警、恢复、滚动升级 |
| 安全 / 合规 | 凭证、审计、客户交付和许可边界 |
清理策略:每个项目只能清理自己的 prefix;清理前必须 dry-run;删除必须要求 CONFIRM_DELETE 等于 prefix;共享环境清理记录到变更日志。
默认密码变化会拖垮新人接入
旧文章常写 admin/admin,OpenSearch 2.12 及以上的新安装要求 OPENSEARCH_INITIAL_ADMIN_PASSWORD。模板必须锁定实际版本和镜像 digest,.env.example 只保留强密码占位。
Security plugin 不是摆设
为了省事禁用安全插件或全员共享 admin,会让 Dashboards Dev Tools 拥有写入、删除、改 mapping 的能力。本地 demo 可以简化,团队共享环境必须按 role / index prefix 分权。
-k 只能定位问题
本地 demo 使用 -k 可以接受;项目模板要预留 CA 路径,生产和共享环境必须受控证书。
OpenSearch 不是 Elasticsearch 的透明替代
迁移前做 API、客户端、插件、mapping、snapshot、Dashboards saved objects 和性能回归。不要在团队模板里写“二者完全兼容”。
ISM 删除策略会真的删数据
ISM policy 进入共享环境前评审;删除动作要和 snapshot、保留周期、恢复演练绑定。
Snapshot 没恢复演练等于没有备份
至少按季度恢复到临时集群,记录耗时、数据校验、权限校验、alias 和回切方案。
磁盘水位会把可写服务变成只读服务
长期治理要看磁盘、水位、shard 分配、ISM、snapshot 保留和增长趋势,不靠临时删 index。
Dashboard 不是只读浏览器
生产 Dashboards 默认只读;Dev Tools 权限只给 owner;高危操作走脚本和审计。
安装后:
OpenSearch 版本已固定,不使用 latest。OpenSearch、Dashboards、客户端版本和镜像 digest 已记录。Docker Desktop 内存或 Linux vm.max_map_count 满足要求。
宿主端口只绑定 127.0.0.1。OPENSEARCH_INITIAL_ADMIN_PASSWORD 使用强密码,未进入仓库。curl -k -u admin 能返回 200。
Dashboards 能打开并连接本地 OpenSearch。
接入前:
.env.example 只有占位值。应用配置包含 URL、账号、密码、CA / TLS 策略和 index prefix。应用账号不是 admin。
index 使用显式 mapping。最小验证完成建 index、写入、查询、删除。共享环境权限按 prefix 隔离。
共享环境前:
owner、版本、证书、账号、角色和清理窗口明确。Dashboards 普通用户默认只读。index template、mapping、alias 和 ISM 边界已评审。
shard / replica 规划有数据量、查询量和恢复窗口依据。snapshot repository、保留周期、恢复演练和责任人已记录。磁盘水位、heap、GC、thread pool、bulk 失败率有观测入口。
清理脚本先 dry-run,并要求显式确认 prefix。OpenSearch 与 Elasticsearch 兼容边界已验证。
排障时:
先确认协议是 HTTP 还是 HTTPS。再确认 admin 密码、证书、端口和旧 volume。再看 Dashboards 配置和版本。
再看 mapping 是否被动态字段污染。再看 health、shard、磁盘水位、heap 和 thread pool。再看脚本是否指向错误环境或错误 index prefix。
