Apollo配置中心实战:从Docker部署到多环境配置管理
1. 为什么你需要一个配置中心?从“硬编码”到“动态管理”的进化
干了这么多年开发,我见过太多项目栽在配置管理上。早期我们都是怎么干的?把数据库连接、Redis地址、各种开关和参数,一股脑儿写在项目的 application.properties 或者 config.xml 文件里。上线前,开发、测试、生产环境得手动改一遍,一不小心就配错了,线上事故往往就是这么来的。后来学聪明了点,用 Maven 的 Profile 或者 Spring 的 @Profile 注解来区分环境,但每次改个配置,哪怕只是调个超时时间,都得重新打包、部署、重启服务,运维同学恨不得顺着网线过来找你。
这就是配置中心要解决的痛点:集中管理、实时生效、环境隔离。你可以把它想象成一个所有微服务共用的“遥控器”。以前,你想调大某个服务的线程池,得跑到服务器上找到那个服务的配置文件,改了再重启,过程繁琐且风险高。现在,你只需要在配置中心的网页上,找到对应的配置项,修改、发布,几秒钟后,所有相关的服务实例就能自动拿到新配置,无需重启。这感觉,就像给整个系统装上了中央空调的遥控面板,随时调节,立竿见影。
在众多配置中心里,Apollo(阿波罗) 是我个人非常偏爱的一个。它出身于携程,经过大规模生产环境的锤炼,稳定性和功能完整性都没得说。它不像有些工具只是简单的键值存储,Apollo 自带了一套完整的管理流程,比如灰度发布——你可以先让新配置只对 10% 的服务器生效,观察没问题再全量推,这能极大降低配置变更的风险。还有权限管理和发布审核,谁改了配置、改了啥、什么时候发布的,都记录得清清楚楚,非常适合有一定规模、需要规范流程的团队。
那么,怎么把它用起来呢?最快速、最标准、也最易于维护的方式,就是 Docker 容器化部署。今天,我就带你走一遍完整的实战流程,从用 Docker 一键拉起 Apollo,到搭建开发、测试、生产多套环境,让你彻底告别配置管理的混乱时代。
2. 快速上手:5分钟用Docker Compose拉起你的第一个Apollo
我知道,一提到部署,很多人就头疼。又要装 Java,又要配 MySQL,一堆环境变量,步骤繁琐还容易出错。别担心,用 Docker 来部署 Apollo,可以说是“傻瓜式”操作。我们完全不用去官网下载那一大堆源码和脚本,直接利用官方和社区维护好的 Docker 镜像,配合 Docker Compose,几分钟就能让 Apollo 跑起来。
首先,确保你的机器上已经安装了 Docker 和 Docker Compose。如果没有,去 Docker 官网按照指引安装,这个过程很简单,这里就不赘述了。
接下来,我们需要准备两个核心的东西:数据库和 Docker Compose 配置文件。Apollo 依赖 MySQL 来存储所有的配置数据、发布历史、用户权限等信息。我们同样用 Docker 来启动一个 MySQL 实例,这样最干净。
创建一个工作目录,比如 apollo-docker,在里面新建一个 docker-compose.yml 文件。这个文件将定义我们所有的服务。
version: '3'
services:
apollo-db:
image: mysql:5.7
container_name: apollo-mysql
environment:
MYSQL_ROOT_PASSWORD: your_strong_password_here
MYSQL_DATABASE: ApolloConfigDB
TZ: Asia/Shanghai
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/init:/docker-entrypoint-initdb.d
ports:
- "13306:3306"
restart: unless-stopped
networks:
- apollo-network
apollo-configservice:
image: apolloconfig/apollo-configservice:latest
container_name: apollo-configservice
depends_on:
- apollo-db
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://apollo-db:3306/ApolloConfigDB?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: your_strong_password_here
EUREKA_INSTANCE_IP_ADDRESS: 192.168.1.100 # 替换为你的宿主机IP
ports:
- "8080:8080"
restart: unless-stopped
networks:
- apollo-network
apollo-adminservice:
image: apolloconfig/apollo-adminservice:latest
container_name: apollo-adminservice
depends_on:
- apollo-db
- apollo-configservice
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://apollo-db:3306/ApolloConfigDB?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: your_strong_password_here
ports:
- "8090:8090"
restart: unless-stopped
networks:
- apollo-network
apollo-portal:
image: apolloconfig/apollo-portal:latest
container_name: apollo-portal
depends_on:
- apollo-db
- apollo-configservice
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://apollo-db:3306/ApolloPortalDB?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: your_strong_password_here
APOLLO_PORTAL_ENVS: dev
DEV_META: http://192.168.1.100:8080 # 替换为你的宿主机IP,指向configservice
ports:
- "8070:8070"
restart: unless-stopped
networks:
- apollo-network
networks:
apollo-network:
driver: bridge
我来解释一下这个文件里的几个关键点:
- apollo-db: 我们启动了一个 MySQL 5.7 的容器,并初始化了
ApolloConfigDB数据库。注意,这里我们只初始化了一个库,因为ApolloPortalDB的建表SQL需要手动执行。我们通过volumes把初始化SQL脚本挂载进去。你需要在./mysql/init目录下放入从 Apollo GitHub 仓库下载的apolloconfigdb.sql和apolloportaldb.sql文件。 - 环境变量: 最需要注意的是
EUREKA_INSTANCE_IP_ADDRESS和DEV_META。在 Docker 容器网络里,服务间可以通过服务名(如apollo-db)通信。但 Portal 需要告诉客户端去哪里找 ConfigService,这个地址必须是客户端(你的业务应用)能访问到的。所以这里我用了宿主机的IP(192.168.1.100,务必替换成你自己的)和映射出来的端口(8080)。这是从容器内服务到外部网络的关键。 - 网络: 我们创建了一个自定义的桥接网络
apollo-network,让四个容器在同一个网络内,它们之间可以直接用容器名互相访问,比如apollo-configservice容器访问数据库,直接用jdbc:mysql://apollo-db:3306就行了。
配置文件准备好后,在目录下执行一条命令:
docker-compose up -d
Docker 会自动拉取镜像(如果本地没有),然后按顺序启动容器。等个一两分钟,你可以用 docker-compose logs -f 查看日志,当看到 ConfigService 和 AdminService 注册到 Eureka(Apollo 内置的),Portal 启动成功的日志时,就大功告成了。
打开浏览器,访问 http://你的宿主机IP:8070,默认账号是 apollo,密码是 admin。登录进去,你就能看到 Apollo 的管理界面了。是不是比想象中简单多了?这只是一个最基础的单环境(DEV)部署,接下来我们要玩点更实际的——多环境。
3. 构建企业级多环境:DEV、FAT、UAT、PRO
在实际项目里,我们至少有开发(DEV)、测试(FAT)、预发布(UAT)、生产(PRO)四套环境。每套环境的数据库、中间件地址、业务参数都不同。用 Apollo 管理多环境的核心思想是:一套 Portal(管理后台) + 多套 ConfigService/AdminService(配置服务)。
Portal 是统一的入口,你在这里管理所有环境的配置。而每一套独立的环境,都需要独立部署一组 apollo-configservice 和 apollo-adminservice,以及一个独立的 ApolloConfigDB 数据库。它们之间物理隔离,数据互不干扰。
3.1 数据库规划与初始化
首先,为每个环境准备一个 MySQL 实例或数据库。为了演示方便,我们可以在同一个 MySQL 服务器上创建不同的数据库。假设我们有 dev, fat, pro 三个环境。
在你的 MySQL 服务器上执行:
CREATE DATABASE `ApolloConfigDB_dev` DEFAULT CHARACTER SET = `utf8mb4`;
CREATE DATABASE `ApolloConfigDB_fat` DEFAULT CHARACTER SET = `utf8mb4`;
CREATE DATABASE `ApolloConfigDB_pro` DEFAULT CHARACTER SET = `utf8mb4`;
然后,分别向这三个数据库导入 apolloconfigdb.sql 脚本。ApolloPortalDB 只需要一个,供 Portal 使用,导入 apolloportaldb.sql 即可。
3.2 部署多套ConfigService与AdminService
现在,我们需要为每个环境启动一对 ConfigService 和 AdminService 容器。如果还用手动写 docker run 命令就太累了,我们升级一下 Docker Compose 文件,用 profiles 功能来定义不同环境。
我们可以创建 docker-compose-dev.yml, docker-compose-fat.yml, docker-compose-pro.yml 等多个文件,但更优雅的方式是使用一个文件配合环境变量。这里我展示一个整合的思路,在实际生产中,你可能需要结合 CI/CD 工具(如 Jenkins)来动态生成或选择配置文件。
假设我们有三台机器,IP 分别是:
- 开发环境(DEV):
192.168.1.101 - 测试环境(FAT):
192.168.1.102 - 生产环境(PRO):
192.168.1.103
每台机器上,我们运行类似下面这样的 Docker Compose 文件(以 DEV 环境为例,文件名为 docker-compose-dev.yml):
version: '3'
services:
apollo-configservice-dev:
image: apolloconfig/apollo-configservice:latest
container_name: apollo-configservice-dev
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql-server-dev:3306/ApolloConfigDB_dev?useUnicode=true&characterEncoding=UTF8
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: dev_db_password
EUREKA_INSTANCE_IP_ADDRESS: 192.168.1.101 # 本机IP
EUREKA_INSTANCE_HOME_PAGE_URL: http://192.168.1.101:8080
ports:
- "8080:8080"
restart: unless-stopped
apollo-adminservice-dev:
image: apolloconfig/apollo-adminservice:latest
container_name: apollo-adminservice-dev
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql-server-dev:3306/ApolloConfigDB_dev?useUnicode=true&characterEncoding=UTF8
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: dev_db_password
ports:
- "8090:8090"
restart: unless-stopped
FAT 和 PRO 环境的文件类似,只需要修改数据库连接、IP 地址和容器名称即可。
3.3 配置Portal关联多环境
现在,三套环境的配置服务都独立运行起来了。我们需要让唯一的 Portal 知道它们的存在。这需要通过修改 Portal 的配置文件 apollo-env.properties 来实现。这个文件定义了每个环境对应的 ConfigService 地址。
最方便的做法是在启动 Portal 容器时,通过环境变量注入这些配置。我们修改 Portal 的 Docker Compose 配置(可以放在另一台专门的管理机器上):
apollo-portal:
image: apolloconfig/apollo-portal:latest
container_name: apollo-portal
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql-server-portal:3306/ApolloPortalDB?useUnicode=true&characterEncoding=UTF8
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: portal_db_password
# 关键:在这里定义所有环境及其meta server地址
APOLLO_PORTAL_ENVS: dev,fat,pro
DEV_META: http://192.168.1.101:8080
FAT_META: http://192.168.1.102:8080
PRO_META: http://192.168.1.103:8080
ports:
- "8070:8070"
restart: unless-stopped
重启 Portal 容器后,再次登录管理界面,你会在右上角看到一个环境下拉框,里面出现了 DEV, FAT, PRO 三个选项。切换环境,你就能管理对应环境的配置了。这意味着,开发同学在 DEV 环境改配置,完全不会影响到测试和生产,真正做到了环境的物理和逻辑隔离。
4. 从零集成:让你的Spring Boot应用接入Apollo
服务端搭好了,接下来就是客户端集成了。这才是最终体现价值的一步。我以最常用的 Spring Boot 应用为例,带你走通全流程。
4.1 添加Maven依赖与基础配置
在你的 Spring Boot 项目的 pom.xml 中,添加 Apollo 客户端依赖。建议使用 apollo-client 和 Spring Boot 的 starter,这样整合度更高。
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>2.2.0</version> <!-- 建议与服务端版本一致 -->
</dependency>
<!-- 如果你用的是 Spring Boot -->
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client-config-data</artifactId>
<version>2.2.0</version>
</dependency>
接下来是核心的配置文件 application.yml(或 application.properties)。这里有个非常重要的概念叫 app.id,它是 Apollo 中你应用的唯一标识。你在 Portal 上创建应用时,必须使用相同的 app.id。
app:
id: your-application-id # 例如:user-service,必须与Portal中创建的应用ID一致
apollo:
bootstrap:
enabled: true # 必须开启,让Apollo在Spring Boot启动早期就初始化
eagerLoad:
enabled: true # 推荐开启,预加载所有命名空间
meta: ${APOLLO_META_SERVER:http://192.168.1.101:8080} # 指定Meta Server地址
注意 apollo.meta 这个配置。它告诉客户端去哪里找 ConfigService。这里我用了环境变量 ${APOLLO_META_SERVER} 来指定,并给了个默认值。这是多环境切换的关键! 我们不需要为不同环境打包不同的应用包,只需要在启动应用时,通过环境变量传入不同的 APOLLO_META_SERVER 值。
- 在 DEV 环境启动:
APOLLO_META_SERVER=http://192.168.1.101:8080 - 在 FAT 环境启动:
APOLLO_META_SERVER=http://192.168.1.102:8080 - 在 PRO 环境启动:
APOLLO_META_SERVER=http://192.168.1.103:8080
这样,同一个 Jar 包,就能自动从不同环境的 Apollo 服务拉取配置。
4.2 在Apollo Portal中创建并管理配置
打开 Portal (http://portal-ip:8070),用 apollo/admin 登录。点击“创建项目”,输入项目信息,关键是 AppId,必须和客户端配置文件里的 app.id 一模一样。
创建成功后,进入项目,点击“新增配置”。你可以添加任何键值对,比如:
- Key:
spring.datasource.url - Value:
jdbc:mysql://dev-db:3306/user_db
填写后,点击“发布”。配置就正式生效了。回到你的 Spring Boot 应用,如果应用正在运行,并且配置了 @RefreshScope,你会看到日志里打印出配置更新的信息,并且新的数据库连接会立即生效(对于 @ConfigurationProperties 或 @Value 注解的字段)。如果应用还没启动,它会在启动时从 Apollo 拉取这些配置,完全替代本地 application.yml 中的同名配置。
4.3 实战技巧:命名空间与灰度发布
Apollo 有两个高级功能特别好用:命名空间(Namespace) 和 灰度发布。
命名空间 用于对配置进行分组。默认的 application 命名空间存放公共配置。你还可以创建私有命名空间,比如 redis-config,专门放 Redis 相关的配置;或者创建公共命名空间,比如 spring-boot-common,被多个应用共享。在客户端,你可以通过 apollo.bootstrap.namespaces 属性来指定要加载哪些命名空间。
apollo:
bootstrap:
namespaces: application,redis-config,spring-boot-common
灰度发布 是 Apollo 的杀手锏。当你修改了一个关键配置,比如线程池大小,直接全量发布心里有点虚?那就用灰度发布。在发布配置时,选择“灰度发布”,然后指定要将新配置推送到哪几台具体的应用实例(Apollo 客户端会上报自己的 IP 和 AppId)。只有这些被选中的实例会接收到新配置,其他实例依然使用旧配置。你可以在监控页面上观察这几台灰度机器的日志和指标,确认没问题后,再点击“全量发布”。这个功能极大地提升了配置变更的安全性,尤其在生产环境。
5. 避坑指南与生产环境加固建议
踩过不少坑之后,我总结了一些经验和建议,希望能帮你少走弯路。
第一坑:Meta Server地址配置。 这是新手最容易出错的地方。apollo.meta 不能配置成 AdminService 的地址(8090端口),必须指向 ConfigService(8080端口)。而且,如果 ConfigService 是集群部署,后面有负载均衡,那么这里应该配置为负载均衡器的地址或域名。客户端是通过这个地址找到 ConfigService,进而获取真正的配置服务地址列表的。
第二坑:网络与防火墙。 确保你的应用服务器能够访问 Apollo ConfigService 的端口(默认8080)。在 Docker 部署时,特别注意宿主机防火墙和 Docker 网络规则。我强烈建议在测试环境,先用 curl http://configservice-ip:8080 验证网络连通性。
第三坑:数据库连接池。 Apollo 服务端本身对数据库连接有一定消耗。在生产环境,务必根据你的实例数量和访问压力,调整 MySQL 的 max_connections 参数,并考虑为 Apollo 的数据库使用性能更好的硬件或 RDS 服务。
生产环境加固建议:
- 高可用部署:ConfigService、AdminService、Portal 每个都至少部署两个实例,前面用 Nginx 做负载均衡。数据库使用主从复制,做好备份。
- 安全加固:
- 修改默认密码:第一时间修改 Portal 的默认账号密码。
- 启用 HTTPS:为 Portal 和 ConfigService 配置 SSL 证书,避免配置信息在传输过程中明文泄露。
- IP白名单:在 Nginx 或应用层面,对 Portal 的管理界面设置访问 IP 白名单,只允许运维网段访问。
- 配置加密:对于数据库密码等极度敏感信息,不要明文存储在 Apollo 中。可以使用 Apollo 提供的密钥加密功能,在界面上存储加密后的密文,客户端配置解密密钥进行解密。
- 监控与告警:Apollo 提供了
/prometheus端点暴露监控指标。将其接入你的 Prometheus + Grafana 监控体系,监控服务健康状态、配置拉取次数、发布频率等。设置关键指标(如服务宕机、配置发布失败)的告警。 - 客户端容灾:一定要配置本地缓存。Apollo 客户端会在
/{user.home}/.apollo/目录下缓存拉取到的配置。当 Apollo 服务端完全不可用时,客户端会使用本地缓存文件启动,这为系统提供了宝贵的降级能力,避免“配置中心挂了,所有应用都启动不了”的灾难性情况。
最后,关于版本,我建议选择 Apollo 的稳定版本,并关注其 GitHub 仓库的更新。将 Apollo 的部署和配置管理也纳入你的 CI/CD 流水线,实现基础设施即代码(IaC)。比如,用 Ansible 脚本或 Kubernetes Helm Chart 来管理 Apollo 的部署,用 Git 来管理 apollo-env.properties 等服务的配置文件,让整个流程可追溯、可重复。
更多推荐


所有评论(0)