龙空技术网

适合ingress-nginx接入外部负载均衡的部署

木讷大叔爱运维 2983

前言:

此刻你们对“nginx负载外网”大体比较关注,大家都想要了解一些“nginx负载外网”的相关资讯。那么小编也在网摘上收集了一些关于“nginx负载外网””的相关知识,希望我们能喜欢,大家一起来学习一下吧!

ingress-nginx是K8S中众多Ingress controller的其中一个,使用NGINX作为反向代理和负载均衡器,可将外部请求转发到K8S集群内部,以实现七层代理。

在生产环境中,一般使用外部的Nginx作为公网的统一访问入口,通过与ingress-nginx对接可以将请求转发到K8S集群。在不使用公有云的情况下,我们可以选择以下两种方式ingress-nginx部署方式。

Over a NodePort Service

一、部署

默认使用Deployment+nodePort部署

1. 先决条件通用部署命令

# yaml文件 wget  部署kubectl apply -f mandatory.yaml# 查看pod# kubectl get pod -n ingress-nginxNAME                                        READY   STATUS    RESTARTS   AGEnginx-ingress-controller-5bb8fb4bb6-kdvbg   1/1     Running   0          2m10s     # 此时还没有svc# kubectl get svc -n ingress-nginxNo resources found in ingress-nginx namespace.

2.创建servcie,接收集群外部流量

wget  apply -f service-nodeport.yaml# 查看svckubectl get svc  -n ingress-nginxNAME            TYPE       CLUSTER-IP   EXTERNAL-IP   PORT(S)                      AGEingress-nginx   NodePort   10.1.9.57    <none>        80:31596/TCP,443:30524/TCP   109s

3. 新建ingress规则

与其关联的service、deployment暂且忽略。

# vim ingress.yaml# 注意使用的是自定义namespace:testapiVersion: extensions/v1beta1kind: Ingressmetadata:  name: helloworld  namespace: testspec:  rules:    - host: hello.test.cn       http:        paths:          - path: /            backend:              serviceName: helloworld              servicePort: 8080# 应用kubectl apply -f ingress.yaml# kubectl get ing -n testNAME         CLASS    HOSTS           ADDRESS     PORTS   AGEhelloworld   <none>   hello.test.cn   10.1.9.57   80      13s

4.测试

# 通过svc访问# curl -x 10.1.9.57:80 hello.test.cnHello,world!# 通过nodePort访问# curl -x 192.168.3.218:31596 hello.test.cnHello,world!

二、源地址转换

默认情况下,NodePort类型的服务执行源地址转换。 这意味着HTTP请求的源IP始终是从nginx接收到该请求的Kubernetes节点的IP地址。

通过查看service日志,我们发现访问来源地址的确都是集群内部IP,而不是实际的客户端IP。

# kubectl logs -f svc/ingress-nginx -n ingress-nginx10.244.0.0 - - [23/Jul/2020:07:48:33 +0000] "GET  HTTP/1.1" 200 12 "-" "curl/7.29.0" 127 0.072 [test-helloworld-8080] [] 10.244.2.96:8080 12 0.072 200 1d2b39f8b24caace07ef56d7c22e13e710.244.1.0 - - [23/Jul/2020:07:48:59 +0000] "GET  HTTP/1.1" 200 12 "-" "curl/7.29.0" 127 0.003 [test-helloworld-8080] [] 10.244.2.96:8080 12 0.004 200 9f912e99f7de3443deb62ba57ef0ff2510.244.1.0 - - [24/Jul/2020:00:09:30 +0000] "GET  HTTP/1.1" 200 12 "-" "curl/7.58.0" 127 0.025 [test-helloworld-8080] [] 10.244.2.96:8080 12 0.025 200 da0a3886f5257769035003a96967e61e

此时我们可以在service上设置externalTrafficPolicy: Local解决。

vim service-nodeport.yaml# 在spec中添加如下一行externalTrafficPolicy: Local# 应用修改kubectl replace -f service-nodeport.yaml --force# 查看日志kubectl logs -f svc/ingress-nginx -n ingress-nginx# 此时真正的源IP已能够正常记录192.168.3.133 - - [24/Jul/2020:00:29:18 +0000] "GET  HTTP/1.1" 200 12 "-" "curl/7.29.0" 127 0.002 [test-helloworld-8080] [] 10.244.2.96:8080 12 0.001 200 71518b244339f7ff945e1b5e3fd8279310.11.2.102 - - [24/Jul/2020:00:29:54 +0000] "GET  HTTP/1.1" 200 12 "-" "curl/7.58.0" 127 0.002 [test-helloworld-8080] [] 10.244.2.96:8080 12 0.002 200 4dffef85c5765a5c8776bbd7965c9ea8

后遗症:

在未设置externalTrafficPolicy: Local前,nodePort允许我们访问任何node节点都可访问到service,但是在设置externalTrafficPolicy: Local后,我们只能通过访问实际运行ingress-nginx pod所在节点的node IP 。

# 未设置前curl -x 192.168.3.217:31596 hello.test.cnHello,world!curl -x 192.168.3.218:31596 hello.test.cnHello,world!curl -x 192.168.3.219:31596 hello.test.cnHello,world!# 设置后,由于replace重启了service,因此端口会更换#  curl -x 192.168.3.217:30643 hello.test.cncurl: (7) Failed connect to 192.168.3.217:30643; Connection refused#  curl -x 192.168.3.218:30643 hello.test.cncurl: (7) Failed connect to 192.168.3.218:30643; Connection refused#  curl -x 192.168.3.219:30643 hello.test.cnHello,world!# kubectl get pod -n ingress-nginx -o wideNAME                                        READY   STATUS    RESTARTS   AGE   IP            NODE           NOMINATED NODE   READINESS GATESnginx-ingress-controller-5bb8fb4bb6-kdvbg   1/1     Running   0          17h   10.244.2.97   node-3-219   <none>           <none>

