news 2026/10/26 15:45:54

Docker + CentOS 7 离线部署 ElasticSearch 单机版实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker + CentOS 7 离线部署 ElasticSearch 单机版实战

内网机房里那台 CentOS 7 服务器,网线插着但出不了公网,yum 源是本地镜像,连pip install都得走内部源。项目要做全文检索,技术选型会上定的是 ElasticSearch,要求单机部署、数据不能丢、重启后能自己起来。我第一次接这类活的时候想得很简单:下个 tar 包,解压,改配置,nohup一挂,完事。结果在那台机器上折腾了大半天,从 JDK 版本到vm.max_map_count,从目录权限到启动用户,一个接一个地冒出来。后来换成 Docker 方案,把镜像在能上网的机器上拉好、导出、拷进去导入,容器一条命令起来,反而清爽很多。

这篇就围绕docker + CentOS 7 + 离线 + ElasticSearch 单机版这个组合,把我实际趟过的流程、踩过的坑、以及每一步为什么要这么做的理由写清楚。适合手上正好有一台不通外网的内网机器、又需要快速搭一个可用 ES 实例的同行参考;对 Docker 刚上手、但已经能看懂基本命令的读者也友好,关键的坑我都会展开讲原因,而不是只丢一条命令给你抄。

1. 内网机器上装搜索服务,为什么我最后把 tar 包方案换成了容器

1.1 一次"解压即用"的幻想是怎么破灭的

ElasticSearch 官方确实提供 tar.gz 发行包,文档里也写着解压后bin/elasticsearch就能启动。听起来很美,但真正在内网机器上执行时,第一个问题就来了:发行包自带 JDK,但自带的 JDK 版本和你系统里已经装的那套大概率不是一个东西。CentOS 7 上很多人习惯性先yum install java-1.8.0-openjdk,然后发现 ES 7.x 之后的版本要求 JDK 11 及以上,环境变量JAVA_HOME一旦被系统那套抢走,启动直接报版本不兼容。你得去改jvm.options或者显式指定ES_JAVA_HOME,而这台机器上可能还有别的 Java 应用在跑,改环境变量又会牵连别人。

第二个问题是启动用户。ElasticSearch 出于安全考虑,禁止以 root 身份运行。内网机器上你登进去十有八九就是 root,于是得useradd一个专用账号,把整个安装目录chown过去,再用su - esuser切过去启动。看着也不算大事,但如果你还要配 systemd 开机自启,service 文件里的User=、Group=、LimitNOFILE、LimitMEMLOCK一个都不能少,写错了就是启动失败、日志里还看不出原因。

第三个问题是清理和回滚。tar 包方案是"散装"的:程序目录、配置目录、数据目录、日志目录、PID 文件,散落在你当时随手定的几个路径上。三个月后要升级版本,你得记住当初每一个路径,还得处理配置文件的手工合并。容器方案在这些事情上是有明确边界的——镜像里是只读的程序和默认配置,你只需要管好挂出来的那几个目录。

我并不是说 tar 包方案不能用,它在某些极端受限的环境(比如连 Docker 都不让装的机器)里仍是唯一选择。但只要机器上允许跑容器,我就会优先选容器,原因在下一小节展开。

1.2 容器方案在离线现场的三个实际好处

第一个好处是依赖被一次性封进镜像。ES 需要什么 JDK、需要哪些系统库、默认配置长什么样,全在镜像里定死了。你在能上网的机器上把镜像拉下来验证过能跑,导出成 tar 文件,拷到内网机器导入,跑起来的行为和验证时一模一样。这中间不存在"内网机器的 glibc 版本低了半档"这类问题,因为容器用的是镜像里的用户态环境。

第二个好处是启动参数即配置。离线环境最怕的是"配置散落各处、出了事找不到是谁改的"。用容器,端口映射、堆内存、单机模式、数据目录,全部集中在一条docker run命令或者一个 compose 文件里。这条命令本身就可以当成部署文档存档,下次重建的时候照着敲一遍就行。

第三个好处是资源边界清晰。单机版 ES 最怕的是把整台机器的内存吃干,导致同机器上其他服务被 OOM Killer 干掉。容器可以用-m限制内存上限,用ES_JAVA_OPTS限制 JVM 堆,两层限制叠起来,至少不会出现"ES 一启动,隔壁服务全躺下"的局面。

