☰
Apollo分布式配置中心Linux环境部署实战指南
2026/10/1 6:23:22 网站建设 项目流程

在 Linux 服务器上部署 Apollo,表面上看就是解压三个发行包、改几行数据库连接、跑一下 startup.sh 的事,但真正动手操作过的人都知道,"配置"和"启动"这两步之间隔着不少坑。尤其是有过 Windows 上 Quick Start 跑通经验的人,一到 Linux 手动部署就很容易遇到"服务进程起来了但就是访问不了"这种玄学问题。Quick Start 帮你隐藏了太多细节,生产环境手动部署绕不开那套组件关系和端口规划。

Apollo 是携程开源的分布式配置中心,在 Java 微服务、Kubernetes 集群、多环境配置管理这些场景下用得非常多。它不会像 Spring Cloud Config 那样把配置丢在 Git 仓库里让各应用自己拉,而是通过服务端集中管理、客户端实时感知变更。这篇博文就围绕 Linux 环境下的完整部署链路展开,从环境准备、数据库初始化、发行包配置、启动验证到启动失败排查都会过一遍,适合第一次在 Linux 上部署 Apollo 的运维和开发同学,也适合想搞懂 Apollo 启动机制、准备自己封装部署脚本的人。

1. 部署前先理清 Apollo 的三个服务和两套库,别当黑盒直接跑

1.1 Config Service、Admin Service、Portal 各管什么

Apollo 不是单体应用,官方发行包拆成了三个独立服务。很多人一上来就急着下载配置,结果连"我到底在启动几个进程"都没概念。先把职责理清楚,后面配 IP 和端口的时候才不会乱。

Config Service,直译是配置服务,核心职责是面向客户端提供配置读取接口。微服务里的每个应用,启动的时候都会通过 Config Service 拉取自己的配置,后续配置变更也是由 Config Service 推送或客户端轮询获取。你可以把它理解为对外暴露的"读配置的大门",所有业务应用的客户端请求都会打到这个服务上。为了支撑大量客户端,Config Service 在实际部署中可以横向扩展出多个实例,挂在负载均衡后面。

Admin Service,管理服务,面向的是 Portal 管理端和 Open API。创建项目、修改配置、发布配置、回滚配置这类写操作,Portal 都会通过 Admin Service 去执行。为什么要单独拆一个服务?因为 Apollo 的设计理念里读写分离很重要——客户端高频读配置走 Config Service,管理端低频写配置走 Admin Service,两边互不影响,也能各自做鉴权和流量控制。

Portal 是门户管理界面,它本身不直接读写数据库,也不直接面对配置客户端,而是对接多个环境的 Admin Service。你在浏览器里看到的 Web 管理页面就是它。这里有一个很关键的设计:一套 Portal 可以管理多套环境(dev、fat、uat、prod),这也是 Apollo 最初的核心卖点之一——集中管理多环境配置,而不是每个环境单独搭一套管理后台。

1.2 ConfigDB 和 PortalDB 的分工,以及被忽略的 Eureka

Apollo 一共需要两个数据库:ApolloConfigDB 和 ApolloPortalDB。新手最容易犯的错是把所有表都导进一个库,或者只初始化了其中一个,然后报各种"Table not found"的错。

ApolloConfigDB 是核心库,存每个环境的配置数据,包括 App、Cluster、Namespace、Item、Release 这些表。客户端每次拉到的配置,归根到底都来自这个库。如果是多环境部署,原则上每个环境都要有一个独立的 ApolloConfigDB。比如 dev 环境一套,prod 环境一套,绝对不能共用。

ApolloPortalDB 存的是 Portal 自身的业务数据,包括用户信息、权限关系、项目元数据、操作审计等。它相当于 Apollo 管理端自己的"后台数据库"。注意,PortalDB 是全环境共享的,一套 Portal 配一个 PortalDB 即可。

还有一个"隐形成员"很容易被忽略——Eureka。Apollo 的服务注册与发现是内置 Eureka 实现的。Config Service 和 Admin Service 启动后,会自动注册到 Eureka 上,Portal 也是通过 Eureka 来发现各环境的服务地址。理解了这一点,你就明白为什么后面配置里到处都是eureka.service.url,也就能理解为什么启动失败经常表现为"服务进程在,但管理页面看不到已注册的实例"。

2. 环境准备:从建库、导表到 JDK 与内存评估

2.1 MySQL 版本与字符集:别等配置成了问号才回头

