互联网服务Kubernetes容器网络

k8s环境下容器网络相关的问题如何有效排查?

在k8s环境下容器网络一般会涉及到CNI 、iptables/ipvs,network policy等,有没好的办法能在出现网络问题时快速定位到问题点?

参与5

2同行回答

mtming333mtming333课题专家组系统架构师某电子支付
kube-OVN集成了一套网络监控方案,可以了解一下显示全部

kube-OVN集成了一套网络监控方案,可以了解一下

收起
互联网服务 · 2020-06-22
浏览2063
晓风晓风其它用友网络
k8s环境下网络不通的问题可能会涉及 Pod 网络、CNI、Service、Iptables、IPVS、Ingress、DNS、NetworkPolicy、Loadbalancer 等等多个组件。 K8s的网络中pod的通信:同一Pod内的容器间通信:     因为pause容器提供pod内网络共享,所以容器直接可以使用localhost(lo)访问...显示全部

k8s环境下网络不通的问题可能会涉及 Pod 网络、CNI、Service、Iptables、IPVS、Ingress、DNS、NetworkPolicy、Loadbalancer 等等多个组件。

K8s的网络中pod的通信:

  1. 同一Pod内的容器间通信:

     因为pause容器提供pod内网络共享,所以容器直接可以使用localhost(lo)访问其他容器

  1. 各Pod彼此之间的通信(两个pod在一台主机上面, 两个pod分布在不同主机之上)

    1)两个pod在一台主机上面: 通过docker默认的docker网桥互连容器(docker0), ifconfig 查了docker0

    2)两个pod分布在不同主机之上: 通过CNI插件实现,eg: flannel,calico,canal

  1. Pod与Service间的通信

    Service分配的ip叫cluster ip是一个虚拟ip(相对固定,除非删除service),这个ip只能在k8s集群内部使用,如果service需要对外提供,只能使用Nodeport方式映射到主机上,使用主机的ip和端口对外提供服务。

    节点上面有个kube-proxy进程,这个进程从master apiserver获取信息,感知service和endpoint的创建,然后做两个事:

    1.为每个service 在集群中每个节点上面创建一个随机端口,任何该端口上面的连接会代理到相应的pod

    2.集群中每个节点安装iptables/ipvs规则,用于clusterip + port路由到上一步定义的随机端口上面, 所以集群中每个node上面都有service的转发规则:iptables -L -n -t filter

排查网络问题基本上从这几种情况出发,定位出具体的网络异常点,进而寻找解决方法。
网络异常可能的原因比较多,常见的有

CNI 网络插件配置错误,导致多主机网络不通,比如

  • IP 网段与现有网络冲突
  • 插件使用了底层网络不支持的协议
  • 忘记开启 IP 转发等

    • sysctl net.ipv4.ip_forward
    • sysctl net.bridge.bridge-nf-call-iptables

Pod 网络路由丢失,比如

  • kubenet 要求网络中有 podCIDR 到主机 IP 地址的路由,这些路由如果没有正确配置会导致 Pod 网络通信等问题
  • 在公有云平台上,kube-controller-manager 会自动为所有 Node 配置路由,但如果配置不当(如认证授权失败、超出配额等),也有可能导致无法配置路由

Flannel Pods 一直处于 Init:CrashLoopBackOff 状态

Flannel 网络插件非常容易部署,只要一条命令即可

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

然而,部署完成后,Flannel Pod 有可能会碰到初始化失败的错误

查看日志会发现

$ kubectl -n kube-system logs kube-flannel-ds-jpp96 -c install-cni
cp: can't create '/etc/cni/net.d/10-flannel.conflist': Permission denied

这一般是由于 SELinux 开启导致的,关闭 SELinux 既可解决。有两种方法:

  • 修改 /etc/selinux/config 文件方法:SELINUX=disabled
  • 通过命令临时修改(重启会丢失):setenforce 0

Pod 无法解析 DNS

如果 Node 上安装的 Docker 版本大于 1.12,那么 Docker 会把默认的 iptables FORWARD 策略改为 DROP。这会引发 Pod 网络访问的问题。解决方法则在每个 Node 上面运行 iptables -P FORWARD ACCEPT,比如

echo "ExecStartPost=/sbin/iptables -P FORWARD ACCEPT" >> /etc/systemd/system/docker.service.d/exec_start.conf
systemctl daemon-reload
systemctl restart docker

如果使用了 flannel/weave 网络插件,更新为最新版本也可以解决这个问题。

DNS 无法解析也有可能是 kube-dns 服务异常导致的,可以通过下面的命令来检查 kube-dns 是否处于正常运行状态

$ kubectl get pods --namespace=kube-system -l k8s-app=kube-dns
NAME                    READY     STATUS    RESTARTS   AGE
...
kube-dns-v19-ezo1y      3/3       Running   0           1h
...

如果 kube-dns 处于 CrashLoopBackOff 状态,那么可以参考 Kube-dns/Dashboard CrashLoopBackOff 排错 来查看具体排错方法。

如果 kube-dns Pod 处于正常 Running 状态,则需要进一步检查是否正确配置了 kube-dns 服务:

$ kubectl get svc kube-dns --namespace=kube-system
NAME          CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
kube-dns      10.0.0.10              53/UDP,53/TCP        1h

$ kubectl get ep kube-dns --namespace=kube-system
NAME       ENDPOINTS                       AGE
kube-dns   10.180.3.17:53,10.180.3.17:53    1h

如果 kube-dns service 不存在,或者 endpoints 列表为空,则说明 kube-dns service 配置错误,可以重新创建 kube-dns service,比如

apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "KubeDNS"
spec:
  selector:
    k8s-app: kube-dns
  clusterIP: 10.0.0.10
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP

Service 无法访问

访问 Service ClusterIP 失败时,可以首先确认是否有对应的 Endpoints

kubectl get endpoints 

如果该列表为空,则有可能是该 Service 的 LabelSelector 配置错误,可以用下面的方法确认一下

# 查询 Service 的 LabelSelector
kubectl get svc  -o jsonpath='{.spec.selector}'

# 查询匹配 LabelSelector 的 Pod
kubectl get pods -l key1=value1,key2=value2

如果 Endpoints 正常,可以进一步检查

  • Pod 的 containerPort 与 Service 的 containerPort 是否对应
  • 直接访问 podIP:containerPort 是否正常 再进一步,即使上述配置都正确无误,还有其他的原因会导致 Service 无法访问,比如
  • Pod 内的容器有可能未正常运行或者没有监听在指定的 containerPort 上
  • CNI 网络或主机路由异常也会导致类似的问题
  • kube-proxy 服务有可能未启动或者未正确配置相应的 iptables 规则,比如正常情况下名为 hostnames的服务会配置 iptables 规则

    $ iptables-save | grep hostnames

Pod 无法通过 Service 访问自己

这通常是 hairpin 配置错误导致的,可以通过 Kubelet 的 --hairpin-mode 选项配置,可选参数包括 "promiscuous-bridge"、"hairpin-veth" 和 "none"(默认为"promiscuous-bridge")。

对于 hairpin-veth 模式,可以通过以下命令来确认是否生效

$ for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done

而对于 promiscuous-bridge 模式,可以通过以下命令来确认是否生效

$ ifconfig cbr0 |grep PROMISC
UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1460  Metric:1

无法访问 Kubernetes API

很多扩展服务需要访问 Kubernetes API 查询需要的数据(比如 kube-dns、Operator 等)。通常在 Kubernetes API 无法访问时,可以首先通过下面的命令验证 Kubernetes API 是正常的:

$ kubectl run curl  --image=appropriate/curl -i -t  --restart=Never --command -- sh 

如果出现超时错误,则需要进一步确认名为 kubernetes 的服务以及 endpoints 列表是正常的:

$ kubectl get service kubernetes
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1            443/TCP   25m
$ kubectl get endpoints kubernetes
NAME         ENDPOINTS          AGE
kubernetes   172.17.0.62:6443   25m

然后可以直接访问 endpoints 查看 kube-apiserver 是否可以正常访问。无法访问时通常说明 kube-apiserver 未正常启动,或者有防火墙规则阻止了访问。

但如果出现了 403 - Forbidden 错误,则说明 Kubernetes 集群开启了访问授权控制(如 RBAC),此时就需要给 Pod 所用的 ServiceAccount 创建角色和角色绑定授权访问所需要的资源。比如 CoreDNS 就需要创建以下 ServiceAccount 以及角色绑定:

# 1. service account
apiVersion: v1
kind: ServiceAccount
metadata:
  name: coredns
  namespace: kube-system
  labels:
      kubernetes.io/cluster-service: "true"
      addonmanager.kubernetes.io/mode: Reconcile
---
# 2. cluster role
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
    addonmanager.kubernetes.io/mode: Reconcile
  name: system:coredns
rules:
- apiGroups:
  - ""
  resources:
  - endpoints
  - services
  - pods
  - namespaces
  verbs:
  - list
  - watch
---
# 3. cluster role binding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
    addonmanager.kubernetes.io/mode: EnsureExists
  name: system:coredns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:coredns
subjects:
- kind: ServiceAccount
  name: coredns
  namespace: kube-system
---
# 4. use created service account
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: coredns
  namespace: kube-system
  labels:
    k8s-app: coredns
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
    kubernetes.io/name: "CoreDNS"
spec:
  replicas: 2
  selector:
    matchLabels:
      k8s-app: coredns
  template:
    metadata:
      labels:
        k8s-app: coredns
    spec:
      serviceAccountName: coredns
      ...

附:IPVS模式下的Kubernetes容器网络排障
CNI插件采用了Calico,而kube-proxy则是开启了IPVS模式。在容器网络的建造中,CNI和kube-proxy都有很多参与。

$ route -n

首先CNI很好排查,只需要记录各个分区上容器的IP(每个副本记一个IP可)然后在各个代表上Ping这些IP,如果可以Ping通,那CNI插件的工作就是完全正常的。

kubectl get pods -o wide -n kube-system|grep 10.244|awk '{print $6}'|xargs nmap -sP|grep Host

运行的结果是排除了CNI的问题。
随后,由于第一次使用nmap扫描对端端口时,使用的是群集IP,也即是Service的IP,而群集IP到Pod IP之间只夹着一条LVS负载均衡规则(或者如果kube-proxy使用替代查询现有的IPVS规则,就发现了只有虚拟服务器终结点而没有真实服务器的IPVS规则:既然,配置,则是iptables规则),既然Pod IP的连通性没有问题,那大概率是IPVS规则出现了问题。

$ ipvsadm -Ln
$ kubectl get service --all-namespaces|grep 10.108.62.55 
$ kubectl get pods -n kube-system|grep calico

至此可以确定是由于由于IPVS规则来将到达服务的流量负载均衡到Pod上导致了这个问题。而IPVS规则是由kube-proxy维护的,打开了kube-proxy的日志,发现了报错

 kubectl logs -n kube-system kube-proxy-cdsgl |tail 


收起
互联网服务 · 2020-06-22
浏览2340

提问者

糖粑
系统分析师中国移动
擅长领域: 云计算容器容器云

问题来自

相关问题

相关资料

相关文章

问题状态

  • 发布时间:2020-06-19
  • 关注会员:3 人
  • 问题浏览:3663
  • 最近回答:2020-06-22
  • X社区推广