在使用Spring Boot和Spring Cloud的MicroServices –第1部分:概述中 ,我们简要介绍了什么是微服务以及如何使用SpringBoot和SpringCloud构建微服务。
在这篇文章中,我们将学习:
- Spring Cloud Config和Vault有什么需求?
- 创建我们的第一个微服务:catalog-service
- 创建Spring Cloud Config服务器
- 使用保管箱存储敏感数据
使用Spring Boot和Spring Cloud的微服务
- 第1部分:使用Spring Boot和Spring Cloud的MicroServices:概述
- 第2部分:MicroServices –使用Spring Cloud Config和Vault进行配置管理
Spring Cloud Config和Vault有什么需求?
SpringBoot已经提供了许多选项来外部化配置属性 。 但是,一旦启动应用程序,您将无法在运行时更改这些属性值。 您需要更新属性并重新启动应用程序,以使这些更改生效。
在微服务世界中,可能会有大量的微服务,并且这些微服务的多个实例正在运行。 手动甚至使用自动化脚本更新配置属性并重新启动所有这些实例可能不可行。 Spring Cloud Config解决了这个问题。
我们可以创建一个Spring Cloud Config Server,该服务器为我们所有的微服务提供配置值。 我们可以使用git , svn , database或Consul作为后端来存储配置参数。 然后,我们可以在我们的微服务中配置Spring Cloud Config服务器的位置,以便在启动应用程序时它将加载所有属性。 除此之外,每当我们更新属性时,我们都可以在微服务中调用/ refresh REST端点,以便它可以重新加载配置更改,而无需重新启动应用程序。
在我们的应用程序中,我们还需要配置各种敏感数据,例如数据库凭据,密钥,令牌等。显然,我们不想将它们存储为纯文本格式。 更好的方法是将它们以加密格式存储,并且Spring Cloud Config Server提供了加密和解密数据的功能。 更好的是,我们应该使用Vault等安全数据存储工具。 Spring Cloud还提供了与Vault的集成,因此我们可以在Vault中存储任何敏感的配置属性。
我已经在Spring Cloud Config Server上编写了一些教程,您可以参考这些教程:
创建我们的第一个微服务:catalog-service
让我们开始我们的第一个微服务,即catalog-service 。 使用Web , JPA , MySQL , Actuator , DevTools和Lombok启动器创建一个SpringBoot应用程序。 到目前为止,典型的SpringBoot应用程序还算不错。
您可以在https://github.com/sivaprasadreddy/spring-boot-microservices-series上找到本文的源代码。
首先,让我们实现一个REST端点来提供产品数据,然后将其重构为使用Cloud Config Server。
我们将使用Docker并将MySQL作为Docker容器运行。
docker-compose.yml
version: '3'
services:
mysqldb:
image: mysql:5.7
container_name: mysqldb
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: admin
MYSQL_DATABASE: catalog
如下配置application.properties中的数据源属性:
server.port=8181
logging.level.com.sivalabs=debug
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/catalog?useSSL=false
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.initialization-mode=always
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
//expose all the Actuator endpoints
management.endpoints.web.exposure.include=*
创建JPA实体Product.java ,如下所示:
import lombok.Data;
import javax.persistence.*;
@Data
@Entity
@Table(name = "products")
public class Product {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false, unique = true)
private String code;
@Column(nullable = false)
private String name;
private String description;
private double price;
}
创建Spring Data JPA存储库ProductRepository.java ,如下所示:
import com.sivalabs.catalogservice.entities.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface ProductRepository extends JpaRepository<Product, Long> {
Optional<Product> findByCode(String code);
}
现在创建仅委托给ProductRepository的 ProductService 。 我们可以将存储库直接注入到我们的Web层组件(控制器)中,但是今后可能会有一些我不希望放在Controller或存储库中的业务逻辑。
import com.sivalabs.catalogservice.entities.Product;
import com.sivalabs.catalogservice.repositories.ProductRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
@Slf4j
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public List<Product> findAllProducts() {
return productRepository.findAll();
}
public Optional<Product> findProductByCode(String code) {
return productRepository.findByCode(code);
}
}
最后,创建我们的REST控制器ProductController.java
import com.sivalabs.catalogservice.entities.Product;
import com.sivalabs.catalogservice.exceptions.ProductNotFoundException;
import com.sivalabs.catalogservice.services.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/products")
@Slf4j
public class ProductController {
private final ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping("")
public List<Product> allProducts() {
return productService.findAllProducts();
}
@GetMapping("/{code}")
public Product productByCode(@PathVariable String code) {
return productService.findProductByCode(code)
.orElseThrow(() -> new ProductNotFoundException("Product with code ["+code+"] doesn't exist"));
}
}
创建扩展RuntimeException的 ProductNotFoundException并使用@ResponseStatus(HttpStatus.NOT_FOUND)进行注释。
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ProductNotFoundException extends RuntimeException {
public ProductNotFoundException() {
}
public ProductNotFoundException(String message) {
super(message);
}
public ProductNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public ProductNotFoundException(Throwable cause) {
super(cause);
}
}
让我们将一些示例产品插入我们的数据库。
src / main / resources / data.sql
DELETE FROM products;
insert into products(id, code, name, description, price) VALUES
(1, 'P001', 'Product 1', 'Product 1 description', 25),
(2, 'P002', 'Product 2', 'Product 2 description', 32),
(3, 'P003', 'Product 3', 'Product 3 description', 50)
;
好的,现在我们可以启动SpringBoot应用程序并点击http:// localhost:8181 / api / products。 您应该能够看到带有产品信息的JSON响应。
创建Spring Cloud Config服务器
我们将使用Git后端创建Spring Cloud Config Server。 Spring Cloud Config Server只是一个SpringBoot项目。 使用Config Server启动器创建SpringBoot项目。
在我们将所有配置文件存储在application.properties文件中的位置,配置git存储库的位置。
spring.config.name=configserver
server.port=8888
spring.cloud.config.server.git.uri=https://github.com/sivaprasadreddy/microservices-config-repo
spring.cloud.config.server.git.clone-on-start=true
management.endpoints.web.exposure.include=*
现在,使用@EnableConfigServer注释入口点类。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
而已。 这是创建Spring Cloud Config Server所需要做的一切,仅需要在git存储库中添加特定于应用程序的配置文件。
如果您已经准备好编写一堆代码来创建Spring Cloud Config Server,请您失望:-)。
重构目录服务以使用Config Server
我们的目录服务将成为Config Server的客户端。 因此,让我们将Config Client启动程序添加到catalog-service ,这将添加以下依赖性。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
确保同时在<properties>部分中添加spring-cloud-dependencies BOM。
使用Spring Cloud Config Server时,属性加载过程分为多个阶段,首先加载bootstrap.properties/ yml,然后再从配置服务器加载。
因此,让我们将application.properties重命名为bootstrap.properties并将其更新为具有以下属性。
spring.application.name=catalog-service
server.port=8181
management.endpoints.web.exposure.include=*
spring.cloud.config.uri=http://localhost:8888
在这里,我们已经配置了Config Server的位置,并使用spring.application.name将名称命名为catalog-service 。
现在,我们需要将catalog-service的所有属性添加到catalog-service.properties中 ,并将其提交/推送到git repo microservices-config-repo中 。
microservices-config-repo / catalog-service.properties
logging.level.com.sivalabs=debug
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/catalog?useSSL=false
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.initialization-mode=always
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
您还可以为不同的文件添加单独的配置文件,例如catalog-service-qa.properties , catalog-service-prod.properties等。
现在,首先启动Config Server应用程序,然后再启动目录服务应用程序。 这应该工作正常。 您可以在启动时检查控制台日志,说明目录服务正在从配置服务器http:// localhost:8888 /获取属性。
现在我们离目标还差得很近,但我们仍以纯文本格式存储凭据。 让我们将敏感的配置属性移动到Vault。
使用保管箱存储敏感数据
保管箱是用于安全存储和访问机密的工具。 您可以在https://www.vaultproject.io/intro/index.html上了解有关Vault的更多信息。 Vault是一个二进制文件,您可以从https://www.vaultproject.io/downloads.html下载。
现在,使用以下命令以开发人员模式启动Vault:
$ Vault服务器-dev
在控制台中,您可以查看有关如何使用保险柜和根令牌的信息 。
打开一个新的终端窗口,设置VAULT_ADDR环境变量。
$ export VAULT_ADDR ='http://127.0.0.1:8200'
注意:Vault开发模式仅用于开发目的,并不用于生产用途。
我们可以使用Vault write secret / somename key1 = value1 key2 = value2向Vault 写入机密
我们还可以将所有机密信息放入JSON文件中,也可以从该文件中写入。 让我们使用MySQL数据库凭据创建JSON文件并写入Vault。
catalog-service-credentials.json
{
"spring.datasource.username": "root",
"spring.datasource.password": "admin"
}
$保险库文件写秘密/目录服务@ catalog-service-credentials.json
您可以通过运行Vault读取secret / catalog-service来验证值。
我们可以使用Docker自动完成设置保管库和使用机密初始化的整个过程。 请查看github上的源存储库以了解如何做,这是一种实现方法。
现在,已使用秘密配置和初始化了Vault。 让我们重构目录服务以使用保管库。
将Vault配置启动程序添加到目录服务中 ,这将添加以下依赖性。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
从microservices-config-repo / catalog-service.properties中删除以下凭证,然后提交。
spring.datasource.username=root
spring.datasource.password=admin
在bootstrap.properties中添加保管库配置属性。
spring.cloud.vault.host=localhost
spring.cloud.vault.port=8200
spring.cloud.vault.scheme=http
spring.cloud.vault.authentication=token
spring.cloud.vault.token=934f9eae-31ff-a8ef-e1ca-4bea9e07aa09
我们已经使用基于令牌的身份验证配置了Vault属性,并配置了在启动Vault服务器时在控制台日志中打印的“ Root Taken” 。
我们都准备好了。 我们将服务属性移至外部配置服务器,并将敏感数据移至Vault。
现在启动配置服务器和目录服务,它应该可以正常工作。
摘要
在本文中,我们学习了如何使用Spring Cloud Config来外部化配置属性以及如何使用Vault来存储机密。 您可以使用Spring Cloud Bus来自动刷新配置更改,如Spring Cloud教程–使用Spring Cloud Bus自动刷新配置更改中所述。
您可以在https://github.com/sivaprasadreddy/spring-boot-microservices-series上找到本文的源代码。
在下一篇文章中,我们将研究如何使用Netflix Eureka进行服务注册和服务发现 。