Azure 负载均衡

使用 Azure Cloud Provider 后,Kubernetes 会为 LoadBalancer 类型的 Service 创建 Azure 负载均衡器以及相关的 公网 IP、BackendPool 和 Network Security Group (NSG)。注意目前 Azure Cloud Provider 仅支持 Basic SKU 的负载均衡,并将在 v1.11 中支持 Standard SKU。BasicStandard SKU 负载均衡相比有一定的局限

Load Balancer Basic Standard
Back-end pool size up to 100 up to 1,000
Back-end pool boundary Availability Set virtual network, region
Back-end pool design VMs in Availability Set, virtual machine scale set in Availability Set Any VM instance in the virtual network
HA Ports Not supported Available
Diagnostics Limited, public only Available
VIP Availability Not supported Available
Fast IP Mobility Not supported Available
Availability Zones scenarios Zonal only Zonal, Zone-redundant, Cross-zone load-balancing
Outbound SNAT algorithm On-demand Preallocated
Outbound SNAT front-end selection Not configurable, multiple candidates Optional configuration to reduce candidates
Network Security Group Optional on NIC/subnet Required

同样,对应的 Public IP 也是 Basic SKU,与 Standard SKU 相比也有一定的局限

Public IP Basic Standard
Availability Zones scenarios Zonal only Zone-redundant (default), zonal (optional)
Fast IP Mobility Not supported Available
VIP Availability Not supported Available
Counters Not supported Available
Network Security Group Optional on NIC Required

在创建 Service 时,可以通过 metadata.annotation 来自定义 Azure 负载均衡的行为,可选的 Annotation 列表请参考 Cloud Provider Azure 文档

在 Kubernetes 中,负载均衡的创建逻辑都在 kube-controller-manager 中,因而排查负载均衡相关的问题时,除了查看 Service 自身的状态,如

  1. kubectl describe service <service-name>

还需要查看 kube-controller-manager 是否有异常发生:

  1. PODNAME=$(kubectl -n kube-system get pod -l component=kube-controller-manager -o jsonpath='{.items[0].metadata.name}')
  2. kubectl -n kube-system logs $PODNAME --tail 100

LoadBalancer Service 一直处于 pending 状态

查看 Service kubectl describe service <service-name> 没有错误信息,但 EXTERNAL-IP 一直是 <pending>,说明 Azure Cloud Provider 在创建 LB/NSG/PublicIP 过程中出错。一般按照前面的步骤查看 kube-controller-manager 可以查到具体失败的原因,可能的因素包括

  • clientId、clientSecret、tenandId 或 subscriptionId 配置错误导致 Azure API 认证失败:更新所有节点的 /etc/kubernetes/azure.json ,修复错误的配置即可恢复服务
  • 配置的客户端无权管理 LB/NSG/PublicIP/VM:可以为使用的 clientId 增加授权或创建新的 az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/<subscriptionID>/resourceGroups/<resourceGroupName>"
  • Kuberentes v1.8.X 中还有可能出现 Security rule must specify SourceAddressPrefixes, SourceAddressPrefix, or SourceApplicationSecurityGroups 的错误,这是由于 Azure Go SDK 的问题导致的,可以通过升级集群到 v1.9.X/v1.10.X 或者将 SourceAddressPrefixes 替换为多条 SourceAddressPrefix 规则来解决

负载均衡公网 IP 无法访问

