使用Spring Cloud Kubernetes基于Kubernetes、Spring Boot和Docker構建微服務架構 - Morioh

19-10-16 banq
                   

在本文中,我們將學習如何啟動Spring Boot微服務項目并使用Kubernetes和Docker快速運行它

本文涵蓋的主題是:

  • 在云原生開發中使用Spring Boot 2.0
  • 使用Spring Cloud Kubernetes項目為所有微服務提供服務發現
  • 使用Kubernetes Config Maps和Secrets將配置設置注入到應用程序Pod中
  • 使用Docker構建應用程序映像并將其使用YAML配置文件部署在Kubernetes上
  • 結合使用Spring Cloud Kubernetes和Zuul代理來公開所有微服務的單個Swagger API文檔

當您構建微服務環境時,Spring Cloud和Kubernetes可能是兩個相互競爭的解決方案。Spring Cloud提供的諸如Eureka,Spring Cloud Config或Zuul之類的組件可以由Kubernetes內置的組件如服務,配置映射,secrets或ingresses替代,但是,即使您決定使用Kubernetes組件而不是Spring Cloud,也可以利用整個Spring Cloud項目中提供的一些有趣功能。

一個對我們有幫助的真正有趣的項目是Spring Cloud Kubernetes。盡管它仍處于孵化階段,但絕對值得花一些時間。它將Spring Cloud與Kubernetes集成在一起。我將向您展示如何使用使用它實現客戶端的發現、與Ribbon客戶端的服務間通信以及使用Spring Cloud Kubernetes的Zipkin發現。

假設有三個獨立的應用程序(employee-service, department-service, organization-service),它們通過REST API相互通信。這些Spring Boot微服務使用Kubernetes提供的一些內置機制:用于分布式配置的配置映射和secrets,用于服務發現的etcd以及用于API網關的入口。

將spring-cloud-dependency聲明為依賴項管理的BOM。

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Spring Cloud Kubernetes不在Spring Cloud Release Trains下發布,因此我們需要明確定義其版本。因為我們使用Spring Boot 2.0,所以我們必須包含spring-cloud-kubernetes工件的最新SNAPSHOT版本,即0.3.0.BUILD-SNAPSHOT。

本文中提供的示例應用程序的源代碼可在此存儲庫的GitHub找到。

前提條件

為了能夠部署和測試我們的示例微服務,我們需要準備一個開發環境。我們可以通過以下步驟來實現:

  • 您至少需要在本地計算機上運行Kubernetes(Minikube)或Openshift(Minishift)的單節點集群實例。您應該啟動它,并公開它們提供的嵌入式Docker客戶端。在OpenShift上部署Java應用程序的快速指南
  • Spring Cloud Kubernetes要求訪問Kubernetes API,以便能夠檢索為單個服務運行的Pod的地址列表。如果您使用Kubernetes,則應該執行以下命令:

    $ kubectl create clusterrolebinding admin --clusterrole=cluster-admin --serviceaccount=default:default
    

1. 使用配置映射和secrets注入配置

使用Spring Cloud時,在系統中實現分布式配置的最明顯選擇是Spring Cloud Config;而使用Kubernetes,您可以使用Config Map。它包含可在Pod中使用或用于存儲配置數據的配置數據的鍵值對。它用于存儲和共享非敏感,未加密的配置信息。要在群集中使用敏感信息,必須使用Secrets。根據MongoDB連接設置的示例,可以完美地演示這兩個Kubernetes對象的使用。在Spring Boot應用程序內部,我們可以使用環境變量輕松地將其注入。這是application.yml?帶有URI配置的文件片段。

spring:
? data:
? ? mongodb:
? ? ? uri: mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@mongodb/${MONGO_DATABASE}

盡管用戶名和密碼是敏感字段,但數據庫名稱不是,因此我們可以將其放在配置映射中。

apiVersion: v1
kind: ConfigMap
metadata:
? name: mongodb
data:
? database-name: microservices

當然,用戶名和密碼被定義為機密secret類型。

apiVersion: v1
kind: Secret
metadata:
? name: mongodb
type: Opaque
data:
? database-password: MTIzNDU2
? database-user: cGlvdHI=

要將配置應用于Kubernetes集群,我們運行以下命令。