需要提前说清楚一点:容器化不会帮你解决内核参数问题。vm.max_map_count这类参数是宿主机层面的,容器共享宿主内核,该调还得调。这一点很多人第一次做会误解,以为进了容器就跟宿主机隔离了,实际上网络、内核参数、部分资源限制都是共享或半共享的。第 3 节会专门讲这块。

2. 断网环境下真正要搬的东西:镜像、运行时与系统基线

2.1 Docker 引擎本身的离线落地顺序

很多人默认内网机器上已经有 Docker,实际去一看,docker -v报 command not found。所以第一件事是确认宿主机有没有容器运行时。CentOS 7 上装 Docker 有两条路:走官方仓库的docker-ce,或者走系统自带的docker(版本很老,1.13 那种,不建议)。离线情况下只能走 rpm 包本地安装。

需要准备的最小包集合大概是这几个(版本号以你实际下载的为准,这里只列依赖关系):

包名作用备注
containerd.io容器运行时底座必须先装,版本要满足 docker-ce 的最低要求
docker-ce-clidocker 命令行和 docker-ce 版本保持一致
docker-ce引擎主体主包
docker-ce-rootless-extras可选不用 rootless 模式可以不装
docker-compose-plugin可选想用 compose 就得带上

在能上网的机器上把这些 rpm 下齐,可以用yumdownloader --resolve --destdir=./rpms docker-ce docker-ce-cli containerd.io,--resolve会把依赖一起拉下来。拷到内网机器后,进目录执行:

# 一次性装完当前目录下所有 rpm,--nodeps 谨慎使用,先不加 yum localinstall -y ./*.rpm # 装完确认 systemctl enable docker systemctl start docker docker version

yum localinstall比rpm -ivh好在它会尝试从已有的本地源里补齐缺失依赖。如果它报缺依赖而你又确定那些依赖已经装了,再考虑加--nodeps,但加之前一定先rpm -qa | grep 包名确认一遍,别图省事把依赖顺序搞乱。

装完之后还有一个容易忽略的点:CentOS 7 默认的存储驱动选择。如果/var/lib/docker所在分区是 xfs,要确认挂载参数里带ftype=1,否则 overlay2 驱动起不来,Docker 会退回到devicemapper,性能和稳定性都差一截。用xfs_info /var/lib/docker | grep ftype看一眼,输出ftype=1就是对的。这个参数是格式化时定的,事后改不了,只能重新格式化分区或者换一个目录挂载。所以在规划磁盘的时候就得多问一句。

2.2 镜像的导出、校验与导入

镜像这一步是整个离线流程里最"物理"的环节。我的习惯是在一台能出公网的机器上操作,尽量让这台机器的 Docker 版本和内网机器接近,减少意外。

# 1. 拉取指定版本,别用 latest,生产环境锁版本 docker pull elasticsearch:7.17.9 # 2. 看一眼镜像大小,心里有个数 docker images | grep elasticsearch # 3. 导出成 tar,加 gzip 压缩一下,体积能小不少 docker save elasticsearch:7.17.9 | gzip > es-7.17.9.tar.gz # 4. 算个校验值,拷贝过程中损坏能第一时间发现 sha256sum es-7.17.9.tar.gz > es-7.17.9.tar.gz.sha256

到了内网机器上,先校验再导入:

# 校验,输出 OK 才继续 sha256sum -c es-7.17.9.tar.gz.sha256 # 导入 gunzip -c es-7.17.9.tar.gz | docker load # 确认镜像在本地 docker images | grep elasticsearch

这里有两个实践细节值得说。一是为什么不用docker export。export导的是容器文件系统快照,会丢掉镜像的分层信息和元数据(比如ENTRYPOINT、环境变量、暴露端口),导进去之后你得到的只是一个"裸文件系统",还得自己拼启动命令。save导的是完整镜像,包括所有层和配置,导入后和原镜像等价。

二是版本锁定。elasticsearch:latest会随上游更新漂移,今天拉的 tomorrow 可能就变了。离线环境里你没法随时重拉,所以一开始就写死具体版本号,把版本号记在部署文档里。同理,如果后面还要装 Kibana 做可视化,Kibana 的版本必须和 ES 主版本严格一致,7.17.9 的 ES 配 7.17.x 的 Kibana,跨大版本基本连不上。