Azure Cloud Provider 会为负载均衡器创建探测器,只有探测正常的服务才可以响应用户的请求。负载均衡公网 IP 无法访问一般是探测失败导致的,可能原因有:

  • 后端 VM 本身不正常(可以重启 VM 恢复)
  • 后端容器未监听在设置的端口上(可通过配置正确的端口解决)
  • 防火墙或网络安全组阻止了要访问的端口(可通过增加安全规则解决)
  • 当使用内网负载均衡时,从同一个 ILB 的后端 VM 上访问 ILB VIP 时也会失败,这是 Azure 的预期行为(此时可以访问 service 的 clusterIP)
  • 后端容器不响应(部分或者全部)外部请求时也会导致负载均衡 IP 无法访问。注意这里包含部分容器不响应的场景,这是由于 Azure 探测器与 Kubernetes 服务发现机制共同导致的结果:

    • (1)Azure 探测器定期去访问 service 的端口(即 NodeIP:NodePort)
    • (2)Kubernetes 将其负载均衡到后端容器中
    • (3)当负载均衡到异常容器时,访问失败会导致探测失败,进而 Azure 可能会将 VM 移出负载均衡
    • 该问题的解决方法是使用健康探针,保证异常容器自动从服务的后端(endpoints)中删除。

内网负载均衡 BackendPool 为空

Kubernetes 1.9.0-1.9.3 中会有这个问题(kubernetes#59746 kubernetes#60060 acs-engine#2151),这是由于一个查找负载均衡所属 AvaibilitySet 的缺陷导致的。

该问题的修复(kubernetes#59747 kubernetes#59083)将包含到 v1.9.4 和 v1.10 中。

外网负载均衡均衡 BackendPool 为空

在使用不支持 Cloud Provider 的工具(如 kubeadm)部署的集群中,如果未给 Kubelet 配置 --cloud-provider=azure --cloud-config=/etc/kubernetes/cloud-config,那么 Kubelet 会以 hostname 将其注册到集群中。此时,查看该 Node 的信息(kubectl get node -o yaml),可以发现其 externalID 与 hostname 相同。此时,kube-controller-manager 也无法将其加入到负载均衡的后端中。

一个简单的确认方式是查看 Node 的 externalID 和 name 是否不同:

  1. $ kubectl get node -o jsonpath='{.items[*].metadata.name}'
  2. k8s-agentpool1-27347916-0
  3. $ kubectl get node -o jsonpath='{.items[*].spec.externalID}'
  4. /subscriptions/<subscription-id>/resourceGroups/<rg-name>/providers/Microsoft.Compute/virtualMachines/k8s-agentpool1-27347916-0

该问题的解决方法是先删除 Node kubectl delete node <node-name>,为 Kubelet 配置 --cloud-provider=azure --cloud-config=/etc/kubernetes/cloud-config,最后再重启 Kubelet。

Service 删除后 Azure 公网 IP 未自动删除

Kubernetes 1.9.0-1.9.3 中会有这个问题(kubernetes#59255):当创建超过 10 个 LoadBalancer Service 后有可能会碰到由于超过 FrontendIPConfiguations Quota(默认为 10)导致负载均衡无法创建的错误。此时虽然负载均衡无法创建,但公网 IP 已经创建成功了,由于 Cloud Provider 的缺陷导致删除 Service 后公网 IP 却未删除。

该问题的修复(kubernetes#59340)将包含到 v1.9.4 和 v1.10 中。

另外,超过 FrontendIPConfiguations Quota 的问题可以参考 Azure subscription and service limits, quotas, and constraints 增加 Quota 来解决。

MSI 无法使用

配置 "useManagedIdentityExtension": true 后,可以使用 Managed Service Identity (MSI) 来管理 Azure API 的认证授权。但由于 Cloud Provider 的缺陷(kubernetes #60691 未定义 useManagedIdentityExtension yaml 标签导致无法解析该选项。

该问题的修复(kubernetes#60775)将包含在 v1.10 中。

Azure ARM API 调用请求过多

有时 kube-controller-manager 或者 kubelet 会因请求调用过多而导致 Azure ARM API 失败的情况,比如

  1. "OperationNotAllowed",\r\n "message": "The server rejected the request because too many requests have been received for this subscription.

特别是在 Kubernetes 集群创建或者批量增加 Nodes 的时候。从 v1.9.2 和 v1.10 开始, Azure cloud provider 为一些列的 Azure 资源(如 VM、VMSS、安全组和路由表等)增加了缓存,大大缓解了这个问题。

一般来说,如果该问题重复出现可以考虑

  • 使用 Azure instance metadata,即为所有 Node 的 /etc/kubernetes/azure.json 设置 "useInstanceMetadata": true 并重启 kubelet
  • 为 kube-controller-manager 增大 --route-reconciliation-period(默认为 10s),比如在 /etc/kubernetes/manifests/kube-controller-manager.yaml 中设置 --route-reconciliation-period=1m 后 kubelet 会自动重新创建 kube-controller-manager Pod。

AKS kubectl logs connection timed out

kubectl logs 命令报 getsockopt: connection timed out 的错误(AKS#232):

  1. $ kubectl --v=8 logs x
  2. I0308 10:32:21.539580 26486 round_trippers.go:417] curl -k -v -XGET -H "Accept: application/json, */*" -H "User-Agent: kubectl/v1.8.1 (linux/amd64) kubernetes/f38e43b" -H "Authorization: Bearer x" https://x:443/api/v1/namespaces/default/pods/x/log?container=x
  3. I0308 10:34:32.790295 26486 round_trippers.go:436] GET https://X:443/api/v1/namespaces/default/pods/x/log?container=x 500 Internal Server Error in 131250 milliseconds
  4. I0308 10:34:32.790356 26486 round_trippers.go:442] Response Headers:
  5. I0308 10:34:32.790376 26486 round_trippers.go:445] Content-Type: application/json
  6. I0308 10:34:32.790390 26486 round_trippers.go:445] Content-Length: 275
  7. I0308 10:34:32.790414 26486 round_trippers.go:445] Date: Thu, 08 Mar 2018 09:34:32 GMT
  8. I0308 10:34:32.790504 26486 request.go:836] Response Body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Get https://aks-nodepool1-53392281-1:10250/containerLogs/default/x: dial tcp 10.240.0.6:10250: getsockopt: connection timed out","code":500}
  9. I0308 10:34:32.790999 26486 helpers.go:207] server response object: [{
  10. "metadata": {},
  11. "status": "Failure",
  12. "message": "Get https://aks-nodepool1-53392281-1:10250/containerLogs/default/x/x: dial tcp 10.240.0.6:10250: getsockopt: connection timed out",
  13. "code": 500
  14. }]
  15. F0308 10:34:32.791043 26486 helpers.go:120] Error from server: Get https://aks-nodepool1-53392281-1:10250/containerLogs/default/x/x: dial tcp 10.240.0.6:10250: getsockopt: connection timed out

