Kubernetesの Service と Ingress を理解する
Kindを使って ローカル環境で立ち上げた Kubernetesクラスタで ServiceリソースとIngressリソースを理解してみる。
サンプルのリソースは Kubernetes完全ガイド の 6章をベースにしている
まずは Kind はデフォルトだとシングルノードなのでマルチノードで起動させる。
適当な場所に設定ファイルを作る。今回は ~/.kind-multi-nodes-config.yaml
を作成し、ワーカーノードを3つ起動する
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker
この設定ファイルを指定して クラスタを作成する
kind create cluster --config ~/.kind-multi-nodes-config.yaml
node を確認する
❯ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kind-control-plane Ready control-plane 13m v1.31.0 172.22.0.5 <none> Debian GNU/Linux 12 (bookworm) 6.10.4-linuxkit containerd://1.7.18
kind-worker Ready <none> 13m v1.31.0 172.22.0.3 <none> Debian GNU/Linux 12 (bookworm) 6.10.4-linuxkit containerd://1.7.18
kind-worker2 Ready <none> 13m v1.31.0 172.22.0.2 <none> Debian GNU/Linux 12 (bookworm) 6.10.4-linuxkit containerd://1.7.18
kind-worker3 Ready <none> 13m v1.31.0 172.22.0.4 <none> Debian GNU/Linux 12 (bookworm) 6.10.4-linuxkit containerd://1.7.18
kind で 作成した ノードは docker コンテナとして起動している。
❯ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5e4e7e9096e8 kindest/node:v1.31.0 "/usr/local/bin/entr…" 20 minutes ago Up 20 minutes kind-worker2
c96f3ac4fe5c kindest/node:v1.31.0 "/usr/local/bin/entr…" 20 minutes ago Up 20 minutes kind-worker
9966a8a663c1 kindest/node:v1.31.0 "/usr/local/bin/entr…" 20 minutes ago Up 20 minutes kind-control-plane
f783f69751d2 kindest/node:v1.31.0 "/usr/local/bin/entr…" 20 minutes ago Up 20 minutes kind-worker3
Cluster IP
Kubernetesクラスタ内で利用される仮想IPが払い出され、各Nodeの kube-proxy でこの仮想IPに対してルーティングすることで 特定のPodへの負荷分散を行う。
apiVersion: v1
kind: Service
metadata:
name: sample-clusterip
spec:
type: ClusterIP
ports:
- name: "http-port"
protocol: "TCP"
port: 8080
targetPort: 80
selector:
app: sample-app
上記のマニフェストでは例えば kind-worker3
ノードの Pod が別のPodをターゲットにアクセスする際に http://sample-clusterip:8080
でアクセスすることができる。 kind-worker3
ノードの kube-proxy は マニフェストに基づき app: sample-app
ラベルが付いたPodに 80番ポートへルーティングを行う。またこの時ターゲットのPodがレプリカを持つならラウンドロビン方式で負荷分散を行う模様
前述のServiceのマニフェストを元に クラスタ内のdns (coredns) では、ClusterIPのIPアドレス宛に対して Service名をドメインとして名前解決が行われる。
まず Serviceの Cluster IP を確認する。今回は 10.96.81.244
が IPアドレスになる。
❯ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 52m
sample-clusterip ClusterIP 10.96.81.244 <none> 8080/TCP 7s
新しいPodを起動して curl で IPアドレス(10.96.81.244) と ドメイン名 (sample-cluseterip) でそれぞれアクセスしてみる
# IPアドレス
❯ kubectl run --image=debian --restart=Never --rm -i testpod \
-- /bin/bash -c "apt update && apt -y install dnsutils curl && curl http://10.96.81.244: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>
pod "testpod" deleted
---
#ドメイン名で実行
❯ kubectl run --image=debian --restart=Never --rm -i testpod \
-- /bin/bash -c "apt update && apt -y install dnsutils curl && curl http://sample-clusterip: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>
pod "testpod" deleted
結果は同じとなる
次に dig を使って確認する
# FQDN (sample-clusterip.default.svc.cluster.local) で 名前解決
kubectl run --image=debian --restart=Never --rm -i testpod \
-- /bin/bash -c "apt -qq update && apt -qq -y install dnsutils curl && dig sample-clusterip.default.svc.cluster.local"
~~~
;; QUESTION SECTION:
;sample-clusterip.default.svc.cluster.local. IN A
;; ANSWER SECTION:
sample-clusterip.default.svc.cluster.local. 30 IN A 10.96.81.244
;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10) (UDP)
;; WHEN: Sun Sep 15 02:36:49 UTC 2024
;; MSG SIZE rcvd: 141
pod "testpod" deleted
---
# ClusterIPから名前解決
❯ kubectl run --image=debian --restart=Never --rm -i testpod \
-- /bin/bash -c "apt -qq update && apt -qq -y install dnsutils curl && dig -x 10.96.81.244"
~~~
;; QUESTION SECTION:
;244.81.96.10.in-addr.arpa. IN PTR
;; ANSWER SECTION:
244.81.96.10.in-addr.arpa. 30 IN PTR sample-clusterip.default.svc.cluster.local.
;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10) (UDP)
;; WHEN: Sun Sep 15 02:39:37 UTC 2024
;; MSG SIZE rcvd: 147
pod "testpod" deleted
内部DNSについては、検証用Podの /etc/resolv.conf
の内容も確認するとわかるが、kubenetes
サービスの IP(10.96.0.1) がDNSサーバーになっていて、default.svc.cluster.local
および svc.cluster.local
cluster.local
ドメインに対して名前解決を行うように指定されている
❯ kubectl run --image=debian --restart=Never --rm -i testpod \
-- /bin/bash -c "apt -qq update && apt -qq -y install dnsutils curl && cat /etc/resolv.conf"
~~~
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
pod "testpod" deleted
ClusterIPは Service作成時に自動でIPアドレスが割り振られるが、マニフェストで固定IPを指定することもできる
apiVersion: v1
kind: Service
metadata:
name: sample-clusterip-vip
spec:
type: ClusterIP
clusterIP: 10.96.5.28
ports:
- name: "http-port"
protocol: TCP
port: 8080
targetPort: 80
selector:
app: sample-app
❯ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 81m
sample-clusterip ClusterIP 10.96.81.244 <none> 8080/TCP 28m
sample-clusterip-vip ClusterIP 10.96.5.28 <none> 8080/TCP 5s
ExternalIP
ここまでは kubernetesクラスタ内部のネットワークに閉じられていたが、クラスタ外からのアクセスも処理する方法になっていく
ExternaIP を指定すると kubernetes の Nodeの IPに対して送信されたリクエスト(トラフィック)を Podに転送することができる。
ExternalIP は Nodeに割り当てられたIPを指定する。全てのNodeを指定する必要はない
apiVersion: v1
kind: Service
metadata:
name: sample-externalip
spec:
type: ClusterIP
externalIPs:
- 172.22.0.2
- 172.22.0.3
ports:
- name: "http-port"
protocol: TCP
port: 8080
targetPort: 80
selector:
app: sample-app
上記のマニフェストでは kind-worker
と kind-worker2
のみに ExternalIPを指定している
❯ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kind-control-plane Ready control-plane 123m v1.31.0 172.22.0.5 <none> Debian GNU/Linux 12 (bookworm) 6.10.4-linuxkit containerd://1.7.18
kind-worker Ready <none> 123m v1.31.0 172.22.0.3 <none> Debian GNU/Linux 12 (bookworm) 6.10.4-linuxkit containerd://1.7.18
kind-worker2 Ready <none> 123m v1.31.0 172.22.0.2 <none> Debian GNU/Linux 12 (bookworm) 6.10.4-linuxkit containerd://1.7.18
kind-worker3 Ready <none> 123m v1.31.0 172.22.0.4 <none> Debian GNU/Linux 12 (bookworm) 6.10.4-linuxkit containerd://1.7.18
❯ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 124m
sample-clusterip ClusterIP 10.96.81.244 <none> 8080/TCP 72m
sample-clusterip-vip ClusterIP 10.96.5.28 <none> 8080/TCP 43m
sample-externalip ClusterIP 10.96.235.233 172.22.0.2,172.22.0.3 8080/TCP 4s
ExternalIP を指定したことで、クラスタ外から 172.22.0.3:8080
または 172.22.0.2:8080
へのアクセスが可能になる。
のだが、kind だからなのか エンドポイントが公開されなかった
# kind-worker コンテナ(172.22.0.3)に Macからアクセスして Listenポートを確認するが 8080が無い
❯ docker exec -it kind-worker /bin/bash
root@kind-worker:/# ss -napt
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.1:10248 0.0.0.0:* users:(("kubelet",pid=217,fd=12))
LISTEN 0 4096 127.0.0.1:10249 0.0.0.0:* users:(("kube-proxy",pid=357,fd=19))
LISTEN 0 4096 127.0.0.1:33639 0.0.0.0:* users:(("containerd",pid=104,fd=11))
LISTEN 0 4096 127.0.0.11:37351 0.0.0.0:*
ESTAB 0 0 172.22.0.3:57920 172.22.0.5:6443 users:(("kubelet",pid=217,fd=14))
ESTAB 0 0 172.22.0.3:52770 172.22.0.5:6443 users:(("kindnetd",pid=535,fd=12))
ESTAB 0 0 172.22.0.3:52762 172.22.0.5:6443 users:(("kube-proxy",pid=357,fd=9))
LISTEN 0 4096 *:10250 *:* users:(("kubelet",pid=217,fd=30))
LISTEN 0 4096 *:10256 *:* users:(("kube-proxy",pid=357,fd=17))
理由は不明。調べてもわからなかった。
NordPort サービス
ExternalIPと同様に外部からのトラフィックを受け付ける方法で、こちらはNodeのIPを公開する方式ではなく、指定したポート番号を公開して 全Nodeでトラフィックを受け付ける。
このため クラスタ外からは 0.0.0.0: nodeport
でアクセスする。
指定できるポートは利用している kubernetesのサービスなどによっても異なるらしいが 30000~32767
あたりを選んでおけば良さそう
apiVersion: v1
kind: Service
metadata:
name: sample-nodeport
spec:
type: NodePort
ports:
- name: http-port
protocol: TCP
port: 8080
targetPort: 80
nodePort: 30080
selector:
app: sample-app
❯ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 154m
sample-clusterip ClusterIP 10.96.81.244 <none> 8080/TCP 102m
sample-clusterip-vip ClusterIP 10.96.5.28 <none> 8080/TCP 73m
sample-externalip ClusterIP 10.96.235.233 172.22.0.2,172.22.0.3 8080/TCP 30m
sample-nodeport NodePort 10.96.106.168 <none> 8080:30080/TCP 4s
sample-nodeport サービスは、 クラスタ内部からは ClusterIP と同様に sample-nodeport:8080
で受け付けられ、 クラスタ外部からは 0.0.0.0:30080
でトラフィックを受け付けるようになる
が、こちらもクラスタ外部からのアクセスができなかった
調べてみたところ kindの場合は nodeport に指定した値を extraPortMapping として指定して受け付けられるようにする必要があった
イメージ的には kind の nodeは docker コンテナで起動しているので、コンテナ内部 (=クラスタ)で公開されている 30080 ポートを dockerコンテナの外部から受け付けられるようにポートマッピングが必要ということだと理解した
そこで kindの設定ファイルを修正してみた。portMappingは control-plane に対して設定する模様。ちなみに containerPort
を NodePort
に合わせる必要があり、 hostPort
の方は クラスタ外部として dockerコンテナ側が受けるポートとなるので、必ずしもNodePortに合わせる必要はなく 80
とか 8080
とかでも良い
設定ファイルを修正したら、kind 再起動(delete -> create)し改めてServiceリソースを作成した。
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30080
hostPort: 30080
protocol: TCP
- role: worker
- role: worker
- role: worker
# macのターミナルからcurlを実行
❯ curl http://0.0.0.0:30080
<!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コンテナの状況はこんな感じ
❯ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5e4e7e9096e8 kindest/node:v1.31.0 "/usr/local/bin/entr…" 3 hours ago Up 3 hours kind-worker2
c96f3ac4fe5c kindest/node:v1.31.0 "/usr/local/bin/entr…" 3 hours ago Up 3 hours kind-worker
9966a8a663c1 kindest/node:v1.31.0 "/usr/local/bin/entr…" 3 hours ago Up 3 hours 0.0.0.0:30080->30080/tcp, 127.0.0.1:60600->6443/tcp kind-control-plane
f783f69751d2 kindest/node:v1.31.0 "/usr/local/bin/entr…" 3 hours ago Up 3 hours kind-worker3
LoadBalancer サービス
クラス外のLoadBalancerに仮想IPを払い出すことで、このIPからアクセスすることができる。
MacなどのローカルでDocker環境を使っている場合は仮想IPの代わりに localhost (127.0.0.1)でアクセスできるみたいなので試す
マニフェスト。spec.ports[0].nodePort
だけど、LoadBalancerを使うときはNodePortが自動で割り振られるらしく、ここではそのポート番号を固定化している。実際にどこで使うのかはわからない
apiVersion: v1
kind: Service
metadata:
name: sample-lb
spec:
type: LoadBalancer
ports:
- name: http-port
protocol: TCP
port: 8080
targetPort: 80
nodePort: 30082
selector:
app: sample-app
サービスリソースを確認
❯ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3h6m
sample-clusterip ClusterIP 10.96.81.244 <none> 8080/TCP 133m
sample-clusterip-vip ClusterIP 10.96.5.28 <none> 8080/TCP 104m
sample-externalip ClusterIP 10.96.235.233 172.22.0.2,172.22.0.3 8080/TCP 61m
sample-lb LoadBalancer 10.96.221.147 <pending> 8080:30082/TCP 5s
sample-nodeport NodePort 10.96.106.168 <none> 8080:30080/TCP 31m
作られたが、EXTERNAL-IP が <pending>
となっているのが怪しい
案の定、IPは割り当てられておらず、外部からもlocalhostではリクエストができなかった
調べてみると、Cloud Provider KIND をインストールする必要がある模様。(あと、Goも)
Cloud Provider KIND が、外部LoadBalancerとして クラスタのサービスと連動して IPアドレスを払い出してくれる
こっちの手順がわかりやすかった
sudo install ~/go/bin/cloud-provider-kind /usr/local/bin
# go のインストール
brew install go
# cloud-provider-kind のインストール
go install sigs.k8s.io/cloud-provider-kind@latest
# バイナリファイルのインストールも必要だった
sudo install ~/go/bin/cloud-provider-kind /usr/local/bin
# kind を立ち上げる
kind create cluster --config .kind-multi-nodes-config.yaml
# cloud-provider-kind を起動する
sudo cloud-provider-kind
# Serviceを作成 (別ターミナルで実施
kubectl apply -f ./chapter06/sample-lb.yaml
# Serviceを確認すると、EXTERNAL-IP が入っている
> kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11m
sample-lb LoadBalancer 10.96.131.27 172.22.0.6 8080:30082/TCP 41s
# curl を実行
❯ curl http://172.22.0.6: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>
今更だけど 検証用Pod (nginx)の index.html に hostname を表示させて 負荷分散されていることを確認する
# index.html をPodのホスト名に書き換える
for PODNAME in `kubectl get pods -l app=sample-app -o jsonpath='{.items[*].metadata.name}'`; do kubectl exec -it ${PODNAME} -- cp /etc/hostname /usr/share/nginx/html/index.html; done
# podの確認 (3つ起動中)
❯ kubectl get pods
NAME READY STATUS RESTARTS AGE
sample-deployment-867c4b7886-9nxn5 1/1 Running 0 27m
sample-deployment-867c4b7886-fks9d 1/1 Running 0 27m
sample-deployment-867c4b7886-th6wj 1/1 Running 0 27m
# Loadbalancerに払い出された外部IPへのリクエストを10回繰り返し呼ばれたPodを確認する
❯ for _ in {1..10}; do
curl http://172.22.0.6:8080
done
sample-deployment-867c4b7886-th6wj
sample-deployment-867c4b7886-9nxn5
sample-deployment-867c4b7886-fks9d
sample-deployment-867c4b7886-fks9d
sample-deployment-867c4b7886-fks9d
sample-deployment-867c4b7886-fks9d
sample-deployment-867c4b7886-th6wj
sample-deployment-867c4b7886-th6wj
sample-deployment-867c4b7886-9nxn5
sample-deployment-867c4b7886-9nxn5
OK!
Ingress
Ingress Nginx Controller を使って試しているけど、defaultの時だけ分散してくれない?もしくは分散しているけど Podが正常に処理できていない?
Nginx Controller はこれを参考にしている
kindの設定を修正
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- containerPort: 30080
hostPort: 30080
protocol: TCP
- role: worker
- role: worker
- role: worker
Ingress の インストール
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
確認用アプリのマニフェスト
apiVersion: v1
kind: Service
metadata:
name: sample-ingress-svc-1
spec:
ports:
- name: http-port
protocol: TCP
port: 8888
targetPort: 80
selector:
ingress-app: sample1
---
apiVersion: v1
kind: Pod
metadata:
name: sample-ingress-apps-1
labels:
ingress-app: sample1
spec:
containers:
- name: nginx-container
image: nginx:1.27
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: sample-ingress-svc-2
spec:
ports:
- name: http-port
protocol: TCP
port: 8888
targetPort: 80
selector:
ingress-app: sample2
---
apiVersion: v1
kind: Pod
metadata:
name: sample-ingress-apps-2
labels:
ingress-app: sample2
spec:
containers:
- name: nginx-container
image: nginx:1.27
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: sample-ingress-default
spec:
ports:
- name: http-port
protocol: TCP
port: 8888
targetPort: 80
selector:
ingress-app: default
---
apiVersion: v1
kind: Pod
metadata:
name: sample-ingress-apps-default
labels:
ingress-app: default
spec:
containers:
- name: nginx-container
image: nginx:1.27
ports:
- containerPort: 80
ingressリソース
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sample-ingress
spec:
ingressClassName: nginx
rules:
- http:
paths:
- pathType: Prefix
path: /path1
backend:
service:
name: sample-ingress-svc-1
port:
number: 8888
- pathType: Prefix
path: /path2
backend:
service:
name: sample-ingress-svc-2
port:
number: 8888
defaultBackend:
service:
name: sample-ingress-default
port:
number: 8888
curlで調べると path1 と path2は返ってくる
❯ curl http://0.0.0.0/path1/
sample-ingress-apps-1
❯ curl http://0.0.0.0/path2/
sample-ingress-apps-2
# defaultだけ404
❯ curl http://0.0.0.0/
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
ただし、この404 が ingressコントローラから返ってきたものか、sample-ingress-app-default Podから返ってきたものかが不明。
sample-ingress-app-default の access.log が、コンソールに流れていたためみられない??
AWSのガイドを見ながら色々試してみる
これ見てて気づいたけど、Serviceリソースで targetPort を指定する際に、selectorに指定したpodの ports.name
を指定することで その containerPort を参照してくれるっぽい
同様に Ingressリソースでもserviceのportを指定するときに service.name.ports.name
で portを参照してくれる。
つまりポート番号の一貫性が保たれる?