2.3 别忘了一起搬过去的小工具

纯离线环境下,很多平时觉得理所当然的工具其实没有。我在一次部署里就吃过亏:ES 起来了,想curl验证一下,结果那台最小化安装的 CentOS 7 上压根没有 curl。所以搬镜像的时候顺手把这几样也带上:

  • curl或者wget,验证接口用;
  • jq,解析 JSON 返回结果,比肉眼数括号强太多;
  • netstat(来自 net-tools)或者ss(来自 iproute),确认端口有没有监听;
  • vim或nano,总得有个能改配置的编辑器;
  • 如果打算用 compose 编排,docker-compose的二进制文件也一并拷过去,放到/usr/local/bin并chmod +x。

这些东西单个都不大,但临时找不到的时候会非常耽误事。我的做法是在内网机器上建一个/opt/offline-tools目录,把这次部署用到的东西全丢进去,写个 README 记下每个文件干什么用的。半年后别人接手,或者自己回来做升级,打开这个目录就一目了然。

3. 容器起来之前的系统层准备:内核参数与资源限制

3.1 vm.max_map_count 到底管什么

这是 ES 部署里被问得最多的一个参数。容器启动后日志里出现:

max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

然后容器退出。原因不复杂:ElasticSearch 底层用 Lucene,Lucene 把索引数据放在内存映射文件(mmap)里,一个索引分片的段文件会占用多个内存映射区域。Linux 内核对单个进程能创建的 vm area 数量有上限,默认 65530,索引一多就不够用了。vm.max_map_count调的就是这个上限。

临时改(重启失效):

sysctl -w vm.max_map_count=262144

永久改,写进配置文件,别直接改/etc/sysctl.conf(那个文件容易被别人覆盖),单独建一个:

cat > /etc/sysctl.d/99-elasticsearch.conf <<'EOF' vm.max_map_count = 262144 vm.overcommit_memory = 1 EOF sysctl --system sysctl vm.max_map_count

vm.overcommit_memory = 1是另一条常被一起加上的。它让内核在分配内存时更宽松,避免 ES 在堆外内存申请时被拒绝。这个参数有没有必要,取决于机器上其他负载的情况,如果你这台机器只跑 ES,加上没坏处;如果机器上还有别的内存敏感服务,得评估一下。

需要强调:这两个参数改的是宿主机,不是容器。容器共享宿主内核,你在容器里执行sysctl -w通常会报Read-only file system,因为/proc/sys在容器里默认是只读挂载的。有些教程会让你给容器加--privileged然后进去改,这是错误做法,改也只影响容器命名空间内的视图,宿主机层面该报错还是报错。正确的位置就是宿主机。

3.2 内存锁定、swap 与文件句柄

bootstrap.memory_lock: true这个配置的意思是让 ES 把自己的堆内存锁定在物理内存里,不被换出到 swap。为什么要锁?因为 JVM 一旦发生堆内存换页,GC 停顿时间会从毫秒级跳到秒级,对搜索服务来说这是灾难性的。

要让它生效,需要两处配合。宿主机上要关掉 swap,或者至少保证 ES 进程的内存不会被换出:

# 临时关闭 swapoff -a # 永久关闭,注释掉 /etc/fstab 里的 swap 行 sed -i '/swap/s/^/#/' /etc/fstab

容器侧要给它足够的 memlock 限额:

--ulimit memlock=-1:-1

同时如果启用了 memory_lock,/usr/share/elasticsearch/config/elasticsearch.yml里对应的配置项要打开。7.x 之后的镜像支持用环境变量覆盖一部分配置,但bootstrap.memory_lock这种嵌套键用环境变量写起来别扭,我更倾向于把配置文件挂进去,或者用-e传 JVM 参数、用挂载覆盖 yml。

文件句柄数也不能少。ES 每个分片都要打开一堆文件,默认的 1024 肯定不够:

--ulimit nofile=65536:65536

这个在docker run里给就行,宿主机的/etc/security/limits.conf也需要配一份,因为 Docker 守护进程自身的限制会传递下来:

* soft nofile 65536 * hard nofile 65536

改完limits.conf要重新登录才生效,验证用ulimit -n。

3.3 存储驱动与磁盘空间的最低调要求

ES 对磁盘的写入是比较重的,单机版尤其如此。规划的时候留足空间,我的经验值是:索引数据本身的 3 倍。一份给数据,一份给段合并过程中的临时空间,一份给快照备份。

磁盘水位线是 ES 自己的一套保护机制,默认配置是:

水位线默认阈值触发行为
low85%不再往该节点分配新分片
high90%尝试把分片迁走(单机版无处可迁,只能告警)
flood_stage95%对索引加只读锁,写入直接失败

单机版没有别的节点可以迁,所以一旦碰到 high 水位线,你会看到集群一直 yellow 或者 red,写入报blocked by: [FORBIDDEN/12/index read-only / allow delete (api)]。这时候就算你删了数据、磁盘降下来了,索引的只读锁也不会自动解除,得手工执行:

curl -XPUT "http://localhost:9200/_all/_settings" -H 'Content-Type: application/json' -d' { "index.blocks.read_only_allow_delete": null }'

这个坑我在一次日志量突增的场景里踩过,当时磁盘从 70% 一路冲到 96%,索引被锁,业务方那边写入全挂。事后我们把水位线调低了一点(比如 low 80、high 85),给自己留出反应时间,同时加了磁盘监控告警。阈值不是越低越好,调太低会导致正常的段合并也被限制,反而影响写入性能。

4. 启动参数的逐条拆解:单机模式、堆内存与数据落盘

4.1 discovery.type=single-node 背后的选举逻辑

ES 是一个分布式系统,即使你只跑一个节点,它内部也在走集群的发现和选举流程。默认配置下,节点会尝试去找cluster.initial_master_nodes或者组播发现其他节点,找不到就会反复重试,日志里刷一堆连接超时的报错,启动很慢,集群状态也可能一直不是 green。

discovery.type=single-node这个设置告诉 ES:跳过发现流程,我就是唯一节点,直接自举成 master。加上它之后,启动时间是秒级的,日志干净很多。

7.x 版本里如果不加这个参数,单纯跑单节点,通常会看到:

the default discovery settings are unsuitable for production use; at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured

然后容器退出。8.x 的行为类似,只是报错文案略有不同。所以单机版部署,这个参数基本是必填项,省不了。

顺带说一句,single-node模式下的集群健康状态通常是yellow而不是 green。原因不是配置错了,而是 ES 默认给每个索引建 1 个主分片 + 1 个副本分片,单节点上副本无处分配,状态就是 yellow。解决办法见第 6 节,把副本数改成 0 就绿了。这个 yellow 在很多监控系统里会触发告警,提前跟运维打个招呼,或者直接把索引模板里的副本数改掉。

4.2 堆内存怎么给,给多少

JVM 堆的设置有两个常见的错误方向:给太小,GC 频繁,查询慢;给太大,堆外内存和文件系统缓存被挤压,反而更慢。

基本规则是这样的:堆内存不超过物理内存的 50%,并且不超过 31GB。前半句是因为 Lucene 依赖操作系统的文件缓存来加速段文件读取,你把内存全给了堆,文件缓存就没了,查询性能会掉。后半句是因为 JVM 在堆小于约 32GB 时可以使用压缩对象指针(compressed oops),超过之后指针宽度翻倍,同样的堆能装的对象反而变少,是纯粹的负收益。

常见的配置对照:

物理内存建议堆大小说明
4 GB1g ~ 2g只适合测试和小数据量
8 GB2g ~ 4g小规模生产可用
16 GB6g ~ 8g中等规模
32 GB12g ~ 16g比较舒服的配置
64 GB24g ~ 31g上限卡在 31g

设置方式是通过环境变量传 JVM 参数:

-e "ES_JAVA_OPTS=-Xms4g -Xmx4g"

-Xms和-Xmx一定要设成一样大。设成不一样,JVM 会做堆的伸缩,伸缩过程本身有开销,而且容易在低峰期缩下来、高峰期再扩上去,触发 Full GC。固定大小是最省心的。

