port-forwardを何気なく使っていてどう動くのか調べたのでそのまとめ。
目次
port-forwardを試してみる
とりあえず、中身を見る前にどんなことが出来るのか見る。そのために、webサーバとなるpodを作成。
$ kubectl run nginx --image=nginx
Podが作成されたことを確認。
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 6m1s 172.28.1.24 k8s-worker1 <none> <none>
kubectlを利用するLinuxノードはk8sクラスタの外部にいるため、PodのIPには直接アクセスできない。
$ ping -c 3 172.28.1.24
PING 172.28.1.24 (172.28.1.24) 56(84) bytes of data.
--- 172.28.1.24 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2015ms
$ curl -m 1 -vvv http://172.28.1.24
* Rebuilt URL to: http://172.28.1.24/
* Trying 172.28.1.24...
* Connection timed out after 1001 milliseconds
* Closing connection 0
curl: (28) Connection timed out after 1001 milliseconds
この状態で、port-forward
をコマンドを利用して、podにアクセスしてみる。
$ kubectl port-forward nginx 10080:80
Forwarding from 127.0.0.1:10080 -> 80
Forwarding from [::1]:10080 -> 80
別のターミナルからlocalhost:10080
にcurlすると、podのIPへ届かなくても、アクセスできるというものである。
$ curl -vvv http://localhost:10080
* Rebuilt URL to: http://localhost:10080/
* Trying ::1...
* Connected to localhost (::1) port 10080 (#0)
> GET / HTTP/1.1
> Host: localhost:10080
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.19.4
< Date: Fri, 06 Nov 2020 06:17:02 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Tue, 27 Oct 2020 15:09:20 GMT
< Connection: keep-alive
< ETag: "5f983820-264"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host localhost left intact
上記の動作はざっくり以下のような図になる。下記から各プロセスについて詳しくみていく
kubectlクライアントからkube-api-server
kubectlのクライアントからkube-api-serverへは下記のURIにPOSTすることで、ストリームのセッションを作成する。
/api/v1/namespaces/<namespace name>/pods/<pod name>/portforward
これにより、port番号10080に対するリクエストはkube-api-serverに対して転送される。
kube-api-serverからworkerのkubelet
Requestを受け取ったkube-api-serverは該当のworkerノードにいるpodに対してリダイレクトを行う。リダイレクトを行う際には、workerノードのkubeletに対して下記のリクエストを投げる
https://<worker node>:10250/portForward/default/nginx
kube-api-serverのログレベルあげることで、実際にリクエストを送っていることが分かる。
I1106 06:31:11.487573 1 upgradeaware.go:275] Connecting to backend proxy (intercepting redirects) https://10.16.181.22:10250/portForward/default/nginx
Headers: map[Connection:[Upgrade] Content-Length:[0] Upgrade:[SPDY/3.1] User-Agent:[kubectl/v1.18.2 (linux/amd64) kubernetes/38ac483] X-Forwarded-For:[10.16.181.15] X-Stream-Protocol-Version:[portforward.k8s.io]]
workerノード上では新規のセッションがkube-api-serverからはられていることが下記コマンドでわかる
$ ss -tn
State Recv-Q Send-Q Local Address:Port Peer Address:Port
<snip>
ESTAB 0 0 [::ffff:10.16.181.22]:10250 [::ffff:10.16.181.21]:52154
これにて準備は完了。kubectlを使用しているノードの10080ポートにアクセスした際には、workerノードまでkube-api-serverを経由してリダイレクトされる。
Podまで
workerのkubeletまでリダイレクトされることは分かったと思うが、実際にpodにどのようにリダイレクトされるのか見ていく。
kubectを叩くクライアントからリクエストを送ると、workerノード上でsocat
プロセスが立ち上がり、実際にクライアントから来たリクエストが転送される。今回は軽量のwebサーバを用いているため、リクエスト完了まで一瞬である。実際に、workerノード上でリダイレクトしているプロセスも一瞬しか立ち上がらない。
ちなみに、下記のようなプロセスが立ち上がる。
root 876 0.0 0.0 27416 3696 ? S 15:42 0:00 \_ /usr/bin/socat - TCP4:localhost:80
このプロセスはnsenter
にてpodと同じLinux namespaceから実施されている。実際に手動で同じことをすると以下のようになる。
$ sudo docker ps | grep nginx
c1da4d32b3a2 nginx "/docker-entrypoint.…" 4 hours ago Up 3 hours k8s_nginx_nginx_default_d8e05bc6-e49f-4743-ac05-9e91493dc0bd_0
048c4c138f4a k8s.gcr.io/pause:3.2 "/pause" 4 hours ago Up 4 hours k8s_POD_nginx_default_d8e05bc6-e49f-4743-ac05-9e91493dc0bd_0
$ sudo docker inspect --format {{.State.Pid}} 048c4c138f4a
3740
$ sudo nsenter --target 3740 --net
# echo -e "GET / HTTP/1.0\r\n" | /usr/bin/socat - TCP4:localhost:80
HTTP/1.1 200 OK
Server: nginx/1.19.4
Date: Fri, 06 Nov 2020 09:37:12 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 27 Oct 2020 15:09:20 GMT
Connection: close
ETag: "5f983820-264"
Accept-Ranges: bytes
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
このようにリクエストが転送されるため、podのeth0上でtcpdumpなどでキャプチャしても実際にはリクエストのパケットが見えないのである。代わりにloインタフェースでキャプチャするとパケットが見える。
# tcpdump -v -n -i lo
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
18:41:20.261137 IP (tos 0x0, ttl 64, id 47223, offset 0, flags [DF], proto TCP (6), length 60)
127.0.0.1.34748 > 127.0.0.1.80: Flags [S], cksum 0xfe30 (incorrect -> 0xbcb5), seq 161563555, win 65495, options [mss 65495,sackOK,TS val 2012694020 ecr 0,nop,wscale 7], length 0
18:41:20.261176 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
127.0.0.1.80 > 127.0.0.1.34748: Flags [S.], cksum 0xfe30 (incorrect -> 0x740f), seq 3288057513, ack 161563556, win 65483, options [mss 65495,sackOK,TS val 2012694020 ecr 2012694020,nop,wscale 7], length 0
18:41:20.261217 IP (tos 0x0, ttl 64, id 47224, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.34748 > 127.0.0.1.80: Flags [.], cksum 0xfe28 (incorrect -> 0x9acb), ack 1, win 512, options [nop,nop,TS val 2012694020 ecr 2012694020], length 0
18:41:20.261469 IP (tos 0x0, ttl 64, id 47225, offset 0, flags [DF], proto TCP (6), length 131)
127.0.0.1.34748 > 127.0.0.1.80: Flags [P.], cksum 0xfe77 (incorrect -> 0xb638), seq 1:80, ack 1, win 512, options [nop,nop,TS val 2012694021 ecr 2012694020], length 79: HTTP, length: 79
GET / HTTP/1.1
Host: localhost:10080
User-Agent: curl/7.47.0
Accept: */*
18:41:20.261495 IP (tos 0x0, ttl 64, id 25399, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.80 > 127.0.0.1.34748: Flags [.], cksum 0xfe28 (incorrect -> 0x9a7b), ack 80, win 511, options [nop,nop,TS val 2012694021 ecr 2012694021], length 0
18:41:20.261661 IP (tos 0x0, ttl 64, id 25400, offset 0, flags [DF], proto TCP (6), length 290)
127.0.0.1.80 > 127.0.0.1.34748: Flags [P.], cksum 0xff16 (incorrect -> 0xf729), seq 1:239, ack 80, win 512, options [nop,nop,TS val 2012694021 ecr 2012694021], length 238: HTTP, length: 238
HTTP/1.1 200 OK
Server: nginx/1.19.4
Date: Fri, 06 Nov 2020 09:41:20 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 27 Oct 2020 15:09:20 GMT
Connection: keep-alive
ETag: "5f983820-264"
Accept-Ranges: bytes
18:41:20.261728 IP (tos 0x0, ttl 64, id 25401, offset 0, flags [DF], proto TCP (6), length 664)
127.0.0.1.80 > 127.0.0.1.34748: Flags [P.], cksum 0x008d (incorrect -> 0xdb54), seq 239:851, ack 80, win 512, options [nop,nop,TS val 2012694021 ecr 2012694021], length 612: HTTP
18:41:20.261788 IP (tos 0x0, ttl 64, id 47226, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.34748 > 127.0.0.1.80: Flags [.], cksum 0xfe28 (incorrect -> 0x998d), ack 239, win 511, options [nop,nop,TS val 2012694021 ecr 2012694021], length 0
18:41:20.261813 IP (tos 0x0, ttl 64, id 47227, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.34748 > 127.0.0.1.80: Flags [.], cksum 0xfe28 (incorrect -> 0x972d), ack 851, win 507, options [nop,nop,TS val 2012694021 ecr 2012694021], length 0
18:41:20.265748 IP (tos 0x0, ttl 64, id 47228, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.34748 > 127.0.0.1.80: Flags [F.], cksum 0xfe28 (incorrect -> 0x9723), seq 80, ack 851, win 512, options [nop,nop,TS val 2012694025 ecr 2012694021], length 0
18:41:20.265797 IP (tos 0x0, ttl 64, id 25402, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.80 > 127.0.0.1.34748: Flags [F.], cksum 0xfe28 (incorrect -> 0x971e), seq 851, ack 81, win 512, options [nop,nop,TS val 2012694025 ecr 2012694025], length 0
18:41:20.265813 IP (tos 0x0, ttl 64, id 47229, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.34748 > 127.0.0.1.80: Flags [.], cksum 0xfe28 (incorrect -> 0x971e), ack 852, win 512, options [nop,nop,TS val 2012694025 ecr 2012694025], length 0