1. 为什么要在 k8s 上部署 Apollo 配置中心
1.1 从一次配置事故说起
前两年我在一家做 SaaS 的公司负责基础架构,当时团队有十几个微服务,配置全部散落在各个项目的application.yml里。有一次线上要调整一个限流阈值,运维同学挨个登录容器改配置、重启服务,结果漏掉了一个实例,导致那台机器还在用旧阈值,流量高峰时直接被限流打挂。事后复盘,大家一致认为必须上一套统一的配置中心。
选型的时候对比过几套方案,最终选了 Apollo。原因很直接:它支持配置的灰度发布、权限管理、版本回滚、多环境多集群隔离,而且有比较完善的管理界面,开发和运维都能上手。更关键的是,Apollo 的客户端支持本地缓存,即使配置中心挂了,服务也能用本地快照继续跑,这对我们这种对可用性要求高的业务来说很重要。
后来公司整体上了 k8s,我就顺手把 Apollo 也搬到了 k8s 上。这篇文章就把整个部署过程、踩过的坑、以及一些参数调优的经验完整地梳理一遍。如果你正在做微服务改造,或者准备把配置中心容器化,这篇内容应该能帮你少走不少弯路。
1.2 Apollo 的核心架构先搞清楚
在动手部署之前,得先弄明白 Apollo 到底由哪几个部分组成,不然部署的时候会一头雾水。Apollo 的架构其实不复杂,主要分四个角色:
- Config Service:提供配置读取接口,客户端从这里拉配置。它是无状态的,可以水平扩展。
- Admin Service:提供配置管理接口,管理界面通过它来修改、发布配置。它也是无状态的。
- Portal:管理界面,给用户操作配置用的,本身不存储配置数据。
- Meta Server:一个轻量级的服务发现组件,客户端通过它找到 Config Service 的地址。在 k8s 里其实可以用 Service 直接替代。
数据存储方面,Apollo 依赖 MySQL,配置数据、发布记录、权限信息都存在数据库里。所以部署 Apollo 本质上就是部署这几个组件加一个 MySQL。
理解了这个架构,后面部署的时候你就知道每个 Deployment 是干嘛的,为什么要这么拆。比如 Config Service 和 Admin Service 要分开部署,是因为它们的职责不同,扩缩容策略也不一样——Config Service 读多写少,可以多副本;Admin Service 写操作多,副本数不用太多。
1.3 为什么用 k8s 部署而不是传统方式
有人可能会问,Apollo 官方提供了 Docker 镜像和脚本,直接跑容器不就行了,为什么非要上 k8s?我自己的体会是,k8s 带来的好处主要体现在三个方面。
第一是服务发现和负载均衡。Apollo 的 Config Service 需要被所有微服务访问,在 k8s 里一个 Service 就搞定了,不用手动维护 IP 列表。而且 k8s 的 Service 自带负载均衡,请求会自动分发到各个 Pod。
第二是弹性伸缩和自愈。Config Service 是无状态的,流量大了直接改副本数就行。Pod 挂了 k8s 会自动重启,不用人工介入。我们之前用传统方式部署的时候,半夜 Config Service 挂了还得爬起来重启,上了 k8s 之后再也没操心过这事。
第三是配置和密钥管理。Apollo 本身需要连 MySQL,数据库密码这种敏感信息可以用 k8s 的 Secret 管理,不用明文写在配置文件里。环境变量、资源限制这些也都能通过 k8s 的声明式配置统一管理。
当然,k8s 也带来了一些额外的复杂度,比如网络策略、存储卷、健康检查这些都要考虑。但总体来说,收益是大于成本的。
2. 部署前的准备工作
2.1 环境与版本选型
部署之前先把环境确认清楚,不然后面出问题排查起来很麻烦。我这次用的环境是 k8s 1.24 以上版本,MySQL 用的是 8.0,Apollo 版本选的 2.1.0。为什么选这个版本?因为 2.x 版本对 k8s 的支持更友好,官方也提供了 Helm Chart,省去了自己写 YAML 的功夫。
这里有个细节要注意:Apollo 2.x 要求 JDK 8 或 11,如果你用的基础镜像里 JDK 版本不对,启动会报错。我建议直接用官方镜像,省得自己折腾。
MySQL 的版本也有讲究。Apollo 的建表脚本对 MySQL 5.7 和 8.0 都兼容,但 8.0 默认的字符集是 utf8mb4,建表的时候要注意排序规则,不然可能会出现中文乱码。我一般会在建库的时候显式指定字符集:
CREATE DATABASE ApolloConfigDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE ApolloPortalDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;Apollo 需要两个数据库,一个是 ConfigDB 存配置数据,一个是 PortalDB 存管理界面的数据。这两个库可以放在同一个 MySQL 实例里,也可以分开,看你的规模。小规模用同一个实例就够了,大规模建议分开,避免 Portal 的操作影响 Config 的读取性能。
2.2 数据库初始化与账号规划
数据库建好之后,要执行 Apollo 提供的建表脚本。这些脚本在官方仓库的scripts/sql目录下,有两个文件:apolloconfigdb.sql和apolloportaldb.sql。直接导入就行。
导入之后,有几个地方需要手动改一下。首先是ServerConfig表,里面存的是 Apollo 各个服务的地址。默认值是http://localhost:8080这种,要改成 k8s 里的 Service 地址。比如 Config Service 的地址可以写成http://apollo-configservice:8080,这样 Portal 就能通过 k8s 的内部 DNS 找到它。
其次是数据库账号。我建议单独建一个 Apollo 专用的账号,只给它这两个库的权限,不要用 root。密码要足够复杂,然后存到 k8s 的 Secret 里。这里有个坑:Apollo 的配置文件里数据库密码是明文写的,如果你用 ConfigMap 存配置,密码就会暴露。所以一定要用 Secret,而且最好开启 etcd 的加密存储(如果 k8s 版本支持的话)。
账号权限方面,ConfigDB 需要 SELECT、INSERT、UPDATE、DELETE 权限,PortalDB 除了这些还需要 CREATE、ALTER 权限,因为 Portal 启动时会自动检查表结构。不过生产环境我建议把自动建表关掉,手动执行脚本更可控。
2.3 镜像与存储卷规划
Apollo 的官方镜像在 Docker Hub 上都有,apolloconfig/apollo-configservice、apolloconfig/apollo-adminservice、apolloconfig/apollo-portal这三个。版本号要跟你的 Apollo 版本对应,比如 2.1.0 就用2.1.0这个 tag。
存储卷方面,Apollo 本身是无状态的,配置都存在 MySQL 里,所以不需要持久化存储。但是日志需要持久化,不然 Pod 重启日志就丢了。我一般会挂一个 emptyDir 或者 hostPath 来存日志,如果公司有统一的日志收集系统,直接输出到 stdout 也行。
这里有个经验:Apollo 的日志量不小,尤其是 Config Service,每次客户端拉配置都会打日志。如果不做日志轮转,磁盘很快就会被占满。我建议在 logback 配置里加上按天滚动和最大保留天数,或者用 sidecar 容器做日志收集。
3. 核心组件的部署实操
3.1 Config Service 的 Deployment 配置
Config Service 是整个 Apollo 里最核心的组件,所有微服务都依赖它。部署的时候有几个关键点要注意。
先看 Deployment 的配置。副本数我一般设 2 个起步,因为它是无状态的,多副本可以保证高可用。资源限制方面,Config Service 比较吃内存,JVM 堆内存建议设 1G 到 2G,具体看你的配置数量和客户端数量。CPU 的话 500m 到 1 核就够了。
apiVersion: apps/v1 kind: Deployment metadata: name: apollo-configservice namespace: apollo spec: replicas: 2 selector: matchLabels: app: apollo-configservice template: metadata: labels: app: apollo-configservice spec: containers: - name: configservice image: apolloconfig/apollo-configservice:2.1.0 ports: - containerPort: 8080 env: - name: SPRING_DATASOURCE_URL value: "jdbc:mysql://mysql-service:3306/ApolloConfigDB?characterEncoding=utf8" - name: SPRING_DATASOURCE_USERNAME valueFrom: secretKeyRef: name: apollo-db-secret key: username - name: SPRING_DATASOURCE_PASSWORD valueFrom: secretKeyRef: name: apollo-db-secret key: password resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "2Gi" cpu: "1" livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 60 periodSeconds: 10 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 5这里有几个细节值得展开说。initialDelaySeconds设 60 秒是因为 Apollo 启动比较慢,要连数据库、初始化缓存,如果设太短,Pod 还没启动完就被判定为不健康,会被反复重启。我一开始设的 20 秒,结果 Pod 一直在 CrashLoopBackOff,排查了半天才发现是启动时间不够。
环境变量这块,Apollo 支持用 Spring Boot 的标准环境变量覆盖配置,所以数据库连接信息可以直接用SPRING_DATASOURCE_URL这种形式注入。这样就不用改镜像里的配置文件了,更符合云原生的做法。
3.2 Admin Service 的部署要点
Admin Service 的部署跟 Config Service 类似,但有几个区别。首先是副本数,Admin Service 主要是管理界面在调用,压力不大,1 到 2 个副本就够了。其次是资源,Admin Service 的内存占用比 Config Service 小一些,堆内存 512M 到 1G 就行。
apiVersion: apps/v1 kind: Deployment metadata: name: apollo-adminservice namespace: apollo spec: replicas: 1 selector: matchLabels: app: apollo-adminservice template: metadata: labels: app: apollo-adminservice spec: containers: - name: adminservice image: apolloconfig/apollo-adminservice:2.1.0 ports: - containerPort: 8090 env: - name: SPRING_DATASOURCE_URL value: "jdbc:mysql://mysql-service:3306/ApolloConfigDB?characterEncoding=utf8" - name: SPRING_DATASOURCE_USERNAME valueFrom: secretKeyRef: name: apollo-db-secret key: username - name: SPRING_DATASOURCE_PASSWORD valueFrom: secretKeyRef: name: apollo-db-secret key: password resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m"Admin Service 的端口是 8090,不是 8080,这个别搞错了。另外 Admin Service 也需要注册到 Meta Server,在 k8s 里就是通过 Service 暴露出去。Portal 会通过 Meta Server 找到 Admin Service 的地址,所以 Service 的名字要跟 Portal 配置里的一致。
3.3 Portal 的部署与访问入口
Portal 是管理界面,部署的时候要注意它的访问方式。因为它是给内部人员用的,一般不需要暴露到公网,用 Ingress 或者 NodePort 在内部访问就行。
apiVersion: apps/v1 kind: Deployment metadata: name: apollo-portal namespace: apollo spec: replicas: 1 selector: matchLabels: app: apollo-portal template: metadata: labels: app: apollo-portal spec: containers: - name: portal image: apolloconfig/apollo-portal:2.1.0 ports: - containerPort: 8070 env: - name: SPRING_DATASOURCE_URL value: "jdbc:mysql://mysql-service:3306/ApolloPortalDB?characterEncoding=utf8" - name: SPRING_DATASOURCE_USERNAME valueFrom: secretKeyRef: name: apollo-db-secret key: username - name: SPRING_DATASOURCE_PASSWORD valueFrom: secretKeyRef: name: apollo-db-secret key: password - name: APOLLO_PORTAL_ENVS value: "dev,prod" - name: DEV_META value: "http://apollo-configservice:8080" - name: PROD_META value: "http://apollo-configservice:8080"这里APOLLO_PORTAL_ENVS是配置 Portal 支持哪些环境,DEV_META和PROD_META是各个环境的 Meta Server 地址。在 k8s 里,Meta Server 其实就是 Config Service 的 Service,所以直接填 Config Service 的地址就行。
Portal 的端口是 8070,访问的时候用 Ingress 映射一下:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: apollo-portal-ingress namespace: apollo spec: rules: - host: apollo.internal.example.com http: paths: - path: / pathType: Prefix backend: service: name: apollo-portal port: number: 80703.4 Service 与网络配置
三个组件都需要 Service 来暴露。Config Service 和 Admin Service 的 Service 是给内部服务调用的,用 ClusterIP 就行。Portal 的 Service 也是 ClusterIP,通过 Ingress 对外暴露。
apiVersion: v1 kind: Service metadata: name: apollo-configservice namespace: apollo spec: selector: app: apollo-configservice ports: - port: 8080 targetPort: 8080 type: ClusterIP --- apiVersion: v1 kind: Service metadata: name: apollo-adminservice namespace: apollo spec: selector: app: apollo-adminservice ports: - port: 8090 targetPort: 8090 type: ClusterIP --- apiVersion: v1 kind: Service metadata: name: apollo-portal namespace: apollo spec: selector: app: apollo-portal ports: - port: 8070 targetPort: 8070 type: ClusterIP这里有个容易踩的坑:Apollo 的 Config Service 在启动时会把自己的地址注册到数据库的ServerConfig表里。如果 Service 的名字跟注册的地址不一致,客户端就找不到 Config Service。所以要么保证 Service 名字跟ServerConfig表里的配置一致,要么在启动时通过环境变量指定apollo.config-service.url。
我一般会在 Config Service 的 Deployment 里加一个环境变量:
- name: APOLLO_CONFIG_SERVICE_URL value: "http://apollo-configservice:8080"这样它注册到数据库的地址就是 k8s 内部的 Service 地址,客户端通过这个地址就能访问到。
4. 配置管理与客户端接入
4.1 Apollo 环境与集群的划分
Apollo 里有环境(Env)和集群(Cluster)两个概念,很多人一开始会搞混。环境是指 dev、test、prod 这种,不同环境的配置是完全隔离的,存在不同的数据库或者同一数据库的不同记录里。集群是指同一个环境下的不同分组,比如按机房划分,或者按业务线划分。
在 k8s 里部署的时候,我建议每个环境单独部署一套 Apollo,而不是用一套 Apollo 管所有环境。原因很简单:如果 dev 和 prod 共用一套 Apollo,万一 dev 环境有人误操作改了 prod 的配置,后果很严重。分开部署虽然多占点资源,但安全性高很多。
如果资源有限,至少要把数据库分开,Config Service 和 Portal 可以共用,但数据库一定要隔离。Apollo 支持在 Portal 里配置多个环境,每个环境指向不同的 Meta Server,这样也能实现隔离。
集群的划分看你的实际需求。如果只有一个机房,用默认的default集群就行。如果有多个机房,可以按机房划分集群,这样配置可以按机房差异化。比如 A 机房的数据库连接池大小跟 B 机房不一样,就可以用集群来区分。
4.2 命名空间与权限设计
Apollo 的命名空间(Namespace)是配置的最小隔离单位。每个应用可以有自己的私有命名空间,也可以关联公共命名空间。私有命名空间里的配置只有这个应用能读,公共命名空间可以被多个应用关联。
我一般会这样设计:每个微服务一个私有命名空间,存这个服务特有的配置,比如数据库连接、线程池大小。然后建几个公共命名空间,比如common-db、common-redis、common-mq,存各个服务共用的配置。这样改一处,所有关联的服务都能生效。
权限方面,Apollo 支持按项目、按命名空间授权。我建议给开发同学只读权限,给运维同学读写权限,给管理员全部权限。这样既能保证开发能查看配置,又能防止误操作。Apollo 的权限模型是基于角色的,可以跟公司的 LDAP 或者 OAuth 集成,实现单点登录。
4.3 客户端接入与本地缓存机制
客户端接入 Apollo 很简单,加个依赖、配个app.id和 Meta Server 地址就行。以 Spring Boot 为例:
<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>2.1.0</version> </dependency>然后在application.properties里配:
app.id=my-service apollo.meta=http://apollo-configservice:8080 apollo.bootstrap.enabled=true apollo.bootstrap.namespaces=application,common-db这里apollo.bootstrap.enabled=true很重要,它让 Apollo 在 Spring Boot 启动的早期阶段就加载配置,这样@Value注解才能拿到 Apollo 里的值。如果不加这个,配置加载会晚于 Bean 初始化,导致注入失败。
Apollo 客户端的本地缓存机制值得单独说一下。客户端从 Config Service 拉到配置后,会在本地磁盘写一份快照,默认路径是/opt/data/{appId}/config-cache。如果 Config Service 挂了,客户端会读本地快照,保证服务能继续运行。这个机制在 k8s 里特别有用,因为 Pod 重启后本地缓存还在(如果挂了持久卷的话),不会因为配置中心短暂不可用导致服务起不来。
不过要注意,本地缓存目录如果挂的是 emptyDir,Pod 重启后缓存就没了。我建议挂一个 hostPath 或者 PVC,这样缓存能持久化。当然,如果 Config Service 一直可用,缓存有没有持久化影响不大,但万一遇到 Config Service 故障,有缓存就能救命。
5. 常见问题与排查技巧
5.1 启动失败类问题排查
Apollo 部署过程中最常见的问题就是启动失败。我把遇到过的情况整理成一个排查表,方便对照。
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Pod CrashLoopBackOff | 数据库连不上 | 看 Pod 日志有没有 SQLException | 检查数据库地址、账号密码、网络策略 |
| 启动后立即退出 | JDK 版本不对 | 看日志有没有 UnsupportedClassVersionError | 换用官方镜像或升级 JDK |
| 健康检查失败 | 启动时间不够 | 看日志是否还在初始化 | 加大 initialDelaySeconds |
| Portal 打不开 | Ingress 配置错误 | 检查 Ingress 规则和 Service | 修正 Ingress 的 host 和 path |
| 客户端拉不到配置 | Meta Server 地址错误 | 检查客户端配置和 Service 地址 | 修正 apollo.meta 配置 |
数据库连不上是最常见的。有一次我部署的时候,Pod 一直重启,日志里报Communications link failure。排查了半天,发现是 k8s 的 NetworkPolicy 限制了 Apollo 命名空间访问 MySQL 命名空间。加上对应的 NetworkPolicy 规则就好了。所以如果你的集群开了网络策略,记得放行 Apollo 到 MySQL 的流量。
还有一个坑是数据库连接池配置。Apollo 默认的连接池大小是 10,如果客户端多、并发高,可能会不够用。可以在环境变量里调大:
- name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE value: "50"但也不是越大越好,MySQL 的最大连接数有限,调太大反而会导致连接被拒绝。一般 20 到 50 就够了,具体看你的客户端数量。
5.2 配置读取异常排查
配置读取异常一般表现为客户端拿到的配置跟 Portal 上显示的不一致,或者配置更新后客户端没生效。这种情况通常是以下几个原因。
第一是命名空间没关联。客户端配置里写的命名空间,必须在 Portal 里关联到对应的应用,否则读不到。我遇到过好几次,开发同学在 Portal 上建了命名空间,但忘了关联到应用,客户端一直读默认值。
第二是缓存没刷新。Apollo 客户端默认是长轮询,配置变更后 1 秒内会推送到客户端。但如果网络有问题,推送可能失败,客户端会退化成定时拉取,默认 5 分钟一次。如果发现配置更新后很久才生效,可以检查客户端的日志,看长轮询是否正常。
第三是配置优先级问题。Apollo 的配置优先级比本地application.properties高,但比命令行参数低。如果发现 Apollo 的配置没生效,检查一下是不是被命令行参数覆盖了。
排查的时候,可以打开客户端的 debug 日志:
logging.level.com.ctrip.framework.apollo=DEBUG这样能看到配置拉取的详细过程,包括请求了哪个 Config Service、返回了什么配置、有没有走本地缓存。这个日志在排查问题时非常有用。
5.3 性能与稳定性调优
Apollo 在生产环境跑起来之后,还需要做一些调优,保证稳定性和性能。
JVM 调优方面,Config Service 的堆内存建议设成 2G 到 4G,具体看配置数量。如果配置项特别多(比如几万个),堆内存要相应加大。GC 策略用 G1 就行,Apollo 官方也是推荐的 G1。
- name: JAVA_OPTS value: "-Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"数据库调优方面,Apollo 的读操作远多于写操作,所以可以给 MySQL 加只读副本,Config Service 读副本,Admin Service 写主库。不过 Apollo 本身不支持读写分离配置,需要自己在数据库层面做,比如用 ProxySQL 或者 MySQL Router。
缓存调优方面,Apollo 的 Config Service 内部有缓存,默认缓存 1000 个配置。如果配置数量超过这个值,会频繁淘汰,影响性能。可以在环境变量里调大:
- name: APOLLO_CONFIG_CACHE_SIZE value: "5000"限流与熔断方面,如果客户端数量特别多,Config Service 可能会被打爆。可以在 Service 前面加一层限流,或者用 k8s 的 HPA 自动扩缩容。我一般会配一个 HPA,CPU 超过 70% 就扩容:
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: apollo-configservice-hpa namespace: apollo spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: apollo-configservice minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 705.4 备份与容灾策略
配置数据是核心资产,丢了会很麻烦。所以备份策略一定要做好。
数据库备份是最基本的。我一般会配一个定时任务,每天凌晨全量备份一次 ApolloConfigDB 和 ApolloPortalDB,保留最近 30 天。如果数据量大,可以加增量备份。备份文件存到对象存储里,不要跟数据库放同一台机器。
配置导出方面,Apollo 的 Portal 支持导出配置为 Excel 或者 JSON。我建议每次重大变更前手动导出一次,存到版本控制里。这样即使数据库挂了,也能从导出文件恢复。
多活部署方面,如果对可用性要求极高,可以部署两套 Apollo,跨机房或者跨可用区。客户端配置多个 Meta Server 地址,一个挂了自动切到另一个。不过 Apollo 本身不支持多活同步,需要自己写同步工具,或者用数据库的主从复制。
容灾演练方面,定期模拟 Config Service 挂掉、数据库挂掉的情况,验证客户端能否用本地缓存继续运行,验证备份能否正常恢复。我一般每季度做一次演练,确保真出问题的时候不慌。
6. 一些实操心得与避坑建议
6.1 部署顺序与依赖关系
Apollo 的部署顺序有讲究,搞错了会浪费时间。正确的顺序是:先建数据库、导入脚本,再部署 Config Service 和 Admin Service,最后部署 Portal。因为 Portal 启动时会去连 Config Service 和 Admin Service,如果它们还没起来,Portal 会报错。
Config Service 和 Admin Service 之间没有强依赖,可以同时部署。但它们都依赖数据库,所以数据库必须先就绪。我一般会写一个 initContainer,等数据库端口通了再启动主容器:
initContainers: - name: wait-for-db image: busybox command: ['sh', '-c', 'until nc -z mysql-service 3306; do echo waiting for db; sleep 2; done']这样能避免因为数据库还没起来导致 Pod 反复重启。
6.2 版本升级的注意事项
Apollo 的版本升级不能跳太多,最好一个一个小版本升。比如从 1.9 升到 2.1,要先升到 2.0,再升到 2.1。因为数据库脚本可能有变化,跨版本升级容易出问题。
升级之前一定要备份数据库,然后在测试环境先升一遍,确认没问题再升生产。升级的时候,先升 Config Service 和 Admin Service,最后升 Portal。因为 Portal 是管理界面,晚点升影响不大,但 Config Service 如果升挂了,所有服务都受影响。
升级过程中,客户端可能会短暂拉不到配置,但因为本地缓存的存在,服务不会挂。所以升级窗口可以选在业务低峰期,但不用停服。
6.3 监控与告警配置
Apollo 上线之后,监控一定要配起来。我一般会监控这几个指标:
- Config Service 的 QPS 和响应时间
- Config Service 的 JVM 堆内存和 GC 次数
- 数据库的连接数和慢查询
- 客户端的配置拉取成功率
Apollo 本身提供了/health和/metrics接口,可以对接 Prometheus。我一般会配一个 ServiceMonitor,让 Prometheus 自动发现 Apollo 的 Pod:
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: apollo-monitor namespace: apollo spec: selector: matchLabels: app: apollo-configservice endpoints: - port: http path: /metrics interval: 30s告警规则方面,Config Service 的响应时间超过 1 秒、错误率超过 1%、Pod 重启次数超过 3 次,都应该告警。数据库连接数超过 80% 也要告警,避免连接池被打满。
6.4 安全加固建议
最后说几个安全方面的建议。Apollo 的 Portal 默认没有认证,任何人都能访问。生产环境一定要开启认证,可以对接公司的 SSO,或者用 Apollo 自带的账号体系。
Config Service 和 Admin Service 的接口默认也没有认证,如果暴露到公网很危险。在 k8s 里,用 NetworkPolicy 限制只有特定的命名空间能访问 Apollo:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: apollo-network-policy namespace: apollo spec: podSelector: matchLabels: app: apollo-configservice ingress: - from: - namespaceSelector: matchLabels: name: business ports: - port: 8080这样只有business命名空间的 Pod 能访问 Config Service,其他命名空间访问不了。数据库的密码用 Secret 存,不要写在 ConfigMap 里。Secret 最好开启加密存储,避免被直接读取。
还有一点,Apollo 的管理界面默认密码是apollo/admin,第一次登录后一定要改掉。我见过好几个团队部署完忘了改密码,结果被内部人员误操作改了配置,排查了半天才发现。
整体部署下来,Apollo 在 k8s 上跑还是挺稳的。关键是前期把数据库、网络、权限这些基础打好,后期做好监控和备份。我自己的经验是,第一次部署可能会花一两天,但把 YAML 模板整理好之后,后面再部署新环境就是改几个参数的事。如果你也在做类似的事情,希望这篇内容能帮你省点时间。