另外容器本身也限制一下内存上限,比如-m 8g,这样即使 JVM 那边算错了,也不会把宿主机吃穿。注意-m的值要大于堆大小加上堆外开销,堆 4g 的话,容器给 8g 是合理的,给 5g 就很容易被 OOM Killer 干掉。

4.3 目录挂载与权限:uid 1000 这个坎

ES 镜像内部默认用elasticsearch用户运行,这个用户的 uid 是 1000。你挂载宿主目录进去的时候,容器内进程要对这个目录有读写权限,这就要求宿主目录的属主是 uid 1000(或者权限足够宽)。

常见的错误做法是chmod 777一把梭,能用,但不安全也不优雅。正确的做法是:

# 建目录 mkdir -p /data/es/{data,logs,config} # 把属主改成 1000,和容器内用户对齐 chown -R 1000:1000 /data/es # 看一眼 ls -ln /data/es

如果宿主机上恰好已经有一个 uid 1000 的账号,你chown 1000:1000之后ls -l会显示成那个用户名,这个不影响使用,uid 对得上就行。

要挂载的目录一般是三个:

宿主机路径容器内路径用途
/data/es/data/usr/share/elasticsearch/data索引数据,最重要的一个
/data/es/logs/usr/share/elasticsearch/logs日志,排查问题靠它
/data/es/config/usr/share/elasticsearch/config配置覆盖

data目录是命根子,绝对不能只放在容器里。容器的可写层在容器删除后就没了,docker rm一下数据全丢。这个错误我见过不止一次,尤其是用docker run --rm习惯了的同学。数据目录一定要挂出来,而且这个目录要纳入日常备份。

配置文件目录挂载有个小坑:如果你把一个空目录挂到/usr/share/elasticsearch/config,容器内原有的默认配置文件会被"遮盖"掉,ES 找不到elasticsearch.yml、jvm.options、log4j2.properties,启动直接失败。正确做法是先把镜像里的默认配置拷出来再改:

# 先起一个临时容器把配置拷出来 docker run --rm --entrypoint cat elasticsearch:7.17.9 \ /usr/share/elasticsearch/config/elasticsearch.yml > /data/es/config/elasticsearch.yml # 其它几个文件同理 docker run --rm --entrypoint cat elasticsearch:7.17.9 \ /usr/share/elasticsearch/config/jvm.options > /data/es/config/jvm.options

或者更省事的办法:不挂整个 config 目录,只挂单个文件:

-v /data/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro

单文件挂载不会遮盖目录里其他文件,出问题的概率小很多。:ro表示只读,防止容器内进程意外改配置。

4.4 一份可以直接抄的启动脚本

把前面这些串起来,一份完整的启动命令长这样:

docker run -d \ --name es-single \ --restart=always \ -p 9200:9200 \ -p 9300:9300 \ -e "discovery.type=single-node" \ -e "ES_JAVA_OPTS=-Xms4g -Xmx4g" \ -e "TZ=Asia/Shanghai" \ -v /data/es/data:/usr/share/elasticsearch/data \ -v /data/es/logs:/usr/share/elasticsearch/logs \ -v /data/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro \ --ulimit nofile=65536:65536 \ --ulimit memlock=-1:-1 \ -m 8g \ elasticsearch:7.17.9

逐条解释一下关键项的理由:

  • --restart=always:机器重启后容器自动起来。内网机器很少做精细的进程管理,这个参数能省掉很多"重启后服务没了"的投诉。
  • -p 9300:9300:传输端口。单机版其实用不到,但如果后面要接 Kibana 或者别的节点,提前开出来省得改。安全考虑的话,单机版可以只开 9200。
  • -e TZ=Asia/Shanghai:容器默认是 UTC,日志时间对不上本地时间,排查问题时换算很烦。设时区能直接解决。
  • --ulimit两项对应前面讲的文件句柄和内存锁定。
  • -m 8g容器内存上限,前面解释过要和堆大小留出余量。

如果不想每次都敲这么长的命令,可以写成docker-compose.yml,但离线环境下需要先确认 compose 插件已经装好。我个人的偏好是:单节点就用docker run,把命令存成一个start-es.sh脚本放在/opt/es/下面,简单直接;要编排多个服务(ES + Kibana + 采集器)再用 compose。

