网络异常排错

    说到 Kubernetes 的网络,其实无非就是以下三种情况之一

    • Pod 访问容器外部网络
    • 从容器外部访问 Pod 网络
    • Pod 之间相互访问

    当然,以上每种情况还都分别包括本地访问和跨主机访问两种场景,并且一般情况下都是通过 Service 间接访问 Pod。

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

    • CNI 网络插件配置错误,导致多主机网络不通,比如
      • IP 网段与现有网络冲突
      • 插件使用了底层网络不支持的协议
      • 忘记开启 IP 转发等
        • sysctl net.bridge.bridge-nf-call-iptables
    • Pod 网络路由丢失,比如
      • kubenet 要求网络中有 podCIDR 到主机 IP 地址的路由,这些路由如果没有正确配置会导致 Pod 网络通信等问题
      • 在公有云平台上,kube-controller-manager 会自动为所有 Node 配置路由,但如果配置不当(如认证授权失败、超出配额等),也有可能导致无法配置路由
    • 主机内或者云平台的安全组、防火墙或者安全策略等阻止了 Pod 网络,比如
      • 非 Kubernetes 管理的 iptables 规则禁止了 Pod 网络
      • 公有云平台的安全组禁止了 Pod 网络(注意 Pod 网络有可能与 Node 网络不在同一个网段)
      • 交换机或者路由器的 ACL 禁止了 Pod 网络

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

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

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

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

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

    1. $ kubectl get svc kube-dns --namespace=kube-system
    2. NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    3. kube-dns 10.0.0.10 <none> 53/UDP,53/TCP 1h
    4. $ kubectl get ep kube-dns --namespace=kube-system
    5. NAME ENDPOINTS AGE
    6. kube-dns 10.180.3.17:53,10.180.3.17:53 1h

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

    1. apiVersion: v1
    2. kind: Service
    3. metadata:
    4. name: kube-dns
    5. namespace: kube-system
    6. labels:
    7. k8s-app: kube-dns
    8. kubernetes.io/cluster-service: "true"
    9. kubernetes.io/name: "KubeDNS"
    10. spec:
    11. selector:
    12. k8s-app: kube-dns
    13. clusterIP: 10.0.0.10
    14. ports:
    15. - name: dns
    16. port: 53
    17. protocol: UDP
    18. - name: dns-tcp
    19. port: 53
    20. protocol: TCP

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

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

    1. # 查询 Service 的 LabelSelector
    2. kubectl get svc <service-name> -o jsonpath='{.spec.selector}'
    3. # 查询匹配 LabelSelector 的 Pod
    4. kubectl get pods -l key1=value1,key2=value2

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

    • 直接访问 podIP:containerPort 是否正常

    再进一步,即使上述配置都正确无误,还有其他的原因会导致 Service 无法访问,比如

    • Pod 内的容器有可能未正常运行或者没有监听在指定的 containerPort 上
    • CNI 网络或主机路由异常也会导致类似的问题
    • kube-proxy 服务有可能未启动或者未正确配置相应的 iptables 规则,比如正常情况下名为 hostnames 的服务会配置以下 iptables 规则
    1. $ iptables-save | grep hostnames
    2. -A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
    3. -A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
    4. -A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
    5. -A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
    6. -A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
    7. -A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
    8. -A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
    9. -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
    10. -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3

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

    1. $ for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
    2. 1
    3. 1
    4. 1
    5. 1

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

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

    1. $ kubectl run curl --image=appropriate/curl -i -t --restart=Never --command -- sh
    2. If you don't see a command prompt, try pressing enter.
    3. / #
    4. / # KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
    5. / # curl -sSk -H "Authorization: Bearer $KUBE_TOKEN" https://$KUBERNETES_SERVICE_HOST:$KUBERNET
    6. ES_SERVICE_PORT/api/v1/namespaces/default/pods
    7. {
    8. "kind": "PodList",
    9. "apiVersion": "v1",
    10. "metadata": {
    11. "selfLink": "/api/v1/namespaces/default/pods",
    12. "resourceVersion": "2285"
    13. },
    14. "items": [
    15. ...
    16. ]
    17. }

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

    1. $ kubectl get service kubernetes
    2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    3. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 25m
    4. $ kubectl get endpoints kubernetes
    5. NAME ENDPOINTS AGE
    6. kubernetes 172.17.0.62:6443 25m

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

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

    1. # 1. service account
    2. apiVersion: v1
    3. kind: ServiceAccount
    4. metadata:
    5. name: coredns
    6. namespace: kube-system
    7. labels:
    8. kubernetes.io/cluster-service: "true"
    9. addonmanager.kubernetes.io/mode: Reconcile
    10. ---
    11. # 2. cluster role
    12. apiVersion: rbac.authorization.k8s.io/v1
    13. kind: ClusterRole
    14. metadata:
    15. labels:
    16. addonmanager.kubernetes.io/mode: Reconcile
    17. name: system:coredns
    18. rules:
    19. - apiGroups:
    20. resources:
    21. - endpoints
    22. - services
    23. - pods
    24. - namespaces
    25. verbs:
    26. - list
    27. - watch
    28. ---
    29. # 3. cluster role binding
    30. apiVersion: rbac.authorization.k8s.io/v1
    31. kind: ClusterRoleBinding
    32. metadata:
    33. annotations:
    34. rbac.authorization.kubernetes.io/autoupdate: "true"
    35. labels:
    36. kubernetes.io/bootstrapping: rbac-defaults
    37. addonmanager.kubernetes.io/mode: EnsureExists
    38. name: system:coredns
    39. roleRef:
    40. apiGroup: rbac.authorization.k8s.io
    41. kind: ClusterRole
    42. name: system:coredns
    43. subjects:
    44. - kind: ServiceAccount
    45. name: coredns
    46. namespace: kube-system
    47. ---
    48. # 4. use created service account
    49. apiVersion: extensions/v1beta1
    50. kind: Deployment
    51. metadata:
    52. name: coredns
    53. namespace: kube-system
    54. labels:
    55. k8s-app: coredns
    56. kubernetes.io/cluster-service: "true"
    57. addonmanager.kubernetes.io/mode: Reconcile
    58. kubernetes.io/name: "CoreDNS"
    59. spec:
    60. replicas: 2
    61. selector:
    62. matchLabels:
    63. k8s-app: coredns
    64. template:
    65. metadata:
    66. labels:
    67. k8s-app: coredns
    68. spec:
    69. serviceAccountName: coredns
    70. ...