14.2 运行配置服务器

14.2 运行配置服务器

Spring Cloud Config Server为配置数据提供了中心化的数据源。与Eureka类似,我们可以将Config Server视为另一个微服务,在更大的应用中,它的角色就是为应用中的其他服务提供配置数据。

Config Server暴露了REST API,客户端(也就是其他的服务)可以通过它来消费配置属性。通过Config Server提供的配置来源于Config Server之外,通常来源于一个像Git这样的源码控制系统。图14.1阐述了它是如何运行的。

image-20211021155947793

图14.1 Spring Cloud Config Server通过支撑的Git仓库或Vault私密存储来为其他服务提供配置属性

注意,在图14.1中,我使用的是Git的图标,而不是GitHub的图标。这是很重要的,我们可以使用任意的Git实现来存储配置信息,包括但不限于GitHub。GitLab、微软的Team Foundation Server和Gogs都是合法的Config Server后端可选方案。

注意:不管使用哪个Git服务器,Config Server几乎没有什么差异。在这里,我选择使用Gogs,这是一个轻量级、易于搭建的Git服务器。更具体来讲,我在开发使用的机器运行Gogs时完全遵循了Docker中运行Gogs的指南。

将配置信息存储在像Git这样的源码控制系统中,配置可以像应用源码那样实现版本化、使用分支、添加标签、恢复和指摘(blame)。但是,为了让配置信息与使用它们的源码分离,这些配置可以独立于应用演化和版本化。

你可能注意到了,在图14.1中还包含了HashiCorp Vault。如果想要保持配置属性完全私密,并且要将它们锁起来直到需要的时候才取出,那么Vault非常有用。我们将会在14.5节中讨论如何组合使用Config Server和Vault。

14.2.1 启用配置服务器

作为更大应用系统中的一个微服务,Config Server会作为一个独立的应用进行开发和部署。所以,我们需要为Config Server创建一个全新的项目。要实现这一点,最简单的方式就是使用Spring Initializr或它的某个客户端(比如Spring ToolSuite中的New Spring Starter Project向导)。

配置:重载的术语

当我们讨论Spring Cloud Config Server的时候,会经常用到“配置(configuration)”这个术语,但是它所指的并不总是同一件事。我们将会编写配置属性来配置Config Server本身。同时,Config Server还会为应用提供配置属性。Config Server的名字中还有“Config”这个单词,这会导致一定的混乱。

在使用“configuration”这个单词的时候,我都会尽力表达清楚到底指的是哪个配置,而在代指Config Server的时候,我都会使用“Config”这个缩写形式。

我一般会将项目命名为“config-server”,但是你可以选取任何你喜欢的名称。最重要的是要选中Config Server复选框,这样就能声明对Config Server的依赖。这样做的结果就是会在所生成项目的pom.xml文件中添加如下的依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

Config Server的版本是根据选择的Spring Cloud release train确定的。在pom.xml文件中,必须要配置Spring Cloud release train。在我编写本书的时候,最新的Spring Cloud发布版本是Finchley.SR1。所以,在pom.xml文件中将会发现如下的属性和<dependencyManagement>代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<properties>
...
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
...
<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>

尽管Config Server依赖将Spring Cloud添加到了项目的类路径下,但是这里并没有自动配置启动它,所以我们需要为某个配置类添加@EnableConfigServer。顾名思义,这个注解会在应用运行的时候启用一个Config Server。通常,我会将@EnableConfigServer放到主类中,如下所示:

1
2
3
4
5
6
7
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}

在我们想要启动应用并查看Config Server如何运行之前,必须还要做另外一件事情:我们必须要告诉它,它要对外提供的配置属性都位于何处。作为开始,我们将会使用来自Git仓库的配置,所以我们需要将spring.cloud.config.server.git.uri属性设置为配置仓库的URL:

1
2
3
4
5
6
7
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}

在14.2.2小节,我们将会看到如何为Git仓库填充属性。

为了在本地开发环境运行,我们可能还要配置另一个属性。在测试本地服务的时候,我们最终会有多个服务一直运行并且它们要监听localhost的不同端口。作为典型的Spring Boot Web应用,Config Server默认会监听8080端口。为了避免端口冲突,我们可以通过设置server.port属性指定一个唯一的端口号:

1
2
server:
port: 8888

在这里,我们将server.port设置为8888,是因为在14.3节中我们将会看到这是Config客户端试图获取配置信息时默认使用的端口。可以将其设置成任意值,但是在配置客户端服务中必须要与其匹配。

很重要的一点需要注意,我们此时所编写的配置是针对Config Server本身的。它与Config Server对外提供的配置是不同的。Config Server会对外提供从Git或Vault获取到的配置信息。

此时,如果启动应用,就会有一个监听8888端口的Config Server,它还不能提供任何的配置属性。我们目前还没有任何Config Server客户端,但是可以通过curl命令行(或者提供同样功能的HTTP客户端)模拟一个客户端:

1
2
3
4
5
6
7
8
9
10
11
$ curl localhost:8888/application/default
{
"name": "application",
"profiles": [
"default"
],
"label": null,
"version": "ca791b15df07ce41d30c24937eece4ec4b208f4d",
"state": null,
"propertySources": []
}

它会向Config Server的“/application/default”路径发送HTTP GET请求。这个请求可以由两部分或3部分组成,如图14.2所示。

image-20211021160333988

图14.2 Config Server对外暴露了一个REST API(通过它可以消费配置属性)

路径的第一部分,即“application”,指的是发送请求的应用的名称。在14.4.1小节中将会看到,Config Server是如何利用请求路径中这部分的内容为我们提供特定应用配置的。现在,我们没有特定应用的配置,所以任意值都是可以的。