Apollo 对 MySQL 的版本要求不算苛刻,5.7 及以上、8.x 都没问题,但有两个细节必须注意。

一是字符集。Apollo 官方表结构脚本默认用的是 utf8mb4,如果你建库时用了 latin1 或者 utf8,中文配置项存进去后轻则显示乱码,重则直接写入失败或者客户端拉到的中文变成问号。我习惯在建库时就显式指定:

CREATE DATABASE ApolloConfigDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE DATABASE ApolloPortalDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

另外,如果服务器上已经有一个老库,里面数据和字符集都已经是现实的存量,千万不要手工去改库字符集,更稳妥的做法是新建库、导入数据、切换连接串,把旧库保留作回退。

二是时区。Apollo 服务端连接 MySQL 时,JDBC URL 里如果不带 serverTimezone 参数,在 MySQL 8.x 下大概率会报时间相关的异常,比如The server time zone value 'CST' is unrecognized。这个坑在部署阶段非常常见,建议数据库连接串直接写成:

jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8&serverTimezone=Asia/Shanghai

2.2 建用户、导表结构:脚本是现成的,别自己手工建表

我建议单独创建一个数据库账号给 Apollo 用,不要直接用 root 到处跑。以 MySQL 8.x 为例:

CREATE USER 'apollo'@'%' IDENTIFIED BY 'YourStrongPassword'; GRANT ALL PRIVILEGES ON ApolloConfigDB.* TO 'apollo'@'%'; GRANT ALL PRIVILEGES ON ApolloPortalDB.* TO 'apollo'@'%'; FLUSH PRIVILEGES;

版本升级时不要直接对整个库跑新版本的初始化脚本,Apollo 的升级脚本是增量式的,放在 migration 目录下按版本号分目录存放,全量脚本覆盖很容易把已有配置弄丢。这点我在生产环境升级时吃过亏,全量导了一次,旧环境的配置直接被清空,还好当时有备份。

2.3 JDK 版本与内存评估:太小气的机器跑不动

Apollo 服务端是 Java 应用,部署前需要确认服务器上装了 JDK。生产环境建议用 JDK 8 或 11,不同版本 Apollo 的兼容性有差异,我用得最多的是 Apollo 1.9.x,在 JDK 8 下最稳。查看版本:

java -version

没有 JDK 的话,用 OpenJDK 也行:

sudo apt install openjdk-8-jdk # Debian/Ubuntu sudo yum install java-1.8.0-openjdk # CentOS/RHEL

内存方面,Apollo 三个服务默认的 JVM 堆设置并不算小。如果不调直接启动,一台 2G 内存的机器很容易在启动阶段就卡死甚至 OOM。下面这种场景很典型:物理机内存只有 2G,三个服务叠加大约要吃掉 1.5G~2G,加上 MySQL 自己还要占内存,结果就是 Java 进程起来了,MySQL 挂了,或者三个服务互相拖垮。所以启动前建议先看服务器内存:

free -h

然后按实际资源调整后面章节会讲到的 JVM 参数。

3. 手动部署三个服务:配置项逐个拆开讲清楚

3.1 获取发行包与目录规划

生产环境不要用 Quick Start 那套,那是 demo 级别的。我都是直接从 GitHub Releases 或国内镜像源下载三个 zip 包:

  • apollo-configservice-x.x.x-github.zip
  • apollo-adminservice-x.x.x-github.zip
  • apollo-portal-x.x.x-github.zip

版本号一定要保持一致,混用容易出现 API 不兼容。解压后每个目录的结构类似这样:

apollo-configservice/ ├── bin/ │ ├── startup.sh │ └── apollo-configservice.sh ├── config/ │ ├── application-github.properties │ └── apollo-env.properties ├── scripts/ │ └── sql/ │ └── apolloconfigdb.sql └── lib/

建议把三个服务放到独立目录,比如:

mkdir -p /opt/apollo/{configservice,adminservice,portal}

各自解压进去,方便后面做 systemd 托管或写启停脚本。目录规划这一步很容易被忽略,等到后面升级、回滚、看日志的时候,你就会发现良好的目录结构能省太多事。

3.2 Config Service 的配置与 Eureka 端口

Config Service 的配置主要在config/application-github.properties文件里。因为 Apollo 默认会读取application-github.properties这个文件,所以一般不用改文件名,直接在里面改内容即可。关键的配置项:

