chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

k2s LBを設定する(リトライ)

背景:仕事で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>

今回構築したLB+Nginxのサーバ構成は以下

上記構成を実現するための設定ファイル
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構成を組んだ時の構成図が書けたのも、「完全ガイド」のお陰でございます。

「完全ガイド」はかなり分厚い本らしいのですが*1、深い所まで理解した人が執筆されているおかげか、読んでいてK8Sの機能や概念がすんなり入ってくる印象です。解説される機能の幅と解説の詳細さがベストバランスという感じです。K8Sを使って仕事をする人にはいろいろ助けになる書籍ではないかと思います。
■仕事レベルとの差異
K8Sの入り口までは学んだが、、仕事のレベルとは程遠い。今後学ぶべきは、、(1)ロードバランサIngress(ALB)、(2)コンテナイメージを管理するリポジトリ、Dockerイメージのビルド,(3)多重化構成、キャパシティ管理 , (4)AWSで満たすべき各種権限(Policy/Role)、(5) CI/CD化、といった所か。。

*1:自分はKindleなので物理的な量が分からず