使用NGINX Plus负载均衡Kubernetes服务

jopen 9年前

【编者的话】

此篇文章是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 nodes
NAME 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 pods
NAME 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: v1
kind: 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=nginx
NAME 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: v1
kind: 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=4
scaled

要检查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=1
scaled
$ 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>

来自: http://dockone.io/article/957