Kubernetes 是 Google 开发的开源系统,用于在集群中运行和管理基于容器化微服务的应用。 使用 Kubernetes 的人们经常需要让他们在 Kubernetes 中创建的服务可从 Kubernetes 集群外部访问。
尽管Kubernetes提供了用于公开服务的内置解决方案(如下面的使用内置解决方案公开 Kubernetes 服务中所述),但这些解决方案将您限制在第 4 层负载均衡或循环 HTTP 负载均衡上。
这篇文章展示了如何使用NGINX Plus作为高级第 7 层负载平衡解决方案,将 Kubernetes 服务公开到互联网,无论您是在云中还是在自己的基础架构上运行 Kubernetes。
我们假设您对 Kubernetes(pod、服务、复制控制器和标签)以及正在运行的 Kubernetes 集群有基本的了解。 要了解有关 Kubernetes 的更多信息,请参阅官方Kubernetes 用户指南。
Kubernetes 提供了多种公开服务的选项。 其中两个——NodePort 和 LoadBalancer——对应特定类型的服务。 第三个选项 Ingress API 在 Kubernetes 1.1 版中作为测试版推出。
将服务类型指定为NodePort可使该服务在每个 Kubernetes 节点上的相同端口上可用。 要将服务公开到互联网,您需要在该端口上公开一个或多个节点。 为了实现高可用性,您可以公开多个节点并使用基于 DNS 的负载均衡在它们之间分配流量,或者您可以将节点置于您选择的负载均衡器后面。
当传入流量到达端口上的节点时,它会在服务的 pod 之间进行负载均衡。 每个节点上运行的 Kubernetes 网络代理 ( kube-proxy
) 所完成的负载均衡仅限于 TCP/UDP 负载均衡。
将服务类型指定为LoadBalancer会分配一个云负载均衡器,该均衡器在服务的 pod 之间分配传入流量。
LoadBalancer 解决方案仅受某些云提供商和Google Container Engine支持,如果您在自己的基础架构上运行 Kubernetes,则不可用。 此外,即使云负载均衡器具有会话持久性或请求映射等高级功能,Kubernetes 也只允许您配置循环 TCP 负载均衡。
创建Ingress资源使您能够通过自定义 URL 向 Internet 公开服务(例如,URL /foo上的服务 A 和 URL /bar上的服务 B)和多个虚拟主机名(例如,一组服务为foo.example.com ,另一组服务为bar.example.com )。 Ingress 控制器使用 Ingress 资源并设置外部负载均衡器。
Ingress 控制器不是标准 Kubernetes 部署的一部分:您需要选择最适合您需求的控制器或自行实现一个控制器,并将其添加到您的 Kubernetes 集群中。 预计许多控制器实现将很快出现,但目前唯一可用的实现是Google Compute Engine HTTP 负载均衡器的控制器,该实现仅在您在Google Compute Engine或Google Container Engine上运行 Kubernetes 时才有效。 即使实际的负载均衡器支持高级功能,Ingress API 也仅支持循环 HTTP 负载均衡。
在撰写本文时,Ingress API 和 Google Compute Engine HTTP 负载均衡器的控制器均处于测试阶段。
更新- NGINX 和 NGINX Plus 的 NGINX Ingress Controller 现已在我们的GitHub 存储库中提供。 产品详情请参见NGINX Ingress Controller 。
虽然上述解决方案设置简单且开箱即用,但它们不提供任何高级功能,尤其是与第 7 层负载均衡相关的功能。
[编辑 – 本节已更新,参考NGINX Plus API ,它取代并弃用了最初在此处讨论的单独动态配置模块。]
为了将 NGINX Plus 与 Kubernetes 集成,我们需要确保 NGINX Plus 配置与 Kubernetes 保持同步,以反映 Kubernetes 服务的变化,例如添加或删除 pod。 使用 NGINX 开源,您可以手动修改 NGINX 配置文件并重新加载配置。 使用 NGINX Plus,有两种方法可以动态更新配置:
我们假设您已经有一个正在运行的 Kubernetes 集群和一个带有可用于管理集群的kubectl
实用程序的主机;有关说明,请参阅适合您的集群类型的Kubernetes 入门指南。 您还需要构建一个 NGINX Plus Docker 镜像,有关说明请参阅我们博客上的使用 Docker 部署 NGINX 和 NGINX Plus 。
以下是我们将要做的事情的概要:
笔记: 我们使用在 Google Compute Engine 上运行的Kubernetes 1.0.6和本地Vagrant设置测试了本博客中描述的解决方案,这正是我们下面所使用的。
在命令中,与您的 Kubernetes 设置可能不同的值以斜体显示。
我们将 NGINX Plus 放在向互联网公开的节点上的 Kubernetes pod 中。 我们的 pod 是由复制控制器创建的,我们也正在设置它。 我们的 Kubernetes 特定的 NGINX Plus 配置文件位于 NGINX Plus pod 和节点之间共享的文件夹中,这使得维护更简单。
为了指定 NGINX Plus pod 运行的节点,我们为该节点添加一个标签。 我们通过运行以下命令获取所有节点的列表:
$ kubectl 获取节点名称标签状态 10.245.1.3 Kubernetes.io/hostname=10.245.1.3 就绪 10.245.1.4 Kubernetes.io/hostname=10.245.1.4 就绪 10.245.1.5 Kubernetes.io/hostname=10.245.1.5 就绪
我们选择第一个节点并通过运行以下命令为其添加标签:
$ kubectl 标签节点10.245.1.3角色=nginxplus
我们不是直接创建 NGINX Plus pod,而是通过复制控制器创建。 我们在名为nginxplus-rc.yaml的 Kubernetes 声明文件中为 NGINX Plus pod 配置复制控制器。
副本
数设置为 1,这意味着 Kubernetes 确保一个 NGINX Plus pod 始终在运行:如果 pod 出现故障,则会由新的 pod 替换它。nodeSelector
字段中,我们指定在标有role:
nginxplus 的
节点上创建 NGINX Plus pod。api版本:v1
种类: ReplicationController
元数据:
名称:nginxplus-rc
规格:
副本: 1
选择器:
应用程序:nginxplus
模板:
元数据:
标签:
应用程序:nginxplus
规范:
节点选择器:
角色:nginxplus
容器:
- 名称:nginxplus
imagePullPolicy: IfNotPresent
图像:nginxplus
端口:
- 名称:http
容器端口: 80
主机端口: 80
- 名称:http-alt
容器端口: 8080
主机端口: 8080
volumeMounts:
-mountPath:“/etc/nginx/conf.d”
名称:etc-nginx-confd
volumes:
-hostPath:
路径:“/etc/nginx/conf.d”
名称:etc-nginx-confd
正如我们上面所说,我们已经构建了一个 NGINX Plus Docker 镜像。 现在我们使其在节点上可用。 为了简单起见,我们不使用私有 Docker 存储库,而只是手动将映像加载到节点上。
在我们构建Docker 镜像的主机上,运行以下命令将镜像保存到文件中:
$ docker save -o nginxplus.tar nginxplus
我们将nginxplus.tar传输到节点,并在节点上运行以下命令从文件中加载镜像:
$ docker load -i nginxplus.tar
在 NGINX Plus 容器的/etc/nginx文件夹中,我们保留了 NGINX Plus 包附带的默认主nginx.conf配置文件。 默认文件中的include
指令从/etc/nginx/conf.d文件夹读取其他配置文件。 正如 NGINX Plus 复制控制器 ( nginxplus-rc.yaml ) 的声明文件所指定,我们与容器共享 NGINX Plus 节点上的/etc/nginx/conf.d文件夹。 共享意味着我们可以更改存储在文件夹(在节点上)中的配置文件,而不必重建 NGINX Plus Docker 镜像,如果我们直接在容器中创建文件夹,就必须这样做。 我们将 Kubernetes 特定的配置文件 ( backend.conf ) 放在共享文件夹中。
首先,让我们在节点上创建/etc/nginx/conf.d文件夹。
$ sudo mkdir -p /etc/nginx/conf.d
然后我们在那里创建backend.conf文件并包含以下指令:
resolver——
定义 NGINX Plus 使用的 DNS 服务器,定期重新解析我们用来识别上游服务器的域名(在上游
块内的服务器
指令中,将在下一项中讨论)。 我们通过其域名kube-dns.kube-system.svc.cluster.local来识别该 DNS 服务器。 valid
参数告诉 NGINX Plus 每五秒发送一次重新解析请求。
(请注意,该指令的解析过程与上游服务器的解析过程不同:仅在 NGINX 启动或重新加载时解析此域名,并且 NGINX Plus 使用系统 DNS 服务器或/etc/resolv.conf文件中定义的服务器来解析它。)
上游
– 创建一个名为后端的上游组来包含提供我们正在公开的 Kubernetes 服务的服务器。 我们不是单独列出服务器,而是在单个服务器
指令中使用完全限定的主机名来标识它们。 resolve
参数告诉 NGINX Plus 根据resolver
指令指定的设置在运行时重新解析主机名。
由于 Kubernetes DNS 和 NGINX Plus(R10 及更高版本)都支持 DNS 服务( SRV
)记录,因此 NGINX Plus 可以通过 DNS 获取上游服务器的端口号。 我们包含服务
参数以使 NGINX Plus 请求SRV
记录,指定我们服务公开的端口的名称( _http
)和协议( _tcp
)。 我们在下面为服务创建复制控制器中讨论的webapp-svc.yaml文件中声明这些值。
有关使用 DNS 进行服务发现的更多信息,请参阅我们博客上的使用 DNS 通过 NGINX 和 NGINX Plus 进行服务发现。
服务器
(两次)– 定义两个虚拟服务器:
解析器 kube-dns.kube-system.svc.cluster.local valid=5s;
上游后端 {
区域上游后端 64k;
服务器 webapp-svc.default.svc.cluster.local service=_http._tcp resolve;
}
服务器 {
监听 80;
status_zone 后端服务器;
位置 /webapp {
proxy_pass http://backend;
health_check;
}
}
服务器 {
监听 8080;
根 /usr/share/nginx/html;
位置 = /dashboard.html { }
位置 = / {
返回 302 /dashboard.html;
}
位置 /api {
api write=on;
}
}
现在我们准备通过运行以下命令来创建复制控制器:
$ kubectl 创建 -f nginxplus-rc.yaml
为了验证 NGINX Plus pod 是否已创建,我们运行:
$ kubectl get pods NAME READY STATUS RESTARTS AGE nginxplus-rc-0ts5t 1/1 运行 0 17 秒
我们在本地 Vagrant 设置上运行 Kubernetes,因此我们知道我们节点的外部 IP 地址是 10.245.1.3,我们将在本示例的其余部分使用该地址。 如果您在云提供商上运行 Kubernetes,则可以通过运行以下命令获取节点的外部 IP 地址:
$ kubectl 获取节点节点名称-o json | grep -i externalIP -A 1 “类型”: “外部 IP”,“地址”: XXX.XXX.XXX.XXX
如果您在云上运行,请不要忘记设置防火墙规则以允许 NGINX Plus 节点接受传入流量。 请参阅您的云提供商的文档。
我们可以通过查看 NGINX Plus 实时活动监控仪表板来检查我们的 NGINX Plus pod 是否启动并正在运行,该仪表板位于节点外部 IP 地址的 8080 端口上(在我们的例子中为http://10.245.1.3:8080/dashboard.html )。 然而,如果我们看这一点,我们看不到任何用于我们服务的服务器,因为我们尚未创建该服务。
现在是时候创建 Kubernetes 服务了。 我们的服务由两个 Web 服务器组成,每个服务器都提供一个网页,其中包含有关其正在运行的容器的信息。
首先,我们创建一个复制控制器,以便 Kubernetes 确保集群中始终运行指定数量的 Web 服务器副本(pod)。 以下是声明文件( webapp-rc.yaml ):
api版本: v1种类: ReplicationController
元数据:
名称:webapp-rc
规范:
副本: 2
选择器:
应用程序:webapp
模板:
元数据:
标签:
应用程序:webapp
规范:
容器:
- 名称:hello
图像:nginxdemos/hello
端口:
- 容器端口: 80
我们的控制器由两个网络服务器组成。 我们声明一个由具有单个容器的 pod 组成的控制器,公开端口 80。 nginxdemos/hello镜像将从 Docker Hub 中提取。
要创建复制控制器,我们运行以下命令:
$ kubectl 创建 -f webapp-rc.yaml
要检查我们的 pod 是否已创建,我们可以运行以下命令。 我们使用标签选择器app=webapp
来仅获取上一步中复制控制器创建的 pod:
$ kubectl get pods -l app=webapp名称 就绪状态 重新启动时间 webapp-rc-544f1 1/1 运行 0 2m webapp-rc-uk6pm 1/1 运行 0 2m
接下来我们为我们的复制控制器创建的 pod 创建一个服务。 我们使用以下文件( webapp-service.yaml )声明服务:
api版本: v1种类: 服务
元数据:
名称:webapp-svc
规范:
clusterIP: 无
端口:
- 端口: 80
目标端口: 80
协议: TCP
名称:http
选择器:
应用程序:webapp
在这里,我们通过将ClusterIP
字段设置为None
来声明一个特殊的无头服务。 对于这种类型的服务,不会分配集群 IP 地址,并且无法通过 kube 代理使用该服务。 对 Kubernetes DNS 的 DNS 查询返回多个A
记录(我们的 pod 的 IP 地址)。
我们还声明了 NGINX Plus 将用于连接 pod 的端口。 除了指定端口和目标端口号之外,我们还指定名称( http
)和协议( TCP
)。 我们在 NGINX Plus 配置文件中使用这些值,其中我们告诉 NGINX Plus 使用SRV
记录通过 DNS 获取 pod 的端口号。
通过将选择器
字段设置为app:
webapp
,我们声明哪些 pod 属于该服务,即由我们的 NGINX 复制控制器创建的 pod(在webapp-rc.yaml中定义)。
我们运行以下命令来创建服务:
$ kubectl 创建 -f webapp-service.yaml
现在,如果我们刷新仪表板页面并单击右上角的“Upstreams”选项卡,我们就会看到我们添加的两台服务器。
我们还可以检查 NGINX Plus 是否在服务的 pod 之间平衡流量负载。 如果是,当我们在浏览器中访问http://10.245.1.3/webapp/时,页面会显示有关 Web 服务器正在运行的容器的信息,例如主机名和 IP 地址。
如果我们多次刷新此页面并查看状态仪表板,我们会看到请求如何分布在两个上游服务器之间。
[编辑器 – 本节已更新为使用NGINX Plus API ,它取代并弃用了最初使用的单独状态模块。]
现在让我们向我们的服务添加另外两个 pod,并确保 NGINX Plus 配置再次自动更新。 我们运行此命令,通过扩展复制控制器将 pod 的数量更改为 4 个:
$ kubectl scale rc webapp-rc --replicas=4 scaled
要检查 NGINX Plus 是否已重新配置,我们可以再次查看仪表板,但这次我们使用NGINX Plus API 。 我们运行以下命令10.245.1.3
是我们的 NGINX Plus 节点的外部 IP 地址,3
NGINX Plus API的版本。为了整齐地格式化 JSON 输出,我们将其通过管道传输到jq
。
$ curl -s10.245.1.3 :8080/api/3 /http/upstreams/backend/servers | jq { “peers”:[ { “id”: 1、“服务器”: “10.0.0.1:80”, “备份”: false, “权重”: 1、“状态”:“不健康”、“活跃”: 0,“请求”: 1,“响应”:{“1xx”: 0,“2xx”: 0,“3xx”: 0,“4xx”: 0,“5xx”: 0,“总计”: 0 }, “已发送”: 0,“已收到”: 0,“失败”: 0,“不可用”: 0,“健康检查”:{“检查”: 1、“失败”: 1、“不健康”: 1, “last_passed”:false }, “停机时间”: 33965,"低迷期": 1445378182275,"已选择": 1445378131000 }, { "id": 2、“服务器”: “10.246.1.6:80”, ... }, { “id”: 3、“服务器”: “10.246.3.2:80”, ... {“id”: 4、“服务器”: “10.0.0.2:80”, ... } ], “保持活动”: 0 }
JSON 输出中的peers
数组恰好有 4 个元素,每个 Web 服务器一个。
现在让我们将 pod 的数量从 4 个减少到 1 个,然后再次检查 NGINX Plus 状态:
$ kubectl scale rc webapp-rc --replicas=1 scaled $ curl -s10.245.1.3 :8080/api/3 /http/upstreams/backend/servers | jq
现在 JSON 输出中的peers
数组仅包含一个元素(输出与上一个示例命令中 ID 为 1 的对等体相同)。
现在我们已经启动并运行了 NGINX Plus,我们可以开始利用它的高级功能,例如会话持久、 SSL/TLS 终止、请求路由、高级监控等。
NGINX Plus 中提供的动态重新配置选项让您可以轻松地将其与 Kubernetes 集成:通过 API 以编程方式或完全通过 DNS。 使用 NGINX Plus 将 Kubernetes 服务公开到互联网提供了许多当前内置 Kubernetes 负载平衡解决方案所缺乏的功能。
要了解 NGINX Plus 如何与 Kubernetes 协同工作,请立即开始30 天免费试用或联系我们讨论您的用例。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”