CentOS 7 离线部署 Solr Cloud 5.5.1 集群实战指南
1. 项目背景与整体设计思路1.1 为什么要在离线环境折腾 Solr Cloud有些部署场景天生就跟互联网绝缘——内网机房、专有网络、安全隔离区这些地方连个 yum 源都摸不到更别提从公网拉取依赖包了。我这次接到的需求就是在 CentOS 7 上离线搭建一套 Solr Cloud 集群版本锁定在 5.5.1。为什么是 5.5.1 这么个古董版本因为业务系统里已经有一套基于这个版本开发的检索模块版本必须对齐否则索引格式和 API 行为对不上迁移过去就是一堆坑。Solr Cloud 跟单机版 Solr 最大的区别在于它依赖 ZooKeeper 做集群协调。单机 Solr 你解压就能跑但 Solr Cloud 需要 ZooKeeper 来管理集群状态、配置文件和分片路由信息。这就意味着离线环境下你不仅要准备 Solr 的安装包还得把 ZooKeeper 的包、JDK 的包全部提前下载好一并搬到目标机器上。这套方案适合谁参考我觉得三类人最需要一是做内网系统部署的运维工程师二是需要在隔离环境做检索服务验证的开发者三是想理解 Solr Cloud 底层协调机制的技术爱好者。哪怕你用的不是 5.5.1整个离线部署的思路和踩坑经验是通用的。1.2 离线部署的核心难点拆解离线部署跟在线部署的本质区别就一个字缺。缺依赖、缺源、缺网络。在线环境下yum install一条命令搞定的事离线环境你得手动把每一个 rpm 包找出来还得处理包之间的依赖关系。Java 环境更是如此CentOS 7 自带的 OpenJDK 版本往往跟 Solr 5.5.1 要求的 JDK 版本不匹配。我梳理了一下离线搭建 Solr Cloud 5.5.1 主要面临这几个难点JDK 版本匹配Solr 5.5.1 官方推荐 JDK 7 或 JDK 8CentOS 7 默认可能装的是 OpenJDK 8但有些精简版系统连 JDK 都没装。离线环境下你得自己准备 JDK 的 tar.gz 包。ZooKeeper 依赖Solr Cloud 需要 ZooKeeper 3.4.x 系列5.5.1 版本的 Solr 内置了一个 ZooKeeper 启动脚本但生产环境建议独立部署 ZooKeeper 集群。依赖包缺失Solr 运行需要一些基础库比如lsof、unzip、nc等工具离线环境下这些可能都没有。配置文件上传Solr Cloud 的配置集需要通过zkcli上传到 ZooKeeper这个过程在离线环境下需要确保所有路径和权限正确。注意离线环境部署前务必在一台有网络的同版本 CentOS 7 机器上把所有依赖包下载齐全包括 rpm 包和 tar.gz 包否则到了目标机器上发现缺东西来回拷贝非常折腾。1.3 整体架构与组件选型这套 Solr Cloud 集群我采用的架构是3 个 ZooKeeper 节点 2 个 Solr 节点。为什么是 3 个 ZooKeeper因为 ZooKeeper 集群需要奇数个节点来保证选举正常3 个节点允许挂掉 1 个而不影响集群可用性。Solr 节点 2 个是最小集群配置能体现分布式特性又不会占用太多资源。组件版本清单如下组件版本用途CentOS7.x操作系统JDK1.8.0_181Java 运行环境ZooKeeper3.4.10集群协调服务Solr5.5.1检索服务为什么 ZooKeeper 选 3.4.10因为 Solr 5.5.1 官方文档明确推荐 ZooKeeper 3.4.x 系列3.4.10 是该系列比较稳定的版本跟 Solr 5.5.1 配合经过大量验证。JDK 选 1.8.0_181 是因为这个版本在 CentOS 7 上兼容性最好且 Solr 5.5.1 对 JDK 8 支持完善。2. 离线环境准备与依赖处理2.1 离线包清单与获取方式离线部署的第一步是在一台有网络的机器上把所有需要的包下载下来。我列了一份完整的清单你可以直接照着准备基础工具类 rpm 包CentOS 7 最小化安装可能缺失lsofSolr 启动脚本会用到检查端口占用unzip解压 Solr 的 zip 包nc网络连通性测试vim编辑配置文件net-tools提供netstat命令这些 rpm 包可以从 CentOS 7 的 ISO 镜像里提取路径是Packages/目录下。如果你手头没有 ISO也可以在有网的 CentOS 7 机器上用yumdownloader下载yumdownloader --resolve --destdir/tmp/rpms lsof unzip nc vim net-tools--resolve参数会自动把依赖包也下载下来这个很关键否则到了离线机器上装的时候会报依赖缺失。JDK 包去 Oracle 官网或者 OpenJDK 社区下载jdk-8u181-linux-x64.tar.gz。注意要选 tar.gz 格式不要选 rpm 格式因为 tar.gz 解压即用不依赖系统的包管理。ZooKeeper 包Apache 官网下载zookeeper-3.4.10.tar.gz。Solr 包Apache 官网下载solr-5.5.1.zip。注意 Solr 5.5.1 提供的是 zip 包不是 tar.gz所以目标机器上必须有unzip命令。实操心得我习惯把所有离线包放在一个目录里比如/opt/offline-packages/然后用tar打包成一个文件传到目标机器。这样比一个个 scp 快得多也不容易漏文件。2.2 系统环境初始化配置目标机器是 CentOS 7 最小化安装拿到机器后先做一轮基础配置。这些配置在离线环境下同样重要因为很多默认设置会影响 Solr 和 ZooKeeper 的运行。关闭防火墙和 SELinux内网环境通常不需要防火墙而且 Solr 和 ZooKeeper 之间需要大量端口通信开着防火墙会各种连不上。systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i s/SELINUXenforcing/SELINUXdisabled/g /etc/selinux/config配置主机名和 hostsSolr Cloud 集群节点之间通过主机名通信所以每台机器的主机名和 hosts 必须配好。假设三台机器分别叫zk-node1、zk-node2、zk-node3Solr 节点叫solr-node1、solr-node2。hostnamectl set-hostname zk-node1 cat /etc/hosts EOF 192.168.1.101 zk-node1 192.168.1.102 zk-node2 192.168.1.103 zk-node3 192.168.1.104 solr-node1 192.168.1.105 solr-node2 EOF调整系统参数ZooKeeper 和 Solr 都需要较多的文件句柄和内存映射数默认值往往不够。cat /etc/security/limits.conf EOF * soft nofile 65536 * hard nofile 65536 * soft nproc 65536 * hard nproc 65536 EOF创建专用用户不建议用 root 跑 Solr 和 ZooKeeper创建一个专用用户更安全。useradd solr passwd solr2.3 离线安装 JDK 与基础工具先把基础 rpm 包装上。进入存放 rpm 包的目录执行rpm -ivh *.rpm --nodeps --force--nodeps和--force是为了避免因为依赖检查失败而装不上离线环境下这些包通常都是匹配的但有时候 rpm 数据库状态不对会误报依赖问题。接下来安装 JDK。把jdk-8u181-linux-x64.tar.gz解压到/usr/local/tar -zxvf jdk-8u181-linux-x64.tar.gz -C /usr/local/ mv /usr/local/jdk1.8.0_181 /usr/local/java配置环境变量编辑/etc/profile在末尾追加export JAVA_HOME/usr/local/java export JRE_HOME$JAVA_HOME/jre export CLASSPATH.:$JAVA_HOME/lib:$JRE_HOME/lib export PATH$JAVA_HOME/bin:$PATH然后source /etc/profile让配置生效用java -version验证。如果输出java version 1.8.0_181就说明 JDK 装好了。注意有些 CentOS 7 系统自带 OpenJDKjava -version可能显示的是系统自带的版本。这时候要检查which java的路径确保指向/usr/local/java/bin/java。如果不对可能需要把系统自带的 Java 软链接替换掉或者调整 PATH 顺序。3. ZooKeeper 集群离线部署实操3.1 ZooKeeper 安装与配置ZooKeeper 是 Solr Cloud 的大脑负责管理集群元数据、配置文件和分片状态。我选择在三台独立机器上部署 ZooKeeper而不是用 Solr 内置的嵌入式 ZooKeeper因为独立部署更稳定也更容易排查问题。在三台 ZooKeeper 节点上分别执行tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/ mv /opt/zookeeper-3.4.10 /opt/zookeeper mkdir -p /opt/zookeeper/data mkdir -p /opt/zookeeper/logs创建 ZooKeeper 配置文件/opt/zookeeper/conf/zoo.cfgtickTime2000 initLimit10 syncLimit5 dataDir/opt/zookeeper/data dataLogDir/opt/zookeeper/logs clientPort2181 server.1zk-node1:2888:3888 server.2zk-node2:2888:3888 server.3zk-node3:2888:3888这里解释几个关键参数tickTime2000ZooKeeper 的基本时间单元单位毫秒。2000ms 是官方推荐值表示心跳间隔。initLimit10Follower 节点与 Leader 节点建立连接的超时时间是tickTime的倍数10 表示 20 秒。syncLimit5Follower 与 Leader 之间同步数据的超时时间5 表示 10 秒。server.X定义集群节点格式是主机名:通信端口:选举端口。2888 是数据同步端口3888 是 Leader 选举端口。然后在每台机器的dataDir目录下创建myid文件内容分别是 1、2、3# zk-node1 上执行 echo 1 /opt/zookeeper/data/myid # zk-node2 上执行 echo 2 /opt/zookeeper/data/myid # zk-node3 上执行 echo 3 /opt/zookeeper/data/myidmyid文件是 ZooKeeper 集群识别节点的唯一标识必须跟zoo.cfg里的server.X编号对应否则集群起不来。3.2 ZooKeeper 集群启动与验证在三台机器上分别启动 ZooKeeper/opt/zookeeper/bin/zkServer.sh start启动后检查状态/opt/zookeeper/bin/zkServer.sh status正常情况下一台会显示Mode: leader另外两台显示Mode: follower。如果三台都显示standalone说明集群没组起来通常是myid文件或者zoo.cfg配置有问题。我踩过的一个坑zoo.cfg里的主机名必须跟/etc/hosts里配置的一致而且每台机器都要能 ping 通其他节点的主机名。有一次我 hosts 文件里写的是 IP 地址但zoo.cfg里写的是主机名结果集群一直起不来排查了半天才发现是解析问题。验证 ZooKeeper 集群是否正常工作可以用zkCli.sh连接/opt/zookeeper/bin/zkCli.sh -server zk-node1:2181连接成功后执行ls /能看到根节点下的内容说明 ZooKeeper 工作正常。Solr Cloud 启动后会在 ZooKeeper 里创建/solr节点到时候你可以用ls /solr查看集群状态。实操心得ZooKeeper 的日志在logs/目录下如果启动失败先看zookeeper.out文件。常见错误包括端口被占用、myid文件缺失、主机名解析失败。另外ZooKeeper 对时间同步很敏感集群节点之间时间差不能太大建议配置 NTP 或者手动同步时间。3.3 ZooKeeper 开机自启配置生产环境需要 ZooKeeper 开机自启用 systemd 管理最方便。创建/etc/systemd/system/zookeeper.service[Unit] DescriptionZooKeeper Service Afternetwork.target [Service] Typeforking Usersolr Groupsolr ExecStart/opt/zookeeper/bin/zkServer.sh start ExecStop/opt/zookeeper/bin/zkServer.sh stop ExecReload/opt/zookeeper/bin/zkServer.sh restart Restarton-failure [Install] WantedBymulti-user.target然后执行systemctl daemon-reload systemctl enable zookeeper systemctl start zookeeper这样 ZooKeeper 就会随系统启动自动运行。注意User和Group要设置成之前创建的solr用户同时确保/opt/zookeeper目录的属主是solrchown -R solr:solr /opt/zookeeper4. Solr Cloud 集群离线部署实操4.1 Solr 安装与目录规划Solr 节点我部署在两台机器上分别是solr-node1和solr-node2。先把 Solr 的 zip 包解压unzip solr-5.5.1.zip -d /opt/ mv /opt/solr-5.5.1 /opt/solr chown -R solr:solr /opt/solrSolr 5.5.1 的目录结构跟 4.x 版本有较大变化主要目录说明目录用途bin/启动脚本包括solr和solr.in.shserver/核心运行目录包含solr.xml、solr-webapp等server/solr/默认的 Solr Home 目录存放核心配置和数据example/示例配置和zkcli工具dist/编译好的 jar 包Solr Cloud 模式下Solr 的配置集不放在本地server/solr/目录而是上传到 ZooKeeper 统一管理。本地server/solr/目录主要存放solr.xml这个全局配置文件。4.2 Solr 环境变量与启动参数配置编辑/opt/solr/bin/solr.in.sh这是 Solr 的启动参数配置文件。关键配置项SOLR_HEAP4g ZK_HOSTzk-node1:2181,zk-node2:2181,zk-node3:2181 SOLR_HOSTsolr-node1 SOLR_PORT8983SOLR_HEAP4gJVM 堆内存大小。根据机器内存调整一般建议不超过物理内存的一半。我这两台机器是 16G 内存给 4G 堆足够。ZK_HOSTZooKeeper 集群地址多个节点用逗号分隔。Solr 启动时会连接这个地址把集群信息注册到 ZooKeeper。SOLR_HOST当前 Solr 节点的主机名必须跟/etc/hosts里配置的一致。这个参数在 Solr Cloud 模式下非常重要因为 ZooKeeper 里记录的是这个主机名其他节点通过它来通信。SOLR_PORT8983Solr 的 HTTP 端口默认 8983。注意SOLR_HOST如果配错集群节点之间会互相找不到。我见过有人配成localhost结果 Solr 启动后注册到 ZooKeeper 的地址是localhost:8983其他节点根本连不上。一定要用真实的主机名或 IP。4.3 启动 Solr 并接入 ZooKeeper 集群在两台 Solr 节点上分别启动 Solrsu - solr /opt/solr/bin/solr start -c -z zk-node1:2181,zk-node2:2181,zk-node3:2181参数说明-c表示以 Cloud 模式启动这是 Solr Cloud 的关键参数。-z指定 ZooKeeper 连接地址。虽然solr.in.sh里已经配了ZK_HOST但命令行再指定一次更保险。启动后检查状态/opt/solr/bin/solr status如果输出显示Found 1 Solr nodes并且状态是running说明 Solr 进程起来了。但这时候还不算完需要确认 Solr 是否成功注册到 ZooKeeper。用zkCli.sh连接 ZooKeeper查看/solr/live_nodes节点/opt/zookeeper/bin/zkCli.sh -server zk-node1:2181 ls /solr/live_nodes正常情况下应该能看到两个 Solr 节点的地址格式类似solr-node1:8983_solr和solr-node2:8983_solr。如果只看到一个或者一个都没有说明 Solr 没有成功注册需要检查SOLR_HOST配置和网络连通性。4.4 上传配置集到 ZooKeeperSolr Cloud 的配置集ConfigSet需要上传到 ZooKeeper所有节点共享同一份配置。Solr 5.5.1 提供了zkcli.sh工具位于server/scripts/cloud-scripts/目录下。假设我们有一个名为mycore的配置集存放在/opt/solr/server/solr/configsets/mycore/conf/目录下。上传命令cd /opt/solr ./server/scripts/cloud-scripts/zkcli.sh -zkhost zk-node1:2181,zk-node2:2181,zk-node3:2181 -cmd upconfig -confdir /opt/solr/server/solr/configsets/mycore/conf -confname mycore参数解释-zkhostZooKeeper 集群地址。-cmd upconfig上传配置集的命令。-confdir本地配置集目录必须包含solrconfig.xml和schema.xml等核心文件。-confname配置集在 ZooKeeper 里的名称创建 Collection 时会引用这个名字。上传成功后可以在 ZooKeeper 里验证ls /configs/mycore应该能看到solrconfig.xml、schema.xml等文件。实操心得zkcli.sh上传配置集时如果-confdir路径末尾多了斜杠有时候会导致上传的配置集名称异常。建议路径末尾不要加斜杠。另外如果配置集里有中文内容确保文件编码是 UTF-8否则上传后可能出现乱码。4.5 创建 Collection 并验证集群配置集上传完成后就可以创建 Collection 了。Solr 提供了 Collections API通过 HTTP 请求操作curl http://solr-node1:8983/solr/admin/collections?actionCREATEnamemycollectionnumShards2replicationFactor2collection.configNamemycore参数说明namemycollectionCollection 名称。numShards2分片数量。2 个分片可以分布在两个 Solr 节点上实现负载均衡。replicationFactor2每个分片的副本数。2 表示每个分片有一个主副本和一个从副本提供高可用。collection.configNamemycore使用的配置集名称必须跟之前上传到 ZooKeeper 的名称一致。创建成功后返回 JSON 会显示success:true。然后验证 Collection 状态curl http://solr-node1:8983/solr/admin/collections?actionCLUSTERSTATUSnamemycollection返回的 JSON 里会显示分片和副本的分布情况。正常情况下两个分片会分别落在两个 Solr 节点上每个分片有两个副本。最后做一次索引和查询测试确认集群真正可用# 索引一条测试数据 curl http://solr-node1:8983/solr/mycollection/update?committrue -H Content-Type: application/json -d [{id:1,title:test document}] # 查询数据 curl http://solr-node1:8983/solr/mycollection/select?q*:*如果查询结果里能看到刚才索引的文档说明整个 Solr Cloud 集群工作正常。5. 常见问题与排查技巧实录5.1 Solr 启动失败排查思路Solr 启动失败是最常见的问题排查思路可以按这个顺序来第一步看日志。Solr 的日志在/opt/solr/server/logs/solr.log启动失败的原因通常在这里有明确提示。常见错误包括java.net.BindException: Address already in use端口被占用。用lsof -i:8983或netstat -tlnp | grep 8983查看哪个进程占用了端口。ClassNotFoundException缺少 jar 包。离线环境下可能是 Solr 包解压不完整重新解压一次。java.lang.OutOfMemoryError内存不足。调大SOLR_HEAP或者检查机器内存。第二步检查 ZooKeeper 连接。如果日志里出现Unable to connect to ZooKeeper说明 Solr 连不上 ZooKeeper。用telnet zk-node1 2181测试端口连通性检查ZK_HOST配置是否正确。第三步检查SOLR_HOST。如果 Solr 启动成功但注册到 ZooKeeper 的地址不对集群会异常。确认solr.in.sh里的SOLR_HOST跟主机名一致。5.2 ZooKeeper 集群异常处理ZooKeeper 集群异常通常表现为zkServer.sh status显示standalone或者Error contacting service。排查要点问题现象可能原因解决方法三台都是 standalonemyid文件缺失或内容错误检查每台机器的dataDir/myid文件两台 follower 一台 leader 但连不上防火墙或端口不通检查 2181、2888、3888 端口启动后立即退出zoo.cfg配置错误检查dataDir路径是否存在、是否有写权限集群频繁选举节点间时间不同步配置 NTP 同步时间我遇到过一次 ZooKeeper 集群频繁选举的问题日志里全是Looking for leader。排查后发现是两台机器的时间差了 5 分钟ZooKeeper 对时间敏感时间差过大会导致心跳超时触发重新选举。同步时间后问题解决。5.3 Collection 创建失败与分片异常创建 Collection 时可能遇到各种错误常见的有错误一collection already exists。说明同名 Collection 已经存在换个名字或者先删除旧的curl http://solr-node1:8983/solr/admin/collections?actionDELETEnamemycollection错误二config name mycore not found。说明配置集没有上传到 ZooKeeper或者名称不对。用zkCli.sh检查/configs节点下的配置集列表。错误三numShards must be greater than 0。参数没传对检查 URL 里的numShards和replicationFactor参数。错误四分片分布不均。如果两个分片都落在同一个 Solr 节点上可能是另一个节点没注册成功。检查/solr/live_nodes里的节点数量。避坑技巧创建 Collection 时replicationFactor不能超过 Solr 节点数量。比如只有 2 个 Solr 节点replicationFactor最大只能设 2。设成 3 会报错因为找不到足够的节点放副本。5.4 离线环境特有的坑与应对离线环境跟在线环境最大的区别是什么都得自己准备我总结几个特有的坑坑一缺少unzip命令。Solr 5.5.1 是 zip 包如果目标机器没装unzip解压都解不了。所以离线包清单里一定要包含unzip的 rpm 包。坑二JDK 版本不匹配。Solr 5.5.1 编译时用的 JDK 版本可能跟目标机器上的不一致导致UnsupportedClassVersionError。解决办法是统一用 JDK 8 运行不要混用 JDK 7 和 JDK 8。坑三主机名解析失败。离线环境通常没有 DNS全靠/etc/hosts。如果 hosts 文件里漏配了某个节点集群通信就会失败。建议部署前用ping命令逐个验证主机名解析。坑四文件权限问题。用solr用户跑 Solr 和 ZooKeeper 时如果目录属主是 root会报权限错误。部署前统一执行chown -R solr:solr /opt/solr /opt/zookeeper。坑五SELinux 拦截。CentOS 7 默认开启 SELinux即使防火墙关了SELinux 也可能拦截 Solr 的网络通信。用setenforce 0临时关闭或者修改配置文件永久关闭。5.5 性能调优与日常维护建议集群跑起来之后还需要做一些调优和维护工作。以下是我在实际操作中总结的几个要点JVM 调优SOLR_HEAP不要设得太大超过物理内存的 50% 会导致频繁 GC。另外可以加上 GC 日志参数方便排查内存问题GC_TUNE-XX:UseG1GC -XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:/opt/solr/server/logs/solr_gc.logZooKeeper 快照清理ZooKeeper 会定期生成快照和事务日志时间长了会占满磁盘。配置autopurge.snapRetainCount和autopurge.purgeInterval自动清理autopurge.snapRetainCount3 autopurge.purgeInterval24表示保留最近 3 个快照每 24 小时清理一次。Solr 日志轮转Solr 日志默认不会自动轮转时间长了solr.log会变得很大。可以用log4j.properties配置轮转策略或者用系统的logrotate管理。定期备份配置集ZooKeeper 里的配置集是集群的核心资产建议定期用zkcli.sh下载备份./server/scripts/cloud-scripts/zkcli.sh -zkhost zk-node1:2181 -cmd downconfig -confdir /backup/configs/mycore -confname mycore这样即使 ZooKeeper 数据丢失也能快速恢复配置。我个人在实际操作中的体会是离线部署 Solr Cloud 最耗时间的不是安装本身而是排查各种环境问题。把基础环境做扎实——JDK 版本对、主机名解析通、权限配好、防火墙和 SELinux 关掉——后面的事情就顺理成章了。另外强烈建议在正式部署前先在一台机器上把单机版 Solr 跑通确认 JDK 和基础依赖没问题再扩展到集群模式这样能省下大量排查时间。