tosca 容器服务编排
重要要点
- 在企业测试方案中,需要以与在生产环境中运行软件相同的方式对软件进行测试,以确保软件能够按预期运行。
- 一个普遍的挑战是微服务应用程序直接或间接依赖于需要在测试场景中进行编排的其他服务。
- 本文介绍了容器编排如何提供对服务实例的抽象,以及如何用模拟实例替换它们。
- 此外,服务网格使我们能够重新路由流量并注入错误的响应或延迟以验证我们的服务的弹性。
- 本文包含示例代码,该示例代码来自随附的示例Java咖啡馆示例应用程序,该应用程序已部署到Kubernetes和Istio并在其上进行了测试。
在企业测试方案中,需要以与在生产环境中运行软件相同的方式对软件进行测试,以确保软件能够按预期运行。 一个普遍的挑战是微服务应用程序直接或间接依赖于需要在测试场景中进行编排的其他服务。
本文介绍了容器编排如何提供对服务实例的抽象,以及如何用模拟实例替换它们。 最重要的是, 服务网格使我们能够重新路由流量并注入错误的响应或延迟以验证我们的服务的弹性。
我们将使用一个咖啡店示例应用程序 ,该应用程序已部署到容器编排和服务网格集群。 我们选择了Kubernetes和Istio作为示例环境技术。
测试场景
假设我们要测试应用程序的行为而不考虑其他外部服务。 该应用程序的运行方式与生产环境中的配置方式相同,因此稍后我们可以确保其行为方式完全相同。 我们的测试用例将通过使用定义良好的通信接口连接到应用程序。
但是,外部服务不应成为测试方案的一部分。 通常,测试用例应集中于单个被测对象,并掩盖其余所有对象。 因此,我们将外部服务替换为模拟服务器。
![](https://i-blog.csdnimg.cn/blog_migrate/51a0761f29892e5510e9798685038b03.png)
容器编排
将应用程序重新配置为使用模拟服务器而不是实际的后端与以在生产中相同的方式运行微服务的想法相矛盾,因为这可能会导致配置。 但是,如果将我们的应用程序部署到容器编排集群(例如Kubernetes),则可以将抽象的服务名称用作配置的目标,并让该集群解析后端服务实例。
以下示例显示了网关类,该网关类是咖啡店应用程序的一部分,并通过端口8080
连接到coffee-processor
主机。
public class OrderProcessor {
// definitions omitted ...
@PostConstruct
private void initClient() {
final Client client = ClientBuilder.newClient();
target = client.target("http://coffee-processor:8080/processes");
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
public void processOrder(Order order) {
OrderStatus status = retrieveOrderStatus(order);
order.setStatus(status);
entityManager.merge(order);
}
// ...
private JsonObject sendRequest(final JsonObject requestBody) {
Response response = target.request()
.buildPost(Entity.json(requestBody))
.invoke();
// ...
return response.readEntity(JsonObject.class);
}
// definitions omitted ...
}
该主机名是通过Kubernetes集群DNS解析的,这会将流量定向到正在运行的处理器实例之一。 但是,支持coffee-processor
服务的实例将是模拟服务器,在我们的示例中为WireMock 。 这种替换对我们的应用程序是透明的。
系统测试方案不仅连接到应用程序以调用所需的业务用例,而且还将在单独的管理界面上与模拟服务器通信,以控制其响应行为并验证应用程序是否以正确的方式调用了模拟方式。 这与通常由JUnit和Mockito实现的类级单元测试的想法相同。
![](https://i-blog.csdnimg.cn/blog_migrate/aa4c6b37093738a7adfbeea519741dd2.png)
对外服务
通过此设置,我们可以模拟和控制在容器编排群集中运行的服务。 但是,如果外部服务在群集之外怎么办?
通常,我们可以创建一个不带指向外部IP的选择器的Kubernetes 服务 ,然后重写我们的应用程序以始终使用由集群解析的服务名称。 这样,我们定义了服务将路由到的单一责任点。
下面的代码片段显示了外部Kubernetes服务和端点定义,该定义将coffee-shop-db
路由到外部IP地址1.2.3.4
:
kind: Service
apiVersion: v1
metadata:
name: coffee-shop-db
spec:
ports:
- protocol: TCP
port: 5432
---
kind: Endpoints
apiVersion: v1
metadata:
name: coffee-shop-db
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 5432
服务网格
服务网格使我们能够透明地支持与微服务有关的跨领域通信问题。 到目前为止,Istio是最常用的服务网格技术之一。 它添加了与我们的应用程序容器一起放置的Sidecar代理容器,这些容器实现了这些其他问题。 代理容器还允许出于弹性测试目的有意操纵或减慢连接。
在端到端测试中,我们可以引入错误或较慢的响应,以验证我们的应用程序是否正确处理了这些错误情况。
下面的代码片段显示了一个Istio虚拟服务定义,该定义以50%的响应和10%的响应失败的3秒延迟来注释到coffee-processor
的路由。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: coffee-processor
spec:
hosts:
- coffee-processor
http:
- route:
- destination:
host: coffee-processor
subset: v1
fault:
delay:
fixedDelay: 3s
percent: 50
abort:
httpStatus: 500
percent: 10
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: coffee-processor
spec:
host: coffee-processor
subsets:
- name: v1
labels:
version: v1
现在,我们可以运行其他测试,并验证我们的应用程序如何应对这些增加的响应时间和故障情况。
除了注入错误响应的可能性外,服务网格技术还允许增加环境的弹性。 代理容器可以处理超时 ,实现断路器和隔板,而无需应用程序来处理这些问题。
结论
容器编排和服务网格通过将关注点从应用程序提取到操作环境中,从而提高了微服务应用程序的可测试性。 服务抽象实现发现,并允许我们透明地替代服务或重新路由。 服务网格不仅允许更复杂的路由,而且允许我们注入故障或响应速度慢,从而使我们的应用程序承受压力并验证其相应的行为。
更多资源
tosca 容器服务编排