参考:

Apollo

Apollo

玩转apollo配置中心——通过源码进行多env环境搭建 - swing·wang - 博客园

一、Apollo(配置中心)

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置配置修改后能够实时推送到应用端,并且具备规范的权限流程治理等特性,适用于微服务配置管理场景。

Apollo服务端基于Spring Boot 和Spring Cloud开发,打包后可以直接运行,不需要额外部署Tomcat等应用容器。

Apollo客户端不依赖任何框架,能够运行于所有Java运行时环境,同时对Spring/Spring Boot 环境也有较好的支持。

二、Apollo特性

A、统一管理配置
Apollo提供了一个统一界面集中式管理不同的环境(environment)、不同的集群(cluster)、不同的命名空间(namespace)的配置。

同一份代码部署在不同的集群,可以有不同的配置,比如zk的地址。

通过命名空间(namespace)可以很方便的支持多个不同应用共享同一份配置,同时还允许应用对共享的配置进行覆盖。

B、配置修改实时生效(热发布)
用户在Apollo修改完配置并发布后,Apollo客户端能实时(1s)接收到最新的配置,并通知到应用程序。

C、版本发布管理
所有的配置发布都有版本概念,从而可以方便的支持配置的回滚。

D、客户端配置信息的监控
可以方便的看到配置在被哪些实例使用。

E、便于集成
支持Spring Placeholder,Annotation和Spring Boot 的ConfigurationProperties,方便应用使用。

三、配置中心解决的痛点,在这样的背景下,Apollo配置中心应运而生。Apollo支持四个维度Key-Value格式的配置分布式配置中心--Apollo - hanease - 博客园

  • Application(应用) 实际使用配置的应用,Apollo客户端在运行时需要知道当前应用是谁,从而可以去获取对应的配置。每个应用都有对应的身份标识--appId,需要在代码中配置
  • Environment(环境) 配置对应的环境,Apollo客户端需要知道当前应用出于哪个环境,,从而可以去获取应用的配置;环境和代码无关,同一份代码部署在不同的环境就应该获取不同环境的配置;环境默认是通过读取机器上的配置(server.properties的env属性)指定的
  • Cluster(集群) 一个应用下不同实例的分组,例如按照不同数据中心划分,把上海机房的实例分为一个集群、把深圳机房的实例分为一个集群;对于不同的Cluster,同一个配置可以有不一样的值;集群默认是通过读取机器上的配置指定的(server.properties的idc属性)
  • Namespace(命名空间) 一个应用下不同配置的分组,是配置项的集合,可以简单地把Namespace类别为(配置)文件,不同类型的配置存放在不同的文件中,例如数据库配置文件、RPC配置文件、应用自身的配置文件等;应用可以直接读取到公共组件的配置namespace,例如DAL、RPC等;应用也可以通过继承公共组件的配置namespace来对公共组件的配置做调整,如DAL的初始数据库连接数

四、Apollo执行流程

在这里插入图片描述

  1. Apollo客户端和Apollo服务端保持长连接,从而能在第一时间获得配置更新的推送
  2. Apollo客户端会定时从Apollo服务端拉取应用的最新配置
    • fallback机制,防止推送机制失败导致配置不更新
    • Apollo客户端定时拉取上报本地版本,所以一般情况下,对于定时拉取的操作,服务端会返回304-Not Modified(?)
    • 拉取频率为5分钟一次,客户端可以指定
  3. Apollo客户端从Apollo服务端获取到应用的最新配置后,会保存在内存中
  4. 客户端会把从服务端获取到的配置在本地文件系统缓存一份
    • 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
  5. 应用程序从Apollo客户端获取最新的配置,订阅配置更新通知

五、Apollo的安全性和可用性分析

A、安全
配置文件除了改变程序行为之外,可能还包含一些敏感信息,比如: 开放接口的tokenId、secretKey、数据库账号等
Apollo有一个默认的超级用户,可以在安装的时候初始化密码。
Apollo登录可以对接SSO,使用域账户登录。具体参考:Portal实现用户登录功能
Apollo在创建每个应用(映射到物理上的应用:转单、拉单)的时候,强制创建者指定一个owner,只有这个owner用户拥有该应用配置的编辑、发布权限。每个namespace(相当于.properties的概念)除了owner拥有全部权限,也可以给他人单独授权,这样就可以将配置项按照不同的安全级别划分到不同的namespace中,从而实现更小粒度的权限控制:比如db相关的namespace只有运维能编辑、发布。

B、可用性
配置中心作为基础服务,可用性要求非常高,下面描述了不同场景下Apollo的可用性:

1.某台config service下线,无影响,config service无状态,客户端重连其它config service。

2.所有config service下线,客户端无法读取最新配置,Portal无影响,客户端重启时,可以读取本地缓存配置文件。

3.某台admin service下线,无影响,Admin service无状态,Portal重连其它admin service。

4.所有admin service下线,客户端无影响,portal无法更新配置。

5.某台portal下线,无影响,Portal域名通过slb绑定多台服务器,重试后指向可用的服务器。

6.全部portal下线,客户端无影响,portal无法更新配置。

7.某个数据中心下线,无影响,多数据中心部署,数据完全同步,Meta Server/Portal域名通过slb自动切换到其它存活的数据中心。

按照官方的分析,只要部署时避免ConfigService(包含Meta Server)、AdminService、Protal Server单点。并且对各节点加上存活监控(以便短时间内dump节点的恢复),就能在保证高可用性。

在admin可以看到哪些实例(具体到IP)应用了哪些配置,上面原理中有体现,客户端也会定时上报自己的配置版本,避免了「推送失败,而服务端不知道」的情况。

六、web-admin端部署

1.Meta Server:这个一般是针对分布式存储集群而言的,metadata server存储的是实际的数据的位置(例如文件1在哪个机器上),又称元数据服务器。
2.ConfigService: 用于提供给client获取配置信息,以及配置更新后通知client的服务;      ConfigService仅为client提供服务,且每个环境对应相应的ConfigService集群。
3. Admin Service:提供配置管理接口,提供配置修改发布接口,服务于管理界面Portal
4. Client:为应用获取配置,支持实时更新,通过MataServer获取ConfigService服务列表,使用客户端软负载SLB方式调用ConfigService
5. Portal:配置管理界面,通过MetaServer获取AdminService的服务列表,使用客户端负载SLB方式调用AdminService

七、应用端接入

 

7.2、Java程序集成Apollo

1)首先在pom.xml中引入apollo的引用。
2)告诉应用程序获取的是apollo配置的哪个项目的配置信息,即配好appid

3)将具体的环境配置(比如dev,uat,pro)直接配在具体的服务器上,将环境配置信息与应用进行一个解耦。一个机器只要确定是用于生产环境它就几乎确定下来,不会变了。在服务器中的/opt/settings/server.properties文件中进行配置具体的环境。直接告诉应用程序我这台机器指向哪几台机器(具体的环境)。
4)通过代码调用apollo的配置信息,在resources/META-INF下填写配置信息,配置该项目对应的namespace即可使用
5)使用方法直接在变量上面加@Value(“${***}”)即可

八、工作原理

1、Config Service 提供配置的读取、推送等功能,服务对象是Apollo客户端
2、Admin Service 提供配置的修改、发布等功能,服务对象是Apollo Portal (管理界面)
3、Config Service和 Admin Service都是多实例、无状态部署,所以需要将自己注册到Eureka中并保持心跳
4、在Eureka之上我们架了一层Meta Server用于封装Eureka的服务发现接口
5、Client通过域名访问Meta Server获取Config Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Client侧会做load balance、错误重试
6、Portal 通过域名访问Meta Server获取Admin Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Portal侧会做load balance、错误重拾
7、为了简化部署。我们实际上会把Config Service、Eureka和Meta Server三个逻辑角色部署在同一个JVM进程中

架构:

部署功能作用环境ip
portal项目配置管理系统,你访问的8070端口就是访问的这个服务,该服务包含了后台管理系统以及创建的项目信息,使用portal这个数据库172.16.10.66
configserver和adminserver

1.配置服务,向其他业务系统提供配置推拉更新的服务,业务系统和该服务建立连接来获取项目配置,使用config这个数据库;

2.环境管理,负责指定环境的所有配置修改、发布、回滚、以及历史记录等等一系列的后台管理功能,使用config这个数据库

dev172.16.10.67
configserver和adminserver

1.配置服务,向其他业务系统提供配置推拉更新的服务,业务系统和该服务建立连接来获取项目配置,使用config这个数据库;

2.环境管理,负责指定环境的所有配置修改、发布、回滚、以及历史记录等等一系列的后台管理功能,使用config这个数据库

sit172.16.10.68
configserver和adminserver

1.配置服务,向其他业务系统提供配置推拉更新的服务,业务系统和该服务建立连接来获取项目配置,使用config这个数据库;

2.环境管理,负责指定环境的所有配置修改、发布、回滚、以及历史记录等等一系列的后台管理功能,使用config这个数据库

prod172.16.10.69

部署图 :

