1. 程式人生 > >Kubernetes服務發現與故障排除

Kubernetes服務發現與故障排除

本文為Kubernetes監控系列的第二篇文章,系列目錄如下:本文是關於在生產中使用Kubernetes系列的一部分。第一部分介紹了Kubernetes和監控工具的基礎知識;第二部分涵蓋了Kubernetes報警的最佳實踐;這部分將介紹對Kubernetes故障排除,特別是服務發現,最後一部分是監控Kubernetes的實際使用案例。像Kubernetes,DC / OS Mesos或Docker Swarm這樣的容器編排平臺可以幫助你方便地管理容器,但對解決容器問題並沒有什麼幫助:
  • 它們是孤立的,你和你要監視的程序之間存在一定的障礙,在主機上執行的傳統故障排除工具不瞭解容器、名稱空間和編排平臺。

  • 它們帶來最小的執行時間,只需要服務及其依賴項,而不需要所有的故障排除工具,想象下只用busybox來進行故障排除!

  • 它們排程在你叢集、進行容器移動,擴容或者縮容。隨著流程的結束,它們變得非常不穩定,出現並消失。

  • 並通過新的虛擬網路層互相通訊。


本文將通過一個真實用例講訴如何在Kubernetes中進行故障排除。我們將涵蓋Kubernetes內部3個不同層的故障排除:
  • 第一部分:Kubernetes故障排除之服務發現

  • 第二部分:Kubernetes故障排除之DNS解析

  • 第三部分:Kubernetes故障排除之使用Docker執行容器


本文場景將使用一個簡單的Kubernetes服務[1],包括3個Nginx pod和一個帶有curl命令的客戶端。在連結中可以看到場景需要使用的yaml檔案 backend.yaml。如果你剛剛接觸Kubernetes,可以看下“Understanding how Kubernetes DNS services work[2]”這篇文章來學習如何部署上述服務。實際操作如下:
$ kubectl create
namespace critical-app
namespace "critical-app" created
$ kubectl create -f backend.yaml
service "backend" created
deployment "backend" created

然後建立一個客戶端來載入我們的後端服務:
$ kubectl run -it --image=tutum/curl client --namespace critical-app --restart=Never
Kubernetes故障排除之服務發現在我們的client容器中可以執行一個簡單測試 [email protected]
:/# curl backend 來檢視我們的Kubernetes service是如何工作的。但我們不想遺漏下什麼,我們認為可以使用FQDN進行服務發現測試,在Kubernetes文件[3]中會發現,每個service都會獲得一個預設的DNS:my-svc.my-namespace.svc.cluster.local。因此,我們用它作為FQDN進行測試。
在client容器的shell端執行:[email protected]:/# curl backend.critical-app.svc.cluster.local,不過這次curl命令卡住了10秒,然後返回了預期網址!作為一個分散式系統工程師,這是最糟糕的事情之一:你希望直接失敗或者成功,而不是等待10秒。
為了定位出問題,我們使用了Sysdig。Sysdig是一個開源Linux視覺化工具,提供對容器的本地可見性,包括Docker、Kubernetes、DC / OS和Mesos等等。將htop,tcpdump,strace,lsof,netstat等的功能組合在一起,Sysdig提供了Kubernetes基礎架構環境中的所有系統呼叫和應用程式資料。Monitoring Kubernetes with Sysdig[4]中很好地介紹瞭如何在Kubernetes中使用這個工具。
為了分析上述問題原因,我們將請求sysdig來dump所有資訊到捕獲檔案中:
$ sudo sysdig -k http://127.0.0.1:8080 -s8192 -zw capture.scap

快速解釋下每個引數:
  • -k http://localhost:8080 連線Kubernetes API

  • -s8192 擴大IO緩衝區,因為我們需要顯示全部內容,否則預設情況下會被切斷,

  • -zw capture.scap 將系統呼叫與元資料資訊壓縮並dump到檔案中

同時,我們將再次執行curl命令來複現這個問題:# curl backend.critical-app.svc.cluster.local。這確保了我們在上面捕獲的檔案中擁有所有資料,以重現場景並解決問題。
一旦curl返回,我們可以執行Ctrl+C來終止資料捕獲,並且我們將擁有一個10s的捕獲檔案,包括我們的Kubernetes主機中發生的所有事情以及服務發現過程。現在我們可以開始在叢集內或叢集外解決問題,只要在安裝了sysdig的環境中複製檔案:
$ sysdig -r capture.scap -pk -NA "fd.type in (ipv4, ipv6) and (k8s.ns.name=critical-app or proc.name=skydns)" | less

快速解釋下每個引數:
  • -r capture.scap 讀取捕獲檔案

  • -pk 列印Kubernetes部分到stdout中

  • -NA 顯示ASCII輸出


以及雙引號中是過濾內容。Sysdig能夠理解Kubernetes語義,因此我們可以過濾來自名稱空間critical-app中的任何容器或任何名為skydns的程序的套接字IPv4或IPv6上的流量。加入proc.name = skydns因為這是內部Kubernetes DNS解析器,並且作為Kubernetes基礎結構的一部分執行在我們的名稱空間之外。

