Kubernetes 前端和后端之间的通信
Posted
技术标签:
【中文标题】Kubernetes 前端和后端之间的通信【英文标题】:Kubernetes Communication between Frontend and Backend 【发布时间】:2017-12-23 05:47:20 【问题描述】:对于本地开发,我有一个可以工作的 minikube。我们在那里部署了不同的服务。现在我想连接前端和后端。
前端是一个 Angular 应用程序,它存在于自己的服务中。 后端是一个 node.js 应用程序,也使用单独的服务并使用 DNS 连接到其他内部服务,如 mongodb。
现在我想从前端与后端进行通信。 DNS 不工作,因为前端不知道如何解析命名路由。问题是告诉前端它应该使用哪个后端 URL 和端口来发送请求?
当我第一次使用类型 NodePort 启动后端服务并将 url 和端口复制到前端目标 URL 时,接近了唯一的工作状态。我认为这对我来说很不干净。是否有另一种方法可以将后端请求的 url 获取到前端?
我知道当我们在 type="LoadBalancer" 的生产系统上部署服务时,该服务由外部 IP 公开,然后我可以从那里访问该服务。并且外部 IP 在 pod 更新等时将是永久的。我还看到的问题是后端 IP 需要通过额外的提交注入到 docker 容器中。
编辑(1):后端服务
apiVersion: v1
kind: Service
metadata:
name: backend
labels:
app: some-app
tier: backend
spec:
type: NodePort
ports:
- port: 3000
selector:
app: some-app
tier: backend
Edit(2):当我使用 fqn 从客户端请求时,我也会收到此响应:
OPTIONS http://backend.default.svc.cluster.local:3000/signup/ net::ERR_NAME_NOT_RESOLVED
【问题讨论】:
您是否使用服务公开了您的后端?如果是,您应该已经在服务定义中定义了端口,并且您应该获得 DNS 解析,例如 backend:80/<route> @SebastienGoasguen 我已经添加了后端服务定义。所以我为后端定义了端口 3000,它也无法访问。我认为 Angular 应用程序不知道如何解析 dns 名称“后端”。 你能像这样backend.default.svc.cluster.local
尝试使用 fqn。您可能需要用您的名称空间替换默认值。
@sfgroups 遗憾的是,fqn 解决方案也不起作用。也许是一个愚蠢的问题。但是 url 必须以 http 之类的协议为前缀?
您最后的评论正是重点:前端(角度应用程序)在浏览器中运行,而浏览器很可能不在 k8s 集群内。所以使用服务名调用后端是行不通的。
【参考方案1】:
首先我会尝试解决您的具体问题
当我第一次启动后端时,接近了唯一的工作状态 服务类型为 NodePort 并将 url 和端口复制到 前端目标 URL。我认为这对我来说很不干净。有没有 另一种将后端请求的 url 获取到 前端?
您在这里有几个选项 1) 正如您所说,使用 type="LoadBalancer"。或 2) 通过前端服务器代理所有后端调用
我知道当我们在生产系统上部署服务时 type="LoadBalancer" 表示服务由外部 IP 公开,并且 然后我可以从那里访问该服务。而外部IP 将在 pod 更新等处永久存在。我也看到的问题是 后端 IP 需要通过以下方式注入到 docker 容器中 一个额外的提交。
-
通过将配置从代码移到平台(比如 k8s configmap 或像 consul/eureka 这样的外部 KV 注册表),使它成为一个 12 要素应用程序(或者更接近 12 要素应用程序 :))
即使它保留在代码中,如您所说,外部 IP 也是可引用的,除非您这样做,否则它不会改变。我不明白你为什么需要另一个部署
通过前端服务器代理所有后端调用
如果您正在通过前端的服务器端路由(或愿意路由)所有微服务/后端调用,并且如果将前端和后端部署在同一个命名空间中的同一个 k8s 集群中,那么您可以使用 KubeDNS 插件(如果它在您的 k8s 集群中尚不可用,您可以与 k8s 管理员联系)将后端服务名称解析为其 IP。从您的前端服务器,您的后端服务将始终可以通过其名称解析。
由于您的 k8s 集群中有 kubeDNS,并且前端和后端服务都驻留在同一个 k8s 集群和同一个命名空间中,我们可以利用 k8s 内置的服务发现机制。后端服务和前端服务将通过其名称相互发现。这意味着,您可以简单地使用 DNS 名称“backend”从您的前端 pods 访问您的后端服务。因此,只需通过前端 nginx 将所有后端请求代理到上游后端服务。在前端 nginx pods 中,后端服务的 IP 将解析为域名“backend”。这也将为您节省 CORS 头痛。此设置是可移植的,也就是说,无论您是在 dev、stage 还是 prod 中部署,名称“backend”都将始终解析为相应的后端。
这种方法的一个潜在缺陷是,您的后端可能无法独立于前端进行扩展;在我看来,这没什么大不了的;在 k8s 环境中,如果需要,只需启动更多 pod。
只是好奇- 什么在为您的前端提供服务(哪种服务器技术将您的 index.html 传送到用户的浏览器)?是像 nginx 或 apache httpd 这样的静态服务器,还是你在这里使用 nodejs?
【讨论】:
对不起,我没有提到目前它前面有一个处理后端和前端的 nginx 服务。所以我的后端、前端和 nginx 在同一个命名空间中。而且 Kubedns 也被定义了。 不用担心。因此,您很容易实现,通过您的前端 nginx 将所有后端请求代理到您的上游后端服务。在前端 nginx 中,后端服务的 IP 将解析为域名“后端”。这也将为您节省 CORS 头痛。此设置是可移植的,这意味着无论您是在 dev、stage 或 prod 中部署,名称“backend”将始终解析为相应的后端。 感谢您的帮助。结合 Marc Sluiter 的回答,我得到了它的工作。提前致谢。 @so-random-dude 谢谢你的回答,非常简洁明了。只有一件事我不明白,当你说:“这也会让你免于 CORS 头痛”时,你是什么意思?因为据我了解,即使这两个服务在同一个集群中,它们的 clusterIP 仍然有很大不同,一个可能是 10.271.28.9,另一个是 10.110.8.6。所以CORS还是会有问题的。 @orkenstein 我们为代理到后端的路由我们使用与 so-random-dude 的答案不同的方法(这是一个很好的解决方案):我们让后端服务器为前端文件提供服务。我们为两者分离了 docker 图像,但仅使用 1 个 pod。前端作为 init 容器运行并将文件复制到 emptydir 卷。后端还安装该卷并在/
上提供它(所有后端资源都在其他路径上提供)。这样前端和后端就在同一个主机上提供服务。
您可以在 Angular 代码中使用 window.location.protocol + '//' + window.location.host
获取当前主机(现在也是后端主机)。
在本地开发机器上进行开发期间,我们在各自独立的服务器上运行前端和后端。所以我们有一个小助手函数可以在所有情况下获取正确的后端 url:
public getBackendUrl(): string
return this.getBackendUrlInternal(window.location.protocol, window.location.host);
private getBackendUrlInternal(protocol: string, host: string): string
if (host === 'localhost:3000')
// running in local dev server, connect to local dev backend
return 'http://localhost:8585';
else
// running in docker compose or on k8s, backend is on same host and port as we are
return protocol + '//' + host;
(有两种方法,因为我们对第二种方法进行了一些测试)
【讨论】:
【参考方案3】:我建议使用 Kubernetes 自己的灵活流量摄取方式和 Ingress/IngressController。将入口控制器部署在集群中,您可以轻松创建入口定义,告诉控制器在特定 URL 下公开服务。然后,您需要做的就是将该名称在 DNS 中指向 Ingress Controller 负载均衡器(在大多数云设置中,将使用 CNAME 指向 LB fqdn)。
https://kubernetes.io/docs/concepts/services-networking/ingress/ https://github.com/kubernetes/ingress
【讨论】:
【参考方案4】:这就是我如何配置 Ingress 以使用服务 names 将前端应用程序连接到后端。
Bankend(Spring Boot App)注意服务的name属性
服务配置apiVersion: v1 kind: Service metadata: name: student-app-api spec: selector: app: student-app-api ports: - port: 8080 protocol: TCP targetPort: 8080
前端前端不需要知道后端服务器。
假设前端需要调用后端的getAllStudents
API。前端可以像这样调用 API
示例反应代码
const get = id =>
return http.get(`api/students/`);
;
//这会将请求发送到自己的服务器,我们将使用 Ingress 配置将该请求重定向到后端,如下所示
入口
apiVersion: networking.k8s.io/v1beta1 # for versions before 1.14 use extensions/v1beta1
kind: Ingress
metadata:
name: student-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- http:
paths:
- path: /?(.*)
backend:
serviceName: student-app-client-service
servicePort: 80
- path: /api/?(.*) //Redirect all request to backend (backend service name)
backend:
serviceName: student-app-api
servicePort: 8080
【讨论】:
会产生错误。最好为每个服务添加一个入口。这家伙没有负载均衡器。 你遇到了什么错误?我们不需要负载均衡器,服务是 ClusterIP 类型的以上是关于Kubernetes 前端和后端之间的通信的主要内容,如果未能解决你的问题,请参考以下文章