一个portal对应自己的数据库portaldb和三个环境(每个环境都要有自己的数据库和两个服务,既configserver和adminserver两个服务和两个服务对应的数据库configDB)

一、Apollo配置中心介绍

Apollo(阿波罗)是携程框架部门研发的开源配置管理中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的额权限、流程治理等特性。

Apollo支持的四个维度管理的配置:

  • application (应用)
  • environment (环境)
  • cluster (集群)
  • namespace (命名空间)

同时,Apollo基于开源模式开发,开源地址:https://github.com/ctripcorp/apollo

 二、Apollo配置中心部署

1.环境准备

jdk8

mysql8

2.下载安装包

前往github下载安装包 

[root@harbor ~]# wget https://github.com/apolloconfig/apollo/releases/download/v2.0.1/apollo-portal-2.0.1-github.zip
[root@harbor ~]# wget https://github.com/apolloconfig/apollo/releases/download/v2.0.1/aapollo-adminservice-2.0.1-github.zip
[root@harbor ~]# wget https://github.com/apolloconfig/apollo/releases/download/v2.0.1/apollo-configservice-2.0.1-github.zip



 三个服务需要的数据库初始化文件:

[root@localhost db]# wget https://raw.githubusercontent.com/apolloconfig/apollo/v2.0.1/scripts/sql/apolloportaldb.sql

[root@localhost db]# wget https://raw.githubusercontent.com/apolloconfig/apollo/v2.0.1/scripts/sql/apolloconfigdb.sql

3.服务介绍

这里先稍微提一下,Apollo配置中心的四个配置维度以及服务端的三个服务

A:服务端三个服务

1)portal——项目配置管理系统,你访问的8070端口就是访问的这个服务,该服务包含了后台管理系统以及创建的项目信息,使用portal这个数据库

2)configService——配置服务,向其他业务系统提供配置推拉更新的服务,业务系统和该服务建立连接来获取项目配置,使用config这个数据库

3)adminService——环境管理,负责指定环境的所有配置修改、发布、回滚、以及历史记录等等一系列的后台管理功能,使用config这个数据库

上面三个就是三个JVM进程。

a:portal:端口8070

b:configService进程里面包含如下三个:Meta Server ,   Eureka(端口8080),    ConfigService

c:adminService:端口8090

图:

B:配置的四个维度

1)application——项目,项目可以认为是一级维度,用来区分不同项目的配置

2)environment——环境,诸如dev、sit、uat等等的这类环境,Apollo的这个环境要支持的话稍微有些复杂,后面再做详细交流,一个环境对应了一套配置服务,如果你要支持三个环境,那就要启动2*3+1=7个Apollo服务

3)cluster——集群,携程为了自己的某些实际情况加的一个维度,个人感觉对大部分项目都用不上。比如同一个项目,北京部署了2个,上海部署了2个,北京部署的访问的oss为***beijing.com,上海部署的访问的oss为***shanghai.com,这种情况就需要用到cluster,一般情况下我们都用默认,后面再做详细交流

4)namespace——命名空间,这个比较好,比如一个项目的配置,包含了关系型数据库信息、缓存数据库信息、支付相关配置等等,为了避免所有的配置都在一个里面不便管理,就可以在配置中添加多个命名空间,关系型数据库可以创建一个“datasource”命名空间,缓存数据库可以创建“cache”,支付的配置可以创建“pay”,这样项目在读取配置的时候直接读取这三个中的一个或多个配置,后面再做详细交流

4. Apollo项目结构介绍

前面介绍过Apollo项目服务端包含的三个服务:

  • 配置提供服务(config),使用config库,负责指定环境(env)配置的推送、获取,向客户端(业务项目)提供服务,间接接受来自admin的调用      另内置eurka提供该环境所有config和admin的注册功能以提供多节点部署 高可用
  • 配置管理服务(admin),使用config库,负责指定环境(env)配置的修改、发布、历史记录,主要向portal提供服务,通过数据库间接调用config服务
  • 配置更新服务(portal),使用portal库,负责配置中心的页面渲染,功能提交,协调各环境(env)进行配置的修改、发布,调用admin服务

       官方也说  Apollo核心是四个服务,还有一个客户端,现在我们从服务端的角度来讲环境搭建,暂时忽略客户端,请大家见谅,后面我们讨论springboot整合Apollo实现配置的时候再仔细说客户端(client)

通过以上三个服务,我们很明显能看出,一个config和admin,使用同一个库,负责一个环境(env),而portal这个服务是全局唯一的,在启动的时候需要指定它来管理哪些环境的config和admin,一张图表示如下

                   ---->     dev环境  admin服务  <---->config服务