5. 起不来看日志:离线现场最常卡住的几个环节

5.1 容器反复重启的排查顺序

容器起不来的时候,docker ps看不到它,docker ps -a能看到状态是Restarting或者Exited (1)。这时候按下面的顺序查,效率最高:

第一步,看日志:

docker logs --tail 100 es-single

--tail 100只看最后 100 行,ES 的启动日志很长,全刷出来反而不好找重点。想持续观察就加-f。

第二步,判断是配置类错误还是资源类错误。配置类错误日志里通常有明确的关键词,比如max virtual memory areas、unable to lock JVM memory、failed to obtain node locks、Permission denied。资源类错误则可能是 OOM,用dmesg | grep -i oom能看到内核的 OOM Killer 记录。

第三步,如果日志里什么都没有,容器秒退,大概率是入口脚本就挂了。用覆盖入口的方式进容器里看:

docker run --rm -it --entrypoint bash elasticsearch:7.17.9

进去之后手动执行bin/elasticsearch,前台跑,报错直接打屏幕上,比翻日志快。

第四步,如果是数据目录的问题,常见的是failed to obtain node locks,意思是上一个进程的锁文件还在。这个锁是data/nodes/0/node.lock,正常情况下进程退出会释放,异常退出会残留。确认没有 ES 进程在跑之后,删掉这个 lock 文件即可。但要注意,删之前一定确认进程真的没了,ps aux | grep elasticsearch看一眼,别把正在写的节点搞坏。

5.2 bootstrap checks 报错与对应关系表

ES 启动时会做一系列自检,叫 bootstrap checks,不通过就退出。这是刻意设计的,宁可起不来也不让你在错误配置下跑生产。常见的几条对照如下:

报错关键词含义处理方式
max virtual memory areas ... too lowvm.max_map_count 太小宿主机 sysctl 调到 262144
max file descriptors [4096] ... too low文件句柄太少ulimit nofile 调到 65536
memory locking requested ... cannot lock内存锁定失败关 swap,加 ulimit memlock=-1:-1
max number of threads ... too low线程数不够ulimit nproc 调到 4096
system call filters failed系统调用过滤失败较老内核上偶发,检查 seccomp 配置
max size virtual memory ... too low虚拟内存限制ulimit as 设为 unlimited

这张表建议直接存进部署文档。离线现场没法上网搜,有个对照表能省掉大量试错时间。

需要单独说一下system call filters failed这条。在某些定制化内核或者容器环境下,seccomp 的默认策略会导致 ES 的系统调用过滤自检失败。有两种处理方向:调内核/运行时配置让它支持,或者在确认环境可控的前提下,通过bootstrap.system_call_filter=false关掉这项检查。后者是有安全折衷的,只建议在完全内网、风险可控的单机测试环境里用,生产环境还是想办法把底层配好。

5.3 8.x 版本的安全配置差异

如果你用的不是 7.17 而是 8.x 系列,有一个大变化必须知道:8.x 默认开启安全功能。具体表现是首次启动会生成证书、要求密码、HTTP 接口走 HTTPS,curl http://localhost:9200直接拒绝。

如果你只是要一个内网单机版的搜索服务,不需要这套安全机制,可以在启动时关掉:

-e "xpack.security.enabled=false" \ -e "xpack.security.http.ssl.enabled=false" \ -e "xpack.security.transport.ssl.enabled=false"

这三个环境变量一起给,8.x 的行为就接近 7.x 了。如果确实需要开启安全(比如这个服务要暴露给多个部门用),那就得走完整的证书生成流程,离线环境下没网下载工具,需要用镜像里自带的elasticsearch-certutil来做:

# 生成一个基础证书包 docker exec -it es-single bin/elasticsearch-certutil ca docker exec -it es-single bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12

生成的文件在容器内的/usr/share/elasticsearch/config下,记得它挂载出来的是宿主机目录,拷出来备份好。这一步在离线环境里比较繁琐,如果对安全没有硬性要求,我的建议是先用关掉安全的方式把服务跑通,验证业务逻辑,安全加固作为第二阶段单独做。别一上来就卡在证书上,把整个部署节奏拖住。

