在本文中,您将学习如何将三个简单的 Java 服务部署到 Kubernetes(通过新的 Docker for Mac/Windows 集成在本地运行),并通过 Kubernetes 原生的 Ambassador API 网关向最终用户公开前端服务。所以,拿起您选择的含咖啡因饮料,在您的终端机前享受舒适吧!

快速回顾:架构和部署

去年 10 月,我扩展了我基于 Java 微服务的简单“Docker Java Shopping”容器部署演示,支持Kubernetes。如果您有时间完成本教程,您将在 Docker 镜像中打包三个简单的 Java 服务 — shopfront 和 stockmanager Spring Boot 服务,以及产品目录 Java EE DropWizard 服务 — ,并将生成的容器部署到本地minikube-供电Kubernetes 集群。我还向您展示了如何通过使用NodePort Service映射和公开 Kubernetes 集群端口来向最终用户开放店面服务。尽管这对演示很有用,但你们中的许多人询问如何在 API 网关后面部署应用程序。这是一个很好的问题,因此我很想在本教程系列中添加另一篇文章,目标是在开源 Kubernetes-nativeAmbassador API Gateway后面部署“Docker Java Shopping”Java 应用程序。

Docker Java Shopping app

图 1. 使用大使 API 网关部署的“Docker Java 购物”应用程序

旁白:为什么要使用 API 网关?

我相信你们中的许多人以前会使用过(或至少遇到过)API 网关的概念。 Chris Richardson 在microservices.io上写了一篇很好的细节概述,而创建 Ambassador API 网关Datawire背后的团队也谈到了使用Kubernetes-native API 网关的好处。简而言之,API 网关允许您集中应用程序的许多横切关注点,例如负载平衡、安全性和速率限制。运行 Kubernetes 原生 API 网关还可以让您将与部署和维护网关相关的一些操作问题 — 例如实现弹性和可扩展性 — 交给 Kubernetes 本身。

Java 开发者有很多 API Gateway 选择,例如开源的Netflix 的 Zuul、Spring Cloud Gateway和Mashape 的 Kong;有云厂商的实现(如亚马逊的 API Gateway);当然还有传统的最爱NGINX和HAProxy;最后,还有更现代的变体,如Traefik。为您的用例选择最佳 API 网关可能涉及大量工作 — 这是您的基础架构的关键部分,它将触及进入您应用程序的每一点流量。特别要注意潜在的高耦合点 — 例如,我已经看到将“过滤器”(Groovy 脚本)动态部署到 Netflix 的 Zuul 中的能力使业务逻辑能够在服务和服务之间传播(耦合)网关 — 以及随着最终用户流量的增加需要部署复杂的数据存储 — 例如,Kong 需要Cassandra 集群或 Postgres 安装才能水平扩展。与任何关键技术选择一样,需要考虑许多权衡。

为了在本文中简单起见,我将使用开源的 Kubernetes 原生大使 API 网关。我喜欢大使,因为实现的简单性降低了意外将任何业务逻辑与其耦合的能力,而且我可以通过声明性方法(我用于所有其他 Kubernetes 配置)指定服务路由的事实感觉更像“云” native" — 我还可以轻松地将路由存储在版本控制中,并将其与所有其他代码更改一起发送到 CI/CD 构建管道。

入门:NodePorts 和 LoadBalancers 101

首先,确保您从一个新的(空的)Kubernetes 集群开始。因为我喜欢偶尔拥抱我内心的潮人,所以我将使用 Docker for Mac 中的新 Kubernetes 集成来运行这个演示。如果您想继续,您需要确保您已安装 Edge 版本的Docker for Mac或Docker for Windows,并按照Docker Kubernetes 文档中的说明启用 Kubernetes 支持.

接下来克隆我的“Docker Java Shopfront” GitHub 存储库。如果您想探索目录结构并了解有关构成应用程序的三个服务中的每一个的更多信息,那么我建议您查看本系列中的上一篇文章或相关的迷你书“Containerizing Continuous Delivery在 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

进入全屏模式 退出全屏模式

