Apollo配置中心部署实战:从源码编译到服务启动与热更新 大学小学期实践课的第四节课我们安排的是Apollo软件部署实操。如果你没接触过这门课前面三节分别是Linux基础命令、MySQL数据库使用、Git和Maven工程管理到这节突然上了一个配置中心组件很多同学第一反应是为什么要单独装一个软件管配置直接在配置文件里改不行吗这节实操课结束之后我才发现Apollo这个选型其实非常贴合小学期实践的调性——它不像某些课程那样一句已经部署好了大家直接用就带过而是需要学生自己拉源码、配数据库、打包、启动三个Java服务中间会遇到一堆真实部署才会碰到的问题。今天这篇就把整节课的完整过程、踩坑记录和给学生讲的核心原理梳理一遍给准备上同样内容的朋友一个参考。1. 课程背景为什么小学期第四课要选Apollo来练部署1.1 前三节课的铺设逻辑很多小学期课程容易走两个极端要么全是理论课学生听三天PPT然后交一份报告要么直接丢一个高大上的项目学生照着文档复制粘贴根本不知道自己在干什么。我们这门课的实际思路是先让你有操作Linux、操作数据库、管理工程代码的底子再拿一个真实可用的开源软件让你独立部署一遍把前面三节课的知识全部串起来。部署Apollo这件事恰好能把前三节全部用上你需要用Linux命令解压、创建目录、查看进程需要连MySQL建库导数据需要用Maven拉依赖打jar包需要改配置文件然后用Git版本管理你的改动。如果前面三节课是散的点这节就是一条把它们拧在一起的线。1.2 Apollo配置中心到底解决什么问题先跟完全没接触过的同学说一下Apollo是什么。假设你现在维护五个微服务每个服务里都有一份application.yml里面写着数据库地址、Redis地址、各种开关参数。哪天数据库地址变了你得挨个改五个文件然后挨个重新打包、重新部署、重启服务一整套流程下来半小时起步。Apollo做的事情就是把所有配置集中到一个管理界面上服务启动的时候从Apollo拉取配置你在Apollo后台改一个配置项运行中的服务能在几秒内自动拿到新值不需要重新打包也不用重启。这种配置集中管理动态下发的能力是微服务架构里非常基础的一环。单纯讲概念学生很难有体感但让他自己部署一遍再写个demo验证配置实时生效理解就完全不一样了。2. 部署前必须理清的两个认知三组件分工和两套数据库2.1 Config Service、Admin Service、Portal三者的关系Apollo整体拆成了三个Java服务名字很容易混淆但分工其实很清晰。Config Service负责给业务系统的客户端提供配置读取、订阅推送功能。业务应用启动时会连它拿配置配置变更时也是它把最新值推给客户端。它默认端口是8080。Admin Service负责给管理后台提供配置管理、发布、回滚等功能。你可以理解成它是干活的管理后台的所有操作最终由它落到数据库里。默认端口8090。Portal管理员操作的可视化网页默认端口8070。它本身不直接操作业务配置而是通过Admin Service完成操作。刚开始部署时学生最容易搞混的就是这三个服务的职责我通常会打一个比方Portal是餐厅前台Admin Service是后厨Config Service是外卖窗口。顾客业务应用去外卖窗口取餐前台负责下单后厨负责做饭三者不能互相替代。2.2 两个数据库分别存什么Apollo部署需要初始化两个MySQL数据库这个也是初学者容易搞乱的地方。ApolloConfigDB存的是配置本身包括每个项目的配置项、发布历史、灰度规则等Config Service和Admin Service都用这个库。ApolloPortalDB存的是Portal后台的用户信息、项目列表、权限等门户数据Portal服务使用这个库。为什么拆成两个而不是一个因为ConfigDB是运行时数据业务依赖非常重写操作频繁PortalDB是管理端数据相对独立。拆开之后我们可以把ConfigDB做更细的读写分离和容灾Portal的日常维护也不会影响配置读写链路。你在执行SQL脚本的时候一定分清哪个脚本进哪个库装反了后面会非常痛苦。3. 部署实操链路从源码编译到三个服务全部启动3.1 获取源码与工程结构我们课程里让学生从官方仓库Clone了Apollo的源码工程也可以用课程组准备的离线包反正结构是一样的。Clone下来之后主目录下有几个子工程重点看这三个apollo-configserviceapollo-adminserviceapollo-portal它们的共同父工程里管理着Maven依赖版本。我建议学生先不要改任何代码先跑通官方默认配置成功之后再尝试改端口、改数据库连接这样排查问题的时候能分清楚是环境问题还是代码问题。3.2 初始化数据库的具体步骤用MySQL客户端连上数据库后执行官方项目里的两个SQL脚本一个叫apolloconfigdb.sql一个叫apolloportaldb.sql。需要说明的是不同版本脚本内容有差异最好和源码版本保持一致不要拿1.x的脚本配2.x的代码。执行命令大概是这样的mysql -u root -p apolloconfigdb.sql mysql -u root -p apolloportaldb.sql执行完可以进MySQL看一眼SHOW DATABASES; USE ApolloConfigDB; SHOW TABLES;正常情况下能看到一堆表比如APP、ITEM、RELEASE等没有报错说明导入成功。这里有两个小坑建库时字符集需要是utf8mb4否则后面如果存了Emoji之类的特殊字符会报错。如果之前执行过脚本想重新导入建议先DROP DATABASE再重新执行避免残留数据干扰。3.3 打包编译与启动参数的调整源码工程拿到并且数据库就绪后进到Apollo工程根目录执行打包mvn clean package -DskipTests这一步会拉取大量依赖耗时比较长建议提前把Maven仓库配好国内镜像。打包完成后三个子工程的target目录下会各自生成一个可执行的jar包类似apollo-configservice-2.x.x.jarapollo-adminservice-2.x.x.jarapollo-portal-2.x.x.jar在没有改任何配置的情况下直接启动默认连的是开发者本机的localhost数据库密码为空。实际教学环境里MySQL一定有密码所以必须改数据库连接配置。这个配置在三个服务各自的application.properties里核心配置如下# Config Service 和 Admin Service 都需要改 spring.datasource.urljdbc:mysql://localhost:3306/ApolloConfigDB?characterEncodingutf8serverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.password你的密码 # Portal 改的是自己的库 spring.datasource.urljdbc:mysql://localhost:3306/ApolloPortalDB?characterEncodingutf8serverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.password你的密码这里如果MySQL是8.x版本连接串里建议加上useSSLfalse和allowPublicKeyRetrievaltrue不然启动时经常报Public Key Retrieval is not allowed这个错我在几个学生机器上反复见到过。3.4 按顺序启动服务并验证注册情况配置改好之后按顺序启动。先启动Config Servicejava -Xms256m -Xmx512m -jar apollo-configservice-2.x.x.jar看到日志里出现Tomcat started on port 8080并且没有异常堆栈说明Config Service起来了。然后启动Admin Servicejava -Xms256m -Xmx512m -jar apollo-adminservice-2.x.x.jar它的日志里会包含向注册中心注册自己的信息。最后启动Portaljava -Xms256m -Xmx512m -jar apollo-portal-2.x.x.jar三个服务全部启动后浏览器访问http://localhost:8080可以看到一个Eureka注册中心页面正常的话应该列出CONFIG-SERVICE和ADMIN-SERVICE两个服务实例。这一步是整个部署过程中的核心验证点——如果Eureka页面能看到两个服务说明前两个服务注册成功如果只有Config Service或者两个都没有那问题就出在注册链路或数据库连接上。4. 实测中躲不开的几个坑与排查思路4.1 服务起不来先查数据库连接日志前面说的数据库连接问题是最常见的一类。学生启动服务时经常出现进程起来了服务自己也打印了启动日志但几秒之后就报错退出。这种时候第一反应应该是去看最后几行异常信息而不是重新启动一次。我之前带学生排查过一个案例某同学MySQL使用的是8.x版本密码没问题连接地址也没问题但没加allowPublicKeyRetrievaltrue报错内容是Public Key Retrieval is not allowed。这个报错信息其实已经告诉你了——MySQL8默认使用caching_sha2_password认证第一次连接时需要从服务端获取公钥来加密密码传输连接参数里没允许这个行为就拒绝执行。加上参数就好了。还有一种情况是数据库连接串里的serverTimezone没设置Java驱动和MySQL时区不一致会直接抛异常。这类问题在教学中出现的频率比我预想的高很多根源是很多同学习惯照抄别人的连接串却不知道每个参数是干什么用的。4.2 Eureka页面显示两个服务但Portal登录后看不到环境另一个高频坑是Eureka页面一切正常Config Service和Admin Service都注册上了Portal也能登录进去但界面上找不到环境选项或者创建项目时无法选择环境。这个问题的根子在Portal这个服务需要知道自己该往哪个环境发布配置。它读取的配置项是apollo.portal.envs和dev.metaapollo.portal.envsdev dev.metahttp://localhost:8080第一行告诉Portal当前系统里有哪些环境我们只准备了一个dev环境所以就是dev。第二行告诉Portaldev环境的元数据服务地址是Config Service的地址也就是8080端口。这里有个特别容易写错的地方dev.meta填的是Config Service的地址不是Admin Service也不是Portal自己。很多同学会想当然地填成8090或者8070结果就是后台永远感知不到配置服务的存在。如果这个配置没写Portal会出现一种很迷惑的状态能登录、能进后台但是看不到任何环境按钮项目创建流程没法完成。这个时候别急着检查容器网络先看一眼Portal的配置文件里有没有这两行。4.3 Eureka自我保护模式下的红色告警误判还有一次遇到的情况很有意思某个同学的Eureka页面是正常的黄色背景上面有一行红色WARNING提示大意是自我保护模式已启用某个服务实例可能已经崩溃。这位同学以为自己部署失败了紧张地截图发过来。其实这是Eureka自我保护机制触发了。如果服务实例心跳不健康Eureka宁可让旧实例待在注册表里也不轻易把它踢出去防止因网络抖动导致大面积下线。在单机部署的Apollo里只有一个Config Service实例和一个Admin Service实例没有其他副本做心跳备份很容易进入这个状态。遇到这种情况不要慌重点看页面上的实例列表只要CONFIG-SERVICE和ADMIN-SERVICE都在UP状态就说明服务是正常的。如果确实是DOWN了再去翻对应服务的日志多半是配置变更或内存不足导致的而不是自我保护机制本身的问题。4.4 教学机的内存限制与启动参数调整小学期教室里的电脑配置一般不会太好每台机器还要同时开虚拟机、数据库、浏览器如果三个Java服务都用默认参数启动内存会很紧张。默认情况下JVM堆可能分配得比较大学生机器直接卡死的情况我们都遇到过。建议在课程开始时统一让大家用这个参数启动java -Xms128m -Xmx256m -jar apollo-configservice-2.x.x.jar java -Xms128m -Xmx256m -jar apollo-adminservice-2.x.x.jar java -Xms64m -Xmx128m -jar apollo-portal-2.x.x.jar把堆内存调小之后三个服务加起来大约占400MB左右教学机完全扛得住。不过堆内存太小会让GC频繁一些在练习环境里完全够用这是教学演示场景下非常实用的一个取舍。5. 部署完成后的验证闭环从管理后台到客户端热更新5.1 管理后台创建项目并发布一条配置部署不是终点得真正用起来才算完成。登录Portalhttp://localhost:8070默认账号是apollo密码是admin不过不同版本初始账号可能不同以官方文档为准。登进去后第一步是创建项目应用IDAppId建议取一个唯一值比如demo001应用名称随便填应用部门/负责人填空或随便填都可以创建完成以后进入配置管理界面新增一个配置项。比如Keytest.nameValuehello-apollo填完点击发布。到这里你已经在Apollo里成功发布了一条配置到dev环境这条配置现在对任何配置了AppIddemo001的客户端开放了。5.2 写一个最小的Spring Boot客户端来拉取配置为了让学生真实感受到配置下发链路我们要求每人写一个极简的Spring Boot工程来连接Apollo。核心依赖和代码就几行但信息密度很高。先在pom.xml里引入客户端依赖dependency groupIdcom.ctrip.framework.apollo/groupId artifactIdapollo-client/artifactId version2.x.x/version /dependency创建一个Spring Boot入口类加上Apollo配置注解SpringBootApplication EnableApolloConfig public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }然后在application.properties里指定当前项目的AppId和Apollo地址app.iddemo001 apollo.metahttp://localhost:8080这里app.id必须和你在Portal里创建项目时填的完全一致Apollo就是靠这个app.id去定位配置的。apollo.meta指向Config Service端口不是Admin也不是Portal。接着写一个接口读取配置RestController public class ConfigController { Value(${test.name:default}) private String testName; GetMapping(/test) public String test() { return testName; } }启动这个Spring Boot应用访问http://localhost:8081/test能看到返回hello-apollo说明客户端从Apollo拉到了配置。5.3 热更新演示不重启应用的配置修改验证热更新是整节课最直观的高潮环节。保持Spring Boot应用不重启回到Portal后台把test.name的值从hello-apollo改成hello-again点发布然后再次访问客户端的/test接口。正常情况下几秒钟后返回结果就变成了hello-again全程不需要重启任何进程。这个体验对初次接触配置中心的同学来说非常震撼——原来改配置可以不用发版本。背后是客户端和Config Service之间维持了一条长连接服务端配置变化时会主动推送变更事件给客户端客户端收到事件后刷新Spring容器里的配置值。这一环是整个实操课程里最值得花时间讲透的部分。前面所有部署、踩坑、排错都是为了最后这一刻能顺滑地演示配置发布即生效的效果。6. 这节实操课我的总结与建议Apollo的部署实操放到小学期第四课从难度上说刚好卡在一个微妙的位置——比单纯装软件难比从零写框架简单非常适合用来锻炼学生排查问题的基本功。我在带课过程中最深的一个体会是大部分学生第一次部署失败都不是因为步骤写得不清楚而是遇到报错后慌慌张张重来一遍没有先理解报错信息在说什么。建议准备上类似课程的同学无论你是老师还是学生都提前做三件事把数据库连接串里的每个参数弄清楚再动手尤其是时区、SSL、认证方式这些容易被忽略的项它们在现代MySQL版本里会频繁出来找麻烦。理解三个服务加两个数据库的拓扑关系后再敲命令不要照着文档一路启动否则出了问题你连哪个服务挂了都判断不了。把部署成功后的验证环节当成课程的一部分每次都加一个客户端拉配置的demo确保整个链路确实通了而不是停留在页面能打开的表面成功。最后说一个我实际带课时给学生的经验部署类实操最容易出错的地方往往不是大段代码逻辑而是那些看起来最不起眼的配置文件单行参数。教大家在排查问题的时候先看配置文件再看数据库连接最后才去看代码——按这个顺序排查大部分问题都能在十分钟内定位。小学期的价值不在于把每一个工具都玩得精通而在于建立部署、验证、排错这个完整闭环的直觉这比记住任何一条具体命令都重要。