spring.datasource.url = jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8&serverTimezone=Asia/Shanghai spring.datasource.username = apollo spring.datasource.password = YourStrongPassword eureka.service.url = http://localhost:8080/eureka/

这里有一个非常大的新手误区:eureka.service.url不是让你填一个远程 Eureka 中心的地址,而是 Config Service 和 Admin Service 互相注册的地址。因为 Apollo 把 Eureka 内嵌进来了,每个服务启动后既会把自己注册到别的服务,也接受别的服务注册到自己。所以 Config Service 的eureka.service.url通常就是它自己的地址加/eureka/,端口默认 8080。

Config Service 默认端口是 8080。如果服务器上已经有别的应用占了 8080,要改server.port。注意,改了端口之后,eureka.service.url也要跟着改,不然服务之间会找不到彼此。这一点我下面启动排查章节还会重点讲。

3.3 Admin Service 和 Portal 的配置差异,以及 Meta Server 的作用

Admin Service 的配置逻辑几乎和 Config Service 一样,只不过数据库还是指向 ApolloConfigDB,默认端口是 8090。很多人想不通为什么两个服务用同一个库,因为前面说了,Config Service 是读配置的,Admin Service 是写配置的,它们面对的是同一份数据的读写两端,用同一个库完全合理。

Portal 的配置要稍微复杂一点,因为它要管理多环境,至少要改这几个地方:

spring.datasource.url = jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8&serverTimezone=Asia/Shanghai spring.datasource.username = apollo spring.datasource.password = YourStrongPassword apollo.portal.envs = dev apollo.portal.meta.servers = http://localhost:8080

apollo.portal.envs是这套 Portal 允许管理的环境列表,dev、fat、uat、pro 用逗号分隔。apollo.portal.meta.servers是每个环境对应的 Meta Server 地址。这里我要重点展开说 Meta Server 和 Config Service 的区别:Meta Server 是 Apollo 内部做服务发现用的一个逻辑地址,它本身不提供配置读写能力,但它会告诉你 Config Service 和 Admin Service 具体在哪个地址。对客户端来说,只需要配置 Meta Server 地址,客户端会自动发现 Config Service;对 Portal 来说,同样是通过apollo.portal.meta.servers去发现各环境的 Admin Service。在单环境、单机部署的情况下,Meta Server 地址就是 Config Service 的地址,填http://localhost:8080即可。

如果后面接了多个环境,apollo.portal.meta.servers要写成这样:

apollo.portal.envs = dev,pro apollo.portal.meta.servers = {"dev":"http://dev-config:8080","pro":"http://pro-config:8080"}

这个 JSON 格式的映射关系到 Portal 能不能正确访问到各环境的服务,写错一个 key 就会导致 Portal 里某个环境的项目列表打不开。

4. 启动验证与失败排查:从日志到 Eureka 页面的完整链路

4.1 启动顺序:不是绝对不能乱,但稳妥顺序能省事

先启动哪个?其实 Apollo 的三个服务没有严格的启动顺序要求,因为内置了 Eureka 之后,服务之间是靠注册和定时拉取来互相发现的。不过实际部署中,我习惯先启动 Config Service,等它的日志里出现 Eureka 启动完成的标志后,再启动 Admin Service,最后启动 Portal。这样做的原因有两个:第一,Config Service 作为整个配置中心的"读入口",它启动后哪怕 Admin Service 还没起来,客户端也不会大面积报错;第二,Portal 启动时如果发现不了 Admin Service,虽然不会让进程退出,但管理页面里的项目列表可能加载不出来,给人一种"启动失败"的错觉。

启动命令很简单:

cd /opt/apollo/configservice && ./bin/startup.sh cd /opt/apollo/adminservice && ./bin/startup.sh cd /opt/apollo/portal && ./bin/startup.sh

startup.sh内部其实调用了java -jar,并把日志重定向到了 logs 目录下。启动后别急着看端口,先看日志更靠谱。

4.2 验证三件事:端口、日志、Eureka 页面

第一步验证端口是否在监听:

ss -tlnp | grep -E '8080|8090|8070'

正常情况下,8080 是 Config Service,8090 是 Admin Service,8070 是 Portal。

第二步是看启动日志有没有明显异常。每个服务目录下都有 logs 目录,比如:

tail -f /opt/apollo/configservice/logs/apollo-configservice.log

