【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。
让我们继续执行。目前,SpringCloud的最新稳定版本是Finchley.RELEASE。该版本的spring-cloud-dependencies应该声明一个依赖管理的
BOM。
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文件的片段。
虽然用户名和密码是敏感字段,但数据库名不是,因此我们可以将其放在config map中。
当然,用户名和密码被定义在secrets中。
要将配置应用于Kubernetes集群,我们运行以下命令。
完成之后,我们应该将配置属性注入到应用 程序的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。
服务可用于访问kubernetes集群外的应用程序或用于集群内的服务间通信。但是,使用SpringCloud Kubernetes可以更轻松的实现微服务之间的通信。首先,我们需要在项目pom.xml引入下面依赖。
然后我们微应用程序启用客户端发现,就像我们呢在Spring Cloud Netflix Eureka中一直做的发现一样。这允许你按名称查询Kubernetes endpoint。这种发现功能也被SpringCloud Kubernetes Ribbon或Zipkin项目用来分别微需要负载均衡的微服务获取已定义的pod列表,或者可用于追踪或聚合的Zipkin服务器。
本节最后一个重要事项是保证Spring应用程序名与Kubernetes服务名完全相同。对于应用程序employee-service就是employee。
使用Docker构建为服务并在Kubernetes上部署
在我们的示例微服务中没有任何不正常。我们已经包含了一些用于构建基于REST的微服务,集成MongoDB和使用Swagger2生成API文档的标准Spring依赖。
为了与MongoDB集成,我们应该创建一个继承了标准Spring Data CurdRepository接口。
实体类应该用Mongo的注解@Document,主键字段用@Id。
该repository bean已经被注入到controller类中。以下是我们呢employee-service中REST API的完整实现。
为了在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 镜像。
最后一步是在 Kubernetes 上部署有应用程序的 Docker 容器。为此,只需在 YAML 配置文件上执行 kubectl apply 命令。 employee-service 的示例部署文件已经在步骤 1 中演示了。所有需要的部署字段都可以在项目repository的 kubernetes 目录中找到。
使用Spring Cloud Kubernetes Ribbon进行微服务之间的通信
所有微服务都部署在kubernetes上。现在,讨论服务间的通信相关的一些方面是很值得的。应用程序employee-service和其他微服务相比,它没有调用任何其他微服务。让我们看一下其他为服务调用由employee-service公开的API并与其它微服务进行通信(organization-service 调用 department-service API)。
首先,我们需要在项目中引入一些额外的依赖。我们使用SpringCloud Ribbon和OpenFegin。或者你也可以使用Spring的@LoadBalancedRestTemplate。
下面是department-service的主类。它使用@EnableFeignClients注解启用Fegin客户端。它的工作原理与基于 Spring Cloud Netflix Eureka 的服务发现相同。OpenFeign微客户端使用Ribbon进行负载均衡。Spring Cloud Kubernetes Ribbon 提供了一些 bean,通过 Fabric8 的 KubernetesClient 强制 Ribbon 使用 Kubernetes API 进行通信。
下面是用于调用 employee-service 公开的方法的 Feign 客户端实现。
最后,我们必须将Feign客户端的bean注入到REST controller。现在,我们可以调用 EmployeeClient 内部定义的方法,这相当于调用 REST endpoint。
5、使用Kubernetes Ingress构建API网关
Ingress是允许传入请求到达下游服务的一组规则。在我们的微服务架构中,ingress扮演API网关的角色。要创建它,我们应该首先准备一个YAML描述文件。描述文件应该包含网关可用的主机名和到达下游服务的映射规则。
你必须执行下面命令才能将该配置应用于 Kubernetes 集群。
$ kubectl apply -f kubernetes\ingress.yaml
要在本地测试本解决方案,我们必须在hosts文件里的 ingress 定义中插入 IP 地址和主机名之间的映射设置,如下所示。之后,我们可以使用自定义主机名通过 ingress 测试服务,如下所示:
http://microservices.info/employee
你可以通过执行 kubectl describe ing gateway-ingress 命令检查创建的 ingress 的详情。
使用 Swagger2 在网关上启用 API 规范
如果我们想为 Kubernetes 上部署的所有微服务公开一个 Swagger 文档该怎么做?好吧,这里的事情变复杂了。我们可以运行一个有 Swagger UI 的容器,并通过手动公开 ingress 映射所有路径,但是这不是一个好的解决方案。
在这种情况下,我们可以再次使用 Spring Cloud Kubernetes Ribbon,这次是与 Spring Cloud Netflix Zuul 一起使用。Zuul 将作为只为 Swagger API 服务的网关。
Kubernetes discovery 客户端将检测集群上公开的所有服务。我们只想显示三个微服务的文档。这就是我为什么为 Zuul 定义以下 route。
现在我们可以使用 ZuulProperties bean 从 Kubernetes discovery 中获取 route 的地址,并将他们配置为 Swagger 的资源,如下所示。
应用程序 gateway-service 应该和其他应用程序一样部署在集群上。你可以通过执行 kubectlgetsvc 命令来查看运行中的服务列表。Swagger 文档在地址 http://192.168.99.100:31237/swagger-ui.html 可以看见。
实际上我正在为 Spring Cloud Kubernetes 项目做准备,该项目仍处于孵化阶段。Kubernetes 作为一个平台的受欢迎程度在过去几个月中迅速增长,但是它仍有一些弱点。其中之一就是服务间通信。Kubernetes 没有给我们许多允许我们配置更高级规则的开箱即用的机制。这是 Kubernetes 上为服务网格创建如 Istio 或 Linkered 等框架的原因。这些项目仍然是相对较新的解决方案,但 Spring Cloud 是一个稳定坚固的框架。为什么不用它来提供服务发现、服务间通信或者负载均衡呢?感谢 Spring Cloud Kubernetes,这是可能的。