使用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 指定服务类型为[负载平衡](http://kubernetes.io/v1.1/docs/user-guide/services.html#type-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,我们可以看到新加的两台服务器。
11.png
我们还可以检查NGINX Plus是否已经在pods的服务间负载均衡流量。如果是,当我们在浏览器中访问http://10.245.1.3/nginx-service/时,我们可以看到默认NGINX的欢迎页面。
22.png
如果我们刷新此页面几次,并查看仪表盘状态,我们可以看到请求是如何分布在两个上游服务器上的。 扩展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 (翻译:朱高校)

2 个评论

Ingress API的控制器已经出现两种新的控制器,nginx-alpha和nginx-third-party。在k8s上架设load balancer都在rc档内用到hostPort,暗示它们是功能性的。k8s之外还是要考虑一层能挡千军万马的load balancer cluster,或花大钱买硬件。
我只看到nginx的内部的配置,那与外部进行负载平衡在哪里,有点不明白。

要回复文章请先登录注册