本頁面說明如何解決 Google Kubernetes Engine (GKE) 中 kube-dns 的問題。
找出 kube-dns 中的 DNS 問題來源
dial tcp: i/o timeout
、no such
host
或 Could not resolve host
等錯誤通常表示 kube-dns 無法解析查詢。
如果看到上述錯誤訊息,但不確定原因為何,請參閱下列章節,找出問題所在。下列各節的順序是從最有可能解決問題的步驟開始,因此請依序嘗試各節的步驟。
檢查 kube-dns Pod 是否正在執行
Kube-dns Pod 是叢集內名稱解析的重要項目。如果未執行,您可能會遇到 DNS 解析問題。
如要確認 kube-dns Pod 正在執行,且最近未重新啟動,請查看這些 Pod 的狀態:
kubectl get pods -l k8s-app=kube-dns -n kube-system
輸出結果會與下列內容相似:
NAME READY STATUS RESTARTS AGE
kube-dns-POD_ID_1 5/5 Running 0 16d
kube-dns-POD_ID_2 0/5 Terminating 0 16d
在這個輸出內容中,POD_ID_1
和 POD_ID_2
代表自動附加至 kube-dns Pod 的專屬 ID。
如果輸出內容顯示任何 kube-dns Pod 的狀態不是 Running
,請按照下列步驟操作:
使用「管理員活動」稽核記錄,調查近期是否有任何變更,例如叢集或節點集區版本升級,或是 kube-dns ConfigMap 的變更。如要進一步瞭解稽核記錄,請參閱 GKE 稽核記錄資訊。如果發現變更,請還原變更,然後再次查看 Pod 的狀態。
如果找不到任何相關的近期變更,請調查您是否在 kube-dns Pod 執行的節點上遇到 OOM 錯誤。如果 Cloud Logging 記錄訊息中出現類似下列內容的錯誤,表示這些 Pod 發生 OOM 錯誤:
Warning: OOMKilling Memory cgroup out of memory
這則訊息表示 Kubernetes 因資源消耗過多而終止程序。Kubernetes 會根據資源要求排程 Pod,但允許 Pod 使用的資源量最多可達資源限制。如果限制高於要求,或沒有限制,Pod 的資源用量可能會超過系統資源。
如要解決這個錯誤,您可以刪除有問題的工作負載,或設定記憶體或 CPU 限制。如要進一步瞭解如何設定限制,請參閱 Kubernetes 說明文件的「Pod 和容器的資源管理」。如要進一步瞭解 OOM 事件,請參閱「排解 OOM 事件」。
如果找不到任何 OOM 錯誤訊息,請重新啟動 kube-dns 部署作業:
kubectl rollout restart deployment/kube-dns --namespace=kube-system
重新啟動 Deployment 後,請檢查 kube-dns Pod 是否正在執行。
如果上述步驟無法解決問題,或所有 kube-dns Pod 的狀態都是 Running
,但您仍遇到 DNS 問題,請確認 /etc/resolv.conf
檔案設定正確無誤。
確認 /etc/resolv.conf
設定正確無誤
檢查發生 DNS 問題的 Pod 的 /etc/resolv.conf
檔案,確認其中包含的項目正確無誤:
查看 Pod 的
/etc/resolv.conf
檔案:kubectl exec -it POD_NAME -- cat /etc/resolv.conf
將 POD_NAME 替換為發生 DNS 問題的 Pod 名稱。如果有多個 Pod 發生問題,請針對每個 Pod 重複執行本節中的步驟。
如果 Pod 二進位檔不支援
kubectl exec
指令,這個指令可能會失敗。如果發生這種情況,請建立簡單的 Pod 做為測試環境。這個程序可讓您在與有問題的 Pod 相同的命名空間中,執行測試 Pod。確認
/etc/resolv.conf
檔案中的名稱伺服器 IP 位址是否正確:- 使用主機網路的 Pod 應使用節點
/etc/resolv.conf
檔案中的值。名稱伺服器 IP 位址應為169.254.169.254
。 對於未使用主機網路的 Pod,kube-dns 服務 IP 位址應與名稱伺服器 IP 位址相同。如要比較 IP 位址,請完成下列步驟:
取得 kube-dns 服務的 IP 位址:
kubectl get svc kube-dns -n kube-system
輸出結果會與下列內容相似:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 192.0.2.10 <none> 53/UDP,53/TCP 64d
記下「叢集 IP」欄中的值。在本例中為
192.0.2.10
。比較 kube-dns Service IP 位址與
/etc/resolv.conf
檔案中的 IP 位址:# cat /etc/resolv.conf search default.svc.cluster.local svc.cluster.local cluster.local c.PROJECT_NAME google.internal nameserver 192.0.2.10 options ndots:5
在這個範例中,這兩個值相符,因此問題並非由錯誤的名稱伺服器 IP 位址所致。
不過,如果 IP 位址不相符,表示應用程式 Pod 的資訊清單中已設定
dnsConfig
欄位。如果
dnsConfig.nameservers
欄位中的值正確無誤,請檢查 DNS 伺服器,確認運作正常。如果不想使用自訂名稱伺服器,請移除該欄位,然後對 Pod 執行滾動重新啟動:
kubectl rollout restart deployment POD_NAME
將
POD_NAME
替換為 Pod 的名稱。
- 使用主機網路的 Pod 應使用節點
在
/etc/resolv.conf
中驗證search
和ndots
項目。請確認沒有拼字錯誤、過時的設定,以及失敗的要求指向正確命名空間中的現有服務。
執行 DNS 查詢
確認 /etc/resolv.conf
設定正確且 DNS 記錄無誤後,請使用 dig 指令列工具,從回報 DNS 錯誤的 Pod 執行 DNS 查詢:
在 Pod 內開啟殼層,直接查詢 Pod:
kubectl exec -it POD_NAME -n NAMESPACE_NAME -- SHELL_NAME
更改下列內容:
POD_NAME
:回報 DNS 錯誤的 Pod 名稱。NAMESPACE_NAME
:Pod 所屬的命名空間。SHELL_NAME
:要開啟的殼層名稱。例如sh
或/bin/bash
。
如果 Pod 不允許
kubectl exec
指令,或 Pod 沒有 dig 二進位檔,這個指令可能會失敗。如果發生這種情況,請使用已安裝 dig 的映像檔建立測試 Pod:kubectl run "test-$RANDOM" ti --restart=Never --image=thockin/dnsutils - bash
檢查 Pod 是否能正確解析叢集的內部 DNS 服務:
dig kubernetes
由於
/etc/resolv.conf
檔案指向 kube-dns 服務 IP 位址,因此執行這項指令時,DNS 伺服器就是 kube-dns 服務。您應該會看到 DNS 回應成功,其中包含 Kubernetes API 服務的 IP 位址 (通常類似
10.96.0.1
)。如果看到SERVFAIL
或沒有任何回應,通常表示 kube-dns Pod 無法解析內部服務名稱。檢查 kube-dns 服務是否可以解析外部網域名稱:
dig example.com
如果特定 kube-dns Pod 無法回應 DNS 查詢,請檢查該 Pod 是否可以解析外部網域名稱:
dig example.com @KUBE_DNS_POD_IP
將
KUBE_DNS_POD_IP
替換為 kube-dns Pod 的 IP 位址。如果您不知道這個 IP 位址的值,請執行下列指令:kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
IP 位址位於
IP
欄。如果指令解析成功,您會看到
status: NOERROR
和 A 記錄的詳細資料,如下列範例所示:; <<>> DiG 9.16.27 <<>> example.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31256 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;example.com. IN A ;; ANSWER SECTION: example.com. 30 IN A 93.184.215.14 ;; Query time: 6 msec ;; SERVER: 10.76.0.10#53(10.76.0.10) ;; WHEN: Tue Oct 15 16:45:26 UTC 2024 ;; MSG SIZE rcvd: 56
結束殼層:
exit
如果上述任何指令失敗,請對 kube-dns Deployment 執行滾動重新啟動:
kubectl rollout restart deployment/kube-dns --namespace=kube-system
重新啟動後,請再次嘗試執行 dig 指令,看看是否成功。如果仍無法連線,請繼續擷取封包。
擷取封包
擷取封包,確認 kube-dns Pod 是否收到 DNS 查詢並正確回應:
使用 SSH 連線至執行 kube-dns Pod 的節點。例如:
前往 Google Cloud 控制台的「VM Instances」(VM 執行個體) 頁面。
找出要連線的節點,如果您不知道 kube-dns Pod 上的節點名稱,請執行下列指令:
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
節點名稱會列在「節點」欄中。
在「連線」欄中,按一下「SSH」。
在終端機中啟動 toolbox,這項工具已預先安裝,可用於偵錯:
toolbox
在根層級提示中,安裝
tcpdump
套件:apt update -y && apt install -y tcpdump
使用
tcpdump
擷取 DNS 流量的封包:tcpdump -i eth0 port 53" -w FILE_LOCATION
將
FILE_LOCATION
替換為要儲存擷取內容的路徑。查看封包擷取記錄。檢查是否有目的地 IP 位址與 kube-dns 服務 IP 位址相符的封包。這樣可確保 DNS 要求能抵達正確的解析目的地。如果 DNS 流量未導向正確的 Pod,可能表示有網路政策封鎖要求。
檢查網路政策
限制性網路政策有時會中斷 DNS 流量。如要確認 kube-system 命名空間中是否有網路政策,請執行下列指令:
kubectl get networkpolicy -n kube-system
如果找到網路政策,請檢查該政策是否允許必要的 DNS 通訊。舉例來說,如果您有封鎖所有輸出流量的網路政策,該政策也會封鎖 DNS 要求。
如果輸出內容為 No resources found in kube-system namespace
,表示你沒有任何網路政策,因此可以排除這個原因。調查記錄有助於找出更多失敗點。
啟用暫時性 DNS 查詢記錄
如要找出 DNS 回應不正確等問題,請暫時啟用 DNS 查詢的偵錯記錄。如要啟用查詢,請根據現有的 kube-dns Pod 建立 Pod。系統會自動還原對 kube-dns Deployment 所做的任何變更。
啟用暫時性 DNS 查詢記錄會耗用大量資源,因此建議您在收集到適當的記錄樣本後,立即刪除所建立的 Pod。
如要啟用暫時性 DNS 查詢記錄,請完成下列步驟:
擷取 kube-dns Pod,並儲存在名為
POD
的變數中:POD=$(kubectl -n kube-system get pods --selector=k8s-app=kube-dns -o jsonpath="{.items[0].metadata.name}")
建立名為
kube-dns-debug
的 Pod。這個 Pod 是儲存在POD
變數中的 Pod 副本,但已啟用 dnsmasq 記錄功能。這項指令不會修改原始的 kube-dns Pod:kubectl apply -f <(kubectl get pod -n kube-system ${POD} -o json | jq -e ' ( (.spec.containers[] | select(.name == "dnsmasq") | .args) += ["--log-queries"] ) | (.metadata.name = "kube-dns-debug") | (del(.metadata.labels."pod-template-hash")) ')
檢查記錄:
kubectl logs -f --tail 100 -c dnsmasq -n kube-system kube-dns-debug
您也可以在 Cloud Logging 中查看查詢。
查看完 DNS 查詢記錄後,請刪除
kube-dns-debug
Pod:kubectl -n kube-system delete pod kube-dns-debug
調查 kube-dns Pod
透過 Cloud Logging 瞭解 kube-dns Pod 如何接收及解析 DNS 查詢。
如要查看與 kube-dns Pod 相關的記錄項目,請完成下列步驟:
前往 Google Cloud 控制台的「Logs Explorer」頁面。
在查詢窗格中輸入下列篩選器,查看與 kube-dns 容器相關的事件:
resource.type="k8s_container" resource.labels.namespace_name="kube-system" resource.labels.Pod_name:"kube-dns" resource.labels.cluster_name="CLUSTER_NAME" resource.labels.location="CLUSTER_LOCATION"
更改下列內容:
CLUSTER_NAME
:kube-dns Pod 所屬叢集的名稱。CLUSTER_LOCATION
:叢集位置。
點選「執行查詢」
查看輸出內容。以下範例輸出內容顯示您可能會看到的一種錯誤:
{ "timestamp": "2024-10-10T15:32:16.789Z", "severity": "ERROR", "resource": { "type": "k8s_container", "labels": { "namespace_name": "kube-system", "Pod_name": "kube-dns", "cluster_name": "CLUSTER_NAME", "location": "CLUSTER_LOCATION" } }, "message": "Failed to resolve 'example.com': Timeout." },
在本例中,kube-dns 無法在合理時間內解析
example.com
。這類錯誤可能由多種問題造成。舉例來說,kube-dns ConfigMap 中的上游伺服器可能設定有誤,或是網路流量過高。
如果未啟用 Cloud Logging,請改為查看 Kubernetes 記錄:
Pod=$(kubectl get Pods -n kube-system -l k8s-app=kube-dns -o name | head -n1)
kubectl logs -n kube-system $Pod -c dnsmasq
kubectl logs -n kube-system $Pod -c kubedns
kubectl logs -n kube-system $Pod -c sidecar
調查 kube-dns ConfigMap 的近期變更
如果叢集突然發生 DNS 解析失敗的情況,其中一個原因是對 kube-dns ConfigMap 進行了不正確的設定變更。特別是對存根網域和上游伺服器定義的設定變更,可能會導致問題。
如要檢查存根網域設定是否有更新,請完成下列步驟:
前往 Google Cloud 控制台的「Logs Explorer」頁面。
在查詢窗格中,輸入下列查詢:
resource.labels.cluster_name="clouddns" resource.type="k8s_container" resource.labels.namespace_name="kube-system" labels.k8s-pod/k8s-app="kube-dns" jsonPayload.message=~"Updated stubDomains to"
點選「執行查詢」
查看輸出內容。如有任何更新,輸出內容會與下列內容相似:
Updated stubDomains to map[example.com: [8.8.8.8 8.8.4.4 1.1.3.3 1.0.8.111]]
如果看到更新,請展開結果,進一步瞭解變更內容。 確認所有存根網域及其對應的上游 DNS 伺服器都已正確定義。如果這裡的項目有誤,這些網域可能會無法解析。
如要檢查上游伺服器的變更,請完成下列步驟:
前往 Google Cloud 控制台的「Logs Explorer」頁面。
在查詢窗格中,輸入下列查詢:
resource.labels.cluster_name="clouddns" resource.type="k8s_container" resource.labels.namespace_name="kube-system" labels.k8s-pod/k8s-app="kube-dns" jsonPayload.message=~"Updated upstreamNameservers to"
點選「執行查詢」
查看輸出內容。如有任何變更,輸出內容會類似於以下內容:
Updated upstreamNameservers to [8.8.8.8]
展開結果即可進一步瞭解異動內容。確認上游 DNS 伺服器清單正確無誤,且這些伺服器可從叢集連線。如果這些伺服器無法使用或設定錯誤,一般 DNS 解析可能會失敗。
如果您已檢查存根網域和上游伺服器的變更,但未找到任何結果,請使用下列篩選器檢查所有變更:
resource.type="k8s_cluster"
protoPayload.resourceName:"namespaces/kube-system/configmaps/kube-dns"
protoPayload.methodName=~"io.k8s.core.v1.configmaps."
查看列出的變更,確認是否導致錯誤。
與 Cloud Customer Care 聯絡
如果已完成上述各節的步驟,但仍無法診斷問題原因,請與 Cloud Customer Care 團隊聯絡。
解決常見問題
如果遇到特定錯誤或問題,請參閱下列各節的建議。
問題:間歇性 DNS 逾時
如果發現 DNS 流量增加或營業時間開始時,發生間歇性 DNS 解析逾時,請嘗試下列解決方案,提升 DNS 效能:
檢查叢集上執行的 kube-dns Pod 數量,並與 GKE 節點總數比較。如果資源不足,請考慮擴大 kube-dns Pod。
如要縮短平均 DNS 查詢時間,請啟用 NodeLocal DNS 快取。
解析外部名稱的 DNS 可能會造成 kube-dns Pod 負載過重。如要減少查詢次數,請調整
/etc/resolv.conf
檔案中的ndots
設定。ndots
代表網域名稱中必須出現的點數,才能在初始絕對查詢之前解析查詢。以下範例是應用程式 Pod 的
/etc/resolv.conf
檔案:search default.svc.cluster.local svc.cluster.local cluster.local c.PROJECT_ID.internal google.internal nameserver 10.52.16.10 options ndots:5
在本例中,kube-dns 會在查詢的網域中尋找五個點。如果 Pod 對
example.com
進行 DNS 解析呼叫,記錄會類似下列範例:"A IN example.com.default.svc.cluster.local." NXDOMAIN "A IN example.com.svc.cluster.local." NXDOMAIN "A IN example.com.cluster.local." NXDOMAIN "A IN example.com.google.internal." NXDOMAIN "A IN example.com.c.PROJECT_ID.internal." NXDOMAIN "A IN example.com." NOERROR
如要解決這個問題,請將 ndots 的值變更為
1
,只尋找單一點,或在查詢或使用的網域結尾附加點 (.
)。例如:dig example.com.
問題:部分節點間歇性無法進行 DNS 查詢
如果發現部分節點間歇性 DNS 查詢失敗,可能會出現下列徵兆:
- 當您對 kube-dns 服務 IP 位址或 Pod IP 位址執行 dig 指令時,DNS 查詢會間歇性失敗並逾時。
- 從與 kube-dns Pod 相同的節點上,透過 Pod 執行 dig 指令會失敗。
如要解決這個問題,請完成下列步驟:
- 執行連線測試。將有問題的 Pod 或節點設為來源,並將 kube-dns Pod 的 IP 位址設為目的地。藉此檢查是否已設定必要的防火牆規則,允許這類流量。
如果測試失敗,且流量遭到防火牆規則封鎖,請使用 Cloud Logging 列出對防火牆規則所做的任何手動變更。找出會封鎖特定類型流量的變更:
如果防火牆規則沒有任何變更,請檢查節點集區版本,確認該版本與控制層和其他工作節點集區相容。如果叢集的任何節點集區比控制層舊超過兩個子版本,可能會導致問題。如要進一步瞭解這項不相容問題,請參閱「節點版本與控制層版本不相容」。
如要判斷要求是否傳送至正確的 kube-dns 服務 IP,請擷取有問題節點上的網路流量,並篩選通訊埠 53 (DNS 流量)。擷取 kube-dns Pod 本身的流量,查看要求是否傳送至預期 Pod,以及是否已成功解析。
後續步驟
如需診斷 Kubernetes DNS 問題的一般資訊,請參閱「偵錯 DNS 解析」。
如果無法在說明文件中找到問題的解決方法,請參閱「取得支援」一文,尋求進一步的協助, 包括下列主題的建議:
- 與 Cloud 客戶服務聯絡,建立支援案件。
- 在 StackOverflow 上提問,並使用
google-kubernetes-engine
標記搜尋類似問題,向社群尋求支援。你也可以加入#kubernetes-engine
Slack 頻道,取得更多社群支援。 - 使用公開問題追蹤工具回報錯誤或提出功能要求。