telepresence 入門 (1)

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

次のようなリソースが作成されます。

KindNamespaceName
ServiceAccountambassadortraffic-manager
Secretambassadormutator-webhook-tls
ClusterRole-traffic-manager-ambassador
ClusterRoleBinding-traffic-manager-ambassador
Roleambassadortraffic-manager
RoleBindingambassadortraffic-manager
Serviceambassadortraffic-manager
Serviceambassadoragent-injector
Deploymentambassadortraffic-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.153   80/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 で終了できます。

Built with Hugo
テーマ StackJimmy によって設計されています。