使用NGINX Plus负载均衡Kubernetes服务
【编者的话】
此篇文章是Nginx的Michael Pleshakov发表在Nginx官方博客的一篇博文,通过这篇文章概括回顾了Kubernetes暴露服务相关的解决方案,并对最新的Ingreess API进行了说明,最后给出了Kubernetes通过集成NGINX Plus来暴露服务到互联网的解决方案。这个方案解决了目前Kubernetes暴露服务的短板,整个实现过程也比较简单,步骤清晰,具有很强的参考性。我们华三目前也在调研这方面的工作,希望此文能对大家有所帮助。
Kubernetes是由谷歌开发的一个开源系统,用于在集群内运行和管理以容器微服务为基础的应用。使用Kubernetes的人经常需要确保可以从Kubernetes集群外部访问在Kubernetes内创建的服务。
虽然 Kubernetes 提供了内置暴露服务解决方案,正像在下面 Kubernetes内置暴露服务解决方案 中所描述的,这些解决方案会局限你在4层负载均衡或循环HTTP负载平衡。
这篇文章会告诉你如何使用 NGINX Plus 作为高级7层负载均衡解决方案,用于暴露Kubernetes服务到互联网上,无论你是在云服务还是在自有基础设施上运行Kubernetes。
我们假定你对Kubernetes有所了解(pods, services, replication controllers, and labels)并且有一个运行的Kubernetes集群。要了解更多Kubernetes,请访问官方 Kubernetes用户指南 。
Kubernetes内置暴露服务解决方案
Kubernetes为暴露 服务 提供了多种选择。其中两种是NodePort和负载平衡器,分别对应不同类型的服务。Ingress API在Kubernetes1.1版本开始作为beta测试版供使用,已经成为第三种选择。
NodePort
指定服务类型为 NodePort ,会使得服务在每个Kubernetes节点上在相同的端口可用。为了暴露服务到互联网,你在这个端口上暴露一个或多个节点。为了高可用性,你可以暴露多个节点并使用基于DNS的负载均衡在它们中分布流量,或者你把这些节点放在你选择的负载均衡后。
当传入流量访问端口的一个节点时,它会被在服务的pods之间负载平衡。负载平衡由每个节点上的 Kubernetes代理 完成,并且仅限于TCP/UDP负载均衡。
LoadBalancer
指定服务类型为 负载平衡 会分配用于在服务pods之间分布传入流量的云负载平衡器。
只有特定的云服务供应商和 Google Container Engineand 支持负载均衡器解决方案,如果你在你自己的基础设施上运行Kubernetes,它是不可用的。此外,Kubernetes只允许你配置循环TCP负载均衡,即使云负载均衡器有高级功能,例如会话持久或请求映射。
Ingress API
创建 Ingress 资源使得你可以通过自定义URL(例如,服务A在URL /foo和服务B在URL /bar)和多个虚拟主机名(例如,一组服务是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 Load Balancer控制器都还在测试阶段。
尽管上面提到的解决方案配置简单并且马上就可以使用,他们没有提供任何高级功能,特别是7层负载均衡相关的功能。
使用NGINX Plus暴露Kubernetes服务
为了集成NGINX Plus到Kubernetes上,我们需要确保NGINX Plus配置与Kubernetes保持同步,可以反映像pods添加或删除这样的Kubernetes服务变更。 使用开源的NGINX软件,你需要手动修改NGINX配置文件并重新加载配置。使用NGINX Plus,有两种方法 动态 更新配置:
- With APIs – 此方法使用NGINX Plus的 on-the-fly reconfiguration API 添加和删除Kubernetes pods在NGINX Plus配置中的条目,并使用Kubernetes API来获取pods的IP地址。这种方法需要我们写一些代码,这里我们也不进行深入讨论。有关详细信息,可以看Kelsey Hightower的网络研讨会, Bringing Kubernetes to the Edge with NGINX Plus ,他在其中探讨了API并新建了利用他们的应用。
- 通过 重新解析DNS名称 -这个方法像在以下章节描述的一样仅仅需要对NGINX Plus的一次恰当配置。
Utilizing DNS-Based Reconfiguration
我们假设你已经有一个正在运行的Kubernetes集群并有一个可以使用kubectl工具的主机用于管理集群;有关说明,请参阅有关你群集类型的 Kubernetes入门指南 。你还需要编译一个NGINX Plus容器镜像,创建的命令就在这篇 博客文章 。
下面是我们怎么做的一个概括:
1. 配置一个NGINX Plus pod用于暴露和负载均衡我们在步骤2中创建的服务。
2. 创建一个提供静态网页的简单服务,。
3. 扩展或者缩减服务,查看NGINX Plus如何自动重新配置。
注:我们使用运行在Google Compute Engine的 Kubernetes 1.0.6 版本测试了这个博客所描述的解决方案,而我们下面使用的是一个本地配置的 Vagrant 。
在命令的斜体字中,你Kubernetes设置中的值可能不同。
配置 NGINX Plus Pod
我们把NGINX Plus放在要暴露到互联网节点的Kubernetes pod内。我们的pod通过复制控制器创建,我们同样进行配置。我们Kubernetes相关的NGINX Plus配置文件放在NGINX Plus pod和节点的共享目录内,这样更加方便维护。
选择运行NGINX Plus Pod的节点
为了指定NGINX Plus pod运行的节点,我们添加一个标签到该节点。运行下面命令,查询运行的所有节点列表:
$ kubectl get nodesNAME LABELS STATUS
10.245.1.3 Kubernetes.io/hostname=10.245.1.3 Ready
10.245.1.4 Kubernetes.io/hostname=10.245.1.4 Ready
10.245.1.5 Kubernetes.io/hostname=10.245.1.5 Ready
我们选择第一个节点,并通过下面命令添加一个标签给它:
$ kubectl label node 10.245.1.3 role=nginxplus为NGINX Plus Pod配置Replication Controller
我们不会直接创建NGINX Plus pod而是通过复制控制器。我们在Kubernetes的nginxplus-rc.yaml文件配置NGINX Plus pod 复制控制器。
- 我们设置replicas的数量是1,就是说Kubernetes会确保始终有1个NGINX Plus pod在运行:如果pod出现故障,它会被新的pod替换。
- 在nodeSelector处我们指定NGINX Plus pod在标记角色: nginxplus的节点上创建。
- 我们的NGINX Plus容器暴露两个端口,80和8080,并且我们配置他们与节点端口80和8080之间的映射。
- 我们的NGINX Plus容器共享节点的/etc/nginx/conf.d文件夹。作为下面 配置NGINX Plus 的进一步解释,共享文件夹让我们可以不用重建容器镜像而重新配置NGINX Plus。
apiVersion: v1 kind: ReplicationController metadata: name: nginxplus-rc spec: replicas: 1 selector: app: nginxplus template: metadata: labels: app: nginxplus spec: nodeSelector: role: nginxplus containers: - name: nginxplus image: nginxplus ports: - name: http containerPort: 80 hostPort: 80 - name: http-alt containerPort: 8080 hostPort: 8080 volumeMounts: - mountPath: "/etc/nginx/conf.d" name: etc-nginx-confd volumes: - hostPath: path: "/etc/nginx/conf.d" name: etc-nginx-confd
在节点上使NGINX Plus Docker Image可用
正如我们上面所说,我们已经建立了一个NGINX Plus Docker镜像。 现在我们让它在节点上可用。为简单起见,我们不使用私有Docker repository,我们只是在节点上手动加载镜像。
在我们编译Docker镜像的主机上,运行以下命令将镜像保存成文件:
$ docker save -o nginxplus.tar nginxplus我们把nginxplus.tar传送到节点,在节点上运行以下命令从文件加载镜像:
$ docker load -i nginxplus.tar配置NGINX Plus
在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容器镜像,而如果我们直接在容器中创建的文件夹这是我们必须做的。我们把我们的Kubernetes特定配置文件(backend.conf)放在共享目录。
首先,让我们在节点上创建/etc/nginx/conf.d文件夹。
$ mkdir /etc/nginx/conf.d然后,我们创建backend.conf文件,其中包括以下指令:
- resolver -定义Kubernetes DNS解析的IP地址,使用默认的IP地址,10.0.0.10。valid参数告诉NGINX Plus每五秒钟解析所有DNS名称。 您的Kubernetes DNS服务IP地址可能会有所不同。运行此命令查看:
$ kubectl get svc kube-dns --namespace=kube-system - upstream -创建一个名为后端的Kubernetes服务,我们揭露上游组。我们确定了服务器在上游组主机名,包括了resolve指令告诉NGINX重新解析主机名在运行时。
- server (两次) -定义两个虚拟服务器:
- 第一个服务器监听端口80并且负载均衡在我们服务pods中的/nginx-service传入请求。我们还配置积极的健康检查 。
- 第二个服务器监听端口8080。这里的我们成立了现场活动的监控NGINX Plus。稍后我们将用它来检查 NGINX Plus 是否被正确地重新配置。
resolver 10.0.0.10 valid=5s; upstream backend { zone upstream-backend 64k; server nginx-service.default.svc.cluster.local resolve; } server { listen 80; status_zone backend-servers; location /nginx-service/ { proxy_pass http://backend/; health_check; } } server { listen 8080; root /usr/share/nginx/html; location = /status.html { } location /status { status; }
}
创建Replication Controller
现在,我们已经准备好在我们的节点上运行此命令创建复制器:
$ kubectl create -f nginxplus-rc.yaml为了验证创建NGINX Plus pod,运行:
$ kubectl get podsNAME READY STATUS RESTARTS AGE
nginxplus-rc-0ts5t 1/1 Running 0 17s
我们在本地Vagrant 设定运行Kubernetes,所以我们知道,我们的节点的外部IP地址为10.245.1.3,在这个例子中的剩下部分我们将使用该地址。如果你在一个云服务提供商运行Kubernetes,您可以通过运行下面命令得到您节点的外部IP地址:
$ kubectl get nodes node-name -o json | grep -i externalIP -A 1"type": "ExternalIP",
"address": XXX.XXX.XXX.XXX
如果您在云服务中运行,别忘了设置防火墙规则允许NGINX Plus节点接收传入的流量。请参阅您的云服务提供商文档。
通过查看NGINX Plus实时活动监控仪表板可以检查我们的NGINXPlus pod是否 起来并运行了,它节点的外部IP地址在端口8080上是否可用(在我们的例子中是 http://10.245.1.3:8080/status.html )。 如果我们这时候访问,不过我们是看不到我们服务的任何服务器,因为我们还没有创建它。
创建一个简单Kubernetes Service
现在是时候创建Kubernetes服务了。我们的服务包括两个(开源)NGINX 服务器提供静态web页面。
为服务创建Replication Controller
首先,我们创建一个Replication Controller,这样Kubernetes会确保指定数量的NGINX Web服务器副本(pods)始终在群集中运行。以下是声明文件(nginx-rc.yaml):
apiVersion: v1kind: ReplicationController
metadata:
name: nginx-rc
spec:
replicas: 2
selector:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
我们的控制器由两个NGINX Web服务器组成。我们声明一个控制器包含单个NGINX容器pods用于暴露端口80。Nginx的镜像将从Docker Hub下载。
要创建复制控制器,我们在节点上运行以下命令:
$ kubectl create -f nginx-rc.yaml要检查是否已创建pods,我们可以运行下面的命令。我们使用标签选择app=nginx获得仅在上一步的replication controller中创建的pods:
$ kubectl get pods -l app=nginxNAME READY STATUS RESTARTS AGE
nginx-rc-544f1 1/1 Running 0 2m
nginx-rc-uk6pm 1/1 Running 0 2m
创建Service
接下来,我们为replication controller创建的pods创建服务。我们使用下列文件(nginx-service.yaml)创建服务:
apiVersion: v1kind: Service
metadata:
name: nginx-service
spec:
ClusterIP: None
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: nginx
这里我们通过设置ClusterIP字段为None来声明一个特殊的 headless service 。通过这类服务,不会分配集群的IP地址并且通过kube代理也无法使用服务。DNS查询Kubernetes DNS会返回多个A记录(我们pods的IP地址)。
通过设置selector field为app: nginx,我们声明该pods属于服务,即使用NGINX replication controller 创建的pods(在nginx-rc.yaml中定义)。
在节点上我们运行下面的命令创建服务:
$ kubectl create -f nginx-service.yaml现在,如果我们刷新仪表盘页面,并单击右上角的Upstreams tab,我们可以看到新加的两台服务器。
我们还可以检查NGINX Plus是否已经在pods的服务间负载均衡流量。如果是,当我们在浏览器中访问 http://10.245.1.3/nginx-service/ 时,我们可以看到默认NGINX的欢迎页面。
如果我们刷新此页面几次,并查看仪表盘状态,我们可以看到请求是如何分布在两个上游服务器上的。
扩展Kubernetes服务
现在,让我们再添加两个pods到我们的服务并确保NGINX Plus配置会再次自动更新。我们运行此命令扩展replication controller 把pods数量更改为4:
$ kubectl scale rc nginx-rc --replicas=4scaled
要检查NGINX Plus是否重新配置,我们可以再查看仪表盘,但这次我们使用NGINX Plus状态的API来替代。在我们的节点运行下面的命令,10.245.1.3是我们的NGINX Plus节点外部IP地址。为了格式化JSON输出,我们管道输出到 jq 。
$ curl -s 10.245.1.3:8080/status/upstreams/backend | jq .{
"peers": [
{
"id": 1,
"server": "10.0.0.1:80",
"backup": false,
"weight": 1,
"state": "unhealthy",
"active": 0,
"requests": 1,
"responses": {
"1xx": 0,
"2xx": 0,
"3xx": 0,
"4xx": 0,
"5xx": 0,
"total": 0
},
"sent": 0,
"received": 0,
"fails": 0,
"unavail": 0,
"health_checks": {
"checks": 1,
"fails": 1,
"unhealthy": 1,
"last_passed": false
},
"downtime": 33965,
"downstart": 1445378182275,
"selected": 1445378131000
},
{
"id": 2,
"server": "10.246.1.6:80",
...
},
{
"id": 3,
"server": "10.246.3.2:80",
...
{
"id": 4,
"server": "10.0.0.2:80",
...
}
],
"keepalive": 0
}
在peers中的JSON输出数组正好有四个元素,对应每个NGINX Web服务器。
现在,让我们减少pods的数量从4到1,再次检查NGINX Plus状态:
$ kubectl scale rc nginx-rc --replicas=1scaled
$ curl -s 10.245.1.3:8080/status/upstreams/backend | jq .
现在peers中的JSON输出数组只包含一个元素。
现在,我们已经把NGINX Plus运行起来,我们就可以开始利用其高级功能,如 会话持久性 , SSL终止 , 请求路由 , 高级监控 和 更多 。
总结
NGINX Plus的on-the-fly reconfiguration让你的Kubernetes集成更轻松:不管是通过API编程还是完全使用DNS的方式。使用NGINX Plus暴露Kubernetes服务到互联网提供了许多当前Kubernetes内置负载均衡解决方案缺乏的功能。
原文链接: Load Balancing Kubernetes Services with NGINX Plus (翻译:朱高校)
</div>