ingress-nginx pod运行在192.168.3.219上,只能通过192.168.3.219:30643访问服务。

问题思考:

如果通过nodePort 方式接入外部负载均衡,由于设置`externalTrafficPolicy: Local`导致访问的node节点无法固定,再加上一旦service重启端口变化,势必会影响外部负载均衡无法正确转发流量。

下面我们通过解决pod单点故障问题,使pod在所有node节点运行,那么IP就可以固定了。

三、单点故障

基于Deployment+nodePort部署我们发现ingress-nginx 只有一个pod,由于`mandatory.yaml`中定义副本数为1,因此存在单点故障。

虽然我们可以将副本数改为2或多个解决单点问题,但是由于pod的端口固定为80//443,当pod调度分配到同一个节点时,会出现端口分配问题,这也正是Deployment类型的副本数默认为1的原因。

$ kubectl -n ingress-nginx describe pod <unschedulable-ingress-nginx-controller-pod>...Events:  Type     Reason            From               Message  ----     ------            ----               -------  Warning  FailedScheduling  default-scheduler  0/3 nodes are available: 3 node(s) didn't have free ports for the requested pod ports.

这种情况我们可以通过将pod设置为DaemonSet解决,利用DaemonSet特性,保证每个节点运行pod。

注意:DaemonSet会在node、master点都运行,但因为master节点默认有污点NoSchedule,表示k8s将不会将pod调度到具有该污点的master节点上。

# vim mandatory.yaml # 1.将Deployment更改为DaemonSetapiVersion: apps/v1kind: DaemonSet# 2.删除replicas: 1# 3.应用kubectl apply -f  mandatory.yaml # 注意:通过replace重载配置,并不会在节点上新建daemon类型pod;但是通过重新部署(delete后重新apply)才可以在node节点上创建DaemonSet类型的pod的。# 4.查看pod,应分布在集群的两个node节点上了。# kubectl get pod -n ingress-nginx -o wideNAME                             READY   STATUS    RESTARTS   AGE   IP            NODE           NOMINATED NODE   READINESS GATESnginx-ingress-controller-gnx8l   1/1     Running   0          66m   10.244.2.98   uvmsvr-3-219   <none>           <none>nginx-ingress-controller-sdn86   1/1     Running   0          66m   10.244.1.59   uvmsvr-3-218   <none>           <none># 5.访问service#  curl -x 192.168.3.218:31516 hello.test.cnHello,world!#  curl -x 192.168.3.219:31516 hello.test.cnHello,world!

设置externalTrafficPolicy: Local并使用DaemonSet后,我们就可以在所有的node节点访问service了,但是由于nodeport暴露的端口是随机端口,即使在前面再搭建一套负载均衡器来转发请求,也需维护因service重启导致的端口变化问题。

NodePort方式暴露ingress虽然简单方便,但是NodePort多了一层NAT,在请求量级很大时可能对性能会有一定影响。下面我们来介绍Via the host network。

Via the host network

hostNetwork方式允许使用主机网络,即可以将pod的端口80和443直接绑定到Kubernetes宿主机网络上,直接通过宿主机IP+端口80/443访问服务,而无需NodePort Services施加额外的网络转换。

注意:由于此时外部请求直接访问pod所在节点的IP+port,并没有通过service访问,因此service可忽略,官方建议删除。

结合前面使用DaemonSet+hostNetwork部署。

# vim mandatory.yaml# 1.更改DaemonSetapiVersion: apps/v1kind: DaemonSet# 2.更改hostNetworktemplate:  spec:    hostNetwork: true# 应用,可不必应用service-nodeport.yamlkubectl apply -f deploy.yaml# 查看pod# kubectl get pod -n ingress-nginx -o wideNAME                             READY   STATUS    RESTARTS   AGE   IP              NODE           NOMINATED NODE   READINESS GATESnginx-ingress-controller-7frnd   1/1     Running   0          10s   192.168.3.218   uvmsvr-3-218   <none>           <none>nginx-ingress-controller-gqvrz   1/1     Running   0          10s   192.168.3.219   uvmsvr-3-219   <none>           <none>```此时两个pod的IP不再是10.244.0.0段集群内部IP,而是node节点IP。我们可直接使用pod的80或443端口直接访问。```bash# 直接访问pod# curl -x 192.168.3.219:80  hello.test.cnHello,world! # curl -x 192.168.3.218:80  hello.test.cnHello,world!

通过HostNetwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/433端口就能访问服务。由于IP和端口固定,我们可以将pod直接暴露给外部的负载均衡使用。

这种方式没有经过nodePort的NAT,而是直接利用宿主机节点的网络和端口,性能会更高些。比较适合大并发的生产环境使用。

使用DaemonSet,当集群新加入新的node节点时会自动创建新的pod,由于hostNetwork下没有使用service,也就无法使用便签选择器selector自动添加新增的pod。如果接入外部负载均衡时,需手动添加新节点,这是此种部署的一个缺点。

总结

1. ingress-nginx通过调用k8s api 动态感知集群中pod的变化,动态更新配置文件nginx并重载nginx。配合外部负载均衡,可以很好的实现nginx配置的自动管理。

2. ingress-nginx接入外部负载均衡的要点是IP和端口尽量固定,否则外部负载均衡就无法定位到ingress-nginx。

通过以上两种部署方式,学习到了ingress-nginx的一些细节,帮助我们更好的了解并使用K8S。

参考:

标签: #nginx负载外网