如果日志里出现类似Started ApolloConfigService in xx seconds的信息,说明该服务启动成功了。如果一直卡在数据库连接或者 Eureka 注册阶段,日志里通常会有异常堆栈,这时候把关键异常信息复制到搜索引擎里查,大多能找到原因。

第三步是通过 Eureka 的健康检查接口确认服务实例是否注册成功。直接访问 Config Service 的 Eureka 页面:

http://<服务器IP>:8080/

正常页面上应该能看到两个服务实例:CONFIG-SERVICE 和 ADMIN-SERVICE。如果只看到 CONFIG-SERVICE,说明 Admin Service 没注册上来,去检查 Admin Service 的日志和两台服务之间的网络连通性。如果两个都看不到,那基本可以断定 Eureka 配置有问题,回到eureka.service.url去查。

Portal 的验证方式是打开浏览器访问:

http://<服务器IP>:8070/

默认登录账号是 apollo,密码 admin。能进入管理页面,说明 Portal 端到 Admin Service 再到数据库这条链路是通的。

4.3 启动失败排查:我把最常见的三类问题列出来

第一类是端口被占用。Linux 上很多服务默认占用了 8080 或 8090,比如某些监控组件、Java 应用服务器。查端口占用用:

lsof -i :8080

或者:

ss -tlnp | grep 8080

如果被占,需要修改对应服务的server.port,同时联动修改eureka.service.url里的端口和 Portal 里的 meta server 配置。这里三处联动最容易漏,漏一处就会出现"Portal 能打开但访问不了 Config Service"的诡异问题。

第二类是内存不足。在 2G 内存的服务器上部署,不调 JVM 参数的话,三个服务加上 MySQL 基本会 OOM。内存不足时,日志里常见OutOfMemoryError,或者服务启动后没过几分钟就自动退出。解决办法是调小所有服务的堆内存,在startup.sh里或者环境变量里加上JAVA_OPTS,比如:

export JAVA_OPTS="-Xms512m -Xmx512m"

Config Service 和 Admin Service 我一般给 512m 就够用,Portal 因为是管理端界面,256m 到 512m 也可以。单机部署时三个服务加 MySQL 控制在 2G 以内是可以跑起来的。

第三类是数据库连不上。启动日志里如果出现CannotGetJdbcConnection或者Access denied,优先检查数据库连接串里的 IP、用户名、密码,以及 MySQL 的bind-address。注意 MySQL 8 默认的认证插件是caching_sha2_password,而 Apollo 内置的数据库驱动版本如果太老,连接时会报认证失败,需要把数据库用户的认证插件改成mysql_native_password:

ALTER USER 'apollo'@'%' IDENTIFIED WITH mysql_native_password BY 'YourStrongPassword';

改完记得重启服务,让连接池重新建立连接。

4.4 用 systemd 托管,告别手动启停

手动跑startup.sh适合首次部署验证,但生产环境里还是建议用 systemd 做进程托管。以 Config Service 为例,在/etc/systemd/system/apollo-configservice.service里写:

[Unit] Description=Apollo Config Service After=network.target mysql.service [Service] Type=simple User=apollo WorkingDirectory=/opt/apollo/configservice ExecStart=/opt/apollo/configservice/scripts/startup.sh Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target

但注意,Apollo 自带的startup.sh里用nohup启动后命令会立即返回,这和 systemd 的Type=simple配合得并不好。更合适的做法是直接调java -jar,或者把startup.sh里的 Java 启动命令提取出来写进 systemd,把Type设为simple,这样 systemd 才能真正跟踪到进程状态,服务挂掉后能自动拉起。三个服务都配置好之后:

systemctl daemon-reload systemctl enable apollo-configservice apollo-adminservice apollo-portal systemctl start apollo-configservice

这样的托管方式省心很多,服务器重启后服务能自动恢复,不用手动去点。

5. 部署完别急着接业务,先做这几件事

5.1 创建项目、Namespace,理解"发布"这个动作

服务都正常跑起来后,第一件事不是急着写客户端代码,而是在 Portal 里把项目结构建好。在 Portal 的首页点击"创建项目",填一个 AppId,这个 AppId 将来就是客户端引用配置时的唯一标识。默认项目会带一个 application 的 Namespace,可以往里加 Key-Value 格式的配置项。

这里有一个非常重要的动作:配置填好之后,一定要点"发布"。Apollo 的配置不会在保存时生效,必须先发布,生成 Release 记录,客户端才能拿到。很多第一次用 Apollo 的同事,在 Portal 里改了配置以为保存就是生效,结果客户端一直拿不到最新值,折腾了半天发现只是没点"发布"。

