背景:仕事でK8Sを使う必要性が出てきた。k8sを理解する必要あり
アプローチ:K8S解説本を読みながら、ローカル環境でK8Sを動かして仕組みを理解する
結論:Pod + LBの構成で、nginxを動かすところまではできた (kind (Kubernetes in Docker)を使用)
詳細:
昨日はkindを使ってUbuntu上にk8s環境を構築、nginxのPODを走らせるまでは進んだが、Ubuntu側のネットワークから、nginxのホストにLB経由で接続することができなかった。再度トライする
再起動したUbuntu上で、kindが動いていて、podも稼働している。試行錯誤したLBService類は一旦全て消した。
$ kubectl get all NAME READY STATUS RESTARTS AGE pod/hello-world 0/1 Completed 0 15h pod/nginx 1/1 Running 1 (4m39s ago) 14h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15h $ kubectl describe pod nginx Name: nginx Namespace: default Priority: 0 Service Account: default Node: kind-worker2/172.18.0.2 Start Time: Sat, 10 Jun 2023 21:18:46 +0900 Labels: <none> Annotations: <none> Status: Running IP: 10.244.2.2 IPs: IP: 10.244.2.2
Pod同士が通信するInternalNetworkにおいて、nginxのIPは、10.244.2.2であると。
もう一度NodeやContainerに入って確認する
Node内で確認したIP
# ip address 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: vethcf9343aa@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 86:95:4e:35:9e:e7 brd ff:ff:ff:ff:ff:ff link-netns cni-ba734f88-cf32-09d3-7cb1-0f3d7d23e39c inet 10.244.2.1/32 scope global vethcf9343aa valid_lft forever preferred_lft forever 5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
Pod内で確認したIP
$ kubectl exec -it nginx -- /bin/bash # ip address 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 92:8e:29:95:85:71 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.244.2.2/24 brd 10.244.2.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::908e:29ff:fe95:8571/64 scope link valid_lft forever preferred_lft forever # apt update # apt install iproute2 # ss -napt | grep LISTEN LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1,fd=6)) LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=1,fd=7))
再度解説本に従って、lb-config.yamlを作成
apiVersion: v1 kind: Service metadata: name: ngx-lb spec: type: LoadBalancer selector: app: nginx ports: - name: "http-port" protocol: "TCP" port: 8080 targetPort: 80
読み込ませて確認
$ kubectl apply -f lb_config.yaml service/ngx-lb created $ kubectl describe service ngx-lb Name: ngx-lb Namespace: default Labels: <none> Annotations: <none> Selector: app=nginx Type: LoadBalancer IP Family Policy: SingleStack IP Families: IPv4 IP: 10.96.187.65 IPs: 10.96.187.65 LoadBalancer Ingress: 172.18.255.200 Port: http-port 8080/TCP TargetPort: 80/TCP NodePort: http-port 30028/TCP Endpoints: <none> Session Affinity: None External Traffic Policy: Cluster Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal IPAllocated 103s metallb-controller Assigned IP ["172.18.255.200"] $ curl -v http://172.18.255.200:8080 * Trying 172.18.255.200:8080... * connect to 172.18.255.200 port 8080 failed: No route to host * Failed to connect to 172.18.255.200 port 8080 after 3058 ms: No route to host * Closing connection 0 curl: (7) Failed to connect to 172.18.255.200 port 8080 after 3058 ms: No route to host
やっぱりエラーになる。Endpointsが <none>だし。。そもそも、NodePortが作られていないのがおかしいのではないか。
kind的な書き方をしないとだめなのではなかろうか。
kindのドキュメントに従ってもう一度やってみる。振り出し用IPレンジの設定はOKだろう
$ docker network inspect -f '{{.IPAM.Config}}' kind [{172.18.0.0/16 172.18.0.1 map[]} {fc00:f853:ccd:e793::/64 fc00:f853:ccd:e793::1 map[]}] $ kubectl get IPAddressPool -n metallb-system NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES example true false ["172.18.255.200-172.18.255.250"] $ kubectl get L2Advertisement -n metallb-system NAME IPADDRESSPOOLS IPADDRESSPOOL SELECTORS INTERFACES empty
LB Serviceのdemoを動かしてみる
kind: Pod apiVersion: v1 metadata: name: foo-app labels: app: http-echo spec: containers: - name: foo-app image: hashicorp/http-echo:0.2.3 args: - "-text=foo" --- kind: Pod apiVersion: v1 metadata: name: bar-app labels: app: http-echo spec: containers: - name: bar-app image: hashicorp/http-echo:0.2.3 args: - "-text=bar" --- kind: Service apiVersion: v1 metadata: name: foo-service spec: type: LoadBalancer selector: app: http-echo ports: - port: 5678
$ kubectl describe service foo-service Name: foo-service Namespace: default Labels: <none> Annotations: <none> Selector: app=http-echo Type: LoadBalancer IP Family Policy: SingleStack IP Families: IPv4 IP: 10.96.38.248 IPs: 10.96.38.248 LoadBalancer Ingress: 172.18.255.200 Port: <unset> 5678/TCP TargetPort: 5678/TCP NodePort: <unset> 31116/TCP Endpoints: 10.244.1.3:5678,10.244.2.3:5678 Session Affinity: None $ kubectl get all NAME READY STATUS RESTARTS AGE pod/bar-app 1/1 Running 0 4m12s pod/foo-app 1/1 Running 0 4m12s pod/nginx 1/1 Running 1 (92m ago) 16h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/foo-service LoadBalancer 10.96.38.248 172.18.255.200 5678:31116/TCP 4m12s service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16h $ kubectl describe pod foo-app Name: foo-app Namespace: default Priority: 0 Service Account: default Node: kind-worker2/172.18.0.2 Start Time: Sun, 11 Jun 2023 13:28:04 +0900 Labels: app=http-echo Annotations: <none> Status: Running IP: 10.244.2.3
$ kubectl get svc/foo-service -o json *略* "status": { "loadBalancer": { "ingress": [ { "ip": "172.18.255.200" } ] } } }
連続的に呼び出してみると確かにバランスしている。NodePortもEndPointも設定されている。
$ while [ 1 ] ; do curl http://172.18.255.200:5678 ; sleep 1 ; done bar bar foo foo bar foo bar bar
nginxに対してLBが設定できないのは、Lables: <none>が原因ではないかと推測
$ kubectl describe pod nginx Name: nginx Namespace: default Priority: 0 Service Account: default Node: kind-worker2/172.18.0.2 Start Time: Sat, 10 Jun 2023 21:18:46 +0900 Labels: <none>
labelを追加したyamlを適用
$ cat nginx-pod.yaml apiVersion: v1 kind: Pod metadata: name: nginx labels: app: nginx spec: containers: - name: nginx image: nginx:latest $ kubectl apply -f nginx-pod.yaml pod/nginx configured $ kubectl describe pod nginx Name: nginx Namespace: default Priority: 0 Service Account: default Node: kind-worker2/172.18.0.2 Start Time: Sat, 10 Jun 2023 21:18:46 +0900 Labels: app=nginx Annotations: <none> Status: Running IP: 10.244.2.2
Labelが付いたところで、再度LB設定を行う
$ cat lb-ngin-service.yaml kind: Service apiVersion: v1 metadata: name: lb-nginx spec: type: LoadBalancer selector: app: nginx ports: - port: 8080 $ kubectl apply -f lb- lb-demo.yaml lb-ngin-service.yaml lb-service.yaml $ kubectl apply -f lb-ngin-service.yaml service/lb-nginx created $ kubectl describe service lb-nginx Name: lb-nginx Namespace: default Labels: <none> Annotations: <none> Selector: app=nginx Type: LoadBalancer IP Family Policy: SingleStack IP Families: IPv4 IP: 10.96.242.14 IPs: 10.96.242.14 LoadBalancer Ingress: 172.18.255.201 Port: <unset> 8080/TCP TargetPort: 8080/TCP NodePort: <unset> 31628/TCP Endpoints: 10.244.2.2:8080 Session Affinity: None External Traffic Policy: Cluster
今度は、NodePort, Endpointsも設定されている。curlで確認
$ curl -v http://172.18.255.201:8080 * Trying 172.18.255.201:8080... * connect to 172.18.255.201 port 8080 failed: Connection refused * Failed to connect to 172.18.255.201 port 8080 after 0 ms: Connection refused * Closing connection 0 curl: (7) Failed to connect to 172.18.255.201 port 8080 after 0 ms: Connection refused
これはTargetPortが8080になっているためと思われる。再度修正
$ cat lb-ngin-service.yaml kind: Service apiVersion: v1 metadata: name: lb-nginx spec: type: LoadBalancer selector: app: nginx ports: - port: 8080 targetPort: 80 $ kubectl apply -f lb-ngin-service.yaml service/lb-nginx created $ kubectl describe service lb-nginx Name: lb-nginx Namespace: default Labels: <none> Annotations: <none> Selector: app=nginx Type: LoadBalancer IP Family Policy: SingleStack IP Families: IPv4 IP: 10.96.242.14 IPs: 10.96.242.14 LoadBalancer Ingress: 172.18.255.201 Port: <unset> 8080/TCP TargetPort: 80/TCP NodePort: <unset> 31628/TCP Endpoints: 10.244.2.2:80 Session Affinity: None External Traffic Policy: Cluster
TargetPortが80になってるのを確認。curlで叩くとHTMLが返却された。
$ curl http://172.18.255.201: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>
上記構成を実現するための設定ファイル
file: nginx-pod.yaml
apiVersion: v1 kind: Pod metadata: name: nginx labels: app: nginx spec: containers: - name: nginx image: nginx:latest
file: lb-nginx-service.yaml
apiVersion: v1 kind: Service metadata: name: lb-nginx spec: type: LoadBalancer selector: app: nginx ports: - port: 8080 targetPort: 80
■補足 バージョン確認
$ kubectl version --short Flag --short has been deprecated, and will be removed in the future. The --short output will become the default. Client Version: v1.27.2 Kustomize Version: v5.0.1 Server Version: v1.27.1
■K8S 学習パス
K8Sは多機能かつ、複雑であり、どこから勉強を開始したらよいのやらという感じと思います。
自分の場合は、、AWS EKS HandsOnをやってK8Sの全体像を薄く理解して、次に「Dockerから入るKubernetes」をざっと読んで、「Kubernetes完全ガイド」を詳しく読んで勉強しています。
「Dockerから入る・・」は表紙の印象で易しそうな感じですが、実際は手を動かす演習が大量に入っています。「Dockerから入る・・」を読みながら、HelloWorldだけのPODを走らせたり、Nginx+LBの構成を組んでみたりしました。Nginxの入ったPodを単体で動かして上位にLBを配置する構成で、Podとの疎通が行えない問題が発生しました。詳しい人だとログを調べて原因を絞り込めるのかもしれませんが、なぜEndpointが生成されないのか?まで突き止められませんでした。「Kubernetes完全ガイド」は細かい所まで丁寧に解説されていて、K8Sのネットワークサービスがだいたい理解でき、原因も試行錯誤で調べることができました。K8SでLB+Pod構成を組んだ時の構成図が書けたのも、「完全ガイド」のお陰でございます。
■仕事レベルとの差異
K8Sの入り口までは学んだが、、仕事のレベルとは程遠い。今後学ぶべきは、、(1)ロードバランサIngress(ALB)、(2)コンテナイメージを管理するリポジトリ、Dockerイメージのビルド,(3)多重化構成、キャパシティ管理 , (4)AWSで満たすべき各種権限(Policy/Role)、(5) CI/CD化、といった所か。。