portal          ---->     sit环境   admin服务   <---->config服务

                   ---->     uat环境  admin服务  <---->config服务

portal启动时必须通过启动参数 -Ddev_meta=http://xxdevxx:port  -Dsit_meta=http://xxsitxx:port  -Duat_meta=http://xxuatxx:port来指定当前配置中心portal所能管理的几个环境,配置后面的链接为该环境(env)的eurka注册中心的链接(eurka默认集成在config服务中,即config服务的链接)

并且在portal库的serverconfig表中修改apollo.portal.envs参数的值为dev,sit,uat

当你在后台管理系统(portal)中打开一个环境(env)时,portal会通过该env找到对应的meta配置的admin服务地址,调用该admin服务读取数据库里保存的集群、命名空间以及具体配置信息,返回前端后进行页面渲染

这个过程可概括如下  前端 --> portal --> eurka --> admin --> 读取db      然后原路返回

当你在后台管理系统(portal)中的某个环境(env)中,提交或者修改了一项配置,portal会通过该env找到对应的meta配置的admin服务地址,调用该admin服务进行配置的提交修改,并将修改结果返回前端

当你******,发布配置的时候,portal******,调用该admin服务的发布配置接口,admin服务会在config库中写一条记录,然后返回结果到前端。此时config服务有一个线程会定时读取库中是否有新记录,读取到记录后通知所有和自己有注册关系的客户端(client)来更新配置,由此实现发布配置后,客户端配置的自动更新

综上所述,当我们部署多环境的时候,每个环境(env)必须有自己单独的数据库,用于存放该环境的所有配置、发布记录信息,也必须有配套的admin和config服务,如果要部署五个环境,就需要五个数据库,每个数据库对应启动一套admin和config服务,即至少2*5个服务,可以参考上面那个简易的图来理解

二、多环境部署

172.16.10.67 dev环境的数据库

docker run --name mysql-dev -p 3306:3306 -v /etc/localtime:/etc/localtime -v /opt/apollo/mysql/logs:/var/log/mysql -v /opt/apollo/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456aA  --privileged=true -d  harbor.jettech.com/jettechtools/mysql:8.0.28

172.16.10.68 sit 环境的数据库

[root@localhost ~]# docker run --name mysql-sit -p 3306:3306 -v /etc/localtime:/etc/localtime -v /opt/apollo/mysql/logs:/var/log/mysql -v /opt/apollo/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456aA  --privileged=true -d  harbor.jettech.com/jettechtools/mysql:8.0.28

172.16.10.69 prod 环境的数据库

[root@localhost adminserver]# docker run --name mysql-prod -p 3306:3306 -v /etc/localtime:/etc/localtime -v /opt/apollo/mysql/logs:/var/log/mysql -v /opt/apollo/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456aA  --privileged=true -d  harbor.jettech.com/jettechtools/mysql:8.0.28

copy db到容器中 

[root@localhost dev]# ls db/
apolloconfigdb.sql  apolloportaldb.sql  readme
[root@localhost dev]# docker cp db 1a89ea16:/opt



source /opt/db/apolloportaldb.sql 

source /opt/db/apolloconfigdb.sql

 数据库文件说明:apolloconfigdb.sql

INSERT INTO `ServerConfig` (`Key`, `Cluster`, `Value`, `Comment`)
VALUES
    ('eureka.service.url', 'default', 'http://localhost:8080/eureka/', 'Eureka服务Url,多个service以英文逗号分隔'),
    ('namespace.lock.switch', 'default', 'false', '一次发布只能有一个人修改开关'),
    ('item.key.length.limit', 'default', '128', 'item key 最大长度限制'),
    ('item.value.length.limit', 'default', '20000', 'item value最大长度限制'),
    ('config-service.cache.enabled', 'default', 'false', 'ConfigService是否开启缓存,开启后能提高性能,但是会增大内存消耗!');

说明:eureka.service.url 可以写默认的 因为configserver和adminserver服务都部署在一台服务器而且configserver和eureka是一个进程,如果adminserver和configserver部署在两台不同的机器那么就要写具体的IP了目的是adminserver要注册到eureka。每个环境都有自己的eureka,有几个环境就有几个eureka,而且注册进来的服务只有configserver和adminserver两个。

  数据库文件说明:apolloportaldb.sql