可以看到shopfront服务已经创建好了,虽然没有列出external-ip,但是可以看到stockmanager-service.yaml(8010)中指定的端口已经映射到了31497端口(这里你的端口号可能不同)。如果您使用 Docker for Mac 或 Windows,您现在可以从 localhost 卷曲数据(因为 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 网关

现在是部署大使 API 网关的时候了,以便向最终用户公开您的店面服务。其他两个服务可以在集群中保持私有,因为它们是支持服务,并且不必公开。

首先,创建一个 LoadBalancer 服务,该服务使用 Kubernetes 注释将来自集群外部的请求路由到适当的服务。将以下内容保存在名为ambassador-service.yaml的新文件中。注意getambassador.io/config注释。您可以使用Kubernetes annotations将任意非标识元数据附加到对象,并且诸如 Ambassador 等客户端可以检索此元数据。你能弄清楚这个注释在做什么吗?

Ambassador 注解是网关如何工作的关键 — 它如何将来自集群外部的“入口”流量(例如最终用户请求)路由到集群内的服务。让我们分解一下:

  • “getambassador.io/config:|” — 指定这个注解是给大使的

  • “ — -” — 简单地表明你有多爱YAML!

  • “ apiVersion: Ambassador/v0” — 指定 Ambassador API/schema 版本

  • “kind: Mapping” — 指定你正在创建一个“映射”(路由)配置

  • “name: shopfront” — 是此映射的名称(将显示在调试 UI 中)

  • “前缀:/shopfront/” — 是要内部路由的URI的外部前缀

  • “service: shopfront:8010” — 是您要路由到的 Kubernetes 服务(和端口)

简而言之,这个注释说明任何对 LoadBalancer 服务的外部 IP 的请求(在 Mac/Windows 的 Docker 示例中为“localhost”),前缀为/shopfront/将被路由到运行在 ( ClusterIP) 端口 8010。在您的示例中,当您在 Web 浏览器中输入http://localhost/shopfront/时,您应该会看到 shopfront 服务提供的 UI。希望这一切都有意义,但如果没有,请访问大使吉特并提出任何问题,或在推特上联系我!

随着您对大使路由的新了解(以及所有 API 网关的世界统治仅几步之遥),您可以部署大使服务:

(master *) kubernetes $ kubectl apply -f ambassador-service.yaml
service “ambassador” created

进入全屏模式 退出全屏模式

您还需要部署负责与路由相关的繁重工作的 Ambassador Admin 服务(和相关联的 Pod/容器)。值得注意的是,路由是由“sidecar”代理执行的,在本例中是Envoy 代理。 Envoy 负责 Lyft 内的所有生产网络流量,它的创建者Matt Klein写了很多非常有趣的内容内容关于细节。您可能还听说过新兴的“service mesh”技术,流行的Istio项目也使用了 Envoy。

无论如何,回到教程!您可以在getambassador.io网站上找到为Ambassador Admin预先准备的 Kubernetes 配置文件(对于此演示,您将使用“无 RBAC”版本的服务,但您也可以找到 RBAC -启用版本的配置文件如果您正在运行启用了基于角色的访问控制 (RBAC) 的 Kubernetes 集群。您可以下载配置文件的副本并在应用前查看它,或者直接应用服务通过互联网:

(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

进入全屏模式 退出全屏模式

您会注意到在大使服务上列出了外部 IP,这是 Docker for Mac/Windows](https://www.datawire.io/docker-mac-kubernetes-ingress/)的[已知错误。您仍然可以通过 localhost 访问 LoadBalancer 服务 — 尽管您可能需要等待一两分钟才能在后台成功部署所有内容。

现在让我们尝试使用您之前在大使注释中配置的 `/shopfront/ 路线访问店面。您可以 curl localhost/shopfront/ (无需指定端口,因为您将 Ambassador LoadBalancer 服务配置为侦听端口 80):

{% 要点https://gist.github.com/kelseyevans/1ad64d89409c1deeb5ee985b7f30a1aa%}

而已!您现在正在通过 Ambassador 访问隐藏在 Kubernetes 集群中的店面服务。您还可以通过浏览器访问店面 UI,这提供了更友好的视图!

店面

奖金:大使诊断

如果您想查看大使诊断 UI,则可以使用端口转发。我将在以后的文章中详细解释如何使用它,但目前你可以自己看看。首先,您需要找到一个大使吊舱的名称:

{% 要点https://gist.github.com/kelseyevans/a8fd8d73dcbc97191ec71b55514b7d90%}

在这里,我将选择ambassador-6d9f98bc6c-5sppl。您现在可以从本地网络适配器端口转发到集群内部,并公开在端口 8877 上运行的 Ambassador 诊断 UI。

(master *) kubernetes $ kubectl port-forward ambassador-6d9f98bc6c-5sppl 8877:8877

您现在可以在浏览器中访问http://localhost:8877/ambassador/v0/diag并四处看看!

大使诊断

完成后,您可以通过 ctrl-c 退出端口转发。您还可以通过在 kubernetes 目录中发出kubectl delete -f .来删除已部署到 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 应用程序。

您还可以通过文档阅读有关大使本身的更多详细信息,包括添加auth/security、gRPC 支持和TLS 终止。

这篇文章最初出现在大使博客写的Daniel Bryant.

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