另外 8.x 还有一个变化:默认不再自带 JDK 之外的兼容层,某些老版本客户端可能连不上。如果你的业务方用的是很老的 Java 客户端,升级前先确认客户端版本。

6. 服务跑起来之后:健康校验、分片设置与日常维护

6.1 三层验证:进程、端口、集群健康

容器状态是Up不代表服务真的可用。我有一次遇到容器一直 Up,但业务方连不上,最后发现是 ES 进程还在初始化,端口监听了但集群状态是 red,所有查询都超时。所以验证要分层做。

第一层,看容器和进程:

docker ps | grep es-single docker exec es-single ps -ef | grep elasticsearch

第二层,看端口:

ss -lntp | grep 9200

宿主机上能看到 9200 在监听,说明端口映射是通的。

第三层,看接口和集群状态:

# 基础信息,能返回就说明 HTTP 服务正常 curl -s http://localhost:9200 | jq . # 集群健康 curl -s "http://localhost:9200/_cluster/health?pretty" # 节点列表 curl -s "http://localhost:9200/_cat/nodes?v"

_cluster/health返回的status有三个值:green 全部正常,yellow 主分片可用但副本未分配,red 有主分片不可用。单机版看到 yellow 是正常的(副本没地方放),看到 red 就说明有分片出问题了,得去看_cat/indices?v找出是哪个索引。

再往下一层,做一次真实的写入和查询验证。这一步很多人省掉,结果接口通了但索引写不进去,业务上线才发现:

# 写一条 curl -XPOST "http://localhost:9200/test-index/_doc" \ -H 'Content-Type: application/json' \ -d '{"msg":"hello offline es","ts":"2024-01-01T00:00:00"}' # 查一下 curl -s "http://localhost:9200/test-index/_search?pretty" # 验证完删掉 curl -XDELETE "http://localhost:9200/test-index"

这条链路走通,才算部署完成。

6.2 单机版必须改的副本数

前面提过,默认副本数是 1,单节点上副本分不到地方,集群一直 yellow。改法有两种,看你的使用场景。

如果只是临时验证,可以直接改现有索引:

curl -XPUT "http://localhost:9200/_all/_settings" \ -H 'Content-Type: application/json' \ -d '{"index":{"number_of_replicas":0}}'

如果是长期部署,建议建一个索引模板,让以后新建的索引自动是 0 副本:

curl -XPUT "http://localhost:9200/_index_template/single-node-template" \ -H 'Content-Type: application/json' \ -d' { "index_patterns": ["*"], "template": { "settings": { "number_of_replicas": 0, "number_of_shards": 1 } } }'

number_of_shards也建议显式设成 1。ES 7.x 之后默认是 1 个主分片(6.x 是 5 个),但显式写出来能避免版本差异带来的意外。单机版分片多了没有好处,每个分片都有固定开销,分片太多反而拖慢查询。

还有一点:分片数一旦创建就不能改(只能 reindex),副本数可以随时改。所以分片数在建模阶段就要想清楚。单机版、数据量在千万级以下的,1 个主分片通常够用;如果预计数据量很大,可以按"单分片 10GB~50GB"的经验值来估。

6.3 备份、日志轮转与升级路径

离线环境里,备份的重要性比在线环境还高,因为出了问题你没法临时下载工具救场。ES 的备份有几种做法,单机版最实用的是文件系统级冷备:

# 停容器,保证数据一致 docker stop es-single # 打包数据目录 tar -czf /backup/es-data-$(date +%Y%m%d).tar.gz -C /data/es data # 起容器 docker start es-single

停容器再备份是为了保证数据文件的一致性,热备需要走 snapshot API,配置仓库路径,相对复杂。如果业务不能停,那就得用 snapshot:

# 先在配置里注册一个备份仓库路径 # path.repo: ["/usr/share/elasticsearch/backup"] curl -XPUT "http://localhost:9200/_snapshot/backup_repo" \ -H 'Content-Type: application/json' \ -d '{"type":"fs","settings":{"location":"/usr/share/elasticsearch/backup"}}' # 创建快照 curl -XPUT "http://localhost:9200/_snapshot/backup_repo/snapshot_20240101?wait_for_completion=true"