原来的:
INSERT INTO `ServerConfig` (`Key`, `Value`, `Comment`)
VALUES
    ('apollo.portal.envs', 'dev', '可支持的环境列表'),
    ('organizations', '[{\"orgId\":\"TEST1\",\"orgName\":\"样例部门1\"},{\"orgId\":\"TEST2\",\"orgName\":\"样例部门2\"}]', '部门列表'),
    ('superAdmin', 'apollo', 'Portal超级管理员'),
    ('api.readTimeout', '10000', 'http接口read timeout'),
    ('consumer.token.salt', 'someSalt', 'consumer token salt'),
    ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace'),
    ('configView.memberOnly.envs', 'pro', '只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔'),
    ('apollo.portal.meta.servers', '{}', '各环境Meta Service列表');


自己的:

INSERT INTO `ServerConfig` (`Key`, `Value`, `Comment`)
VALUES
    ('apollo.portal.envs', 'dev,sit,prod', '可支持的环境列表'),
    ('organizations', '[{\"orgId\":\"jettoloader\",\"orgName\":\"性能测试组\"},{\"orgId\":\"jettopro\",\"orgName\":\"一体化平台\"},{\"orgId\":\"jettomock\",\"orgName\":\"挡板平台\"},{\"orgId\":\"jettodata\",\"orgName\":\"数据平台\"}]', '部门列表'),
    ('superAdmin', 'admin', 'Portal超级管理员'),
    ('api.readTimeout', '10000', 'http接口read timeout'),
    ('consumer.token.salt', 'someSalt', 'consumer token salt'),
    ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace'),
    ('configView.memberOnly.envs', 'dev,sit,prod', '只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔'),
    ('apollo.portal.meta.servers', '{dev_meta=http://172.16.10.67:8080,sit_meta=http://172.16.10.68:8080,prod_meta=http://172.16.10.69:8080}', '各环境Meta Service列表');





/*INSERT INTO `Users` (`Username`, `Password`, `UserDisplayName`, `Email`, `Enabled`)
VALUES
        ('apollo', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'apollo', 'apollo@acme.com', 1); */
INSERT INTO `Users` (`Username`, `Password`, `UserDisplayName`, `Email`, `Enabled`)
VALUES
        ('admin', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'admin', 'wu_bo2@hoperun.com', 1);

/*INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('apollo', 'ROLE_user');*/
INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('admin', 'ROLE_user');


在172.16.10.67,172.16.10.68,172.16.10.69 三个服务器启动服务。修改对应的数据库地址

[root@localhost configserver]# cat  config/application-github.properties 
# DataSource
spring.datasource.url = jdbc:mysql://172.16.10.67:3306/ApolloConfigDB?characterEncoding=utf8
spring.datasource.username = root
spring.datasource.password = 123456aA

环境启动: 

[root@localhost configserver]# ./scripts/startup.sh
[root@localhost adminserver]# ./scripts/startup.sh

创建portal启动程序

     由于portal是管理每个环境的一个后台管理系统,所以portal在运行的时候需要指定自己所管理的各个环境的eurka注册中心地址,这样portal才能通过注册中心调用相应的admin服务进行配置的修改、发布等操作。

数据库也要改配置文件也要改
配置文件如下:

[root@localhost portal]# cat config/apollo-env.properties 
#
# Copyright 2022 Apollo Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#local.meta=http://localhost:8080
dev.meta=http://172.16.10.67:8080
sit.meta=http://172.16.10.68:8080
prod.meta=http://172.16.10.69:8080
#lpt.meta=${lpt_meta}
#pro.meta=http://pro.apollo.jettech.com:8084

apolloportaldb.sql数据库如下:

INSERT INTO `ServerConfig` (`Key`, `Value`, `Comment`)
VALUES
    ('apollo.portal.envs', 'dev,sit,prod', '可支持的环境列表'),
    ('organizations', '[{\"orgId\":\"jettoloader\",\"orgName\":\"性能测试组\"},{\"orgId\":\"jettopro\",\"orgName\":\"一体化平台\"},{\"orgId\":\"jettomock\",\"orgName\":\"挡板平台\"},{\"orgId\":\"jettodata\",\"orgName\":\"数据平台\"}]', '部门列表'),
    ('superAdmin', 'admin', 'Portal超级管理员'),
    ('api.readTimeout', '10000', 'http接口read timeout'),
    ('consumer.token.salt', 'someSalt', 'consumer token salt'),
    ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace'),
    ('configView.memberOnly.envs', 'dev,sit,prod', '只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔'),
    ('apollo.portal.meta.servers', '{dev_meta=http://172.16.10.67:8080,sit_meta=http://172.16.10.68:8080,prod_meta=http://172.16.10.69:8080}', '各环境Meta Service列表');





