Telepresence 是一個(gè)開(kāi)源工具,可讓您在本地運(yùn)行單個(gè)服務(wù),同時(shí)將該服務(wù)連接到遠(yuǎn)程 Kubernetes 集群。
為什么需要 Telepresence
k8s pod IP 由 CNI 分配, 通信是走 overlay 網(wǎng)絡(luò), 容器之間的通信都是基于 cluser IP.
cluser IP 并不像我們平常所用的 IP. 它只能在 k8s 集群內(nèi)部使用.
雖然我們可以通過(guò)配置 overlay 網(wǎng)絡(luò)的網(wǎng)段 跟 k8s node 的網(wǎng)段在一個(gè)大的子網(wǎng)段, 然后通過(guò) vpn 把對(duì)應(yīng)網(wǎng)段的流量路由到 overlay 網(wǎng)絡(luò), 我們完全可以通過(guò) kubectl get po -o wide 獲取到 pod IP, 然后訪問(wèn)服務(wù).
也就是說(shuō), 如果我們?cè)诒緳C(jī)想運(yùn)行一個(gè)服務(wù), 不依賴 Telepresence 這種工具是完全可行的.
但是, 其實(shí)我們每個(gè)服務(wù)都有配置, 配置里面寫(xiě)的 grpc 服務(wù)地址可能像這樣: xxxx.svc.cluster.local:1234, 如果我們想要服務(wù)的配置不經(jīng)過(guò)任何修改, 直接在本機(jī)運(yùn)行.
有沒(méi)有辦法呢? 答案當(dāng)然是有的, 設(shè)置 hosts 啊. kubectl get po -o wide 獲取到 pod IP 和 pod name, (假設(shè) pod name 和 service name 命名是一致的,即從 pod name 我們可以字面上得到 service name) 然后拼接成 k8s 里面的 DNS name xxxx.svc.cluster.local, 將這個(gè)域名映射到 pod IP 即可. 假設(shè)我們寫(xiě)了這樣一個(gè)腳本, 但是當(dāng) pod 被調(diào)度到不同的 node, 或者 pod 重建之后, 其 pod IP 必然會(huì)改變, 這個(gè)時(shí)候我們又要手動(dòng)去重新生成這個(gè) hosts 文件. 總體操作來(lái)說(shuō), 還是挺麻煩的.
另一個(gè)問(wèn)題是, 團(tuán)隊(duì)內(nèi)部有很多人, 要讓所有人都學(xué)會(huì)這一招操作, 可能會(huì)有些困難, 或者說(shuō), 這種方式, 對(duì)用戶不太好友.
這個(gè)時(shí)候, Telepresence 橫空出世.
在 k8s 官方文檔中, “ 本地開(kāi)發(fā)和調(diào)試服務(wù)[1]” 一節(jié), Telepresence 是唯一介紹的工具.
對(duì)于用戶來(lái)說(shuō), Telepresence 提供了 3 個(gè)非常重要的功能:
cluster 域名解析
cluster 流量代理
cluster 流量攔截
域名解析 可以使我們?cè)诒镜亻_(kāi)發(fā)機(jī)器直接解析如 xxxx.svc.cluster.local 這種域名.
(注: mac 下面機(jī)制不太一樣, 不可以使用 dig 測(cè)試,但是可以用 curl)
你可以像查詢其它域名一樣使用命令行 dig 工具進(jìn)行查詢, 如 dig kubernetes.default
光有域名解析還不夠, 我們還是沒(méi)法連接集群內(nèi)的其它服務(wù), 因此 流量代理 功能的作用就體驗(yàn)出來(lái)了.
在 Linux 下面, Telepresence 會(huì)建立一個(gè)名叫 tel0 的 tun 設(shè)備. 然后通過(guò) systemd-resolved 服務(wù)將集群命令空間的 cluster domain 添加到這個(gè)設(shè)備. 通過(guò)resolvectl status tel0可以查看到當(dāng)前有哪些命令空間被添加進(jìn)來(lái)了:
resolvectlstatustel0 Link66(tel0) CurrentScopes:DNSLLMNR/IPv4LLMNR/IPv6 Protocols:-DefaultRoute+LLMNR-mDNS-DNSOverTLSDNSSEC=no/unsupported CurrentDNSServer:10.0.165.145 DNSServers:10.0.165.145 DNSDomain:~ambassador~argocd~cluster.local~db~default~devops~istio-system~jaeger~kube-public~kube-system~nacos~observability
流量攔截 可以將集群里指定服務(wù)的流量攔截并轉(zhuǎn)發(fā)到本地開(kāi)發(fā)機(jī)器, 比如調(diào)試復(fù)雜的 app 業(yè)務(wù)接口時(shí),非常方便.
這個(gè)非常有用, 但是老燈平常一般都不用這個(gè). 因?yàn)槲覀兊姆?wù)都有注入 istio side car. 而 Telepresence 的攔截原理其實(shí)也跟 istio 類似, 也是利用 side car 注入然后代理流量. 但是同一個(gè) pod 的流量, 不能同時(shí)被 istio 接管, 然后又被 Telepresence 接管. 這一點(diǎn)我后面再詳細(xì)說(shuō)怎么解決.
日常使用
telepresence connect 連接
telepresence status 查看連接狀態(tài)
curl -ik https://kubernetes.default 測(cè)試連接成功與否( 有任意響應(yīng)就是成功)
telepresence quit -u -r 斷開(kāi)連接并退出 user 和 root daemon
DNS 解析原理
這部分主要參考:
https://www.telepresence.io/docs/latest/reference/routing/
Linux systemd-resolved resolver
以 Linux 上面的實(shí)現(xiàn)為例, 簡(jiǎn)單來(lái)說(shuō), Telepresence 就是新建一 tun 設(shè)備, 這個(gè)設(shè)備的流量會(huì)代理到 k8s 里的 Telepresence traffic manager ( 負(fù)責(zé)流量轉(zhuǎn)發(fā)等). tun 設(shè)備的 DNS 被設(shè)置成了 k8s 集群的 DNS (一般是 coredns 啦). Telepresence 會(huì) watch 整個(gè)集群的所有命名空間, 因此, 當(dāng)發(fā)生變量的時(shí)候, DNS link domain 會(huì)被自動(dòng)更新.
然后, 設(shè)置哪此命令空間后綴要通過(guò)這個(gè) DNS 查詢 是通過(guò) systemd-resolved 的服務(wù)的 dbus 接口[2] SetLinkDomains 操作的. 設(shè)置 DNS 是通過(guò) SetLinkDNS
這種操作, 其實(shí)就相當(dāng)于在 /etc/systemd/resolved.conf.d 下面新建一 k8s.conf 文件, 內(nèi)容類似于:
#https://learn.hashicorp.com/tutorials/consul/dns-forwarding#systemd-resolved-setup [Resolve] DNS=10.0.165.145:53 DNSSEC=false Domains=~ambassador~argocd~cluster.local~db~default~devops~istio-system~jaeger~kube-public~kube-system~nacos~observability
只不過(guò), 通過(guò) dbus 可以動(dòng)態(tài)的修改, 更加方便.
Linux overriding resolver
Linux systems that aren’t configured withsystemd-resolvedwill use this resolver. A Typical case is when running Telepresence inside a docker container[3]. During initialization, the resolver will first establish afallbackconnection to the IP passed as--dns, the one configured aslocal-ipin the local DNS configuration[4], or the primarynameserverregistered in/etc/resolv.conf. It will then use iptables to actually override that IP so that requests to it instead end up in the overriding resolver, which unless it succeeds on its own, will use thefallback.
即, 對(duì)于不支持 systemd-resolved 的 Linux 系統(tǒng)(Centos7 默認(rèn)就沒(méi)安裝), Telepresence 會(huì)自己起一個(gè) DNS 代理服務(wù), 一般是監(jiān)聽(tīng)隨機(jī)端口, 然后再將系統(tǒng) DNS 設(shè)置成 TelepresenceDNS 代理服務(wù)的地址. 即解析的時(shí)候會(huì)先查集群, 沒(méi)有結(jié)果會(huì) fallback 到本機(jī)原來(lái)的 DNS, 比如 Google public DNS 等解析其它域名.這個(gè)會(huì)影響其它應(yīng)用的使用, 這種實(shí)現(xiàn)方式不太好, 以老燈的使用經(jīng)驗(yàn)來(lái)看, 這種方式也不太穩(wěn)定. 容易造成問(wèn)題.
進(jìn)擊云原生 注釋:
通過(guò) iptables DNAT 到 Telepresence 代理服務(wù)
macOS resolver
在 macOS 下面是通過(guò) resolver hooks 實(shí)現(xiàn)的.
This resolver hooks into the macOS DNS system by creating files under/etc/resolver. Those files correspond to some domain and contain the port number of the Telepresence resolver. Telepresence creates one such file for each of the currently mapped namespaces andinclude-suffixesoption. The filetelepresence.localcontains a search path that is configured based on current intercepts so that single label names can be resolved correctly.
這個(gè)解析器通過(guò)在' /etc/resolver '下創(chuàng)建文件來(lái)鉤子到 macOS DNS 系統(tǒng)。這些文件對(duì)應(yīng)某個(gè)域,包含 Telepresence 解析器的端口號(hào)。Telepresence 會(huì)為每個(gè)當(dāng)前映射的名稱空間和“include-suffix”選項(xiàng)創(chuàng)建一個(gè)這樣的文件。文件telepresence.local包含基于當(dāng)前攔截配置的搜索路徑,以便能夠正確解析單個(gè)標(biāo)簽名稱。
troubleshooting
1. 流量攔截不生效
測(cè)試過(guò)程中發(fā)現(xiàn) 流量攔截 與 注入的 istio-proxy 容器存在沖突,即當(dāng) istio-proxy 存在時(shí),流量全部被 istio-proxy 接管了,traffic-agent 沒(méi)有成功攔截到流量。
目前我暫時(shí)的一個(gè) hack 方法是取消 istio-proxy sidecar 注入:
diff--gita/develop/overlays/my_app/deployment.yamlb/develop/overlays/my_app/deployment.yaml index1049d335..26ee38d4100644 ---a/develop/overlays/my_app/deployment.yaml +++b/develop/overlays/my_app/deployment.yaml @@-4,6+4,9@@metadata: name:ttys3 spec: template: +metadata: +annotations: +sidecar.istio.io/inject:"false" spec: containers: -name:my-app
traffic-agent 日志查看:stern --tail 100 ttys3-my-app -c traffic-agent
#公眾號(hào):進(jìn)擊云原生 注釋:
stern是?Kubernetes的多 Pod 和容器日志跟蹤命令行工具
如果是采用 argo cd rollout:
diff--gita/develop/overlays/my-app/rollout.yamlb/develop/overlays/my-app/rollout.yaml index263eab87c..bbc44c378100644 ---a/develop/overlays/my-app/rollout.yaml +++b/develop/overlays/my-app/rollout.yaml @@-6,6+6,9@@metadata: spec: template: +metadata: +annotations: +sidecar.istio.io/inject:"false" spec: securityContext: runAsUser:1000
2. 連接不上
使用新版本的 telepresence v2.x.x 如果“重復(fù)” 出現(xiàn) (偶爾一次可能是意外)以下錯(cuò)誤:
?telepresence: error: the port-forward connection to the traffic manager timed out. The current timeout 20s can be configured as timeouts.trafficManagerConnect
? ? 或 ?
?telepresence: error: the traffic manager gRPC API timed out. The current timeout 20s can be configured as timeouts.trafficManagerAPI in /Users/tomeee/Library/Application Support/telepresence/config.yml ?
? 類似錯(cuò)誤, ? 說(shuō)明同一集群里有多個(gè)人使用不同版本 v2 的客戶端互相在打架。當(dāng) telepresence 連接的時(shí)候, 如果與當(dāng)前版本匹配的 traffic manager 不存在, 則會(huì)自動(dòng)安裝與當(dāng)前版本匹配的 traffic manager. 當(dāng)不同的人, 從不同的地方, 下載了不同的版本, 都在連接同一個(gè)集群的時(shí)候, 問(wèn)題就發(fā)生了.
解決方案:同一集群所有人統(tǒng)一使用相同版本的客戶端 (版本號(hào)要完全相同,對(duì)于 nightly 版, 小版本號(hào)和 commit hash 都要相同) sys op 對(duì)整個(gè)集群的 rbac 做更加安全地配置, 禁止除 devops 組之外的其它開(kāi)發(fā)人員擁有可以 ambassador 命名空間下資源的更新權(quán)限, 這樣就可以阻止開(kāi)發(fā)人員在使用 telepresence 連接的時(shí)候無(wú)意中錯(cuò)誤地在不停地安裝各種版本的 traffic manager. 但是為了保證開(kāi)發(fā)人員可以正常使用, list resource “namespaces“權(quán)限一定要給, 然后就是 create resource “pods/portforward” in API group "” in the namespace “ambassador” 的權(quán)限. ? Client / Root Daemon / User Daemon 3 個(gè)版本號(hào)一定要完全一致:
?telepresenceversion Client:v2.5.4(apiv3) RootDaemon:v2.5.4(apiv3) UserDaemon:v2.5.4(apiv3)
參考官方 issue:
https://github.com/telepresenceio/telepresence/issues/1652 > https://github.com/telepresenceio/telepresence/issues/1689
3. 如何徹底卸載
一般情況下可以直接 telepresence uninstall --everything 搞定.
如果需要手動(dòng)卸載可以這樣操作:
kdeletedeploy-nambassadortraffic-manager kdeletesecretssh.helm.release.v1.traffic-manager.v1-nambassador
注意它并不會(huì)真正檢查 pod 是否存在, 如果檢查到 sh.helm.release.v1.traffic-manager.v1 這個(gè) secrets 就會(huì)直接跳過(guò)安裝了. 所以你要是刪除了 traffic manger 的 deployment, 但是忘記刪除了這個(gè) secrets, 會(huì)導(dǎo)致下次連接的時(shí)候, traffic manger 不會(huì)被安裝.
4. 調(diào)試問(wèn)題
k8s 端問(wèn)題查看:
先檢查 pod 是不是正常:
k get po -n ambassador
k get deploy -n ambassador
查看 pod 日志:
k logs -n ambassador -f traffic-manager
5. 編譯和構(gòu)建容器鏡像
traffic manager 的版本一定要匹配客戶端的版本.
對(duì)于 nightly 版本, 其形式如 v2.5.4-2-g8ccf3c9d
對(duì)于正式版, 其形式如 v2.5.3
不同版本不能連接, 會(huì)提示錯(cuò)誤. 即使是客戶端, 不同版本的 daemon 也是不兼容的, 如:
version mismatch. Client v2.5.3 != User Daemon v2.5.4-2-g8ccf3c9d, please run ’telepresence quit -u’ and reconnect
計(jì)算當(dāng)前 nightly 版本號(hào): git describe --tags --match='v*'
build 的時(shí)候,必須通過(guò) env 指定 TELEPRESENCE_VERSION, 不然 Makefile 會(huì)自動(dòng)運(yùn)行 go run build/genversion.go 生成一個(gè)帶 unix timestamp 的版本號(hào),這樣客戶端和 agent docker image 的版本號(hào)便沒(méi)辦法對(duì)應(yīng)上了。
同時(shí)還要指定 TELEPRESENCE_REGISTRY , 這個(gè)主要是在構(gòu)建時(shí)打 docker tag 用的,主要用于 docker push, 真正程序運(yùn)行的時(shí)候,取的還是 env:"TELEPRESENCE_REGISTRY,default=docker.io/datawire" 因此,如果要防止客戶端安裝官方鏡像, 這一行硬編碼的代碼必須修改.
#構(gòu)建本地bin和容器鏡像并push: makebuildimagepush-imageTELEPRESENCE_VERSION=v2.5.4TELEPRESENCE_REGISTRY=docker.io/ttys3 #只構(gòu)建本地bin makebuildTELEPRESENCE_VERSION=v2.5.4TELEPRESENCE_REGISTRY=docker.io/ttys3
6. 突然無(wú)法訪問(wèn)的 Rust 文檔站點(diǎn) docs.rs
連接 telepresence 后, 發(fā)現(xiàn) https://docs.rs/ 怎么也打不開(kāi)了. dig docs.rs 發(fā)現(xiàn)超時(shí), 并沒(méi)有解析成功.
然后我想到了原因, 有人在集群里創(chuàng)建了一個(gè)名為 rs 的命名空間, 連接 telepresence 后, 導(dǎo)致所有 .rs 域名都無(wú)法解析了(除了 k8s 集群里面的 ).
經(jīng)過(guò)查看源碼, 老燈發(fā)現(xiàn)可以通過(guò) patch updateLinkDomains 這個(gè)方法搞定. 思路就是, 如果一個(gè)命名空間, 是 ICANN 管理的通用 TLD 后綴, 則直接 skip 它, 不設(shè)置.
直接修改 telepresence 源碼然后自己編譯客戶端即可:
diff--gita/pkg/client/rootd/dns/resolved_linux.gob/pkg/client/rootd/dns/resolved_linux.go indexb2e8897bbeb5405170359f5318a7ae40dfc6e949..d90c2735ef9d4d421738fea533f7bfb244172b61100644 ---a/pkg/client/rootd/dns/resolved_linux.go +++b/pkg/client/rootd/dns/resolved_linux.go @@-13,6+13,7@@import( "github.com/datawire/dlib/dtime" "github.com/telepresenceio/telepresence/v2/pkg/client/rootd/dbus" "github.com/telepresenceio/telepresence/v2/pkg/vif" +"golang.org/x/net/publicsuffix" ) func(s*Server)tryResolveD(ccontext.Context,dev*vif.Device,configureDNSfunc(net.IP,*net.UDPAddr))error{ @@-102,28+103,36@@func(s*Server)tryResolveD(ccontext.Context,dev*vif.Device,configureDNSfu func(s*Server)updateLinkDomains(ccontext.Context,paths[]string,dev*vif.Device)error{ namespaces:=make(map[string]struct{}) search:=make([]string,0) -fori,path:=rangepaths{ +pathsFiltered:=make([]string,0) +for_,path:=rangepaths{ ifstrings.ContainsRune(path,'.'){ search=append(search,path) }else{ +//skipnamespacewhichconflictwitheTLDlike`im`,`rs`toavoidpolluteclientnormalDNSquery +ifeTLD,icann:=publicsuffix.PublicSuffix(path);icann&&path==eTLD{ +dlog.Infof(c,"SkipsetLinkdomainsondevice%qfor[%s]duetoconflictwithICANNeTLD[%s]", +dev.Name(),path,eTLD) +continue +} namespaces[path]=struct{}{} //Turnnamespaceintoaroute -paths[i]="~"+path +//paths[i]="~"+path +pathsFiltered=append(pathsFiltered,"~"+path) } } for_,sfx:=ranges.config.IncludeSuffixes{ -paths=append(paths,"~"+strings.TrimPrefix(sfx,".")) +pathsFiltered=append(pathsFiltered,"~"+strings.TrimPrefix(sfx,".")) } -paths=append(paths,"~"+s.clusterDomain) +pathsFiltered=append(pathsFiltered,"~"+s.clusterDomain) namespaces[tel2SubDomain]=struct{}{} s.domainsLock.Lock() s.namespaces=namespaces s.search=search s.domainsLock.Unlock() -iferr:=dbus.SetLinkDomains(dcontext.HardContext(c),int(dev.Index()),paths...);err!=nil{ +iferr:=dbus.SetLinkDomains(dcontext.HardContext(c),int(dev.Index()),pathsFiltered...);err!=nil{ returnfmt.Errorf("failedtosetlinkdomainson%q:%w",dev.Name(),err) } -dlog.Debugf(c,"Linkdomainsondevice%qsetto[%s]",dev.Name(),strings.Join(paths,",")) +dlog.Debugf(c,"Linkdomainsondevice%qsetto[%s]",dev.Name(),strings.Join(pathsFiltered,",")) returnnil }
注意: telepresence 的 exclude-suffixes 選項(xiàng)并不能解決我們這里的問(wèn)題.
https://www.telepresence.io/docs/latest/reference/config/#dns
其默認(rèn)值為 [".arpa", ".com", ".io", ".net", ".org", ".ru"]
exclude-suffixes Suffixes for which the DNS resolver will always fail (or fallback in case of the overriding resolver)
由于我們用的都是基于 systemd-resolved 的 resover, 因此, 如果把 `.rs` 加入這個(gè)列表, 則會(huì)導(dǎo)致 `.rs` 域名總是解析失敗(NXDomain)
所以其實(shí)這個(gè)默認(rèn)列表也是有問(wèn)題的, 為什么默認(rèn)有 `.com`, 而 `google.com` 不會(huì)解析失敗呢? 那是因?yàn)槲覀儧](méi)有名為 `com` 的命名空間. 所以, 假設(shè)你有名為 `com` 的命名空間, 就要小心了.
7. DNS 解析特別慢或超時(shí)
我們同時(shí)在開(kāi)發(fā)和測(cè)試環(huán)境部署了 telepresence (traffic manager).
使用中發(fā)現(xiàn), 測(cè)試環(huán)境絲滑無(wú)比, 開(kāi)發(fā)環(huán)境總是 DNS 解析超時(shí).
通過(guò)查看 log 可發(fā)現(xiàn)類似日志:
LookupHost: lookup kubernetes.default on 10.0.165.145 dial udp 10.0.165.145 i/o timeout
暫時(shí)沒(méi)排查出什么原因, 大概率是 coredns 出問(wèn)題了.
干了 coredns pod 之后, 等 coredns pod 重新 ruuning 了, 再把 traffic manager 的 pod 干了,
重新測(cè)試, 發(fā)現(xiàn)正常了.
8. 某些網(wǎng)段或 IP 的流量不想走 tel0 tun 出去
這個(gè)也是通過(guò) k8s 擴(kuò)展配置實(shí)現(xiàn)的, 之所以放在這里, 我想 telepresence 是考慮到對(duì)于不同人集群, 可以方便支持不同的配置.
修改 ~/.kube/config 文件, 增加配置到 never-proxy 數(shù)組即可, 如要將 10.10.8.9 單個(gè) IP 排除:
-cluster: certificate-authority-data:xxxxxx server:https://example.com extensions: -name:telepresence.io extension: never-proxy: -10.10.8.9/32 name:cluster-001
k8s 集群 api server 的 IP telepresence 是會(huì)默認(rèn)排除的(通過(guò) telepresence status 你可以看到這一點(diǎn)), 除此之外 , 如果你有些外部服務(wù), 因?yàn)榫W(wǎng)段跟 k8s cluster IP 段重合, 導(dǎo)致流量錯(cuò)誤地走了 tel0 tun 設(shè)備, 就可以通過(guò)這個(gè)配置來(lái)修正.
-
容器
+關(guān)注
關(guān)注
0文章
495瀏覽量
22060 -
調(diào)試服務(wù)
+關(guān)注
關(guān)注
0文章
2瀏覽量
6142 -
kubernetes
+關(guān)注
關(guān)注
0文章
224瀏覽量
8709
原文標(biāo)題:K8S 運(yùn)維開(kāi)發(fā)調(diào)試神器 Telepresence 實(shí)踐及踩坑記
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論