日志方面,ES 自己的日志写在logs/目录下,按天滚动,默认保留 7 天。这个可以通过挂载的log4j2.properties调整。容器层面还有docker logs的输出,默认是 json-file 驱动,会一直增长,需要配一下限制:

# 在 /etc/docker/daemon.json 里加 { "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }

改完systemctl restart docker。这个配置对之后新建的容器生效,已有容器需要重建。

升级路径这块,离线环境的升级比在线麻烦,因为你得重复一遍"导出 - 拷贝 - 导入"的流程。ES 的跨大版本升级不能直接换镜像了事,7.x 升 8.x 需要走升级助手检查、重新索引等步骤。我的建议是:单机版尽量锁死版本,把升级当成一个独立项目来做,而不是日常维护的一部分。升级前先用 snapshot 做完整备份,在新版本上用同样的数据验证查询结果一致,再切换流量。

最后分享一个我在内网部署时养成的小习惯:每次部署完,把这次用到的镜像 tag、启动命令、sysctl 配置、目录结构、验证命令,全部写进一个DEPLOY.md放在/opt/es/下。离线环境最怕的是"当时那个人走了,机器还在跑,没人知道它是怎么装起来的"。一份几十行的文档,能省掉后来者几天的摸索。我见过太多内网服务因为缺少这份文档,最后只能推倒重来,数据还得从备份里捞,代价远比写文档大得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 15:45:54

OpenMontage:开源视频智能协作Agent框架解析

1. OpenMontage 是什么&#xff1a;一个被严重低估的开源视频智能协作系统OpenMontage 这个名字乍一听像某个电影剪辑软件的副产品&#xff0c;或者某家初创公司的营销术语——但实际接触过的人会立刻意识到&#xff0c;它根本不是“又一个AI视频工具”&#xff0c;而是一套面向…

作者头像 李华
网站建设 2026/10/26 15:44:52

text-to-cad本质是工程语义到STEP的可计算翻译

1. 这不是“文字变模型”的魔法&#xff0c;而是工程语言的翻译革命 “text-to-cad”这个词最近在工程师、产品设计师和制造业技术群里刷屏&#xff0c;但很多人点开一看——咦&#xff1f;怎么没有一键生成齿轮的按钮&#xff1f;没有输入“一个带M6螺纹孔的铝制支架”就弹出完…

作者头像 李华
网站建设 2026/10/26 15:44:47

RUST cargo工具链使用指南与工程实践

1. 初识RUST与cargo工具链第一次接触RUST语言时&#xff0c;最让我惊讶的不是其独特的所有权系统&#xff0c;而是那个叫cargo的构建工具。作为一个从C转过来的开发者&#xff0c;我早已习惯了手动管理依赖和构建过程的繁琐。而cargo的出现&#xff0c;就像给工地送来了全套自动…

作者头像 李华
网站建设 2026/10/26 15:43:50

OpenMontage:面向视频工业化生产的可验证任务编排引擎

1. OpenMontage 不是“另一个 AI Agent 框架”&#xff0c;而是一套视频生产流水线的底层编排引擎OpenMontage 这个名字一出来&#xff0c;很多人第一反应是&#xff1a;“又一个 LangChain 或 CrewAI 的 Rust 翻版&#xff1f;”——我最初也这么想。直到花三天时间把它的 Git…

作者头像 李华
网站建设 2026/10/26 15:42:43

端到端测试实战:从设计思路到落地优化的完整指南

1. 从“e2e”说起&#xff1a;为什么端到端测试是质量保障的最后一公里第一次看到“e2e”这个词&#xff0c;很多人会愣一下——它其实是 end-to-end 的缩写&#xff0c;中文叫“端到端”。在软件研发的语境里&#xff0c;e2e 测试指的是从用户真实使用路径出发&#xff0c;把整…

作者头像 李华
网站建设 2026/10/26 15:42:43

Agent-Reach:基于CLI的AI Agent编排框架实战指南

1. 项目缘起与核心定位Agent-Reach 这个名字第一次出现在我视野里的时候&#xff0c;我正被一堆零散的 AI Agent 脚本折磨得够呛。手头有五六个不同场景的小助手&#xff0c;有的负责抓取信息&#xff0c;有的负责整理文档&#xff0c;有的负责定时提醒&#xff0c;每个都是独立…

作者头像 李华