$ kubectl apply -f kubernetes/mongodb-configmap.yaml
$ kubectl apply -f kubernetes/mongodb-secret.yaml

然后我們應該將配置屬性注入到應用程序的pod中。在Deployment YAML文件中定義容器配置時,我們必須包括對環境變量和機密secret的引用,如下所示。

apiVersion: apps/v1
kind: Deployment
metadata:
? name: employee
? labels:
? ? app: employee
spec:
? replicas: 1
? selector:
? ? matchLabels:
? ? ? app: employee
? template:
? ? metadata:
? ? ? labels:
? ? ? ? app: employee
? ? spec:
? ? ? containers:
? ? ? - name: employee
? ? ? ? image: piomin/employee:1.0
? ? ? ? ports:
? ? ? ? - containerPort: 8080
? ? ? ? env:
? ? ? ? - name: MONGO_DATABASE
? ? ? ? ? valueFrom:
? ? ? ? ? ? configMapKeyRef:
? ? ? ? ? ? ? name: mongodb
? ? ? ? ? ? ? key: database-name
? ? ? ? - name: MONGO_USERNAME
? ? ? ? ? valueFrom:
? ? ? ? ? ? secretKeyRef:
? ? ? ? ? ? ? name: mongodb
? ? ? ? ? ? ? key: database-user
? ? ? ? - name: MONGO_PASSWORD
? ? ? ? ? valueFrom:
? ? ? ? ? ? secretKeyRef:
? ? ? ? ? ? ? name: mongodb
? ? ? ? ? ? ? key: database-password

2.使用Kubernetes構建服務發現

我們通常使用Docker容器在Kubernetes上運行微服務。一個或多個容器按Pod分組,Pod是在Kubernetes中創建和管理的最小的可部署單元。一個好的做法是在一個pod中只運行一個容器。如果您想擴展微服務,則只需增加正在運行的Pod的數量即可。屬于單個微服務的所有正在運行的Pod在邏輯上都與Kubernetes Service進行了分組。該服務在集群外部可能是可見的,并且能夠在所有正在運行的Pod之間負載平衡傳入的請求。以下服務定義將標有字段app等于的所有Pod分組為employee。

apiVersion: v1
kind: Service
metadata:
? name: employee
? labels:
? ? app: employee
spec:
? ports:
? - port: 8080
? ? protocol: TCP
? selector:
? ? app: employee

服務Service類型可用于訪問Kubernetes集群外部的應用程序或集群內部的服務間通信。但是,使用Spring Cloud Kubernetes可以更輕松地實現微服務之間的通信。首先,我們需要在項目中包括以下依賴項pom.xml

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes</artifactId>
<version>0.3.0.BUILD-SNAPSHOT</version>
</dependency>

然后,我們應該為應用程序啟用發現客戶端,就像我們在Spring Cloud Netflix Eureka中為發現所做的一樣。這使您可以按名稱查詢Kubernetes端點(服務)。Spring Cloud Kubernetes Ribbon或Zipkin項目還使用此發現功能分別獲取為要進行負載平衡的微服務定義的Pod列表,或可用于發送跟蹤或跨度的Zipkin服務器。

@SpringBootApplication
@EnableDiscoveryClient
@EnableMongoRepositories
@EnableSwagger2
public class EmployeeApplication {
?public static void main(String[] args) {
? SpringApplication.run(EmployeeApplication.class, args);
?}
?// ...
}

本部分的最后一件事是確保Spring應用程序名稱與該應用程序的Kubernetes服務Service名稱完全相同。對于應用程序employee-service,它是employee。

spring:
? application:
? ? name: employee

3.使用Docker構建微服務并在Kubernetes上部署

可包括一些標準的Spring依賴關系,用于構建基于REST的微服務,與MongoDB集成以及使用Swagger2生成API文檔。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

為了與MongoDB集成,我們應該創建一個擴展標準Spring Data的接口CrudRepository

public interface EmployeeRepository extends CrudRepository {
?List findByDepartmentId(Long departmentId);
?List findByOrganizationId(Long organizationId);
}

實體類應使用Mongo注釋@Document,主鍵字段應使用?@Id。

@Document(collection = "employee")
public class Employee {
?@Id
?private String id;
?private Long organizationId;
?private Long departmentId;
?private String name;
?private int age;
?private String position;
?// ...
}