Sysdig也有一個互動式的ncurses介面。

為了跟隨此服務發現故障排除示例,你可以下載捕獲檔案capture.scap[5]並使用sysdig自行檢視它。
我們立即看到curl如何嘗試解析域名,但在DNS查詢有效載荷上我們有些奇怪(10049):backend.critical-app.svc.cluster.local.critical-app.svc.cluster.local。看上去由於某些原因,curl不識別已經給定的FQDN,並且決定追加一個搜尋域。
[...]
10030 16:41:39.536689965 0 client (b3a718d8b339) curl (22370:13) < socket fd=3(<4>)
10031 16:41:39.536694724 0 client (b3a718d8b339) curl (22370:13) > connect fd=3(<4>)
10032 16:41:39.536703160 0 client (b3a718d8b339) curl (22370:13) < connect res=0 tuple=172.17.0.7:46162->10.0.2.15:53
10048 16:41:39.536831645 1  (36ae6d09d26e) skydns (17280:11) > recvmsg fd=6(<3t>:::53)
10049 16:41:39.536834352 1  (36ae6d09d26e) skydns (17280:11) < recvmsg res=87 size=87 data=
backendcritical-appsvcclusterlocalcritical-appsvcclusterlocal tuple=::ffff:172.17.0.7:46162->:::53
10050 16:41:39.536837173 1  (36ae6d09d26e) skydns (17280:11) > recvmsg fd=6(<3t>:::53)
[...]

SkyDNS傳送請求(10097)到etcd的API(/local/cluster/svc/critical-app/local/cluster/svc/critical-app/backend),顯然etcd不能識別這個service,然後返回(10167)“Key not found”。這通過DNS查詢響應傳回給curl。
[...]
10096 16:41:39.538247116 1  (36ae6d09d26e) skydns (4639:8) > write fd=3(<4t>10.0.2.15:34108->10.0.2.15:4001) size=221
10097 16:41:39.538275108 1  (36ae6d09d26e) skydns (4639:8) < write res=221 data=
GET /v2/keys/skydns/local/cluster/svc/critical-app/local/cluster/svc/critical-app/backend?quorum=false&recursive=true&sorted=false HTTP/1.1
Host: 10.0.2.15:4001
User-Agent: Go 1.1 package http
Accept-Encoding: gzip
10166 16:41:39.538636659 1  (36ae6d09d26e) skydns (4617:1) > read fd=3(<4t>10.0.2.15:34108->10.0.2.15:4001) size=4096
10167 16:41:39.538638040 1  (36ae6d09d26e) skydns (4617:1) < read res=285 data=
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Etcd-Cluster-Id: 7e27652122e8b2ae
X-Etcd-Index: 1259
Date: Thu, 08 Dec 2016 15:41:39 GMT
Content-Length: 112
{"errorCode":100,"message":"Key not found","cause":"/skydns/local/cluster/svc/critical-app/local","index":1259}
[...]

curl沒有放棄並再次嘗試(10242),不過這次用的是backend.critical-app.svc.cluster.local.svc.cluster.local。看起來這次curl嘗試將critical-app這個追加搜尋域去除,使用一個不同的搜尋域。顯然,當請求到達etcd(10247)時,再次失敗(10345)。
[...]
10218 16:41:39.538914765 0 client (b3a718d8b339) curl (22370:13) < connect res=0 tuple=172.17.0.7:35547->10.0.2.15:53
10242 16:41:39.539005618 1  (36ae6d09d26e) skydns (17280:11) < recvmsg res=74 size=74 data=
backendcritical-appsvcclusterlocalsvcclusterlocal tuple=::ffff:172.17.0.7:35547->
:::53
10247 16:41:39.539018226 1  (36ae6d09d26e) skydns (17280:11) > recvmsg fd=6(<3t>:::53)
10248 16:41:39.539019925 1  (36ae6d09d26e) skydns (17280:11) < recvmsg res=74 size=74 data=
0]backendcritical-appsvcclusterlocalsvcclusterlocal tuple=::ffff:172.17.0.7:35547->
:::53
10249 16:41:39.539022522 1  (36ae6d09d26e) skydns (17280:11) > recvmsg fd=6(<3t>:::53)
10273 16:41:39.539210393 1  (36ae6d09d26e) skydns (4639:8) > write fd=3(<4t>10.0.2.15:34108->10.0.2.15:4001) size=208
10274 16:41:39.539239613 1  (36ae6d09d26e) skydns (4639:8) < write res=208 data=
GET /v2/keys/skydns/local/cluster/svc/local/cluster/svc/critical-app/backend?quorum=false&recursive=true&sorted=false HTTP/1.1
Host: 10.0.2.15:4001
User-Agent: Go 1.1 package http
Accept-Encoding: gzip
10343 16:41:39.539465153 1  (36ae6d09d26e) skydns (4617:1) >
read fd=3(<4t>10.0.2.15:34108->10.0.2.15:4001) size=4096
10345 16:41:39.539467440 1  (36ae6d09d26e) skydns (4617:1) < read res=271 data=
HTTP/1.1 404 Not Found
[...]

