Telepresence 是一個開源工具,可讓您在本地運行單個服務,同時將該服務連接到遠程 Kubernetes 集群。
為什么需要 Telepresence
k8s pod IP 由 CNI 分配, 通信是走 overlay 網絡, 容器之間的通信都是基于 cluser IP.
cluser IP 并不像我們平常所用的 IP. 它只能在 k8s 集群內部使用.
雖然我們可以通過配置 overlay 網絡的網段 跟 k8s node 的網段在一個大的子網段, 然后通過 vpn 把對應網段的流量路由到 overlay 網絡, 我們完全可以通過 kubectl get po -o wide 獲取到 pod IP, 然后訪問服務.
也就是說, 如果我們在本機想運行一個服務, 不依賴 Telepresence 這種工具是完全可行的.
但是, 其實我們每個服務都有配置, 配置里面寫的 grpc 服務地址可能像這樣: xxxx.svc.cluster.local:1234, 如果我們想要服務的配置不經過任何修改, 直接在本機運行.
有沒有辦法呢? 答案當然是有的, 設置 hosts 啊. kubectl get po -o wide 獲取到 pod IP 和 pod name, (假設 pod name 和 service name 命名是一致的,即從 pod name 我們可以字面上得到 service name) 然后拼接成 k8s 里面的 DNS name xxxx.svc.cluster.local, 將這個域名映射到 pod IP 即可. 假設我們寫了這樣一個腳本, 但是當 pod 被調度到不同的 node, 或者 pod 重建之后, 其 pod IP 必然會改變, 這個時候我們又要手動去重新生成這個 hosts 文件. 總體操作來說, 還是挺麻煩的.
另一個問題是, 團隊內部有很多人, 要讓所有人都學會這一招操作, 可能會有些困難, 或者說, 這種方式, 對用戶不太好友.
這個時候, Telepresence 橫空出世.
在 k8s 官方文檔中, “ 本地開發和調試服務[1]” 一節, Telepresence 是唯一介紹的工具.
對于用戶來說, Telepresence 提供了 3 個非常重要的功能:
cluster 域名解析
cluster 流量代理
cluster 流量攔截
域名解析 可以使我們在本地開發機器直接解析如 xxxx.svc.cluster.local 這種域名.
(注: mac 下面機制不太一樣, 不可以使用 dig 測試,但是可以用 curl)
你可以像查詢其它域名一樣使用命令行 dig 工具進行查詢, 如 dig kubernetes.default
光有域名解析還不夠, 我們還是沒法連接集群內的其它服務, 因此 流量代理 功能的作用就體驗出來了.
在 Linux 下面, Telepresence 會建立一個名叫 tel0 的 tun 設備. 然后通過 systemd-resolved 服務將集群命令空間的 cluster domain 添加到這個設備. 通過resolvectl status tel0可以查看到當前有哪些命令空間被添加進來了:
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
流量攔截 可以將集群里指定服務的流量攔截并轉發到本地開發機器, 比如調試復雜的 app 業務接口時,非常方便.
這個非常有用, 但是老燈平常一般都不用這個. 因為我們的服務都有注入 istio side car. 而 Telepresence 的攔截原理其實也跟 istio 類似, 也是利用 side car 注入然后代理流量. 但是同一個 pod 的流量, 不能同時被 istio 接管, 然后又被 Telepresence 接管. 這一點我后面再詳細說怎么解決.
日常使用
telepresence connect 連接
telepresence status 查看連接狀態
curl -ik https://kubernetes.default 測試連接成功與否( 有任意響應就是成功)
telepresence quit -u -r 斷開連接并退出 user 和 root daemon
DNS 解析原理
這部分主要參考:
https://www.telepresence.io/docs/latest/reference/routing/
Linux systemd-resolved resolver
以 Linux 上面的實現為例, 簡單來說, Telepresence 就是新建一 tun 設備, 這個設備的流量會代理到 k8s 里的 Telepresence traffic manager ( 負責流量轉發等). tun 設備的 DNS 被設置成了 k8s 集群的 DNS (一般是 coredns 啦). Telepresence 會 watch 整個集群的所有命名空間, 因此, 當發生變量的時候, DNS link domain 會被自動更新.
然后, 設置哪此命令空間后綴要通過這個 DNS 查詢 是通過 systemd-resolved 的服務的 dbus 接口[2] SetLinkDomains 操作的. 設置 DNS 是通過 SetLinkDNS
這種操作, 其實就相當于在 /etc/systemd/resolved.conf.d 下面新建一 k8s.conf 文件, 內容類似于:
#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
只不過, 通過 dbus 可以動態的修改, 更加方便.
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.
即, 對于不支持 systemd-resolved 的 Linux 系統(Centos7 默認就沒安裝), Telepresence 會自己起一個 DNS 代理服務, 一般是監聽隨機端口, 然后再將系統 DNS 設置成 TelepresenceDNS 代理服務的地址. 即解析的時候會先查集群, 沒有結果會 fallback 到本機原來的 DNS, 比如 Google public DNS 等解析其它域名.這個會影響其它應用的使用, 這種實現方式不太好, 以老燈的使用經驗來看, 這種方式也不太穩定. 容易造成問題.
進擊云原生 注釋:
通過 iptables DNAT 到 Telepresence 代理服務
macOS resolver
在 macOS 下面是通過 resolver hooks 實現的.
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.
這個解析器通過在' /etc/resolver '下創建文件來鉤子到 macOS DNS 系統。這些文件對應某個域,包含 Telepresence 解析器的端口號。Telepresence 會為每個當前映射的名稱空間和“include-suffix”選項創建一個這樣的文件。文件telepresence.local包含基于當前攔截配置的搜索路徑,以便能夠正確解析單個標簽名稱。
troubleshooting
1. 流量攔截不生效
測試過程中發現 流量攔截 與 注入的 istio-proxy 容器存在沖突,即當 istio-proxy 存在時,流量全部被 istio-proxy 接管了,traffic-agent 沒有成功攔截到流量。
目前我暫時的一個 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
#公眾號:進擊云原生 注釋:
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 如果“重復” 出現 (偶爾一次可能是意外)以下錯誤:
?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 ?
? 類似錯誤, ? 說明同一集群里有多個人使用不同版本 v2 的客戶端互相在打架。當 telepresence 連接的時候, 如果與當前版本匹配的 traffic manager 不存在, 則會自動安裝與當前版本匹配的 traffic manager. 當不同的人, 從不同的地方, 下載了不同的版本, 都在連接同一個集群的時候, 問題就發生了.
解決方案:同一集群所有人統一使用相同版本的客戶端 (版本號要完全相同,對于 nightly 版, 小版本號和 commit hash 都要相同) sys op 對整個集群的 rbac 做更加安全地配置, 禁止除 devops 組之外的其它開發人員擁有可以 ambassador 命名空間下資源的更新權限, 這樣就可以阻止開發人員在使用 telepresence 連接的時候無意中錯誤地在不停地安裝各種版本的 traffic manager. 但是為了保證開發人員可以正常使用, list resource “namespaces“權限一定要給, 然后就是 create resource “pods/portforward” in API group "” in the namespace “ambassador” 的權限. ? Client / Root Daemon / User Daemon 3 個版本號一定要完全一致:
?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 搞定.
如果需要手動卸載可以這樣操作:
kdeletedeploy-nambassadortraffic-manager kdeletesecretssh.helm.release.v1.traffic-manager.v1-nambassador
注意它并不會真正檢查 pod 是否存在, 如果檢查到 sh.helm.release.v1.traffic-manager.v1 這個 secrets 就會直接跳過安裝了. 所以你要是刪除了 traffic manger 的 deployment, 但是忘記刪除了這個 secrets, 會導致下次連接的時候, traffic manger 不會被安裝.
4. 調試問題
k8s 端問題查看:
先檢查 pod 是不是正常:
k get po -n ambassador
k get deploy -n ambassador
查看 pod 日志:
k logs -n ambassador -f traffic-manager
5. 編譯和構建容器鏡像
traffic manager 的版本一定要匹配客戶端的版本.
對于 nightly 版本, 其形式如 v2.5.4-2-g8ccf3c9d
對于正式版, 其形式如 v2.5.3
不同版本不能連接, 會提示錯誤. 即使是客戶端, 不同版本的 daemon 也是不兼容的, 如:
version mismatch. Client v2.5.3 != User Daemon v2.5.4-2-g8ccf3c9d, please run ’telepresence quit -u’ and reconnect
計算當前 nightly 版本號: git describe --tags --match='v*'
build 的時候,必須通過 env 指定 TELEPRESENCE_VERSION, 不然 Makefile 會自動運行 go run build/genversion.go 生成一個帶 unix timestamp 的版本號,這樣客戶端和 agent docker image 的版本號便沒辦法對應上了。
同時還要指定 TELEPRESENCE_REGISTRY , 這個主要是在構建時打 docker tag 用的,主要用于 docker push, 真正程序運行的時候,取的還是 env:"TELEPRESENCE_REGISTRY,default=docker.io/datawire" 因此,如果要防止客戶端安裝官方鏡像, 這一行硬編碼的代碼必須修改.
#構建本地bin和容器鏡像并push: makebuildimagepush-imageTELEPRESENCE_VERSION=v2.5.4TELEPRESENCE_REGISTRY=docker.io/ttys3 #只構建本地bin makebuildTELEPRESENCE_VERSION=v2.5.4TELEPRESENCE_REGISTRY=docker.io/ttys3
6. 突然無法訪問的 Rust 文檔站點 docs.rs
連接 telepresence 后, 發現 https://docs.rs/ 怎么也打不開了. dig docs.rs 發現超時, 并沒有解析成功.
然后我想到了原因, 有人在集群里創建了一個名為 rs 的命名空間, 連接 telepresence 后, 導致所有 .rs 域名都無法解析了(除了 k8s 集群里面的 ).
經過查看源碼, 老燈發現可以通過 patch updateLinkDomains 這個方法搞定. 思路就是, 如果一個命名空間, 是 ICANN 管理的通用 TLD 后綴, 則直接 skip 它, 不設置.
直接修改 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 選項并不能解決我們這里的問題.
https://www.telepresence.io/docs/latest/reference/config/#dns
其默認值為 [".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` 加入這個列表, 則會導致 `.rs` 域名總是解析失敗(NXDomain)
所以其實這個默認列表也是有問題的, 為什么默認有 `.com`, 而 `google.com` 不會解析失敗呢? 那是因為我們沒有名為 `com` 的命名空間. 所以, 假設你有名為 `com` 的命名空間, 就要小心了.
7. DNS 解析特別慢或超時
我們同時在開發和測試環境部署了 telepresence (traffic manager).
使用中發現, 測試環境絲滑無比, 開發環境總是 DNS 解析超時.
通過查看 log 可發現類似日志:
LookupHost: lookup kubernetes.default on 10.0.165.145 dial udp 10.0.165.145 i/o timeout
暫時沒排查出什么原因, 大概率是 coredns 出問題了.
干了 coredns pod 之后, 等 coredns pod 重新 ruuning 了, 再把 traffic manager 的 pod 干了,
重新測試, 發現正常了.
8. 某些網段或 IP 的流量不想走 tel0 tun 出去
這個也是通過 k8s 擴展配置實現的, 之所以放在這里, 我想 telepresence 是考慮到對于不同人集群, 可以方便支持不同的配置.
修改 ~/.kube/config 文件, 增加配置到 never-proxy 數組即可, 如要將 10.10.8.9 單個 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 是會默認排除的(通過 telepresence status 你可以看到這一點), 除此之外 , 如果你有些外部服務, 因為網段跟 k8s cluster IP 段重合, 導致流量錯誤地走了 tel0 tun 設備, 就可以通過這個配置來修正.
-
容器
+關注
關注
0文章
495瀏覽量
22066 -
調試服務
+關注
關注
0文章
2瀏覽量
6151 -
kubernetes
+關注
關注
0文章
224瀏覽量
8722
原文標題:K8S 運維開發調試神器 Telepresence 實踐及踩坑記
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論