5. 服务实例
您可以在 App Broker 配置属性中配置服务的详细信息,包括要部署的应用程序、应用程序部署详细信息以及要创建的后台服务。这些属性通常位于 spring.cloud.appbroker.services 下。
5.1. 配置应用程序部署
支持应用程序的部署细节可以静态地在服务代理的应用程序配置中进行配置,也可以通过使用服务实例参数和自定义实现动态地进行配置。
5.1.1. 静态自定义
你可以通过使用 spring.cloud.appbroker 下的属性,在服务代理的应用程序配置中静态配置支持的应用程序部署详细信息。
属性配置
您可以在配置中指定应用程序部署属性。这些属性可以具有默认值和服务特定的值。
对于 Cloud Foundry,您可以为 spring.cloud.appbroker.deployer.cloudfoundry.* 下的所有服务设置默认值,如下所示:
spring:
cloud:
appbroker:
deployer:
cloudfoundry:
properties:
memory: 1G
health-check: http
health-check-http-endpoint: /health
health-check-timeout: 180
api-polling-timeout: 300
下表列出了可以为所有部署或特定部署设置的属性:
| 属性 | 描述 | 默认 |
|---|---|---|
|
用于轮询异步 CF API 调用的超时时间,以秒为单位。 |
300 |
|
用于部署应用程序的构建包。 |
|
|
用于部署应用程序的构建包列表。 |
|
|
用于在为应用程序映射路由时使用的域名。 |
|
|
用于在为应用程序映射路由时使用的域名列表。 |
|
|
部署的应用程序上执行的健康检查类型,如果未在每个应用程序中覆盖。 |
端口 |
|
http 健康检查将使用的路径。 |
/health |
|
健康检查的超时时间(以秒为单位)。 |
120 |
|
用于部署应用程序的 javaOpts。 |
|
|
部署应用程序时要使用的内存。 |
|
|
如果应用程序不需要路由 |
false |
|
部署应用程序时使用的路由路径。 |
|
|
部署应用程序时使用的路由。 |
|
|
部署应用程序时使用的堆栈。 |
您可以在服务配置的 spring.cloud.appbroker.services.* 下为特定服务设置覆盖值,如下所示:
spring:
cloud:
appbroker:
services:
- service-name: example
plan-name: standard
apps:
- name: example-service-app1
path: classpath:app1.jar
properties:
memory: 2G
count: 2
no-route: true
下表列出了可以为所有或特定应用程序部署设置的属性:
| 属性 | 描述 | 默认 |
|---|---|---|
|
||
|
||
|
||
|
||
|
||
|
部署应用程序时用于映射路由的域名。 |
|
|
部署应用程序要绑定的路由。 |
|
|
部署的应用程序要执行的健康检查类型。 |
|
|
HTTP 健康检查使用的路径。 |
|
|
健康检查使用的超时值,以秒为单位。 |
|
|
用于阻塞 API 调用的超时值,以秒为单位。 |
|
|
用于轮询异步 API 端点的超时值(例如,CF 创建/更新/删除服务实例),以秒为单位。 |
|
|
||
|
||
|
||
|
是否在取消部署应用程序时删除路由。 |
|
|
||
|
决定将环境变量写入 SPRING_APPLICATION_JSON 或者将它们作为原始环境变量写入 |
|
环境配置
您可以为已部署的应用程序提供要设置的环境变量。环境变量是通过已部署应用程序下的 environment 属性来设置的,如下所示:
spring:
cloud:
appbroker:
services:
- service-name: example
plan-name: standard
apps:
- name: example-service-app1
path: classpath:app1.jar
environment:
logging.level.spring.security: DEBUG
spring.profiles.active: cloud
服务配置
您可以配置应该绑定到已部署应用程序的服务。服务是通过使用已部署应用程序的 services 下的属性来配置的,如下所示:
spring:
cloud:
appbroker:
services:
- service-name: example
plan-name: standard
apps:
- name: example-service-app1
path: classpath:app1.jar
services:
- service-instance-name: example-db
services:
- service-instance-name: example-db
name: mysql
plan: small
parameters:
param-key: param-value
5.1.2. 动态定制
要通过使用仅在执行服务代理操作时可用的信息,或必须为每个服务实例生成的信息来定制支持应用程序的部署,您可以使用服务代理应用程序配置来提供定制实现的名称。
支持的应用程序目标
你可以使用 target 规范来配置支持应用程序的目标位置(在 Cloud Foundry 中,一个组织和空间),如下例所示:
spring:
cloud:
appbroker:
services:
- service-name: example
plan-name: standard
target:
* name: SpacePerServiceInstance*
apps:
apps:
- name: example-service-app1
path: classpath:app1.jar
默认情况下(如果您未提供 target 规范),所有支持的应用程序都将部署到 spring.cloud.appbroker.deployer 下指定的默认目标。对于 Cloud Foundry,这是由 spring.cloud.appbroker.deployer.cloudfoundry.default-org 命名的组织和由 spring.cloud.appbroker.deployer.cloudfoundry.default-space 命名的空间。
The SpacePerServiceInstance 目标
如果使用 SpacePerServiceInstance 目标,App Broker 会将支持应用程序部署到一个唯一的 target 位置,该位置是通过在服务实例创建时由平台提供的服务实例 GUID 命名的。对于 Cloud Foundry,此目标位置是由 spring.cloud.appbroker.deployer.cloudfoundry.default-org 命名的组织,并且会使用服务实例 GUID 作为空间名称来创建一个新空间。
The ServiceInstanceGuidSuffix 目标
如果使用 ServiceInstanceGuidSuffix 目标,App Broker 会通过使用一个唯一名称和主机名来部署支持应用程序,该名称和主机名包含了平台在服务实例创建时提供的服务实例 GUID。对于 Cloud Foundry,目标位置是名为 spring.cloud.appbroker.deployer.cloudfoundry.default-org 的组织、名为 spring.cloud.appbroker.deployer.cloudfoundry.default-space 的空间,以及名为 [APP-NAME]-[SI-GUID] 的应用程序名称,其中 [APP-NAME] 是在 spring.cloud.appbroker.services.apps 下为应用程序列出的 name,而 [SI-GUID] 是服务实例 GUID。该应用程序还使用一个包含服务实例 GUID 作为后缀的主机名,如 [APP-NAME]-[SI-GUID]。
创建自定义目标
如果你想创建一个自定义目标,App Broker 提供了一种灵活的方式来通过创建一个新的 Bean 来添加新目标,该目标继承自 TargetFactory 并实现 create 方法,如下所示:
public class CustomSpaceTarget extends TargetFactory<CustomSpaceTarget.Config> {
public CustomSpaceTarget() {
super(Config.class);
}
@Override
public Target create(Config config) {
return this::apply;
}
private ArtifactDetails apply(Map<String, String> properties, String name, String serviceInstanceId) {
String space = "my-custom-space";
properties.put(DeploymentProperties.TARGET_PROPERTY_KEY, space);
return ArtifactDetails.builder()
.name(name)
.properties(properties)
.build();
}
public static class Config {
}
}
一旦配置完成,我们可以在服务中指定新的自定义目标,如下所示:
spring:
cloud:
appbroker:
services:
- service-name: example
plan-name: standard
target:
name: CustomSpaceTarget
服务实例参数
当用户在创建或更新服务实例时提供参数,应用代理(App Broker)可以使用参数转换器将这些参数转化为支持应用部署的详细信息。您可以通过使用parameters-transformers下的属性来配置参数转换器,如下所示:
spring:
cloud:
appbroker:
services:
- service-name: example
plan-name: standard
apps:
- name: example-service-app1
path: classpath:app1.jar
parameters-transformers:
- name: EnvironmentMapping
args:
- include: parameter1,parameter2
- name: PropertyMapping
args:
- include: count,memory
名为 parameters-transformers 的引用是指已贡献给 Spring 应用程序上下文的 Java 对象。参数转换器可以接受一个或多个配置其行为的参数,并且可以修改支持的应用程序部署的任何方面(属性、环境变量、服务等)。
EnvironmentMapping 参数转换器
The EnvironmentMapping 参数转换器会根据在创建或更新服务实例时提供的参数,填充支持应用程序的环境变量。它支持一个参数 include,该参数指定映射到环境变量的参数名称。
PropertyMapping 参数转换器
The PropertyMapping 参数转换器根据在创建或更新服务实例时提供的参数,设置支持应用程序的部署属性。它支持一个参数 include,该参数指定应识别的部署属性名称。
5.2. 创建服务实例
Spring Cloud App Broker 提供了 AppDeploymentCreateServiceInstanceWorkflow 工作流,该工作流负责部署配置好的支持应用程序和服务,如前几节所述。服务代理应用程序可以实现 CreateServiceInstanceWorkflow 接口以进一步修改部署。多个工作流可以通过 @Order 注解,以便按照特定顺序处理这些工作流。或者,服务代理应用程序可以实现由 Spring Cloud Open Service Broker 提供的 ServiceInstanceService 接口。参见 服务实例,位于 Spring Cloud Open Service Broker 文档 中。
5.3. 更新服务实例
Spring Cloud App Broker 提供了 AppDeploymentUpdateServiceInstanceWorkflow 工作流程,该流程负责更新已配置的后端应用程序和服务,如前几节所述。如果后端服务列表被更新,默认行为是创建并绑定新的后端服务实例,同时对不再列于配置中的现有后端服务实例进行解绑并删除。
服务代理应用程序可以实现 UpdateServiceInstanceWorkflow 接口以进一步修改部署。多个工作流可以用 @Order 注解,以便按照特定顺序处理这些工作流。或者,服务代理应用程序可以实现由 Spring Cloud Open Service Broker 提供的 ServiceInstanceService 接口。参见 服务实例 在 Spring Cloud Open Service Broker 文档 中。
| 更新应用程序时,修改某些属性(如磁盘和内存)可能会导致停机。 |
5.4. 删除服务实例
Spring Cloud App Broker 提供了 AppDeploymentDeleteServiceInstanceWorkflow 工作流,该工作流负责删除配置的后端应用程序和服务,如前几节所述。服务代理应用程序可以实现 DeleteServiceInstanceWorkflow 接口以进一步修改部署。多个工作流可以使用 @Order 注解,以便按照特定顺序处理这些工作流。此外,服务代理应用程序还可以实现由 Spring Cloud Open Service Broker 提供的 ServiceInstanceService 接口。请参阅 服务实例,位于 Spring Cloud Open Service Broker 文档 中。
5.5. 持久化服务实例状态
Spring Cloud App Broker 提供了 ServiceInstanceStateRepository 接口用于持久化服务实例状态。默认实现是 InMemoryServiceInstanceStateRepository,它使用内存中的 Map 来保存状态,并提供了简单的入门体验。要使用合适的数据库来持久化状态,您可以在应用程序中实现 ServiceInstanceStateRepository。
The InMemoryServiceInstanceStateRepository 仅出于演示和测试目的提供。它不适合用于生产应用程序! |
5.5.1. 示例实现
以下示例展示了一个服务实例状态存储库的实现:
package com.example.appbroker;
class ExampleServiceInstanceStateRepository implements ServiceInstanceStateRepository {
private final ServiceInstanceStateCrudRepository serviceInstanceStateCrudRepository;
ExampleServiceInstanceStateRepository(ServiceInstanceStateCrudRepository serviceInstanceStateCrudRepository) {
this.serviceInstanceStateCrudRepository = serviceInstanceStateCrudRepository;
}
@Override
public Mono<ServiceInstanceState> saveState(String serviceInstanceId, OperationState state, String description) {
return serviceInstanceStateCrudRepository.findByServiceInstanceId(serviceInstanceId)
.switchIfEmpty(Mono.just(new ServiceInstance()))
.flatMap(serviceInstance -> {
serviceInstance.setServiceInstanceId(serviceInstanceId);
serviceInstance.setOperationState(state);
serviceInstance.setDescription(description);
return Mono.just(serviceInstance);
})
.flatMap(serviceInstanceStateCrudRepository::save)
.map(ExampleServiceInstanceStateRepository::toServiceInstanceState);
}
@Override
public Mono<ServiceInstanceState> getState(String serviceInstanceId) {
return serviceInstanceStateCrudRepository.findByServiceInstanceId(serviceInstanceId)
.switchIfEmpty(Mono.error(new IllegalArgumentException("Unknown service instance ID " + serviceInstanceId)))
.map(ExampleServiceInstanceStateRepository::toServiceInstanceState);
}
@Override
public Mono<ServiceInstanceState> removeState(String serviceInstanceId) {
return getState(serviceInstanceId)
.doOnNext(serviceInstanceState -> serviceInstanceStateCrudRepository.deleteByServiceInstanceId(serviceInstanceId));
}
private static ServiceInstanceState toServiceInstanceState(ServiceInstance serviceInstance) {
return new ServiceInstanceState(serviceInstance.getOperationState(), serviceInstance.getDescription(), null);
}
}
持久化服务实例状态的一种选择是使用 Spring Data CrudRepository。以下示例展示了一个 ReactiveCrudRepository 实现:
package com.example.appbroker;
interface ServiceInstanceStateCrudRepository extends ReactiveCrudRepository<ServiceInstance, Long> {
@Query("select * from service_instance where service_instance_id = :service_instance_id")
Mono<ServiceInstance> findByServiceInstanceId(@Param("service_instance_id") String serviceInstanceId);
@Query("delete from service_instance where service_instance_id = :service_instance_id")
Mono<Void> deleteByServiceInstanceId(@Param("service_instance_id") String serviceInstanceId);
}
一个模型对象对于使用 CrudRepository 持久化数据是必要的。以下示例展示了一个 ServiceInstance 模型:
package com.example.appbroker;
class ServiceInstance {
@Id
private Long id;
private String serviceInstanceId;
private String description;
private OperationState operationState;
public ServiceInstance() {
}
public ServiceInstance(String serviceInstanceId, String description, OperationState operationState) {
this.serviceInstanceId = serviceInstanceId;
this.description = description;
this.operationState = operationState;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getServiceInstanceId() {
return serviceInstanceId;
}
public void setServiceInstanceId(String serviceInstanceId) {
this.serviceInstanceId = serviceInstanceId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public OperationState getOperationState() {
return operationState;
}
public void setOperationState(OperationState operationState) {
this.operationState = operationState;
}
}