curl會再次嘗試,我們可以看這次的DNS查詢請求(10418),加上了cluster.local變成backend.critical-app.svc.cluster.local.cluster.local。這次etcd請求(10479)同樣失敗(10524)。
[...]
10396 16:41:39.539686075 0 client (b3a718d8b339) curl (22370:13) < connect res=0 tuple=172.17.0.7:40788->10.0.2.15:53
10418 16:41:39.539755453 0  (36ae6d09d26e) skydns (17280:11) < recvmsg res=70 size=70 data=
backendcritical-appsvcclusterlocalclusterlocal tuple=::ffff:172.17.0.7:40788->
:::53
10433 16:41:39.539800679 0  (36ae6d09d26e) skydns (17280:11) > recvmsg fd=6(<3t>:::53)
10434 16:41:39.539802549 0  (36ae6d09d26e) skydns (17280:11) < recvmsg res=70 size=70 data=
backendcritical-appsvcclusterlocalclusterlocal tuple=::ffff:172.17.0.7:40788->
:::53
10437 16:41:39.539805177 0  (36ae6d09d26e) skydns (17280:11) > recvmsg fd=6(<3t>:::53)
10478 16:41:39.540166087 1  (36ae6d09d26e) skydns (4639:8) > write fd=3(<4t>10.0.2.15:34108->10.0.2.15:4001) size=204
10479 16:41:39.540183401 1  (36ae6d09d26e) skydns (4639:8) < write res=204 data=
GET /v2/keys/skydns/local/cluster/local/cluster/svc/critical-app/backend?quorum=false&recursive=true&sorted=false HTTP/1.1
Host: 10.0.2.15:4001
User-Agent: Go 1.1 package http
Accept-Encoding: gzip
10523 16:41:39.540421040 1  (36ae6d09d26e) skydns (4617:1) >
read fd=3(<4t>10.0.2.15:34108->10.0.2.15:4001) size=4096
10524 16:41:39.540422241 1  (36ae6d09d26e) skydns (4617:1) < read res=267 data=
HTTP/1.1 404 Not Found
[...]

對於未經訓練的人來說,可能看起來我們發現了這個問題:一堆無效的請求。但實際上這不是事實,如果我們檢視時間戳,第一個etcd請求(10097)和最後一個(10479)之間的差異,第二列中的時間戳相距小於10ms。而我們正在尋找一個秒數問題,而不是毫秒——那麼等待的時間在哪裡?
當我們繼續瀏覽捕獲檔案時,我們可以看到curl不停止嘗試使用DNS查詢到SkyDNS,現在使用backend.critical-app.svc.cluster.local.localdomain(10703)。這個.localdomain不被SkyDNS識別為Kubernetes的內部域,因此它決定將這個查詢轉發給它的上游DNS解析器(10691),而不是去etcd進行服務發現。
[...]
10690 16:41:39.541376928 1  (36ae6d09d26e) skydns (4639:8) > connect fd=8(<4>)
10691 16:41:39.541381577 1  (36ae6d09d26e) skydns (4639:8) < connect res=0 tuple=10.0.2.15:44249->8.8.8.8:53
10702 16:41:39.541415384 1  (36ae6d09d26e) skydns (4639:8) > write fd=8(<4u>10.0.2.15:44249->8.8.8.8:53) size=68
10703 16:41:39.541531434 1  (36ae6d09d26e) skydns (4639:8) < write res=68 data=
Nbackendcritical-appsvcclusterlocallocaldomain
10717 16:41:39.541629507 1  (36ae6d09d26e) skydns (4639:8) >
read fd=8(<4u>10.0.2.15:44249->8.8.8.8:53) size=512
10718 16:41:39.541632726 1  (36ae6d09d26e) skydns (4639:8) < read res=-11(EAGAIN) data=
58215 16:41:43.541261462 1  (36ae6d09d26e) skydns (4640:9) >
close fd=7(<4u>10.0.2.15:54272->8.8.8.8:53)
58216 16:41:43.541263355 1  (36ae6d09d26e) skydns (4640:9) < close res=0
[...]

掃描時間戳列時,我們發現SkyDNS發出請求後第一個較大的間隙,然後等待大約4秒(10718-58215)。鑑於.localdomain不是有效的TLD(頂級域名),上游伺服器將只是忽略此請求。超時後,SkyDNS再次嘗試使用相同的查詢(75923),再等待幾秒鐘(75927-104208)。總的來說,我們一直在等待大約8秒鐘,以查詢不存在並且被忽略的DNS條目。
[...]
58292 16:41:43.542822050 1  (36ae6d09d26e) skydns (4640:9) < write res=68 data=
Nbackendcritical-appsvcclusterlocallocaldomain
58293 16:41:43.542829001 1  (36ae6d09d26e) skydns (4640:9