Kubernetes nginx 入口控制器不可靠
Kubernetes nginx ingress controller is unreliable
我需要帮助来详细了解入口控制器,特别是 ingress-nginx 入口控制器应该如何工作。对我来说,它看起来像一个黑盒子,应该监听 public IP,终止 TLS,并将流量转发到 pod。但究竟是如何发生的对我来说是个谜。
这里的主要目标是理解,次要目标是解决我面临的紧迫问题。
我有一个包含五个节点的集群,我正在尝试将 Jupyterhub 应用程序安装到 运行 上。在大多数情况下,它工作正常。我正在使用带有 flannel/calico 的非常标准的 Rancher RKE 设置进行网络连接。节点 运行 带有 iptables 和 firewalld 的 RedHat 7.9,以及 docker 19.03.
Jupyterhub 代理设置了 ClusterIP 服务(我也尝试了 NodePort 服务,它也有效)。我还设置了一个入口。入口有时有效,但通常不响应(连接超时)。具体来说,如果我删除入口,然后重新部署我的 helm chart,入口就会开始工作。此外,如果我重新启动我的一个节点,入口将再次开始工作。入口停止工作的情况我还没有确定。
以下是我的相关服务:
kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hub ClusterIP 10.32.0.183 <none> 8081/TCP 378d
proxy-api ClusterIP 10.32.0.11 <none> 8001/TCP 378d
proxy-public ClusterIP 10.32.0.30 <none> 80/TCP 378d
这行得通; telnet 10.32.0.30 80 按预期响应(当然仅来自其中一个节点)。我也可以直接远程登录到代理-public pod(在我的例子中是 10.244.4.41:8000)。
这是我的入口。
kubectl describe ingress
Name: jupyterhub
Labels: app=jupyterhub
app.kubernetes.io/managed-by=Helm
chart=jupyterhub-1.2.0
component=ingress
heritage=Helm
release=jhub
Namespace: jhub
Address: k8s-node4.<redacted>,k8s-node5.<redacted>
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
tls-jhub terminates jupyterhub.<redacted>
Rules:
Host Path Backends
---- ---- --------
jupyterhub.<redacted>
/ proxy-public:http (10.244.4.41:8000)
Annotations: field.cattle.io/publicEndpoints:
[{"addresses":["",""],"port":443,"protocol":"HTTPS","serviceName":"jhub:proxy-public","ingressName":"jhub:jupyterhub","hostname":"jupyterh...
meta.helm.sh/release-name: jhub
meta.helm.sh/release-namespace: jhub
Events: <none>
目前我对这种情况下的入口的理解:
流量到达 k8s-node4 或 k8s-node5 的 443 端口。
一些魔法(由入口控制器控制)接收该流量,终止 TLS,并将未加密的流量发送到端口 8000 的 pod 的 IP。这是我想更好地理解的部分。
那个黑盒子似乎至少部分涉及 flanel/calico 和一些 iptables 魔法,而且它在某些时候显然也涉及 nginx。
更新:与此同时,我确定了导致 Kubernetes 崩溃的原因:重新启动 firewalld。
据我所知,这会清除所有 iptables 规则,而不仅仅是 firewalld 生成的规则。
我在这里找到了问题的答案:https://www.stackrox.io/blog/kubernetes-networking-demystified/可能需要注意的是,这可能会在一定程度上有所不同,具体取决于您使用的网络 CNI,尽管我看到的一切都与 Kubernetes 本身密切相关.
我还在努力消化这个博客的内容,我强烈建议直接参考那个博客,而不是依赖我的回答,这可能是对故事的拙劣复述。
这里是到达端口 443 的包的大致流程。
您将需要使用命令来查看表格。
iptables -t nat -vnL | less
这个输出看起来相当吓人。
下面切出很多其他链条,呼吁切入正题。在这个例子中:
- 此集群使用 Calico/channel/Flannel 的 CNI 插件。
- 监听端口为443
- nginx-ingress-controller 的 pod 在 10.244.0.183 监听(以及其他)。
在那种情况下,数据包的流动方式如下:
- 数据包进入 PREROUTING 链。
- PREROUTING 链调用(除其他外)CNI-HOSTPORT-DNAT 链。
- POSTROUTING链也调用同一条链。
- CNI-HOSTPORT-DNAT 链依次调用多个 CNI-DN-xxxx 链。
- CNI-DN-xxx 链执行 DNAT 并将目标地址更改为 10.244.0.183。
- nginx-ingress-controller 中的容器侦听 10.244.0.183。
如果 pod 位于与到达的数据包不同的节点上,并且如果同一端口有多个 pods load-balanced,则会涉及一些额外的复杂性。负载平衡似乎是通过 iptables 统计模块随机选择一个或另一个 iptables 规则来处理的。
从服务到 pod 的内部流量遵循类似的流程,但不相同。
在这个例子中:
- 服务在10.32.0.183,端口8001
- pod 位于 10.244.6.112,端口 8001。
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
...
KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0
Chain KUBE-SERVICES (2 references)
...
/* Traffic from within the cluster to 10.32.0.183:8001 */
0 0 KUBE-SVC-ZHCKOT5PFJF4PASJ tcp -- * * 0.0.0.0/0 10.32.0.183 tcp dpt:8001
...
/* Mark the package */
Chain KUBE-SVC-ZHCKOT5PFJF4PASJ (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ tcp -- * * !10.244.0.0/16 10.32.0.183 tcp dpt:8081
0 0 KUBE-SEP-RYU73S2VFHOHW4XO all -- * * 0.0.0.0/0 0.0.0.0/0
/* Perform DNAT, redirecting from 10.32.0.183 to 10.244.6.12 */
Chain KUBE-SEP-RYU73S2VFHOHW4XO (1 references) 0 0 KUBE-MARK-MASQ all -- * * 10.244.6.112 0.0.0.0/0
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp to:10.244.6.112:8081
关于如何使节点可靠工作的问题的第二部分:
- 禁用防火墙。
- 改为使用 Kubernetes 网络策略(如果您使用的是 Calico,则使用 Calico 网络策略)。
我需要帮助来详细了解入口控制器,特别是 ingress-nginx 入口控制器应该如何工作。对我来说,它看起来像一个黑盒子,应该监听 public IP,终止 TLS,并将流量转发到 pod。但究竟是如何发生的对我来说是个谜。
这里的主要目标是理解,次要目标是解决我面临的紧迫问题。
我有一个包含五个节点的集群,我正在尝试将 Jupyterhub 应用程序安装到 运行 上。在大多数情况下,它工作正常。我正在使用带有 flannel/calico 的非常标准的 Rancher RKE 设置进行网络连接。节点 运行 带有 iptables 和 firewalld 的 RedHat 7.9,以及 docker 19.03.
Jupyterhub 代理设置了 ClusterIP 服务(我也尝试了 NodePort 服务,它也有效)。我还设置了一个入口。入口有时有效,但通常不响应(连接超时)。具体来说,如果我删除入口,然后重新部署我的 helm chart,入口就会开始工作。此外,如果我重新启动我的一个节点,入口将再次开始工作。入口停止工作的情况我还没有确定。
以下是我的相关服务:
kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hub ClusterIP 10.32.0.183 <none> 8081/TCP 378d
proxy-api ClusterIP 10.32.0.11 <none> 8001/TCP 378d
proxy-public ClusterIP 10.32.0.30 <none> 80/TCP 378d
这行得通; telnet 10.32.0.30 80 按预期响应(当然仅来自其中一个节点)。我也可以直接远程登录到代理-public pod(在我的例子中是 10.244.4.41:8000)。
这是我的入口。
kubectl describe ingress
Name: jupyterhub
Labels: app=jupyterhub
app.kubernetes.io/managed-by=Helm
chart=jupyterhub-1.2.0
component=ingress
heritage=Helm
release=jhub
Namespace: jhub
Address: k8s-node4.<redacted>,k8s-node5.<redacted>
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
tls-jhub terminates jupyterhub.<redacted>
Rules:
Host Path Backends
---- ---- --------
jupyterhub.<redacted>
/ proxy-public:http (10.244.4.41:8000)
Annotations: field.cattle.io/publicEndpoints:
[{"addresses":["",""],"port":443,"protocol":"HTTPS","serviceName":"jhub:proxy-public","ingressName":"jhub:jupyterhub","hostname":"jupyterh...
meta.helm.sh/release-name: jhub
meta.helm.sh/release-namespace: jhub
Events: <none>
目前我对这种情况下的入口的理解:
流量到达 k8s-node4 或 k8s-node5 的 443 端口。 一些魔法(由入口控制器控制)接收该流量,终止 TLS,并将未加密的流量发送到端口 8000 的 pod 的 IP。这是我想更好地理解的部分。
那个黑盒子似乎至少部分涉及 flanel/calico 和一些 iptables 魔法,而且它在某些时候显然也涉及 nginx。
更新:与此同时,我确定了导致 Kubernetes 崩溃的原因:重新启动 firewalld。
据我所知,这会清除所有 iptables 规则,而不仅仅是 firewalld 生成的规则。
我在这里找到了问题的答案:https://www.stackrox.io/blog/kubernetes-networking-demystified/可能需要注意的是,这可能会在一定程度上有所不同,具体取决于您使用的网络 CNI,尽管我看到的一切都与 Kubernetes 本身密切相关.
我还在努力消化这个博客的内容,我强烈建议直接参考那个博客,而不是依赖我的回答,这可能是对故事的拙劣复述。
这里是到达端口 443 的包的大致流程。
您将需要使用命令来查看表格。
iptables -t nat -vnL | less
这个输出看起来相当吓人。
下面切出很多其他链条,呼吁切入正题。在这个例子中:
- 此集群使用 Calico/channel/Flannel 的 CNI 插件。
- 监听端口为443
- nginx-ingress-controller 的 pod 在 10.244.0.183 监听(以及其他)。
在那种情况下,数据包的流动方式如下:
- 数据包进入 PREROUTING 链。
- PREROUTING 链调用(除其他外)CNI-HOSTPORT-DNAT 链。
- POSTROUTING链也调用同一条链。
- CNI-HOSTPORT-DNAT 链依次调用多个 CNI-DN-xxxx 链。
- CNI-DN-xxx 链执行 DNAT 并将目标地址更改为 10.244.0.183。
- nginx-ingress-controller 中的容器侦听 10.244.0.183。
如果 pod 位于与到达的数据包不同的节点上,并且如果同一端口有多个 pods load-balanced,则会涉及一些额外的复杂性。负载平衡似乎是通过 iptables 统计模块随机选择一个或另一个 iptables 规则来处理的。
从服务到 pod 的内部流量遵循类似的流程,但不相同。
在这个例子中:
- 服务在10.32.0.183,端口8001
- pod 位于 10.244.6.112,端口 8001。
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
...
KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0
Chain KUBE-SERVICES (2 references)
...
/* Traffic from within the cluster to 10.32.0.183:8001 */
0 0 KUBE-SVC-ZHCKOT5PFJF4PASJ tcp -- * * 0.0.0.0/0 10.32.0.183 tcp dpt:8001
...
/* Mark the package */
Chain KUBE-SVC-ZHCKOT5PFJF4PASJ (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ tcp -- * * !10.244.0.0/16 10.32.0.183 tcp dpt:8081
0 0 KUBE-SEP-RYU73S2VFHOHW4XO all -- * * 0.0.0.0/0 0.0.0.0/0
/* Perform DNAT, redirecting from 10.32.0.183 to 10.244.6.12 */
Chain KUBE-SEP-RYU73S2VFHOHW4XO (1 references) 0 0 KUBE-MARK-MASQ all -- * * 10.244.6.112 0.0.0.0/0
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp to:10.244.6.112:8081
关于如何使节点可靠工作的问题的第二部分:
- 禁用防火墙。
- 改为使用 Kubernetes 网络策略(如果您使用的是 Calico,则使用 Calico 网络策略)。