在 AKS 中,kubectl logs, exec, and attach 等命令需要 Master 与 Nodes 节点之间建立隧道连接。在 kube-system namespace 中可以看到 tunnelfrontkube-svc-redirect Pod:

  1. $ kubectl -n kube-system get po -l component=tunnel
  2. NAME READY STATUS RESTARTS AGE
  3. tunnelfront-7644cd56b7-l5jmc 1/1 Running 0 2d
  4. $ kubectl -n kube-system get po -l component=kube-svc-redirect
  5. NAME READY STATUS RESTARTS AGE
  6. kube-svc-redirect-pq6kf 1/1 Running 0 2d
  7. kube-svc-redirect-x6sq5 1/1 Running 0 2d
  8. kube-svc-redirect-zjl7x 1/1 Running 1 2d

如果它们不是处于 Running 状态或者 Exec/Logs/PortForward 等命令报 net/http: TLS handshake timeout 错误,删除 tunnelfront Pod,稍等一会就会自动创建新的出来,如:

  1. $ kubectl -n kube-system delete po -l component=tunnel
  2. pod "tunnelfront-7644cd56b7-l5jmc" deleted

使用 Virtual Kubelet 后 LoadBalancer Service 无法分配公网 IP

使用 Virtual Kubelet 后,LoadBalancer Service 可能会一直处于 pending 状态,无法分配 IP 地址。查看该服务的事件(如 kubectl describe svc)会发现错误 CreatingLoadBalancerFailed 4m (x15 over 45m) service-controller Error creating load balancer (will retry): failed to ensure load balancer for service default/nginx: ensure(default/nginx): lb(kubernetes) - failed to ensure host in pool: "instance not found"。这是由于 Virtual Kubelet 创建的虚拟 Node 并不存在于 Azure 云平台中,因而无法将其加入到 Azure Load Balancer 的后端中。