/*INSERT INTO `Users` (`Username`, `Password`, `UserDisplayName`, `Email`, `Enabled`)
VALUES
        ('apollo', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'apollo', 'apollo@acme.com', 1); */
INSERT INTO `Users` (`Username`, `Password`, `UserDisplayName`, `Email`, `Enabled`)
VALUES
        ('admin', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'admin', 'wu_bo2@hoperun.com', 1);

/*INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('apollo', 'ROLE_user');*/
INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('admin', 'ROLE_user');


portal的环境集成

A、数据库的表ServerConfig中apollo.portal.envs属性值增加pro等环境信息,以英文逗号隔开。
update ServerConfig set Value='dev,pro' WHERE key='apollo.portal.envs';
B、portal的启动脚本中增加-Dpro_meta=等环境的配置即可。

效果图: 

 

apollo集群配置

Apollo配置中心的四个维度其中有一个集群,默认都是没有配置集群的(default),如果有需要,可以这样添加

1.Apollo配置中心新增一个集群

打开Apollo控制台,进入项目,在界面左下方找到添加集群按钮并点击

然后在添加集群页面输入集群名称(我这里用的beijing),勾选所要生效的环境确定即可

2.在springboot项目中使用

springboot中如果不指定集群配置,即使用默认的集群default,如果需要使用新的集群,可在配置文件或者启动参数中加这个配置

apollo.cluster=beijing

最后再在beijing这个集群中添加一些配置并发布,比如

test=8888
server.port=8003

再重启springboot即可看到服务端口变为8003,再打开浏览器访问http://localhost:8085/hello即可看到页面上显示的“hello 8888”

多环境配置

springboot使用Apollo配置中心的多环境配置时有些特殊

并不是通过指定某个env等于dev或者test来确定,而是指定apollo.meta=http://1111111:8080,这个地址为apollo配置中心对应环境的eurka地址

比如以上篇文章的环境为例,要使用dev环境就配置apollo.meta=http://localhost:8081,要使用uat环境就配置apollo.meta=http://localhost:8083

这里我们使用sit环境

apollo.meta=http://localhost:8082

springboot中配置好后,再在apollo的sit环境中添加相应配置并发布,然后重启springboot即可看到配置已生效

多namespace配置

在apollo配置中心里多namespace配置在项目配置界面左下角点击添加namespace按钮,然后在添加页面选择创建namespace,然后输入名称确定即可在项目配置页面看到新建的namespace空间

在这个空间里添加自己的配置

在springboot中指定项目所使用的命名空间稍微有点不同,这里支持同时配置多个namespace,多个用英文逗号隔开

apollo.bootstrap.namespaces=application,datasources,pay.yml

PS1:这里需要注意的是,如果这多个namespace中包含相同的配置,那么以配置靠前的namespace中的配置为准

PS2:如果apollo中添加的namespace配置格式为yml等非properties的时候,springboot中配置应该带上.后缀名,比如我的pay.yml

然后在各个环境中添加好相应的配置并发布,再重启springboot即可看到多namespace的配置已生效

个人建议

首先对于四个维度app,env,cluster,namespace的应用

application,即项目,每个springboot项目都要设置自己的名称spring.application,name,然后apollo配置中心中创建的项目名称尽量要和springboot的项目名称相同,这样在配置中指定app.id=${spring.appcation.name}即可,人员排查配置问题的时候也方便检查,其实重点就是人员肉眼观察起来方便,可读性强

env,环境,这个和springboot的env稍微有点差别,建议根据自己公司情况设置3-5个环境,自己公司有几个运行环境,springboot就配置几套配置文件(application-dev.yml,application-sit.yml....) apollo配置中心也相应的配置几个不同的环境(dev、sit....),在application-dev.yml中配置apollo.meta=http://apollo.dev.meta:8080,在application-sit.yml中配置apollo.meta=http://apollo.sit.meta:8080,这样来指定不同环境所读取的apollo配置

cluster,集群,这个配置可以根据自己项目实际,进行添加配置,目前中小型公司用到的不多,个人觉得作用不是很大,几乎可以忽略。但是在开发dev环境的时候倒是可以用一下,因为开发过程中,我们很多开发人员喜欢自己用自己单独部署的数据库或者其他配置信息,这样在启动参数中添加会很麻烦,改配置文件也涉及到代码提交分支切换等会很麻烦,可以考虑开发人员根据自己实际,在dev环境下新建一个属于自己专属的cluster,启动参数中专门指定自己dev模式下读取的cluster为自己专属的集群即可,-Dapollo.cluster=my-name,这样可以避免启动参数过于复杂以及配置文件有修改变动导致代码提交不方便

