在本文中,您将学习如何将三个简单的Java服务部署到Kubernetes(通过新的Docker for Mac / Windows集成在本地运行),并通过Kubernetes-native Ambassador API Gateway向前端用户公开前端服务。所以,抓住你选择的含咖啡因的饮料,在你的终端前舒服一点!
去年10月,我通过Kubernetes支持扩展了基于Java微服务的“ Docker Java Shopping ”容器部署演示。如果你找到了完成教程的时间,你将在Docker镜像中打包三个简单的Java服务 - 店面和stockmanager Spring Boot服务,以及产品目录Java EE DropWizard服务,并将生成的容器部署到本地minikube驱动的 Kubernetes集群。我还向您展示了如何通过使用NodePort服务映射和公开Kubernetes集群端口来向最终用户打开店面服务。虽然这对于演示来说很有用,但是很多人问你如何在API网关后面部署应用程序。这是一个很好的问题,因此我热衷于在本教程系列中添加另一篇文章,目的是在开源Kubernetes-native Ambassador API Gateway背后部署“Docker Java Shopping”Java应用程序。

图1.使用Ambassador API Gateway部署的“Docker Java Shopping”应用程序
我相信你们中的许多人之前会使用(或者至少会碰到)API网关的概念。Chris Richardson在microservices.io上详细介绍了详细信息,创建大使API网关Datawire的团队也讨论了使用Kubernetes原生API网关的好处。简而言之,API网关允许您集中应用程序的许多横切关注点,例如负载平衡,安全性和速率限制。运行Kubernetes本机API网关还允许您将与部署和维护网关相关的几个操作问题(例如实现弹性和可伸缩性)卸载到Kubernetes本身。
Java开发人员有许多API网关选择,例如开源Netflix的Zuul,Spring Cloud Gateway和Mashape的Kong ; 云供应商的实施(如亚马逊的API网关); 当然,还有传统的NGINX和HAProxy ; 最后,还有像Traefik这样更现代的变种。为您的用例选择最佳API网关可能涉及大量工作 - 这是您的基础架构的关键部分,它将触及进入您的应用程序的每一点流量。与任何关键技术选择一样,需要考虑许多权衡因素。特别要注意潜在的高耦合点 - 例如,我已经看到能够将“过滤器”(Groovy脚本)动态部署到Netflix的Zuul中,使业务逻辑能够在服务和网关之间传播(耦合) -并且随着最终用户流量的增加,需要部署复杂的数据存储 - 例如,Kong需要Cassandra集群或Postgres安装来水平扩展。
为了本文的简单起见,我将使用开源Kubernetes-native Ambassador API Gateway。我喜欢大使,因为实现的简单性降低了意外地将任何业务逻辑耦合到它的能力,以及我可以通过声明方法(我用于所有其他Kubernetes配置)指定服务路由的事实感觉更“云”本机“ - 我还可以在版本控制中轻松存储路由,并在CI / CD构建管道中将其发送到所有其他代码更改。
首先,确保您从一个新的(空)Kubernetes集群开始。因为我喜欢每隔一段时间拥抱一次我的内心时髦,我将使用Docker for Mac中的新Kubernetes集成来运行此演示。如果您想继续操作,则需要确保已安装适用于Mac的Docker for Edge 或适用于Windows的Docker,并且还要按照Docker Kubernetes文档中的说明启用Kubernetes支持。
接下来克隆我的“Docker Java Shopfront”GitHub存储库。如果您想探索目录结构并了解有关构成应用程序的三种服务中的每一项的更多信息,那么我建议您查看本系列的前一篇文章或相关的迷你书“ Containerizing Java中的持续交付 ”,开始了所有这一切。成功克隆repo后,您可以导航到kubernetes目录。如果您正在按照本教程进行操作,那么您将在此目录中进行修改,因此欢迎您自行分配您自己的repo副本并创建一个可以将您的工作推送到的分支。我不建议跳过(或作弊),但是kubernetes-ambassador 目录包含完整的解决方案,以防您想检查您的工作!
$ git clone git@github.com:danielbryantuk/oreilly-docker-java-shopping.git
$ cd oreilly-docker-java-shopping/kubernetes
(master) kubernetes $ ls -lsa
total 24
0 drwxr-xr-x 5 danielbryant staff 160 5 Feb 18:18 .
0 drwxr-xr-x 18 danielbryant staff 576 5 Feb 18:17 ..
8 -rw-r--r-- 1 danielbryant staff 710 5 Feb 18:22 productcatalogue-service.yaml
8 -rw-r--r-- 1 danielbryant staff 658 5 Feb 18:11 shopfront-service.yaml
8 -rw-r--r-- 1 danielbryant staff 677 5 Feb 18:22 stockmanager-service.yaml如果您在所选的编辑器/ IDE中打开shopfront-service.yaml,您将看到我将店面服务公开为可通过TCP端口8010访问的NodePort。这意味着可以通过任何端口8010访问该服务公共(并且未被防火墙阻止)的群集节点IP。
---
apiVersion: v1
kind: Service
metadata:
name: shopfront
labels:
app: shopfront
spec:
type: NodePort
selector:
app: shopfront
ports:
— protocol: TCP
port: 8010
name: http通过minikube运行此服务时,NodePort允许您通过群集外部IP访问服务。通过Docker运行服务时,NodePort允许您通过localhost和Kubernetes分配端口访问服务。假设Docker for Mac或Windows已配置为成功运行Kubernetes,您现在可以部署此服务:
(master) kubernetes $ kubectl apply -f shopfront-service.yaml
service "shopfront" created
replicationcontroller "shopfront" created
(master) kubernetes $
(master) kubernetes $ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h
shopfront NodePort 10.110.74.43 <none> 8010:31497/TCP 0s您可以看到已经创建了店面服务,虽然没有列出external-ip,但您可以看到stockmanager-service.yaml(8010)中指定的端口已映射到端口31497(此端口号可能有所不同) )。如果您正在使用Docker for Mac或Windows,您现在可以从localhostcurl数据(因为Docker应用程序在幕后工作),如果您使用的是minikube,则可以通过在终端键入minikube ip来获取群集IP地址 。
假设您正在使用Docker,并且您只部署了单个店面服务,您应该使用您可以从kubectl get svc 命令中看到的端口号(请参阅31497)从curl中看到此响应 :
(master) kubernetes $ curl -v localhost:31497
* Rebuilt URL to: localhost:31497/
* Trying ::1…
* TCP_NODELAY set
* Connected to localhost (::1) port 31497 (#0)
> GET / HTTP/1.1
> Host: localhost:31497
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 500
< X-Application-Context: application:8010
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Tue, 06 Feb 2018 17:20:19 GMT
< Connection: close
<
* Closing connection 0
{“timestamp”:1517937619690,”status”:500,”error”:”Internal Server Error”,”exception”:”org.springframework.web.client.ResourceAccessException”,”message”:”I/O error on GET request for \”http://productcatalogue:8020/products\": productcatalogue; nested exception is java.net.UnknownHostException: productcatalogue”,”path”:”/”}您会注意到,您正在使用此curl获得HTTP 500错误响应,这是可以预期的,因为您尚未部署所有支持服务。但是,在部署其余服务之前,您需要将NodePort配置更改为ClusterIP以用于所有服务。这意味着每个服务只能在群集中的其他网络上访问。当然,您可以使用防火墙来限制NodePort公开的服务,但是通过将ClusterIP与我们的本地开发环境一起使用,您不得欺骗我们通过我们将部署的API网关以外的任何其他方式来访问我们的服务。
在编辑器中打开shopfront-service.yaml,并将NodePort更改为ClusterIP。您可以在下面看到文件内容的相关部分:
---
apiVersion: v1
kind: Service
metadata:
name: shopfront
labels:
app: shopfront
spec:
type: ClusterIP
selector:
app: shopfront
ports:
— protocol: TCP
port: 8010
name: http现在,您可以将productcatalogue-service.yaml和stockmanager-service.yaml文件中包含的服务修改为ClusterIP。
您现在还可以删除现有的店面服务,为本教程的下一部分中的完整堆栈部署做好准备。
(master *) kubernetes $ kubectl delete -f shopfront-service.yaml
service “shopfront” deleted
replicationcontroller “shopfront” deleted通过再次空的Kubernetes集群,您现在可以部署完整的三服务堆栈并获取每个服务的相关Kubernetes信息:
(master *) kubernetes $ kubectl apply -f .
service "productcatalogue" created
replicationcontroller "productcatalogue" created
service "shopfront" created
replicationcontroller "shopfront" created
service "stockmanager" created
replicationcontroller "stockmanager" created
(master *) kubernetes $
(master *) kubernetes $ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20h
productcatalogue ClusterIP 10.106.8.35 <none> 8020/TCP 1s
shopfront ClusterIP 10.98.189.230 <none> 8010/TCP 1s
stockmanager ClusterIP 10.96.207.245 <none> 8030/TCP 1s您可以看到服务中声明的端口是指定的(即8010,8020,8030) - 每个运行的pod都有自己的集群IP和相关的端口范围(即每个pod都有自己的“网络命名空间”)。我们无法在集群外部访问此端口(就像我们可以使用NodePort),但在集群内,一切都按预期工作。
您还可以看到,使用ClusterIP不会通过尝试curl端点来向外部公开服务(这次您应该收到“拒绝连接”):
(master *) kubernetes $ curl -v localhost:8010
* Rebuilt URL to: localhost:8010/
* Trying ::1…
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8010 failed: Connection refused
* Trying 127.0.0.1…
* TCP_NODELAY set
* Connection failed
* connect to 127.0.0.1 port 8010 failed: Connection refused
* Failed to connect to localhost port 8010: Connection refused
* Closing connection 0
curl: (7) Failed to connect to localhost port 8010: Connection refused现在是时候部署大使API网关,以便向最终用户公开您的店面服务。其他两个服务可以在群集中保持私有,因为它们支持服务,并且不必公开公开。
首先,创建一个LoadBalancer服务,该服务使用Kubernetes注释将来自集群外部的请求路由到适当的服务。将以下内容保存在名为“ambassador-service.yaml”的新文件中。请注意 getambassador.io/config 注释。您可以使用Kubernetes注释将任意非标识元数据附加到对象,而诸如Ambassador之类的客户端可以检索此元数据。你能弄清楚这个注释在做什么吗?
---
apiVersion: v1
kind: Service
metadata:
labels:
service: ambassador
name: ambassador
annotations:
getambassador.io/config: |
---
apiVersion: ambassador/v0
kind: Mapping
name: shopfront
prefix: /shopfront/
service: shopfront:8010
spec:
type: LoadBalancer
ports:
- name: ambassador
port: 80
targetPort: 80
selector:
service: ambassador大使注释是网关如何工作的关键 - 它如何将来自群集外部的“入口”流量(例如最终用户请求)路由到群集内的服务。让我们解释一下:
“getambassador.io/config:|” | 指定此注释适用于大使 |
|---|---|
“ - - ” | 简单地宣称你有多爱YAML! |
“apiVersion:ambassador / v0” | 指定大使API /架构版本 |
“ kind: Mapping” | 指定您正在创建“映射”(路由)配置 |
“ name: shopfront” | 是此映射的名称(将显示在调试UI中) |
“ prefix: /shopfront/” | 是要在内部路由的URI的外部前缀 |
“ service: shopfront:8010” | 是您要路由到的Kubernetes服务(和端口) |
简而言之,此注释指出对前缀的任何对LoadBalancer服务的外部IP(对于Mac / Windows示例中为“localhost”)的任何请求, /shopfront/ 都将被路由到运行在(ClusterIP)上的Kubernetes店面服务)在您的示例中,当您在Web浏览器中输入http:// localhost / shopfront /时,您应该看到店面服务提供的UI。希望这一切都有道理,但如果没有,那么请访问Gitter大使,并提出任何问题,或在twitter上ping我!
随着您对大使路由的新发现(以及仅仅几步之遥的所有API网关的世界统治),您可以部署大使服务:
(主人 *)kubernetes $ kubectl 申请 - f 大使- 服务。YAML您还需要部署负责与路由相关的繁重工作的Ambassador Admin服务(以及相关的pod /容器)。值得注意的是,路由是由“sidecar”代理进行的,在这种情况下,代理是Envoy代理。Envoy负责Lyft内部的所有生产网络流量,它的创建者Matt Klein撰写了大量关于细节的非常有趣的内容。您可能也听说过新兴的“ 服务网格 ”技术,而流行的Istio项目也使用了Envoy。
无论如何,回到教程!您可以在getambassador.io网站上找到大使管理员预先准备好的Kubernetes配置文件(对于此演示,您将使用该服务的“无RBAC”版本,但您也可以找到启用RBAC的配置版本文件,如果您正在运行启用了基于角色的访问控制(RBAC)的Kubernetes集群。您可以下载配置文件的副本并在应用之前查看它,或者直接通过Interwebs应用服务:
(master *) kubernetes $ kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-no-rbac.yaml
service “ambassador-admin” created
deployment “ambassador” created如果您发出`kubectl get svc,您可以看到您的Ambassador LoadBalancer和Ambassador Admin服务已成功部署:
(master *) kubernetes $ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ambassador LoadBalancer 10.102.81.42 <pending> 80:31053/TCP 5m
ambassador-admin NodePort 10.105.58.255 <none> 8877:31516/TCP 1m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20h
productcatalogue ClusterIP 10.106.8.35 <none> 8020/TCP 22m
shopfront ClusterIP 10.98.189.230 <none> 8010/TCP 22m
stockmanager ClusterIP 10.96.207.245 <none> 8030/TCP 22m您将在大使服务上注意到external-ip被列为<pending>,这是Docker for Mac / Windows的一个已知错误。您仍然可以通过localhost访问LoadBalancer服务 - 尽管您可能需要等待一两分钟,而所有内容都在幕后成功部署。
让我们现在尝试使用您之前在Ambassador注释中配置的/ shopfront / route 来访问店面。您可以curllocalhost/shopfront/ (无需指定端口,因为您将Ambassador LoadBalancer服务配置为侦听端口80):
(master *) kubernetes $ curl localhost/shopfront/
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
...
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>就是这样!您现在正通过大使访问隐藏在Kubernete集群中的店面服务。您还可以通过浏览器访问店面用户界面,这样可以提供更友好的视图!

如果您想查看Ambassador Diagnostic UI,那么您可以使用端口转发。我将在以后的文章中详细解释如何使用它,但目前你可以自己看看。首先,您需要找到一个大使吊舱的名称:
(master *) kubernetes $ kubectl get pods
NAME READY STATUS RESTARTS AGE
ambassador-6d9f98bc6c-5sppl 2/2 Running 0 19m
ambassador-6d9f98bc6c-nw6z9 2/2 Running 0 19m
ambassador-6d9f98bc6c-qr87m 2/2 Running 0 19m
productcatalogue-sdtlc 1/1 Running 0 22m
shopfront-gr794 1/1 Running 0 22m
stockmanager-bp7zq 1/1 Running 1 22m在这里,我会选择ambassador-6d9f98bc6c-5sppl。您现在可以从本地网络适配器端口转发到群集内部,并公开在端口8877上运行的Ambassador Diagnostic UI。
(master *) kubernetes $ kubectl port-forward ambassador-6d9f98bc6c-5sppl 8877:8877您现在可以在浏览器中访问http:// localhost:8877 / ambassador / v0 / diag并浏览一下!

完成后,您可以通过ctrl-c退出端口转发。您还可以通过kubectl delete -f 在kubernetes目录中发出一个来删除已部署到Kubernetes集群中的所有服务 。您还需要删除已部署的ambassador-admin服务。
(master *) kubernetes $ kubectl delete -f .
service "ambassador" deleted
service "productcatalogue" deleted
replicationcontroller "productcatalogue" deleted
service "shopfront-canary" deleted
replicationcontroller "shopfront-canary" deleted
service "shopfront" deleted
replicationcontroller "shopfront" deleted
service "stockmanager" deleted
replicationcontroller "stockmanager" deleted
(master *) kubernetes $
(master *) kubernetes $ kubectl delete -f https://getambassador.io/yaml/ambassador/ambassador-no-rbac.yaml
service "ambassador-admin" deleted
deployment "ambassador" deleted我正计划很快创建另一篇文章,讨论如何启动/测试服务,因为大使这使得这很容易。我渴望探索的其他主题是将所有这些集成到CD管道中,并探索如何最好地设置本地开发工作流程。与此密切相关,我也热衷于调查通过Kubernetes部署的Java应用程序。
您还可以通过文档阅读有关大使本身的更多详细信息,包括添加身份验证/安全性,gRPC支持和TLS终止。
原文标题《Deploying Java Apps with Kubernetes and the Ambassador API Gateway》
译者:February
不代表云加社区观点,更多详情请查看原文链接
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。