[16] 為什麼使用 security group for pod 使用 liveness/readiness probes 需要設定環境變數 DISABLE_TCP_EARLY_DEMUX
根據 EKS Security groups for pods [1] 文件,如果 Pod 使用 liveness/readiness probes 時,則需要設置 VPC CNI plugin DISABLE_TCP_EARLY_DEMUX=ture
環境變數。
環境變數 DISABLE_TCP_EARLY_DEMUX
VPC CNI plugin [2] 若在 DISABLE_TCP_EARLY_DEMUX=ture
,則會在 initContainer [3]amazon-k8s-cni-init
設定對應的 kernel 參數 net.ipv4.tcp_early_demux
。
# Set DISABLE_TCP_EARLY_DEMUX to true to enable kubelet to pod-eni TCP communication
# https://lwn.net/Articles/503420/ and https://github.com/aws/amazon-vpc-cni-k8s/pull/1212 for background
if [ "${DISABLE_TCP_EARLY_DEMUX:-false}" == "true" ]; then
sysctl -w "net.ipv4.tcp_early_demux=0"
else
sysctl -e -w "net.ipv4.tcp_early_demux=1"
fi
不過提到此變數將會 提高些微的本地端 TCP 連線延遲。
This will increase the local TCP connection latency slightly.
而根據 kernel 參數 [4]
tcp_early_demux - BOOLEAN
Enable early demux for established TCP sockets.
Default: 1
然而上述 kernel 參數文件並未完整解釋 tcp_early_demux
及為什麼可能影響本地端 TCP 延遲問題。
而在 VPC CNI plugin - Add support to toggle TCP early demux #1212 解釋為什麼需要啟用此 kernel 參數:
了解一下運作
可以確認 net->ipv4.sysctl_ip_early_demux
作為 kernel 參數使用於 ip_rcv_finish
function:
static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
int (*edemux)(struct sk_buff *skb);
struct net_device *dev = skb->dev;
struct rtable *rt;
int err;
/* if ingress device is enslaved to an L3 master device pass the
* skb to its handler for processing
*/
skb = l3mdev_ip_rcv(skb);
if (!skb)
return NET_RX_SUCCESS;
if (net->ipv4.sysctl_ip_early_demux && ---> kernel parameter: sysctl_ip_early_demux
!skb_dst(skb) &&
!skb->sk &&
!ip_is_fragment(iph)) {
const struct net_protocol *ipprot;
int protocol = iph->protocol;
ipprot = rcu_dereference(inet_protos[protocol]);
if (ipprot && (edemux = READ_ONCE(ipprot->early_demux))) {
err = edemux(skb);
if (unlikely(err))
goto drop_error;
/* must reload iph, skb->head might have changed */
iph = ip_hdr(skb);
}
}
/*
* Initialise the virtual path cache for the packet. It describes
* how the packet travels inside Linux networking.
*/
if (!skb_valid_dst(skb)) {
err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
iph->tos, dev);
if (unlikely(err))
goto drop_error;
}
int tcp_v4_early_demux(struct sk_buff *skb)
{
....
....
sk = __inet_lookup_established(dev_net(skb->dev), &tcp_hashinfo,
iph->saddr, th->source,
iph->daddr, ntohs(th->dest),
skb->skb_iif, inet_sdif(skb));
if (sk) {
skb->sk = sk;
skb->destructor = sock_edemux;
if (sk_fullsock(sk)) {
struct dst_entry *dst = READ_ONCE(sk->sk_rx_dst);
if (dst)
dst = dst_check(dst, 0);
if (dst &&
inet_sk(sk)->rx_dst_ifindex == skb->skb_iif)
skb_dst_set_noref(skb, dst);
}
}
return 0;
}
由上述可以了解 tcp_v4_early_demux
檢查是否有建立連線的 socket,若有找到則將 dst 設定到 socket cache 中,因此可以節省尋找 routing 的過程。
問題
如前兩天討論 security group for pod 流程,是使用額外的 Trunk interface 介接設定 vlan 對接 Pod veth。因此使用 security group for pod 的 Pod,流量從 eth0 出去並經由 Trunk interface 回來,因此 Pod SYN-ACK 或 ACK 封包會因 tcp_early_demux
機制而被丟棄。
- 從 Kubelet 到 pod-eni:Kubelet 發送 SYN 封包,Pod 以 SYN-ACK 響應,但 kernel 在主機 veth dev 中接收到封包後丟棄了該封包。
- 從 pod-eni 到 kubelet:Pod-eni 發送 ACK 以響應來自 kubelet 的 SYN-ACK,但 ACK pkt 在主機 veth dev 後再次被 kernel 丟棄。
總結
tcp_v4_early_demux
kernel 參數可以作為 kernel socket protocol stack 的 cache 機制,但是在使用 security group for pod 時,則會導致 kubelet 在進行 liveness/readiness probes 時被 kernel 機制判定而丟棄封包。因此需要啟用此 DISABLE_TCP_EARLY_DEMUX=ture
環境變數。
參考文件
- Security groups for pods - Configure the Amazon VPC CNI add-on for security groups for pods - https://docs.aws.amazon.com/eks/latest/userguide/security-groups-for-pods.html#security-groups-pods-deployment
- https://github.com/aws/amazon-vpc-cni-k8s#disable_tcp_early_demux-v173
- https://github.com/aws/amazon-vpc-cni-k8s/blob/master/scripts/init.sh#L53-L59
- https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
- https://github.com/aws/amazon-vpc-cni-k8s/pull/1212#issuecomment-693540666