解决方法是开启 ServiceNodeExclusion 特性,即设置 kube-controller-manager --feature-gates=ServiceNodeExclusion=true。开启后,所有带有 alpha.service-controller.kubernetes.io/exclude-balancer 标签的 Node 都不会加入到云平台负载均衡的后端中。

注意该特性仅适用于 Kubernetes 1.9 及以上版本。

Node 的 GPU 数总是 0

当在 AKS 集群中运行 GPU 负载时,发现它们无法调度,这可能是由于 Node 容量中的 nvidia.com/gpu 总是0。

解决方法是重新部署 nvidia-gpu 设备插件扩展:

  1. apiVersion: extensions/v1beta1
  2. kind: DaemonSet
  3. metadata:
  4. labels:
  5. kubernetes.io/cluster-service: "true"
  6. name: nvidia-device-plugin
  7. namespace: kube-system
  8. spec:
  9. template:
  10. metadata:
  11. # Mark this pod as a critical add-on; when enabled, the critical add-on scheduler
  12. # reserves resources for critical add-on pods so that they can be rescheduled after
  13. # a failure. This annotation works in tandem with the toleration below.
  14. annotations:
  15. scheduler.alpha.kubernetes.io/critical-pod: ""
  16. labels:
  17. name: nvidia-device-plugin-ds
  18. spec:
  19. tolerations:
  20. # Allow this pod to be rescheduled while the node is in "critical add-ons only" mode.
  21. # This, along with the annotation above marks this pod as a critical add-on.
  22. - key: CriticalAddonsOnly
  23. operator: Exists
  24. containers:
  25. - image: nvidia/k8s-device-plugin:1.10
  26. name: nvidia-device-plugin-ctr
  27. securityContext:
  28. allowPrivilegeEscalation: false
  29. capabilities:
  30. drop: ["ALL"]
  31. volumeMounts:
  32. - name: device-plugin
  33. mountPath: /var/lib/kubelet/device-plugins
  34. volumes:
  35. - name: device-plugin
  36. hostPath:
  37. path: /var/lib/kubelet/device-plugins
  38. nodeSelector:
  39. beta.kubernetes.io/os: linux
  40. accelerator: nvidia

Azure ServicePrincipal 过期

默认情况下,Service Principal 的过期时间是 1 年,可以通过以下的命令延长过期时间:

  1. az ad sp credential reset --name <clientId> --password <clientSecret> --years <newYears>

Node 自动重启

为了保护 AKS 群集,安全更新自动应用于所有的 Linux 节点。 这些更新包括 OS 安全修复项或内核更新,其中的部分更新需要重启节点才能完成。 AKS 不会自动重新启动这些 Linux 节点,但你可以参考这里配置 kured 来自动重启节点。

除此之外,如果你还想收到节点需要重启的通知,可以参考Dashboard and notifications on AKS for required worker nodes reboots 进行配置。

AKS Periscope

AKS Periscope 是一个用于排查 AKS 集群问题的调试工具,开源在

使用方法:

  1. az extension add --name aks-preview
  2. az aks kollect -g MyResourceGroup -n MyManagedCluster --storage-account MyStorageAccount --sas-token "MySasToken"

已知问题及修复版本

  1. 手动更新 VMSS VM 到最新时 Azure LoadBalancer 后端丢失问题

  2. Service 未配置 DNS 标签导致公网 IP 上 DNS 标签丢失问题

  3. 路由表并发更新冲突问题

  4. VMSS VM 并发更新冲突问题

  5. VMSS 缓存不一致问题

参考文档

更多的 AKS 排错信息可以参考 AKS 常见问题