路径的第二部分指的是发送请求的应用中处于激活状态的Spring profile。在14.4.2小节中,我们将会看到Config Server是如何利用请求路径中的profile值提供激活active特定配置的。我们目前没有特定profile的配置,所以任意的profile值都是可以的。

路径的第三部分是可选的,指定了提供配置信息的后端Git仓库的标签或分支。如果没有指定,那么默认会使用“master”分支。

请求的响应为我们提供了一些关于Config Server的基本信息,包括为我们提供配置信息的Git提交的版本和标签。但是,这里明显缺少的就是真正的实际配置信息。正常情况下,我们会在propertySources属性下看到它们,但是在这个响应中,它是空的。这是因为我们需要为Git仓库填充Config Server要对外提供的属性。现在,我们看一下该如何实现。

14.2.2 填充配置仓库

我们有多种办法为Config Server提供属性,最基本、最直接的方案是提交application.properties或application.yml文件到Git仓库的根路径下。

假设我们已经推送了一个名为application.yml的文件到前面章节所配置的Git仓库下。这个配置文件与前面章节的配置是不同的,它是Config Server将要对外提供的配置。假设在这个application.yml文件中我们配置了如下的属性:

1
2
3
4
5
6
server:
port: 0
eureka:
client:
service-url:
defaultZone: http://eureka1:8761/eureka/

尽管这个application.yml文件的内容并不多,但是它所定义的配置是相当重要的。它会告诉应用中的每个服务都选择任意可用的端口并且告诉它们进行服务注册的Eureka在哪里。这意味着,在14.3节中,当我们将服务变成Config Server客户端的时候,我们可以从服务中移除显式的Eureka配置。

作为Config Server的客户端,我们可以使用curl命令行查看Config Server提供的新配置数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ curl localhost:8888/someapp/someconfig
{
"name": "someapp",
"profiles": [
"someconfig"
],
"label": null,
"version": "95df0cbc3bca106199bd804b27a1de7c3ef5c35e",
"state": null,
"propertySources": [
{
"name": "http://localhost:10080/habuma/tacocloudconfig/
application.yml",
"source": {
"server.port": 0,
"eureka.client.service-url.defaultZone":
"http://eureka1:8761/eureka/"
}
}
]
}

与之前对Config Server的请求不同,这个响应的propertySources属性中有了内容。具体来讲,它包含了一个属性源,属性源的name属性指向了Git仓库的引用,source则包含了我们推送至Git仓库中的属性。

从Git子路径下提供配置

按照代码的组织风格,你可能想要将配置信息存储到Git仓库的子目录下,而不是放到根路径下。例如,我们想要将配置放到相对于Git仓库根目录名为“config”的子目录下,就可以按照如下方式设置spring.cloud.config.server.git.search-paths属性,让Config Server不再从根目录而是从“/config”目录下提供配置信息:

1
2
3
4
5
6
7
spring:
cloud:
config:
server:
git:
uri: http://localhost:10080/tacocloud/tacocloud-config
search-paths: config

注意,spring.cloud.config.server.git.search-paths属性是一个复数形式,这意味着我们可以让Config Server提供来自多个路径的配置,只需将它们列出来以逗号分隔即可:

1
2
3
4
5
6
7
spring:
cloud:
config:
server:
git:
uri: http://localhost:10080/tacocloud/tacocloud-config
search-paths: config,moreConfig

这样的话,Config Server会提供Git仓库下来自“/config”和“/moreConfig”路径的配置。

我们还可以使用通配符指定搜索路径:

1
2
3
4
5
6
7
spring:
cloud:
config:
server:
git:
uri: http://localhost:10080/tacocloud/tacocloud-config
search-paths: config,more*

这里,Config Server会提供来自“/config”和所有以“more”开头的子目录的配置。

从Git分支或标签下提供配置

默认情况下,Config Server会提供Git中master分支下的配置。在客户端,我们可以将特定分支或标签设置为请求Config Server路径的第三个成员,如图14.2所示。但是,我们可能会发现让Config Server默认请求Git下特定的标签或分支会非常有用,而不是默认使用master。spring.cloud.config.server.git.default-label属性可以重写默认的标签或分支。

例如,考虑如下的配置,它会让Config Server提供名为“sidework”的分支(或标签)下的配置:

1
2
3
4
5
6
7
spring:
cloud:
config:
server:
git:
uri: http://localhost:10080/tacocloud/tacocloud-config
default-label: sidework

按照这个配置形式,除非Config Server客户端指定,否则将会提供“sidework”分支下的配置。

为Git后端提供认证

Config Server检索配置信息的后端Git仓库很可能会使用用户名和密码进行保护。如果是这样,我们就必须为Config Server提供Git仓库的凭证信息。

spring.cloud.config.server.username和spring.cloud.config.server.password属性可以为后端仓库设置用户名和密码。如下的Config Server配置将设置这些属性:

1
2
3
4
5
6
7
8
spring:
cloud:
config:
server:
git:
uri: http://localhost:10080/tacocloud/tacocloud-config
username: tacocloud
password: s3cr3tP455w0rd

在这里,分别将用户名和密码设置成了tacocloud和s3cr3tP455w0rd。

使用curl作为Config Server的客户端能够帮助我们体验一下Config Server是怎样运行的。实际上,Config Server所能做的远远不止于此。但是,我们所编写的微服务并不会使用curl来获取配置数据。所以在查看Config Server提供配置的其他方式之前,我们将关注点转移到微服务上,看一下如何将它们变成Config Server的客户端。