Azure

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 自身的狀態,如

kubectl describe service <service-name>

還需要查看 kube-controller-manager 是否有異常發生:

PODNAME=$(kubectl -n kube-system get pod -l component=kube-controller-manager -o jsonpath='{.items[0].metadata.name}')
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 是否不同:

$ kubectl get node -o jsonpath='{.items[*].metadata.name}'
k8s-agentpool1-27347916-0
$ kubectl get node -o jsonpath='{.items[*].spec.externalID}'
/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 失敗的情況,比如

"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):

$ kubectl --v=8 logs x
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
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
I0308 10:34:32.790356   26486 round_trippers.go:442] Response Headers:
I0308 10:34:32.790376   26486 round_trippers.go:445]     Content-Type: application/json
I0308 10:34:32.790390   26486 round_trippers.go:445]     Content-Length: 275
I0308 10:34:32.790414   26486 round_trippers.go:445]     Date: Thu, 08 Mar 2018 09:34:32 GMT
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}
I0308 10:34:32.790999   26486 helpers.go:207] server response object: [{
  "metadata": {},
  "status": "Failure",
  "message": "Get https://aks-nodepool1-53392281-1:10250/containerLogs/default/x/x: dial tcp 10.240.0.6:10250: getsockopt: connection timed out",
  "code": 500
}]
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:

$ kubectl -n kube-system get po -l component=tunnel
NAME                           READY     STATUS    RESTARTS   AGE
tunnelfront-7644cd56b7-l5jmc   1/1       Running   0          2d

$ kubectl -n kube-system get po -l component=kube-svc-redirect
NAME                      READY     STATUS    RESTARTS   AGE
kube-svc-redirect-pq6kf   1/1       Running   0          2d
kube-svc-redirect-x6sq5   1/1       Running   0          2d
kube-svc-redirect-zjl7x   1/1       Running   1          2d

如果它們不是處於 Running 狀態或者 Exec/Logs/PortForward 等命令報 net/http: TLS handshake timeout 錯誤,刪除 tunnelfront Pod,稍等一會就會自動創建新的出來,如:

$ kubectl -n kube-system delete po -l component=tunnel
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 設備插件擴展:

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  labels:
    kubernetes.io/cluster-service: "true"
  name: nvidia-device-plugin
  namespace: kube-system
spec:
  template:
    metadata:
      # Mark this pod as a critical add-on; when enabled, the critical add-on scheduler
      # reserves resources for critical add-on pods so that they can be rescheduled after
      # a failure.  This annotation works in tandem with the toleration below.
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ""
      labels:
        name: nvidia-device-plugin-ds
    spec:
      tolerations:
      # Allow this pod to be rescheduled while the node is in "critical add-ons only" mode.
      # This, along with the annotation above marks this pod as a critical add-on.
      - key: CriticalAddonsOnly
        operator: Exists
      containers:
      - image: nvidia/k8s-device-plugin:1.10
        name: nvidia-device-plugin-ctr
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop: ["ALL"]
        volumeMounts:
          - name: device-plugin
            mountPath: /var/lib/kubelet/device-plugins
      volumes:
        - name: device-plugin
          hostPath:
            path: /var/lib/kubelet/device-plugins
      nodeSelector:
        beta.kubernetes.io/os: linux
        accelerator: nvidia

Azure ServicePrincipal 過期

默認情況下,Service Principal 的過期時間是 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 集群問題的調試工具,開源在 <github.com/Azure/aks-periscope>。

使用方法:

az extension add --name aks-preview
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 常見問題

Last updated