使用 Kubernetes、Spring Boot 2.0 和
【SFA官方翻译】使用 Kubernetes、Spring Boot 2.0 和 Docker 的微服务快速指南
原文链接:https://dzone.com/articles/quick-guide-to-microservices-with-kubernetes-sprin
作者:Piotr Mińkowski
译者:Darren Luo
在本教程中你将学习如何使用Kubernetes和Docker快速启动并运行Spring Boot微服务项目。
这是“xxx快速指南”系列的下一篇文章。这次,我们将讨论并在Kubernetes上运行Spring Boot微服务示例。本文的结构将和使用SpringBoot2.0、Eureka和SpringCloud的微服务快速指南非常相似,因为他们都描述了应用程序开发的相同方面。我将重点向你展示SpringCloud和Kubernetes在开发方面的异同。本文涉及的话题有:
1、在云原生开发中使用SpringBoot 2.0
2、使用SpringCloud Kubernetes项目为所有微服务提供服务发现
3、使用Kubernetes的Config Maps和Secrets为应用程序pod(Kubernetes中的最小管理单元)注入配置设置。
4、使用Docker构建应用程序镜像并使用YAML配置文件将他们部署到Kubernetes上。
5、将Spring Cloud Kubernetes和Zuul代理一起使用,为所有微服务公开一个独立的Swagger API文档。
当你构建微服务环境时,Spring Cloud和Kubernetes可能成为互相威胁的竞争解决方案。SpringCloud提供的如Eureka、Spring Cloud Config或Zuul等组件可能被Kubernetes的如service、Config maps、secrets或ingresses等内置对象所替代。但是即使你决定使用Kubernetes组件替代SpringCloud你也可以利用整个SpringCloud项目提供的一些有趣的功能。
在开发中帮助我们的一个非常有趣的项目是SpringCloud Kubernetes。虽然它还处于孵化阶段,但绝对值得在它上面献上一些时间。它将SpringCloud和Kubernetes集成在一起。我将向你展示如何使用客户端发现的实现,与Ribbon客户端的服务间通信以及使用Springcloud Kubernetes的Zipkin发现。
在我们处理源码之前,让我们看一下下面的图。它说明了我们示例程序的架构。它和之前文章提到的关于SpringCloud上的微服务架构非常相似。这里有三个独立应用程序(employee-service,department-service,organization-service),通过REST API互相通信。这些SpringBoot微服务使用一些Kubernetes内置机制:用于分发配置的config maps和secrets,用于服务发现的etcd和用于API网关的ingresses。
![](https://img.haomeiwen.com/i15372936/4116cc6566271151.png)
让我们继续执行。目前,SpringCloud的最新稳定版本是Finchley.RELEASE。该版本的spring-cloud-dependencies应该声明一个依赖管理的
BOM。
![](https://img.haomeiwen.com/i15372936/35f8d7a049640fdf.png)
SpringCloud Kubernetes未在SpringCloud Release Trains发布,所以我们需要显式定义其版本。因为我们使用SpringBoot2.0,所以我们必须包含最新SNAPSHOT版本的spring-cloud-kubernetes工件,版本号为0.3.0.BUILD-SNAPSHOT。
本文提供的示例应用程序的源码可以在 Github 上的此 repository 获得。
前提要求
为了能部署和测试我们的示例微服务,我们需要准备一个开发环境。我们通过以下步骤实现:
1、你至少需要在你本地机器上运行的kubernetes(Minikube)or Openshift(Minishift)的单节点集群实例。你应该启动它并公开他们提供的嵌入式Docker客户端。有关Minishift的详细说明可以在我的在Openshift上部署Java应用程序的快速指南里找到。你也可以使用这份说明来运行Minikube,只需要用“minikube”替换单词“minishift”。事实上,如果你选择Kubernete或Openshift并没有关系,本教程的以下部分对他们都适用。
2、SpringCloud Kubernetes需要访问Kubernetes API,以便于能够检索为单个服务运行的pod的地址列表。如果你是用kubernetes,你应该只执行以下命令:
$ kubectl create clusterrolebinding admin --clusterrole=cluster-admin --serviceaccount=default:default
如果你在Minishift上部署你的微服务,首先你需要启用admin-user插件,然后以集群管理员身份登录并授予所需权限。
$ minishift addons enable admin-user
$ oc login -u system:admin
$ oc policy add-role-to-user cluster-reader system:serviceaccount:myproject:default
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo:latest
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_DATABASE
valueFrom:
configMapKeyRef:
name: mongodb
key: database-name
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb
key: database-user
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb
key: database-password
---
apiVersion: v1
kind: Service
metadata:
name: mongodb
labels:
app: mongodb
spec:
ports:
- port: 27017
protocol: TCP
selector:
app: mongodb
1、使用Config Maps和Secrets住配置
使用springcloud时,在你的系统中实现分发配置的最明显的选择是SpringCloud Config。用kubernetes,你可以使用Config Map。它保存了能在pod中使用或用户存储配置数据的配置数据键值对。它用于存储和共享非敏感,未加密的配置信息。要在你的集群中使用敏感信息,你必须使用Secrets。基于MongoDB链接设置的示例可以完美的演示使用这两个Kubernetes对象。在SpringBoot应用程序中,我们可以使用环境变量轻松注入它。这里是一个带有URI配置的application.yml文件的片段。
![](https://img.haomeiwen.com/i15372936/6748dbcc94d51fde.png)
虽然用户名和密码是敏感字段,但数据库名不是,因此我们可以将其放在config map中。
![](https://img.haomeiwen.com/i15372936/c1d4bb34ed4736cb.png)
当然,用户名和密码被定义在secrets中。
![](https://img.haomeiwen.com/i15372936/fb1c40b8630a7566.png)
要将配置应用于Kubernetes集群,我们运行以下命令。
![](https://img.haomeiwen.com/i15372936/d4b76781bebd1773.png)
完成之后,我们应该将配置属性注入到应用 程序的pod中。在Deployment YAML文件中定义容器配置时,我们必须包含对环境变量和secrets的引用,如下所示。
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构建服务发现
我们通常在kubernetes上用Docker容器运行为服务。一个或多个容器按pod分组,也是kubernetes中可被创建和管理的最小可部署单元。一个好的做法是在一个pod中只运行一个容器。如果你想扩展你的微服务,你只需要增加运行的pod数量。一个独立微服务的所有运行中的pod在逻辑上都被Kubernetes Service组合在一起。此服务可能在集群外可见,并且能够在所有运行中的pod之间对传入的请求进行负载均衡。下面的服务所有的pod组定义标记字段app等于employee。
![](https://img.haomeiwen.com/i15372936/ec6ae6a50b648cbb.png)
服务可用于访问kubernetes集群外的应用程序或用于集群内的服务间通信。但是,使用SpringCloud Kubernetes可以更轻松的实现微服务之间的通信。首先,我们需要在项目pom.xml引入下面依赖。
![](https://img.haomeiwen.com/i15372936/a0b3d343b554b09b.png)
然后我们微应用程序启用客户端发现,就像我们呢在Spring Cloud Netflix Eureka中一直做的发现一样。这允许你按名称查询Kubernetes endpoint。这种发现功能也被SpringCloud Kubernetes Ribbon或Zipkin项目用来分别微需要负载均衡的微服务获取已定义的pod列表,或者可用于追踪或聚合的Zipkin服务器。
![](https://img.haomeiwen.com/i15372936/be7237fcd1b775e0.png)
本节最后一个重要事项是保证Spring应用程序名与Kubernetes服务名完全相同。对于应用程序employee-service就是employee。
![](https://img.haomeiwen.com/i15372936/29d8ecd0963dba21.png)
使用Docker构建为服务并在Kubernetes上部署
在我们的示例微服务中没有任何不正常。我们已经包含了一些用于构建基于REST的微服务,集成MongoDB和使用Swagger2生成API文档的标准Spring依赖。
![](https://img.haomeiwen.com/i15372936/1f307880fada6e49.png)
为了与MongoDB集成,我们应该创建一个继承了标准Spring Data CurdRepository接口。
![](https://img.haomeiwen.com/i15372936/c7109962376d0037.png)
实体类应该用Mongo的注解@Document,主键字段用@Id。
![](https://img.haomeiwen.com/i15372936/40556982aebb6e0e.png)
该repository bean已经被注入到controller类中。以下是我们呢employee-service中REST API的完整实现。
![](https://img.haomeiwen.com/i15372936/ce32b25239ec5f35.png)
![](https://img.haomeiwen.com/i15372936/91dae984b9417568.png)
为了在kubernetes上运行我们的微服务,我们首先应该用mvn clean install 命令构建整个Maven项目。每个微服务都有一个放在根目录中的Dockerfile。这是为了employee-service定义的Dockerfile。
FROM open jdk: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 镜像。
![](https://img.haomeiwen.com/i15372936/05c1ab99d02a30f3.png)
最后一步是在 Kubernetes 上部署有应用程序的 Docker 容器。为此,只需在 YAML 配置文件上执行 kubectl apply 命令。 employee-service 的示例部署文件已经在步骤 1 中演示了。所有需要的部署字段都可以在项目repository的 kubernetes 目录中找到。
![](https://img.haomeiwen.com/i15372936/bbdbfcab95a8cd0b.png)
使用Spring Cloud Kubernetes Ribbon进行微服务之间的通信
所有微服务都部署在kubernetes上。现在,讨论服务间的通信相关的一些方面是很值得的。应用程序employee-service和其他微服务相比,它没有调用任何其他微服务。让我们看一下其他为服务调用由employee-service公开的API并与其它微服务进行通信(organization-service 调用 department-service API)。
首先,我们需要在项目中引入一些额外的依赖。我们使用SpringCloud Ribbon和OpenFegin。或者你也可以使用Spring的@LoadBalancedRestTemplate。
![](https://img.haomeiwen.com/i15372936/fd303f420772a268.png)
下面是department-service的主类。它使用@EnableFeignClients注解启用Fegin客户端。它的工作原理与基于 Spring Cloud Netflix Eureka 的服务发现相同。OpenFeign微客户端使用Ribbon进行负载均衡。Spring Cloud Kubernetes Ribbon 提供了一些 bean,通过 Fabric8 的 KubernetesClient 强制 Ribbon 使用 Kubernetes API 进行通信。
![](https://img.haomeiwen.com/i15372936/0b1cf93ae20b6141.png)
下面是用于调用 employee-service 公开的方法的 Feign 客户端实现。
![](https://img.haomeiwen.com/i15372936/1b30295f24e13bf5.png)
最后,我们必须将Feign客户端的bean注入到REST controller。现在,我们可以调用 EmployeeClient 内部定义的方法,这相当于调用 REST endpoint。
![](https://img.haomeiwen.com/i15372936/c312c48a1e51ea5b.png)
5、使用Kubernetes Ingress构建API网关
Ingress是允许传入请求到达下游服务的一组规则。在我们的微服务架构中,ingress扮演API网关的角色。要创建它,我们应该首先准备一个YAML描述文件。描述文件应该包含网关可用的主机名和到达下游服务的映射规则。
![](https://img.haomeiwen.com/i15372936/cdf41de1eb4fb2e1.png)
你必须执行下面命令才能将该配置应用于 Kubernetes 集群。
$ kubectl apply -f kubernetes\ingress.yaml
要在本地测试本解决方案,我们必须在hosts文件里的 ingress 定义中插入 IP 地址和主机名之间的映射设置,如下所示。之后,我们可以使用自定义主机名通过 ingress 测试服务,如下所示:
http://microservices.info/employee
![](https://img.haomeiwen.com/i15372936/0e7ab5a3691e6146.png)
你可以通过执行 kubectl describe ing gateway-ingress 命令检查创建的 ingress 的详情。
![](https://img.haomeiwen.com/i15372936/43b28fc93892e3e6.png)
使用 Swagger2 在网关上启用 API 规范
如果我们想为 Kubernetes 上部署的所有微服务公开一个 Swagger 文档该怎么做?好吧,这里的事情变复杂了。我们可以运行一个有 Swagger UI 的容器,并通过手动公开 ingress 映射所有路径,但是这不是一个好的解决方案。
在这种情况下,我们可以再次使用 Spring Cloud Kubernetes Ribbon,这次是与 Spring Cloud Netflix Zuul 一起使用。Zuul 将作为只为 Swagger API 服务的网关。
![](https://img.haomeiwen.com/i15372936/8eee985f77e0bf36.png)
Kubernetes discovery 客户端将检测集群上公开的所有服务。我们只想显示三个微服务的文档。这就是我为什么为 Zuul 定义以下 route。
![](https://img.haomeiwen.com/i15372936/054009ea04ae369c.png)
现在我们可以使用 ZuulProperties bean 从 Kubernetes discovery 中获取 route 的地址,并将他们配置为 Swagger 的资源,如下所示。
![](https://img.haomeiwen.com/i15372936/cb458c8401442dda.png)
应用程序 gateway-service 应该和其他应用程序一样部署在集群上。你可以通过执行 kubectlgetsvc 命令来查看运行中的服务列表。Swagger 文档在地址 http://192.168.99.100:31237/swagger-ui.html 可以看见。
![](https://img.haomeiwen.com/i15372936/802bafe2bd113cc8.png)
实际上我正在为 Spring Cloud Kubernetes 项目做准备,该项目仍处于孵化阶段。Kubernetes 作为一个平台的受欢迎程度在过去几个月中迅速增长,但是它仍有一些弱点。其中之一就是服务间通信。Kubernetes 没有给我们许多允许我们配置更高级规则的开箱即用的机制。这是 Kubernetes 上为服务网格创建如 Istio 或 Linkered 等框架的原因。这些项目仍然是相对较新的解决方案,但 Spring Cloud 是一个稳定坚固的框架。为什么不用它来提供服务发现、服务间通信或者负载均衡呢?感谢 Spring Cloud Kubernetes,这是可能的。