存儲庫bean已注入到控制器類中。這是員工服務內部REST API的完整實現。

@RestController
public class EmployeeController {
?private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeController.class);
?@Autowired
?EmployeeRepository repository;
?@PostMapping("/")
?public Employee add(@RequestBody Employee employee) {
? LOGGER.info("Employee add: {}", employee);
? return repository.save(employee);
?}
?@GetMapping("/{id}")
?public Employee findById(@PathVariable("id") String id) {
? LOGGER.info("Employee find: id={}", id);
? return repository.findById(id).get();
?}
?@GetMapping("/")
?public Iterable findAll() {
? LOGGER.info("Employee find");
? return repository.findAll();
?}
?@GetMapping("/department/{departmentId}")
?public List findByDepartment(@PathVariable("departmentId") Long departmentId) {
? LOGGER.info("Employee find: departmentId={}", departmentId);
? return repository.findByDepartmentId(departmentId);
?}
?@GetMapping("/organization/{organizationId}")
?public List findByOrganization(@PathVariable("organizationId") Long organizationId) {
? LOGGER.info("Employee find: organizationId={}", organizationId);
? return repository.findByOrganizationId(organizationId);
?}
}

為了在Kubernetes上運行我們的微服務,我們應該首先使用Maven來構建整個項目:

mvn clean install?

每個微服務在根目錄中都有一個Dockerfile。這是employee-service的Dockerfile定義。

FROM openjdk:8-jre-alpine
ENV APP_FILE employee-service-1.0-SNAPSHOT.jar
ENV APP_HOME /usr/apps
EXPOSE 8080
COPY target/$APP_FILE $APP_HOME/
WORKDIR $APP_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec java -jar $APP_FILE"]

讓我們為所有三個示例微服務構建Docker映像:

$ cd employee-service
$ docker build -t piomin/employee:1.0 .
$ cd department-service
$ docker build -t piomin/department:1.0 .
$ cd organization-service
$ docker build -t piomin/organization:1.0 .

最后一步是在Kubernetes上將Docker容器與應用程序一起部署。為此,只需基于YAML配置文件上執行命令kubectl apply。employee-service已經在步驟1中演示了。所有必需的部署字段在存儲庫中的?kubernetes目錄中找到。

$ kubectl apply -f kubernetes\employee-deployment.yaml
$ kubectl apply -f kubernetes\department-deployment.yaml
$ kubectl apply -f kubernetes\organization-deployment.yaml

4.使用Spring Cloud Kubernetes Ribbon進行微服務之間的通信

所有微服務都部署在Kubernetes上。現在,有必要討論與服務間通信有關的某些方面。employee-service與其他微服務相反,該應用程序未調用任何其他微服務。讓我們看一下其他微服務,這些微服務調用了員工服務公開的API,并且彼此之間進行通信(organization-service 調用 department-service API)。

首先,我們需要在項目中包括一些其他依賴項。我們使用Spring Cloud Ribbon和OpenFeign。另外,您也可以使用Spring?@LoadBalancedRestTemplate。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
<version>0.3.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

這是department-service的主要類。它通過使用@EnableFeignClients注釋作為Feign客戶端。它的工作原理與基于Spring Cloud Netflix Eureka的發現相同。OpenFeign使用Ribbon進行客戶端負載平衡。Spring Cloud Kubernetes Ribbon提供了一些bean,它們迫使Ribbon通過Fabric8 KubernetesClient與Kubernetes API進行通信。

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableMongoRepositories
@EnableSwagger2
public class DepartmentApplication {
?public static void main(String[] args) {
? SpringApplication.run(DepartmentApplication.class, args);
?}
?// ...
}

這是Feign客戶端的實現,用于調用employee-service公開的方法。

@FeignClient(name = "employee")
public interface EmployeeClient {
?@GetMapping("/department/{departmentId}")
?List findByDepartment(@PathVariable("departmentId") String departmentId);
}

最后,我們必須將Feign客戶的Bean注入REST控制器。現在,我們可以調用EmployeeClient內部定義的方法,調用該方法等效于調用REST端點。