namespace,命名空间,这个配置是区分项目中不同类型的配置进行归类存放的,比如数据库信息的配置可以创建一个namespace名称为datasource在里面存放,缓存信息可以创建cache在里面存放,支付相关可以创建pay在里面存放等等。

另外,命名空间有区分共用还是私用,前面我们介绍的全部是私用的,关于共用的比如缓存信息配置,可能多个项目共用一个缓存服务器,这样可以创建一个public的namespace,里面存放缓存相关配置,任何项目都可以在namespace中写上这个cache以引入缓存配置,同时这个公共namespace已经不属于某个项目了,而是整个环境(env)中全局唯一了,所以需要注意命名

添加APOLLO预先定义好的环境

apollo多环境配置 - 灰信网(软件开发博客聚合)

1.新增一套Config Service、Admin Service和ApolloConfigDB

一套Portal可以管理多个环境,但是每个环境都需要独立部署一套Config Service、Admin Service和ApolloConfigDB。

所以新增环境时,需要重新部署一套Config Service、Admin Service和ApolloConfigDB。

另外如果是为已经运行了一段时间的Apollo配置中心增加环境,还对新的环境做初始化(把旧的Config Service和Admin Service数据拷贝到新的环境)。

2.修改ApolloProtalDB.serverconfig中的apollo.portal.envs - 可支持的环境列表

ps:也可以启动Apollo后,在管理界面更改系统参数-apollo.portal.envs

默认值是dev,如果portal需要管理多个环境的话,以逗号分隔即可(大小写不敏感),如:

DEV,FAT,UAT,PRO

3修改apollo-portal/config/apollo-env.properties

apollo-portal安装包下的apollo-env.properties文件中修改环境配置:

把添加的环境配置相对应的config-server地址


local.meta=http://localhost:8080
dev.meta=http://localhost:8080
fat.meta=
uat.meta=
lpt.meta=${lpt_meta}
pro.meta=http://localhost:808

4修改项目中的apollo-meta和evn配置:

  • 本地开发环境下,通过server.properties配置文件配置apollo.metaevn环境

  • 生产环境或者测试环境,通过编译时传参或者启动应用时传参的方式进行配置,具体如下:

    maven:

    mvn -clean install -Dapollo.meta=http://config-service-url -Deureka-url=http://172.16.10.1:8080/eureka/

    java System property:

    java -jar mainWeb.jar --apollo.meta=http://config-service-url --app.id=YOUR-APP-ID

    --eureka-url=http://172.16.10.1:8080/eureka/

二、 添加自定义环境

果需要添加的环境不是Apollo预先定义的环境,请参照如下步骤操作:

1.假设需要添加的环境名称叫beta

2.修改源码中的com.ctrip.framework.apollo.core.enums.Env类,在其中加入BETA枚举:

public enum Env{

LOCAL, DEV, BETA, FWS, FAT, UAT, LPT, PRO, TOOLS, UNKNOWN;

...

}

3.修改源码中的com.ctrip.framework.apollo.core.enums.EnvUtils类,在其中加入BETA枚举的转换逻辑:

public final class EnvUtils {
  
  public static Env transformEnv(String envName) {
    if (StringUtils.isBlank(envName)) {
      return Env.UNKNOWN;
    }
    switch (envName.trim().toUpperCase()) {
      ...
      case "BETA":
        return Env.BETA;
      ...
      default:
        return Env.UNKNOWN;
    }
  }
}

4 修改源码中的apollo-env.properties,增加beta.meta占位符:

local.meta=http://localhost:8080
dev.meta=${dev_meta}
fat.meta=${fat_meta}
beta.meta=${beta_meta}
uat.meta=${uat_meta}
lpt.meta=${lpt_meta}
pro.meta=${pro_meta}

5修改源码中的com.ctrip.framework.apollo.core.internals.LegacyMetaServerProvider类,增加读取BETA环境的meta server地址逻辑:


public class LegacyMetaServerProvider {
    ...
    domains.put(Env.BETA, getMetaServerAddress(prop, "beta_meta", "beta.meta"));
    ...
}

6 新增一套Config Service、Admin Service和ApolloConfigDB与添加Apollo预先定义好的环境的做法一致

7.修改ApolloProtalDB.serverconfig中的apollo.portal.envs - 可支持的环境列表

与添加Apollo预先定义好的环境的做法一致

8.修改项目中的apollo-meta和evn配置

与添加Apollo预先定义好的环境的做法一致

LDAP账户一体化:

Portal 实现用户登录功能 · apolloconfig/apollo Wiki · GitHub

