使用Spring Cloud Kubernetes基於Kubernetes、Spring Boot和Docker構建微服務架構 - Morioh
在本文中,我們將學習如何啟動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。
相關文章
- 基於Spring Boot和Spring Cloud實現微服務架構Spring BootCloud微服務架構
- Spring Cloud構建微服務架構-spring cloud服務監控中心SpringCloud微服務架構
- 詳解Spring Cloud和Docker的微服務架構SpringCloudDocker微服務架構
- spring cloud + spring boot + springmvc+mybatis微服務雲架構CloudSpring BootSpringMVCMyBatis微服務架構
- Spring Cloud Spring Boot mybatis分散式微服務雲架構CloudSpring BootMyBatis分散式微服務架構
- Spring Cloud分散式微服務雲架構構建SpringCloud分散式微服務架構
- 學習使用Spring Boot和Spring Cloud建立微服務架構的5本書 - hackernoonSpring BootCloud微服務架構
- Spring Cloud構建微服務架構-服務閘道器SpringCloud微服務架構
- Spring Cloud構建微服務架構-Hystrix服務降級SpringCloud微服務架構
- spring cloud + spring boot + springmvc+mybatis分散式微服務雲架構CloudSpring BootSpringMVCMyBatis分散式微服務架構
- 在國外是如何用Spring Boot、Spring Cloud、Docker實現微服務系統架構Spring BootCloudDocker微服務架構
- spring cloud微服務分散式雲架構Spring Cloud ZuulSpringCloud微服務分散式架構Zuul
- spring cloud微服務分散式雲架構-Spring Cloud NetflixSpringCloud微服務分散式架構
- spring cloud微服務分散式雲架構-Spring Cloud BusSpringCloud微服務分散式架構
- Spring Cloud 微服務架構進階SpringCloud微服務架構
- 微服務架構:Dubbo VS Spring Cloud微服務架構SpringCloud
- spring cloud微服務架構設計SpringCloud微服務架構
- Spring Cloud雲服務架構 - 企業分散式微服務雲架構構建SpringCloud架構分散式微服務
- Java架構-(一)spring cloud微服務分散式雲架構 - Spring Cloud簡介Java架構SpringCloud微服務分散式
- Spring Cloud構建微服務架構:分散式配置中心(加密解密)SpringCloud微服務架構分散式加密解密
- 使用Spring Boot和GraalVM在Knative上構建微服務 - piotrSpring BootLVM微服務
- spring cloud微服務分散式雲架構 - Spring Cloud簡介SpringCloud微服務分散式架構
- spring cloud微服務雲架構-用java使用 redlockSpringCloud微服務架構Java
- 整合spring cloud雲服務架構 - 企業分散式微服務雲架構構建SpringCloud架構分散式微服務
- 通過Spring Boot,Spring Cloud Gateway構建基於Consul叢集的微服務案例演示 – Piotr的TechBlogSpring BootCloudGateway微服務
- 透過Spring Boot,Spring Cloud Gateway構建基於Consul叢集的微服務案例演示 – Piotr的TechBlogSpring BootCloudGateway微服務
- Spring Cloud構建微服務架構—服務閘道器過濾器SpringCloud微服務架構過濾器
- Spring Cloud 微服務架構解決方案SpringCloud微服務架構
- [雲原生微服務架構](十二) Kubernetes和docker都做了啥微服務架構Docker
- (四)整合spring cloud雲服務架構 - 企業分散式微服務雲架構構建SpringCloud架構分散式微服務
- Spring Cloud Spring Boot mybatis分散式微服務雲架構-hystrix引數詳解CloudSpring BootMyBatis分散式微服務架構
- (一)spring cloud微服務分散式雲架構 - Spring Cloud簡介SpringCloud微服務分散式架構
- (一)spring cloud微服務分散式雲架構-Spring Cloud簡介SpringCloud微服務分散式架構
- spring cloud微服務分散式雲架構--hystrix的使用SpringCloud微服務分散式架構
- 基於Istio/gRPC/Redis/BigQuery/Spring Boot/Spring Cloud和Stackdriver的微服務案例RPCRedisSpring BootCloud微服務
- Spring Cloud系列(一):微服務架構簡介SpringCloud微服務架構
- (十七)spring cloud微服務分散式雲架構-eureka 基礎SpringCloud微服務分散式架構
- spring cloud微服務分散式雲架構-Spring Cloud Config環境庫SpringCloud微服務分散式架構