@RestController
public class DepartmentController {
?private static final Logger LOGGER = LoggerFactory.getLogger(DepartmentController.class);
?@Autowired
?DepartmentRepository repository;
?@Autowired
?EmployeeClient employeeClient;
?// ...
?@GetMapping("/organization/{organizationId}/with-employees")
?public List findByOrganizationWithEmployees(@PathVariable("organizationId") Long organizationId) {
? LOGGER.info("Department find: organizationId={}", organizationId);
? List departments = repository.findByOrganizationId(organizationId);
? departments.forEach(d -> d.setEmployees(employeeClient.findByDepartment(d.getId())));
? return departments;
?}
}

5.使用Kubernetes Ingress入口構建API網關

入口Ingress是一組規則,這些規則允許傳入的請求能夠訪問到達下游服務。在我們的微服務架構中,入口扮演著API網關的角色。要創建它,我們首先應該準備一個YAML描述文件。描述符文件應包含主機名,網關將在該主機名下可用,并將規則映射到下游服務。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
? name: gateway-ingress
? annotations:
? ? nginx.ingress.kubernetes.io/rewrite-target: /
spec:
? backend:
? ? serviceName: default-http-backend
? ? servicePort: 80
? rules:
? - host: microservices.info
? ? http:
? ? ? paths:
? ? ? - path: /employee
? ? ? ? backend:
? ? ? ? ? serviceName: employee
? ? ? ? ? servicePort: 8080
? ? ? - path: /department
? ? ? ? backend:
? ? ? ? ? serviceName: department
? ? ? ? ? servicePort: 8080
? ? ? - path: /organization
? ? ? ? backend:
? ? ? ? ? serviceName: organization
? ? ? ? ? servicePort: 8080

必須執行下面命令將以上配置應用于所有kubernetes集群:

$ kubectl apply -f kubernetes\ingress.yaml

要在本地測試該解決方案,我們必須在主機文件內的入口定義中設置的IP地址和主機名之間插入映射。之后,我們可以使用定義的主機名通過入口測試服務,如下所示:http://microservices.info/employee。

192.168.99.100 microservices.info

?執行命令?kubectl describe ing gateway-ingress可檢查ingress細節。

6.使用Swagger2在網關上啟用API規范

如果我們想為Kubernetes上部署的所有微服務公開一個Swagger文檔怎么辦?好吧,這里的事情變得復雜了……我們可以使用Swagger UI運行一個容器,并手動映射入口暴露的所有路徑,但這不是一個好的解決方案……

在這種情況下,我們可以再次使用Spring Cloud Kubernetes Ribbon,這一次可以與Spring Cloud Netflix Zuul一起使用。Zuul僅充當服務于Swagger API的網關。這是我的gateway-service項目中使用的依賴項列表。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes</artifactId>
<version>0.3.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
<version>0.3.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>

Kubernetes發現客戶端將檢測群集上公開的所有服務。我們只想顯示三個微服務的文檔。這就是為什么我為Zuul定義以下路由。

zuul:
? routes:
? ? department:
? ? ? path: /department/**
? ? employee:
? ? ? path: /employee/**
? ? organization:
? ? ? path: /organization/**

現在我們可以使用?ZuulPropertiesBean從Kubernetes發現中獲取路由的地址,并將其配置為Swagger資源,如下所示:

@Configuration
public class GatewayApi {
?@Autowired
?ZuulProperties properties;
?@Primary
?@Bean
?public SwaggerResourcesProvider swaggerResourcesProvider() {
? return () -> {
? ?List resources = new ArrayList();
? ?properties.getRoutes().values().stream()
? ?.forEach(route -> resources.add(createResource(route.getId(), "2.0")));
? ?return resources;
? };
?}
?private SwaggerResource createResource(String location, String version) {
? SwaggerResource swaggerResource = new SwaggerResource();
? swaggerResource.setName(location);
? swaggerResource.setLocation("/" + location + "/v2/api-docs");
? swaggerResource.setSwaggerVersion(version);
? return swaggerResource;
?}
}

應用程序網關服務應與其他應用程序一樣部署在群集上。您可以通過執行命令kubectl get svc來查看正在運行的服務的列表。Swagger文檔可從以下地址獲得:http://192.168.99.100:31237/swagger-ui.html。

?

                   

1
一级黄色录像影片 夫妻性生活影片 免费在线观看 一级a做爰片