1.1 配置application-ldap.yml

[root@localhost portal]# cat  config/application-ldap.yml 
#
# Copyright 2022 Apollo Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ldap sample for open ldap, need to rename this file to application-ldap.yml to make it effective
# cn=admin,dc=rd,dc=hoperun,dc=com 123456aA
spring:
  ldap:
    base: "dc=rd,dc=hoperun,dc=com"
    username: "cn=admin,dc=rd,dc=hoperun,dc=com" # 配置管理员账号,用于搜索、匹配用户
    password: "123456aA"
    searchFilter: "(uid={0})"  # 用户过滤器,登录的时候用这个过滤器来搜索用户
    urls:
    - "ldap://172.16.10.1:389"

ldap:
  mapping: # 配置 ldap 属性
    objectClass: "inetOrgPerson" # ldap 用户 objectClass 配置
    loginId: "uid" # ldap 用户惟一 id,用来作为登录的 id
#    rdnKey: "uid" # ldap rdn key,可选项,如需启用group search需要配置
    userDisplayName: "cn" # ldap 用户名,用来作为显示名
    email: "mail" # ldap 邮箱属性
#  group: # 启用group search,可选配置,启用后只有特定group的用户可以登录apollo
#    objectClass: "posixGroup"  # 配置groupClassName
#    groupBase: "ou=group" # group search base
#    groupSearch: "(&(cn=dev))" # group filter
#    groupMembership: "memberUid" # group memberShip eg. member or memberUid

1.2 配置startup.sh

修改scripts/startup.sh,指定spring.profiles.active为github,ldap。

SERVICE_NAME=apollo-portal
## Adjust log dir if necessary
LOG_DIR=/opt/logs/100003173
## Adjust server port if necessary
SERVER_PORT=8070

export JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=github,ldap"

SpringBoot整合Apollo - hanease - 博客园

idea:

application.yaml

server:
  port: 7001

app:
  id: appid-jettoloader-eureka
apollo:
  meta: http://172.16.10.67:8080
  bootstrap:
    enabled: true
    eagerLoad:
      enabled: true
logging:
  level:
    com: debug
#  安全认证
spring:
  #cloud:
    #inetutils:
      #ignored-interfaces: ['VMware.*','Hyper-V.*','docker*]
      #preferred-networks: ['192.168.42']
      #use-only-site-local-interfaces: true
  application:
    name: jettoloader-eureka
  security:
    basic:
      enabled: true
    user:
      name: admin
      password: 123456aA
eureka:
  environment: jettoloader-dev
  instance:
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
    hostname: 192.168.42.64
    prefer-ip-address: false
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/

  project:
    version: @project.version@

JettoloaderApplication.java

package com.jettech.jettoloader;

import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@SpringBootApplication
@EnableEurekaServer
@EnableApolloConfig
public class JettoloaderApplication {

    public static void main(String[] args) {
        SpringApplication.run(JettoloaderApplication.class, args);
    }
    @EnableWebSecurity
    static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
            http.csrf().disable();
        }
    }
}
loggingSystem.java
package com.jettech.jettoloader;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.Set;

@Configuration
public class LoggerConfig {
    private static final Logger logger = LoggerFactory.getLogger(LoggerConfig.class);
    private static final String LOGGER_TAG = "logging.level.";


    @Autowired
    private LoggingSystem loggingSystem;

    @ApolloConfig
    private Config config;

    @ApolloConfigChangeListener
    private void configChangeListter(ConfigChangeEvent changeEvent) {
        refreshLoggingLevels();
    }
    @PostConstruct
    private void refreshLoggingLevels() {
        Set<String> keyNames = config.getPropertyNames();
        for (String key : keyNames) {
            if (StringUtils.containsIgnoreCase(key, LOGGER_TAG)) {
                String strLevel = config.getProperty(key, "info");
                LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
                loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level);
                logger.info("{}:{}", key, strLevel);
            }
        }
    }
}
TestController.java
package com.jettech.jettoloader;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class TestController {

    @Value( "${date.value}" )
    String dateValue;

    @GetMapping("test")
    public String test() {
        return "打印配置中心的 dateValue 值: "+ dateValue;
    }

    @GetMapping("test1")
    public void test1() {
        log.info("当前配置中心的 dateValue 值 = {}",dateValue);
    }

    @GetMapping("test2")
    public void test2() {
        log.debug("我是 debug 打印出的日志");
        log.info("我是 info 打印出的日志");
        log.error("我是 error 打印出的日志");
    }

}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jettech</groupId>
    <artifactId>jettoloader</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>jettoloader</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2021.0.3</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