ports なんて飾りです。偉い人にはそれがわからんのですよ。
ports なんて飾りです。
Kubernetes の Pod のマニフェストには ports
と言う項目がある。名前の通り、Pod 内のコンテナがどのポートでリクエストを受け付けるかを指定するためのものである。
指定させるぐらいなんだから、ports
に指定されていないポートではリクエストを受け付けられないような気がするのだが、実はそんなことはない。
と言う事で、確かめてみよう。まず ports
を一切指定していない nginx
の Pod を作成する。
$ kubectl create -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
EOF
pod/nginx created
次に、Pod の IP アドレスを確認する。
$ kubectl get pod/nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 9s 192.168.3.182 wk1 <none> <none>
準備はできた。コイツに対して実際にリクエストを投げてみよう。
$ kubectl run -i --rm --restart=Never --image=curlimages/curl curl -- curl -s http://192.168.3.182
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
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 "curl" deleted
ちゃんとレスポンスが得られた。
docker run
では -p
で指定したポート以外はコンテナ外部に公開されないのでリクエストを受け付けられないのだが、kubernetes の ports
はそういった類のものではないと言う事だ。
やっぱり ports
なんていらんかったんや…
ちなみに、これはちゃんとドキュメントに書いてある。
Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default "0.0.0.0" address inside a container will be accessible from the network.
EXPOSE も飾りです。
ports
を見ると Dockerfile
の EXPOSE
を思い出す。コイツも特に指定が必要な訳では無いし、コイツを指定したからと言って docker run
に -p
が不要になる訳でもない。
と言う事で試してみよう。
EXPOSE が無いケース
まず、EXPOSE
が無い nginx
のイメージを作る。
$ docker build - -t nginx-noexpose <<'EOF'
FROM nginx as base
FROM scratch
copy --from=base / /
STOPSIGNAL SIGQUIT
ENTRYPOINT ["nginx", "-g", "daemon off;"]
EOF
これで絶対に EXPOSE
は無い。
次にこのイメージを -p
付きで実行する。
$ docker run -d -p 8080:80 --rm --name nginx-noexpose nginx-noexpose
7a94975f98693acf6606f513977c53180fab3856d1236ed2ef2fc3d062080698
特に何の文句も言われず実行できた。
念のため docker ps
で状態を見てみる。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7a94975f9869 nginx-noexpose "nginx -g 'daemon of…" 2 seconds ago Up 1 second 0.0.0.0:8080->80/tcp, :::8080->80/tcp nginx-noexpose
ちゃんと PORTS
に指定したポートがある。リクエストを受け付けられそうだ。
では、コイツに対して実際にリクエストを投げてみよう。
$ curl http://localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
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>
ちゃんとレスポンスが得られた。
EXPOSE があるケース
nginx
の公式イメージには EXPOSE
があるのでそのまま使ってもいいが、ここではあえてイメージを作ってみよう。
docker build - -t nginx-expose <<'EOF'
FROM nginx as base
FROM scratch
copy --from=base / /
STOPSIGNAL SIGQUIT
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
EOF
これで絶対に EXPOSE
がある。
次にこのイメージを -p
無しで実行する。
$ docker run -d --rm --name nginx-expose nginx-expose
92ebcbdd2094e6a2a2472281e02b76550632d9b1ecfcb7a085d8f4d1ac66d39e
当然何の文句も言われず実行できた。
docker ps
で状態を見てみる。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
92ebcbdd2094 nginx-expose "nginx -g 'daemon of…" 38 seconds ago Up 37 seconds 80/tcp nginx-expose
ちゃんと PORTS
に EXPOSE
で指定したポートはあるものの、ホスト側のポート番号が無い。既にリクエストを受け付けらる気がしない。
さて、コイツに対して実際にリクエストを投げたいが、そもそもどのポートに投げればよいだろうか。とりあえず 80 にでも投げてみるか。
$ curl http://localhost
curl: (7) Failed to connect to localhost port 80 after 0 ms: Connection refused
当たり前だが、ダメだ。
せっかくなので実際のポートの状況も調べてみる。
$ sudo ss -antp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.1:35429 0.0.0.0:* users:(("containerd",pid=177,fd=15))
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=172,fd=14))
やはり公開されているポートは無い。
やっぱり EXPOSE
なんていらんかったんや…
ちなみにも、これもちゃんとドキュメントに書いてある。
The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published. To actually publish the port when running the container, use the -p flag on docker run to publish and map one or more ports, or the -P flag to publish all exposed ports and map them to high-order ports.
…中略…
Regardless of the EXPOSE settings, you can override them at runtime by using the -p flag.
飾りじゃないのよ ports は
ports
は飾りと言ったな。あれは嘘だ。
ports
にはドキュメンテーション目的以外にもちゃんと役割がある。
ports
で指定した名前(name
)は Service
で targetPort
を指定する際に使用できる。
良く分からない数値を指定されるよりも五万倍は分かりやすい(個人の感想です)。
$ kubectl create -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- port: 80
targetPort: http
selector:
app: nginx
EOF
せっかくなのでちゃんとサービス経由でリクエストを受け付けられることを確認してみる。
$ kubectl run -i --rm --restart=Never --image=curlimages/curl curl -- curl -s http://nginx
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
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 "curl" deleted
当然受け付けられた。
Pod でリクエストを受け付ける場合に Service
を作らないなんてことはまず無いと思うので、ports
はちゃんと指定しておくのが吉だ。
ちなみに、昔はドキュメントに「飾りだぜ」って書いてあったが「そんなことねぇよ」って Issue が立てられて現在の記載になったようで、ドキュメントにその Issue へのリンクが記載されている。
misleading comments in container.Ports がそれなので興味があれば見てみると良い。
飾りじゃないのよ EXPOSE も
EXPOSE
もドキュメンテーション以外に役割がある。
EXPOSE された全ポートの公開
docker run
に -P
なるフラグ(大文字なので注意)があって、これを指定すると EXPOSE
で指定された全てのポートが公開される。
試してみよう。
$ docker run -d --rm --name nginx-expose -P nginx-expose
206d93265405d2b29bbb7e7389f70def7a0d6d25534bbf57bc940a6058ac655c
docker ps
で状態を見てみる。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
206d93265405 nginx-expose "nginx -g 'daemon of…" 4 seconds ago Up 3 seconds 0.0.0.0:32769->80/tcp, :::32769->80/tcp nginx-expose
ちゃんと公開されている。
念のためアクセスしてみる。
$ curl http://localhost:32769
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
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>
アクセスできた。
ただし、公開されるポートのホスト側ポート番号はランダムなので注意が必要だ。
他のコンテナとのリンク
docker run
に --link
なるフラグがあって、コイツを使うと EXPOSE
されているポートは外部に公開していなくとも別のコンテナからアクセスできる。
これも試してみよう。
$ docker run -d --rm --name nginx-expose nginx-expose
ba4e2e2f24a2b941e8933a5a03dd656198a32735a227046cb7cd79776844f360
docker ps
で状態を見てみる。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ba4e2e2f24a2 nginx-expose "nginx -g 'daemon of…" 5 seconds ago Up 3 seconds 80/tcp nginx-expose
-p
も -P
も指定していないので、当然のことながらポートは公開されていない。
しかし、--link
を使えば別コンテナからアクセスできる。
$ docker run --rm --link nginx-expose curlimages/curl -s http://nginx-expose
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
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>
アクセスできた。
ちなみに、コンテナ名でアクセスできているが、これは /etc/hosts を小細工してくれているからだ。
$ docker run --rm --link nginx-expose busybox cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 nginx-expose ba4e2e2f24a2
172.17.0.3 e7ef78312b8b
他にもいろんな環境変数が作成されているので見てみると面白いかもしれない。
$ docker run --rm --link nginx-expose busybox env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=821cd86677e0
TERM=xterm
NGINX_EXPOSE_PORT=tcp://172.17.0.2:80
NGINX_EXPOSE_PORT_80_TCP=tcp://172.17.0.2:80
NGINX_EXPOSE_PORT_80_TCP_ADDR=172.17.0.2
NGINX_EXPOSE_PORT_80_TCP_PORT=80
NGINX_EXPOSE_PORT_80_TCP_PROTO=tcp
NGINX_EXPOSE_NAME=/practical_mclaren/nginx-expose
HOME=/root
飾りであろうがなかろうが書くよね?
どうでもいいことを散々書いてきたが、ports
や EXPOSE
は飾りであろうがなかろうが普通書くよな?
てか頼むから書いてくれ(懇願)。
皆さんも ports
や EXPOSE
をしっかり書いて良いコンテナライフを!(てきとー
Discussion