如果项目里需要区分环境,比如同一个 AppId 在 dev 和 pro 里要有不同的数据库地址,那就给不同环境分别配置即可。Apollo 的模型天然支持这种多环境隔离,每个环境的配置独立保存、独立发布,互不干扰。

5.2 客户端接入的最小配置示例

如果是 Spring Boot 项目,接入 Apollo 其实很轻量。在application.properties里加:

# 指定 Apollo 的 AppId app.id = SampleApp # 指定 Apollo Meta Server 地址 apollo.meta = http://localhost:8080 # 开启 Apollo 配置 apollo.bootstrap.enabled = true # 把 Namespace 注入 Spring 环境 apollo.bootstrap.namespaces = application

然后在 Maven 的pom.xml引入依赖:

<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.9.1</version> </dependency>

启动应用后,如果控制台出现 Apollo 拉取配置成功的日志,说明整条链路已经通了:客户端 -> Config Service -> ApolloConfigDB。接入时注意,apollo.meta和app.id这两个配置一定不能少,写错任何一个都会导致客户端连不上。

5.3 账号安全与权限规划

Apollo 的默认管理员账号是 apollo/admin,这个账号能管理所有项目、所有环境。生产环境上第一步必须修改默认密码,并且不要把 apollo 这个账号直接给开发人员日常使用。

在 Portal 的权限管理里,可以为每个项目分配负责人和开发者角色。开发者的权限是编辑配置、发布配置,负责人则可以管理项目成员。Apollo 的权限模型粒度比较细,支持精确到项目、环境、Namespace 级别的授权。部署完花点时间把权限规划好,后面能省很多麻烦。

这里我多说一句和运维相关的事:Apollo 的数据库密码不要明文写在配置里散落到各个服务器上。我的做法是把密码统一放到环境变量里,或者用专门的配置管理工具在启动时注入,避免配置文件被误传或泄露。

6. 个人踩坑清单:这些坑光看文档发现不了

最后分享几个我自己的实战经验。部署 Apollo 本身不复杂,但有些坑不踩过一遍,光看官方文档很难发现。

第一个坑,服务器上的 MySQL 是旧环境迁移过来的,字符集是 latin1。Apollo 初始化脚本导入时看着没报错,但中文配置发布后到了客户端变成问号。我的解决方式是重建数据库并明确指定 utf8mb4,同时保证应用连接串里也带上characterEncoding=utf8。

第二个坑,服务器上配置了 HTTP 代理环境变量(http_proxy/https_proxy),导致服务间通过 localhost 通信时被代理拦截,Eureka 注册地址变成了一个奇怪的公网地址,服务之间互相看不到。排查思路其实很直接:先确认机器上有没有设置代理环境变量,有的话在启动 Apollo 前清掉,或者改成 no_proxy 里加 localhost。Eureka 页面上的服务实例地址如果是公网 IP 而不是内网 IP,基本就是这个原因。

第三个坑,配置改完忘记发布。这个前面已经强调过了,但还是要再说一次,因为它的出现频率远超想象。Apollo 的配置发布走的是 Release 表,只有发布过的版本才会生成对应的 Release 记录,客户端只能拿到已发布的配置。如果你在 Portal 里改了配置,客户端一直没变化,先去页面上看一眼那条配置是不是处于"未发布"状态。

第四个坑,服务用 systemd 同时拉起,结果 Config Service 和 Admin Service 启动过快,Eureka 注册还没完成,Portal 启动时发现不了 Admin Service,管理页面能打开但项目列表一直是空的。后来改成顺序启动,或者在 systemd 里配置Restart=on-failure,整个体验就顺了很多。

第五个坑,日志和临时目录的磁盘占用。Apollo 默认日志是滚动写的,但如果服务长期不重启,有些版本会产生大量 GC 日志和访问日志。建议统一给日志目录做 logrotate 或者定时清理,特别是/opt/apollo下的 log 目录,别等磁盘满了才发现。

Apollo 这套东西,部署起来确实有点繁琐,但一旦跑顺了,它在微服务配置管理上解决的是真痛点。希望这篇博文能帮你在 Linux 上把 Apollo 顺利跑起来,少踩几个我当年踩过的坑。如果你在部署过程中遇到什么奇怪的问题,多看看 logs 目录下的日志,绝大多数答案都在里面。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询