telepresence というツールがあります。手元の端末が Kubernetes クラスタ内にいるかのような通信を可能にし、Kubernetes の Pod の Container への通信をインターセプトして手元の端末に流すことができます。これの仕組みを調べてみます。(以前は Python で書かれていたようですが、v2 は Go で書き直されたみたいです)
確認に使用した telepresence と mac の version
$ telepresence version Client: v2.4.9 (api v3) $ sw_vers ProductName: macOS ProductVersion: 12.1 BuildVersion: 21C52
Kubernetes クラスタは GKE の v1.21.5-gke.1302
クラスタへの terffic-manager のデプロイ
helm を使ってインストールすることができます。デフォルトではクラスタワイドな設定になりますが、特定の namespace にのみインストールしたり、namespace 毎に権限を分けてインストールすることも可能です。
$ helm repo add datawire https://app.getambassador.io $ helm repo update
Namespace の作成。デフォルトでは ambassador という namespace を使うことになっているようです。
$ kubectl create namespace ambassador
$ helm install traffic-manager --namespace ambassador datawire/telepresence
次のようなリソースが作成されます。
Kind | Namespace | Name |
---|---|---|
ServiceAccount | ambassador | traffic-manager |
Secret | ambassador | mutator-webhook-tls |
ClusterRole | – | traffic-manager-ambassador |
ClusterRoleBinding | – | traffic-manager-ambassador |
Role | ambassador | traffic-manager |
RoleBinding | ambassador | traffic-manager |
Service | ambassador | traffic-manager |
Service | ambassador | agent-injector |
Deployment | ambassador | traffic-manager |
MutatingWebhookConfiguration | – | agent-injector-webhook-ambassador |
テスト用 Service / Deployment のデプロイ
default namespace に hello という Deployment をデプロイし、 expose コマンドで Service を作成します。
$ kubectl create deploy hello --image=k8s.gcr.io/echoserver:1.4
$ kubectl expose deploy hello --port 80 --target-port 8080
$ kubectl get ns,svc,deploy,po NAME STATUS AGE namespace/ambassador Active 104m namespace/default Active 159m namespace/kube-node-lease Active 159m namespace/kube-public Active 159m namespace/kube-system Active 159m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/hello ClusterIP 172.16.1.15380/TCP 11s service/kubernetes ClusterIP 172.16.0.1 443/TCP 159m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/hello 1/1 1 1 20s NAME READY STATUS RESTARTS AGE pod/hello-79dbd5fdfb-t59nr 1/1 Running 0 13s
telepresence connect
手元の端末で telepresence connect
コマンドを実行することで手元の端末と Kubernetes クラスタ間の tunnel を掘る。root 権限で実行する必要のあるものがあるため、sudo のパスワード入力を求められます。
$ telepresence connect Launching Telepresence Root Daemon Need root privileges to run: /Users/teraoka/bin/telepresence daemon-foreground /Users/teraoka/Library/Logs/telepresence '/Users/teraoka/Library/Application Support/telepresence' '' Password: Launching Telepresence User Daemon Connected to context gke_MY_PROJECT_ID_asia-northeast1-a_CLUSTER_NAME (https://203.0.113.123)
telepresence version
コマンドで root で実行する daemon をユーザー権限で実行する daemon の 2 つが実行されているのがわかります。
$ telepresence version Client: v2.4.9 (api v3) Root Daemon: v2.4.9 (api v3) User Daemon: v2.4.9 (api v3)
手元の curl からクラスタ内へのアクセス
たったこれだけで、あら不思議 curl hello.default
とするとクラスタ内の Service に対してアクセスできています。
$ curl -sv hello.default * Trying 172.16.1.153:80... * Connected to hello.default (172.16.1.153) port 80 (#0) > GET / HTTP/1.1 > Host: hello.default > User-Agent: curl/7.79.1 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Server: nginx/1.10.0 < Date: Fri, 31 Dec 2021 08:27:43 GMT < Content-Type: text/plain < Transfer-Encoding: chunked < Connection: keep-alive < CLIENT VALUES: client_address=172.17.0.131 command=GET real path=/ query=nil request_version=1.1 request_uri=http://hello.default:8080/ SERVER VALUES: server_version=nginx: 1.10.0 - lua: 10001 HEADERS RECEIVED: accept=*/* host=hello.default user-agent=curl/7.79.1 BODY: * Connection #0 to host hello.default left intact -no body in request-
hello.default が 172.16.1.153 と名前解決されているのがどうしてなのか気になります。答えは下にありますが、これは mac の場合です、Linux や Windows ではまた別の仕組みが使われているのだろうと思われます。(他の環境も含めて DNS resolution に書かれていました)
サーバー側から見た client_address は 172.17.0.131 となっています、これは ambassador namespace にデプロイされている traffic-manager Deployment の Pod が持つ IP アドレスです。この Pod 経由でアクセスしていることがわかります。
$ kubectl get pod -n ambassador -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES traffic-manager-5cb99c9fd6-mv6v9 1/1 Running 0 124m 172.17.0.131 gke-teraoka-blue-teraoka-blue-pool1-48fb0873-m4cw <none> <none>
telepresence プロセスの役割
telepresence のぞれぞれのプロセスが何をやっているのかを確認してみます。
とりあえず ps コマンドで確認。
$ ps auxww | grep telepresence teraoka 72020 0.0 0.0 34132084 872 s003 R+ 5:18PM 0:00.00 grep telepresence teraoka 71885 0.0 0.3 34956040 46184 s000 S 5:14PM 0:00.65 /Users/teraoka/bin/telepresence connector-foreground root 71881 0.0 0.2 34966080 35296 s000 S 5:14PM 0:00.27 /Users/teraoka/bin/telepresence daemon-foreground /Users/teraoka/Library/Logs/telepresence /Users/teraoka/Library/Application Support/telepresence root 71880 0.0 0.0 34142464 4596 s000 S 5:14PM 0:00.01 sudo --non-interactive --preserve-env /Users/teraoka/bin/telepresence daemon-foreground /Users/teraoka/Library/Logs/telepresence /Users/teraoka/Library/Application Support/telepresence
$ pstree -w 71880 -+= 71880 root sudo --non-interactive --preserve-env /Users/teraoka/bin/telepresence daemon-foreground /Users/teraoka/Library/Logs/telepresence /Users/teraoka/Library/Application Support/telepresence \--- 71881 root /Users/teraoka/bin/telepresence daemon-foreground /Users/teraoka/Library/Logs/telepresence /Users/teraoka/Library/Application Support/telepresence
lsof で確認
root daemon
$ sudo lsof -nPp 71881 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME teleprese 71881 root cwd DIR 1,9 512 47512210 /Users/teraoka/ghq/github.com/yteraoka/terraform-examples/gcp/gke teleprese 71881 root txt REG 1,9 77231136 74147315 /Users/teraoka/bin/telepresence teleprese 71881 root txt REG 1,9 46096 74099023 /Library/Preferences/Logging/.plist-cache.1VLWA6Q0 teleprese 71881 root txt REG 1,9 2160672 1152921500312811906 /usr/lib/dyld teleprese 71881 root txt REG 1,9 32768 74099068 /private/var/db/mds/messages/se_SecurityMessages teleprese 71881 root txt REG 1,9 114087 74099365 /private/var/db/analyticsd/events.whitelist teleprese 71881 root txt REG 1,9 29638976 1152921500312823656 /usr/share/icu/icudt68l.dat teleprese 71881 root 0r CHR 3,2 0t0 317 /dev/null teleprese 71881 root 1w REG 1,9 2099 74169276 /Users/teraoka/Library/Logs/telepresence/daemon.log teleprese 71881 root 2w REG 1,9 2099 74169276 /Users/teraoka/Library/Logs/telepresence/daemon.log teleprese 71881 root 3u KQUEUE count=0, state=0xa teleprese 71881 root 4 PIPE 0xd904b24160bf46ff 16384 ->0x1ac3420f1be0c501 teleprese 71881 root 5 PIPE 0x1ac3420f1be0c501 16384 ->0xd904b24160bf46ff teleprese 71881 root 6w REG 1,9 2099 74169276 /Users/teraoka/Library/Logs/telepresence/daemon.log teleprese 71881 root 7u systm 0x4715bfb02f5e8bd7 0t0 [ctl com.apple.net.utun_control id 4 unit 4] teleprese 71881 root 8 PIPE 0x77446814497e3b77 16384 ->0x67dbd1c5970420d8 teleprese 71881 root 9u unix 0x4715bfb02f991c3f 0t0 /var/run/telepresence-daemon.socket teleprese 71881 root 10 PIPE 0x67dbd1c5970420d8 16384 ->0x77446814497e3b77 teleprese 71881 root 11 NPOLICY teleprese 71881 root 12u unix 0x4715bfb02f991f5f 0t0 /var/run/telepresence-daemon.socket teleprese 71881 root 13u unix 0x4715bfb02f9944df 0t0 ->0x4715bfb02f9945a7 teleprese 71881 root 14u IPv4 0x4715bfb4fc950c67 0t0 UDP *:51812 teleprese 71881 root 15u unix 0x4715bfb02f993477 0t0 ->0x4715bfb02f99353f
root daemon は 51812/udp を listen しています。後でわかりますが、これは DNS サーバーです。クラスタのドメインはここに問い合わせが行われます。utun_control というのは tunnel の制御でしょうか。
utun3 といネットワークデバイスが作成されており
$ ifconfig utun3 utun3: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500 inet 172.16.0.0 --> 172.16.0.1 netmask 0xfffff000 inet 172.17.0.0 --> 172.17.0.1 netmask 0xffffffc0 inet 172.17.0.64 --> 172.17.0.1 netmask 0xffffffc0 inet 172.17.0.128 --> 172.17.0.1 netmask 0xffffffc0
Pod や Service 用サブネットの route がその utun3 へ向けられています。
$ netstat -nr | grep utun3 172.16/20 172.16.0.1 UGCS utun3 172.16.0.1 172.16.0.0 UH utun3 172.16.15.255 172.16.0.1 UGHW3I utun3 22 172.17/26 172.17.0.1 UGCS utun3 172.17.0.1 172.17.0.0 UH utun3 172.17.0.63 172.17.0.1 UGHW3I utun3 22 172.17.0.64/26 172.17.0.1 UGCS utun3 172.17.0.127 172.17.0.1 UGHW3I utun3 22 172.17.0.128/26 172.17.0.1 UGCS utun3 172.17.0.191 172.17.0.1 UGHW3I utun3 22
user daemon
$ lsof -nPp 71885 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME teleprese 71885 teraoka cwd DIR 1,9 512 47512210 /Users/teraoka/ghq/github.com/yteraoka/terraform-examples/gcp/gke teleprese 71885 teraoka txt REG 1,9 77231136 74147315 /Users/teraoka/bin/telepresence teleprese 71885 teraoka txt REG 1,9 46096 74099023 /Library/Preferences/Logging/.plist-cache.1VLWA6Q0 teleprese 71885 teraoka txt REG 1,9 32768 74100907 /private/var/db/mds/messages/501/se_SecurityMessages teleprese 71885 teraoka txt REG 1,9 2160672 1152921500312811906 /usr/lib/dyld teleprese 71885 teraoka txt REG 1,9 114087 74099365 /private/var/db/analyticsd/events.whitelist teleprese 71885 teraoka 0r CHR 3,2 0t0 317 /dev/null teleprese 71885 teraoka 1w REG 1,9 1498 74169289 /Users/teraoka/Library/Logs/telepresence/connector.log teleprese 71885 teraoka 2w REG 1,9 1498 74169289 /Users/teraoka/Library/Logs/telepresence/connector.log teleprese 71885 teraoka 3u KQUEUE count=0, state=0xa teleprese 71885 teraoka 4 PIPE 0xfc30aea6068d98ef 16384 ->0xd5d97fbdd54a6017 teleprese 71885 teraoka 5 PIPE 0xd5d97fbdd54a6017 16384 ->0xfc30aea6068d98ef teleprese 71885 teraoka 6w REG 1,9 1498 74169289 /Users/teraoka/Library/Logs/telepresence/connector.log teleprese 71885 teraoka 7u unix 0x4715bfb02f992347 0t0 /tmp/telepresence-connector.socket teleprese 71885 teraoka 8 PIPE 0xa124ff493681e691 16384 ->0x40a1939f617879e5 teleprese 71885 teraoka 9 PIPE 0x40a1939f617879e5 16384 ->0xa124ff493681e691 teleprese 71885 teraoka 10u IPv6 0x4715bfb9c79fa6f7 0t0 TCP *:53443 (LISTEN) teleprese 71885 teraoka 12u IPv4 0x4715bfbe96aaa46f 0t0 TCP 192.168.210.119:53444->203.0.113.123:443 (ESTABLISHED) teleprese 71885 teraoka 13u unix 0x4715bfb02f9919e7 0t0 ->0x4715bfb02f991f5f teleprese 71885 teraoka 14 NPOLICY teleprese 71885 teraoka 15u unix 0x4715bfb02f992027 0t0 ->0x4715bfb02f99434f teleprese 71885 teraoka 17u IPv4 0x4715bfbe9eb6546f 0t0 TCP 192.168.210.119:53446->203.0.113.123:443 (ESTABLISHED) teleprese 71885 teraoka 18u unix 0x4715bfb02f9945a7 0t0 /tmp/telepresence-connector.socket
Kubernetes の API Server と通信しているのは user daemon のようです。
ログファイルを確認
lsof でログファイルらしきファイルが確認できたので中身を見てみます。
root daemon のログ
/Users/teraoka/Library/Logs/telepresence/daemon.log
2021-12-31 17:14:09.3150 info Logging at this level "info" 2021-12-31 17:14:09.3151 info --- 2021-12-31 17:14:09.3152 info Telepresence daemon v2.4.9 (api v3) starting... 2021-12-31 17:14:09.3152 info PID is 71881 2021-12-31 17:14:09.3152 info 2021-12-31 17:14:09.4008 info daemon/server-grpc : gRPC server started 2021-12-31 17:14:10.6945 info daemon/server-grpc/conn=2 : Adding never-proxy subnet 203.0.113.123/32 2021-12-31 17:14:10.7615 info daemon/watch-cluster-info : Adding service subnet 172.16.0.0/20 2021-12-31 17:14:10.7616 info daemon/watch-cluster-info : Adding pod subnet 172.17.0.0/26 2021-12-31 17:14:10.7616 info daemon/watch-cluster-info : Adding pod subnet 172.17.0.64/26 2021-12-31 17:14:10.7617 info daemon/watch-cluster-info : Adding pod subnet 172.17.0.128/26 2021-12-31 17:14:10.7621 info daemon/watch-cluster-info : Setting cluster DNS to 172.16.0.10 2021-12-31 17:14:10.7621 info daemon/watch-cluster-info : Setting cluster domain to "cluster.local." 2021-12-31 17:14:10.7778 info daemon/server-router/MGR stream : Connected to Manager 2.4.9 2021-12-31 17:14:10.7983 info daemon/server-dns : Generated new /etc/resolver/telepresence.local 2021-12-31 17:14:10.7985 info daemon/server-dns/SearchPaths : setting search paths ambassador default kube-node-lease kube-public kube-system 2021-12-31 17:14:10.7987 info daemon/server-dns/SearchPaths : Generated new /etc/resolver/telepresence.kube-system.local 2021-12-31 17:14:10.7991 info daemon/server-dns/SearchPaths : Generated new /etc/resolver/telepresence.tel2-search.local 2021-12-31 17:14:10.7994 info daemon/server-dns/SearchPaths : Generated new /etc/resolver/telepresence.ambassador.local 2021-12-31 17:14:10.7996 info daemon/server-dns/SearchPaths : Generated new /etc/resolver/telepresence.default.local 2021-12-31 17:14:10.7999 info daemon/server-dns/SearchPaths : Generated new /etc/resolver/telepresence.kube-node-lease.local 2021-12-31 17:14:10.8001 info daemon/server-dns/SearchPaths : Generated new /etc/resolver/telepresence.kube-public.local
root daemon は watch-cluster-info で Service の Subnet や Node 毎に作成される Pod の Subnet をクラスタに合わせて routing 設定しているようですし、DNS の forward 先の管理、そして /etc/resolver 配下に cluster 側に向けるドメイン用のファイルを管理しています。
例えば /etc/resolver/telepresence.kube-system.local の中身は次のようになっており、kube-system で終わるドメインの DNS の問い合わせは 127.0.0.1:51812 に送られるようになっています。51812/udp は root daemon が listen しており、tunnel 経由で Kubernetes 内の DNS Service に転送されてるようになっているみたいです。mac の /etc/resolver の仕組みを知らず、Linux の LD_PRELOAD 的なものが mac にもあってそれが使われているのだろうと調べてて、そんな設定が見つからずしばらく悩んでしまっていました…
$ cat /etc/resolver/telepresence.kube-system.local # Generated by telepresence port 51812 domain kube-system nameserver 127.0.0.1
127.0.0.1:51812 に対して DNS の問い合わせを投げると結果が返ってきます。
mac のこの設定は scutil --dns
でも確認できます。
$ dig +short @127.0.0.1 -p 51812 hello.default.svc.cluster.local a 172.16.1.153
user daemon のログ
/Users/teraoka/Library/Logs/telepresence/connector.log
2021-12-31 17:14:09.5593 info Logging at this level "info" 2021-12-31 17:14:09.5971 info --- 2021-12-31 17:14:09.5972 info Telepresence Connector v2.4.9 (api v3) starting... 2021-12-31 17:14:09.5972 info PID is 71885 2021-12-31 17:14:09.5972 info 2021-12-31 17:14:09.5974 info connector/server-grpc : gRPC server started 2021-12-31 17:14:09.7740 info connector/background-init : Connecting to daemon... 2021-12-31 17:14:09.7747 info connector/background-init : Connecting to k8s cluster... 2021-12-31 17:14:09.8611 info connector/background-init : Server version v1.21.5-gke.1302 2021-12-31 17:14:09.8612 info connector/background-init : Context: gke_MY_PROJECT_ID_asia-northeast1-a_CLUSTER_NAME 2021-12-31 17:14:09.8613 info connector/background-init : Server: https://203.0.113.123 2021-12-31 17:14:09.8613 info connector/background-init : Connected to context gke_MY_PROJECT_ID_asia-northeast1-a_CLUSTER_NAME (https://203.0.113.123) 2021-12-31 17:14:10.0374 info connector/background-init : Connecting to traffic manager... 2021-12-31 17:14:10.0375 info connector/background-init : Waiting for TrafficManager to connect 2021/12/31 17:14:10 Patching synced Namespace 6ed04e58-22c9-4b10-87a2-690867ae2371 2021-12-31 17:14:10.1281 info connector/background-manager : Existing Traffic Manager 2.4.9 not owned by cli or does not need upgrade, will not modify 2021/12/31 17:19:10 Patching synced Namespace 6ed04e58-22c9-4b10-87a2-690867ae2371
telepresence status
telepresence status
コマンドで2つの daemon プロセスの状況を確認できます。DNS 周りの仕組みを見てて、誰かが com とか jp とか TLD と同じ namespace 作ってしまうと困るだろうなと思ってたのですが、いくつかはデフォルトで Exclude suffixes に入っているのですね。telepresence connect
コマンドの --mapped-namespaces
オプションで必要な namespace だけをコンマ区切りで並べることもできます。
$ telepresence status Root Daemon: Running Version : v2.4.9 (api 3) DNS : Remote IP : 172.16.0.10 Exclude suffixes: [.arpa .com .io .net .org .ru] Include suffixes: [] Timeout : 4s Also Proxy : (0 subnets) Never Proxy: (1 subnets) User Daemon: Running Version : v2.4.9 (api 3) Ambassador Cloud : Logged out Status : Connected Kubernetes server : https://203.0.113.123 Kubernetes context: gke_MY_PROJECT_ID_asia-northeast1-a_CLUSTER_NAME Telepresence proxy: ON (networking to the cluster is enabled) Intercepts : 0 total
part 1 はここまで。Kubernetes クラスタ側からのアクセスは次回。
telepresence connect
で起動した 2 つの daemon プロセスは telepresence quit
で終了できます。