Kubernetes Service(2): LoadBalancer(MetalLB)

  • kkumtree

2024-10-02T12:54:17+09:00

kans
kind
metallb
loadbalancer
kubernetes

์ง€๋‚œ ํฌ์ŠคํŒ…, Kubernetes Service(1): ClusterIP/NodePort์— ์ด์–ด LoadBalancer Type์„ ๊ฐ€๋ณ๊ฒŒ ์‚ดํŽด๋ณด๊ณ , MetalLB๋ฅผ ๊ฐ€๋ณ๊ฒŒ ๋ถ™์—ฌ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

CloudNet@์—์„œ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋Š” K8s Advanced Network Study(์ดํ•˜, KANS)๋ฅผ ํ†ตํ•ด ํ•™์Šตํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

1. LoadBalancer Type

Service(1)์—์„œ ์–ธ๊ธ‰๋œ ๋ถ€๋ถ„์€ ๊ฑฐ๋‘์ ˆ๋ฏธํ•˜๊ณ , ์ถ”๊ฐ€๋กœ ์ ์„ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด, ์•„๋ž˜ ํ•œ ์ค„์ด ์žˆ์Šต๋‹ˆ๋‹ค.

You can define a LoadBalancer Service by disabling the load balancer NodePort allocation.

๊ธ€์ž ๊ทธ๋Œ€๋กœ LB์˜ NodePort ํ• ๋‹น์„ ๋น„ํ™œ์„ฑํ•˜์—ฌ, LoadBalancer Service๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
Disabling load balancer NodePort allocation ๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณด๋‹ˆ,
v1.24๋ถ€ํ„ฐ Stable ์ƒํƒœ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

ํ•ด๋‹น ๋ฌธ์„œ์—์„œ ํ•ต์‹ฌ๋งŒ ์ถ”๋ฆฌ์ž๋ฉด…

  • spec.allocateLoadBalancerNodePorts: true (default)
  • Traffic์„ Pod๋กœ ์ง์ ‘ Routingํ•˜๋Š” LB๋ฅผ ๊ตฌํ˜„(implementation)ํ•˜๊ณ ์ž ํ•  ๋•Œ๋งŒ false๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด, ์ฆ‰ Node port๊ฐ€ ํ• ๋‹น๋œ ๊ธฐ์กด ๋…ธ๋“œ์— false๊ฐ€ ์„ค์ •๋˜๋ฉด, Node ports๋Š” ์ž๋™์œผ๋กœ ํ• ๋‹น ํ•ด์ œ๋˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
    • **not** be de-allocated automatically
    • ๋ชจ๋“  ์„œ๋น„์Šค์—์„œ ๋ช…์‹œ์ ์œผ๋กœ nodePorts ๋ฅผ ์ œ๊ฑฐํ•ด์•ผํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ์–ด์กฐ๊ฐ€ ๊ฝค๋‚˜ ์„ผ ํŽธ์ž…๋‹ˆ๋‹ค.

๊ทธ๋งŒ ์•Œ์•„๋ณด์ž

2. MetalLB

์Šคํ„ฐ๋”” ํ›„๋ฐ˜๋ถ€์— AWS EKS๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ํ˜„์žฌ๋Š” kind ํ™˜๊ฒฝ์—์„œ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ MetalLB๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฏธ ์ž‘๋…„์— ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋กœ kubeadm+virtualbox ์กฐํ•ฉ์œผ๋กœ ๊ตฌ์ถ•ํ•  ๋•Œ ์™ธ๋ถ€ ์ ‘๊ทผ์„ ์œ„ํ•ด MetalLB๋ฅผ ์จ๋ดค๊ณ ,
V-raptor SQ nano๋กœ ์ด๊ฒƒ์ €๊ฒƒ ๋งŒ์ ธ๋ณผ๋•Œ, Canonical microk8s์—์„œ๋„ metalLB addon์„ ์ง€์›ํ•˜๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ๋œ ๋ฐ”,

์ด๋ฏธ ํ˜„์—… ๋ถ„๋“ค์—๊ฒŒ๋Š” ์นœ์ˆ™ํ•œ ํˆด์ด๋ผ ์ƒ๊ฐํ•˜๊ณ  ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค(?).
์‚ฌ์‹ค ๋‹น์‹œ์— ์‚ฌ์„œ ๊ณ ์ƒ์„ ํ•ด์„œ, ํฌ์ŠคํŒ…์„ ๋‚จ๊ฒจ๋†จ์„ ๊ฒƒ ๊ฐ™์•˜๋Š”๋ฐ ์ฝ”๋“œ๋กœ๋งŒ ์กด์žฌํ•˜๋„ค์š”. ๋น ๋ฅธ ์†์ ˆ.
๊ทธ๊ฑด ๊ทธ๋ ‡๊ณ  microk8s์—์„œ metalLB addon ํ‹ฐ์ปค๊ฐ€ v1.17๋กœ ๋‚จ์•„์žˆ์–ด ์‹ฌํžˆ ๋ถˆํŽธํ•จ์„ ๊ฐ์ถœ ์ˆ˜ ์—†๊ตฐ์š”

์ผ๋‹จ BGP๋Š” ํด๋Ÿฌ์Šคํ„ฐ๋ง์„ ๋‘ ๊ฐœ๋ฅผ ํ•ด์•ผ๋˜์„œ ์ข€ ๊ทธ๋ ‡๊ณ , Layer2 ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

3. kind ๊ตฌ์„ฑ

a. ์ดˆ๊ธฐ ๊ตฌ์„ฑ

ํ˜„์žฌ ์ž‘์„ฑ ์ค‘์ธ ๋””๋ฐ”์ด์Šค์— kind๊ฐ€ ๊น”๋ ค์žˆ์ง€ ์•Š์•„ ๊ธฐ์กด ํฌ์ŠคํŒ…(๋ฆฌ๋ˆ…์Šค์— KIND ์„ค์น˜ํ•˜๊ธฐ w/golang)๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์„ค์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค.
๊ธฐ์กด ํฌ์ŠคํŒ…(KIND ํ†บ์•„๋ณด๊ธฐ)๊ณผ ๋‹ฌ๋ผ์ง„ ์ ์ด ์žˆ๋‹ค๋ฉด, kindest/node:v1.31.0์œผ๋กœ ๋ฒ„์ „์„ ์˜ฌ๋ ค ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

โฏ go version
go version go1.22.2 linux/amd64
โฏ go env GOPATH
/home/kkumtree/go
โฏ go install sigs.k8s.io/[email protected]
go: downloading sigs.k8s.io/kind v0.24.0
go: downloading github.com/spf13/pflag v1.0.5
go: downloading github.com/alessio/shellescape v1.4.2
go: downloading github.com/spf13/cobra v1.8.0
go: downloading github.com/pkg/errors v0.9.1
go: downloading github.com/mattn/go-isatty v0.0.20
go: downloading golang.org/x/sys v0.6.0
go: downloading github.com/pelletier/go-toml v1.9.5
go: downloading github.com/BurntSushi/toml v1.4.0
go: downloading github.com/evanphx/json-patch/v5 v5.6.0
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading sigs.k8s.io/yaml v1.4.0
go: downloading github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2
โฏ vi .profile # ๋™์  ์ง€์ •ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ž์„ธํ•œ๊ฑด ์ด์ „ ํฌ์ŠคํŒ… ์ฐธ์กฐ  
โฏ source .profile
โฏ kind version
kind v0.24.0 go1.22.2 linux/amd64

b. kind ํด๋Ÿฌ์Šคํ„ฐ yaml ๊ตฌ์„ฑ ๋ฐ ๊ตฌ์ถ•

๋‹น์—ฐํ•œ ์ด์•ผ๊ธฐ์ง€๋งŒ, ์ด๋ฏธ์ง€ ํฌ๊ธฐ๊ฐ€ 900MB๋ฅผ ๋„˜์–ด์„œ๊ธฐ ๋•Œ๋ฌธ์— ์ฒ˜์Œ ๋„์šธ ์‹œ ์‹œ๊ฐ„์ด ๋‹ค์†Œ ์†Œ์š”๋ฉ๋‹ˆ๋‹ค.

(Network)

  • Nodeํ™”๋œ ์ปจํ…Œ์ด๋„ˆ network cidr: 172.18.0.0/16
  • Pod network cidr: 10.10.0.0/16
    • 10.10.1.0/24, 10.10.2.0/24, 10.10.3.0/24, 10.10.4.0/24
      ์ชผ๊ฐœ์ง€๋Š” ์ด์œ  ๋“ค์—ˆ๋˜ ๊ฑฐ ๊ฐ™์€๋ฐ ๋˜ ์žŠ์—ˆ๋‹ค…
  • Service network cidr: 10.200.1.0/24

(Entry)

  • featureGates[k8s]
    • Alpha,Beta ์ƒํƒœ์˜ ๊ธฐ๋Šฅ ๊ด€๋ฆฌ
    • InPlacePodVerticalScaling: false/alpha/1.27/-, ์ œ๊ณง๋‚ด
    • MultiCIDRServiceAllocator: false/beta/1.31/-, IPAddress ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Service ClusterIP์— ๋Œ€ํ•œ IP ์ฃผ์†Œ ํ• ๋‹น ์ถ”์ 
  • extraPortMappings: ํ˜ธ์ŠคํŠธ์™€ ์ปจํ…Œ์ด๋„ˆ ๊ฐ„ ํฌํŠธ ๋งคํ•‘
    • 30000~30004
  • Topology Aware Routing
    • nodes.labels.topology.kubernetes.io/zone: ์ด๊ฒƒ์€ ๋Œ€์ฒด ๋ฌด์—‡์ธ๊ฐ€?์— ๋Œ€ํ•œ ํ•ด๋‹ต
    • <= v1.27: Topology Aware Hints ๋กœ ๋ถˆ๋ฆผ.
    • EndpointSlice controller: ํ• ๋‹น ๊ฐ€๋Šฅํ•œ CPU ์ฝ”์–ด ์ˆ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์—”๋“œํฌ์ธํŠธ ๋ฐ kube-proxy ํ• ๋‹น
    • ๊ตญ๋ฌธ: ํ† ํด๋กœ์ง€ ์ธ์ง€ ํžŒํŠธ, ํ† ํด๋กœ์ง€ ํ‚ค
      • deprecated: ์ดํ›„ ์—†์–ด์งˆ ์ˆ˜ ์žˆ์Œ
  • [kubeadmConfigPatches]
    • ์ œ๊ณง๋‚ด… ์˜ ๋А๋‚Œ์ด ์†”์†” ๋‚˜์ง€๋งŒ
    • extraArgs.runtime-config: api/all=true ์˜๋ฏธ๋Š”?
    • ์ฑ„์ฐํ”ผํ‹ฐ์ฝ”ํŒŒ์ผ๋Ÿฟ์—๊ฒŒ ๋ฌผ์–ด๋ดค๋”๋‹ˆ, ๋Œ€์ถฉ ๋งํฌ๋ฅผ ๋˜์ ธ์คฌ์Šต๋‹ˆ๋‹ค.
    • Runtime Configuration
    • ํฌ๋งท: --runtime-config <comma-separated 'key=value' pairs>
    • ์‹ค์ œ: -runtime-config=api/all=true
    • ํ•ด๋‹น ํŒŒ๋ผ๋ฏธํ„ฐ: api/all=true|false controls all API versions
    • ๋‹คํ–‰ํžˆ ๋งํฌ๋Š” ์ž˜ ์ฃผ์…จ๊ตฐ์š”.
    • ์ด๋Ÿฐ ์ˆญ์•…ํ•œ ๊ฑธ ๋‹ค๋“ค ์–ด๋–ป๊ฒŒ ์“ฐ์‹œ๋Š” ๊ฑฐ์ง€ @.@
cat <<EOT> kind-metallb-test.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
  "InPlacePodVerticalScaling": true
  "MultiCIDRServiceAllocator": true
nodes:
- role: control-plane
  labels:
    mynode: control-plane
    topology.kubernetes.io/zone: ap-northeast-2a
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
  - containerPort: 30002
    hostPort: 30002
  - containerPort: 30003
    hostPort: 30003
  - containerPort: 30004
    hostPort: 30004
  kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    apiServer:
      extraArgs:
        runtime-config: api/all=true
    controllerManager:
      extraArgs:
        bind-address: 0.0.0.0
    etcd:
      local:
        extraArgs:
          listen-metrics-urls: http://0.0.0.0:2381
    scheduler:
      extraArgs:
        bind-address: 0.0.0.0
  - |
    kind: KubeProxyConfiguration
    metricsBindAddress: 0.0.0.0
- role: worker
  labels:
    mynode: worker1
    topology.kubernetes.io/zone: ap-northeast-2a
- role: worker
  labels:
    mynode: worker2
    topology.kubernetes.io/zone: ap-northeast-2b
- role: worker
  labels:
    mynode: worker3
    topology.kubernetes.io/zone: ap-northeast-2c
networking:
  podSubnet: 10.10.0.0/16
  serviceSubnet: 10.200.1.0/24
EOT

์ดํ›„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

kind create cluster --config kind-metallb-test.yaml --name myk8s --image kindest/node:v1.31.0
# Install additional tools 
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bsdmainutils bridge-utils net-tools dnsutils ipset ipvsadm nfacct tcpdump ngrep iputils-ping arping git vim arp-scan -y'

4. ํ…Œ์ŠคํŠธ Pod ๊ตฌ์„ฑ

a. ํ™˜๊ฒฝ ๊ธฐ๋ณธ์ •๋ณด ํ™•์ธ

# cidr check
โฏ kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
                            "--service-cluster-ip-range=10.200.1.0/24",
                            "--cluster-cidr=10.10.0.0/16",
# confirm kube-proxy mode: iptables proxy mode
โฏ kubectl describe  configmap -n kube-system kube-proxy | grep mode
mode: iptables
# iptables info  
# ์ถœ๋ ฅ๊ฐ’์€ ๋„ˆ๋ฌด ๊ธธ์–ด์„œ ์ƒ๋žต / MetalLB ์„ค์น˜ ํ›„ ๋Œ€์กฐ์šฉ  
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-control-plane  iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker  iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker2 iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker3 iptables -t $i -S ; echo; done

b. ํ…Œ์ŠคํŠธ Pod ์ƒ์„ฑ

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: webpod1
  labels:
    app: webpod
spec:
  nodeName: myk8s-worker
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: webpod2
  labels:
    app: webpod
spec:
  nodeName: myk8s-worker2
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
EOF

# pod/webpod1 created
# pod/webpod2 created

5. MetalLB ์„ค์น˜

BGP๋ชจ๋“œ๋Š” ์˜ˆ์ „์— ์‹œ๋„๋„ ํ•ด๋ดค์—ˆ์ง€๋งŒ, ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ด์œ ๋กœ L2 Layer ๋ฐฉ์‹์œผ๋กœ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ : kube-proxy ์˜ ipvs ๋ชจ๋“œ ์‚ฌ์šฉ ์‹œ ‘strictARP: true’ ์„ค์ • ํ•„์š”

  • ๊ทธ๋ƒฅ Documentation ๋ณด์„ธ์š”
    https://metallb.universe.tf/installation/
  • ์Šคํ„ฐ๋”” ์‹œ๊ฐ„์—๋Š” Manifest๋กœ ์ง„ํ–‰ํ–ˆ์ง€๋งŒ,
    ์˜ค๋Š˜๋„ ์ฒญ๊ฐœ๊ตฌ๋ฆฌ๋Š” Operator๋กœ ์„ค์น˜ํ•  ๊ฒ๋‹ˆ๋‹ค (?_?)
  • OperatorHub: metallb-operator
  • (์ฐธ๊ณ ์šฉ) FRR๋ชจ๋“œ
    • BGP์„ธ์…˜์„ BFD์„ธ์…˜์œผ๋กœ ๋ฐฑ์—…
    • BGP Only ๋Œ€๋น„ ๋น ๋ฅด๊ฒŒ ์˜ค๋ฅ˜๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
    • BFD?: Docs/Juniper Networks
    • Bidirectional Forwarding Detection

a. GitHub, GitHub๋ฅผ ๋ณด์ž…

์Œ, ์˜คํผ๋ ˆ์ดํ„ฐํ—ˆ๋ธŒ์— ๋“ค์–ด์™”๋”๋‹ˆ ๋ญ๊ฐ€ ๋ญ”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. GitHub๋กœ ์žฌ๋น ๋ฅด๊ฒŒ ๋„?๋ง์นฉ๋‹ˆ๋‹ค.

๋‹คํ–‰ํžˆ README๋Š” ๋ฉ€์ฉกํ•˜๋„ค์š”. ์•„๋‹ˆ ์ƒ๊ฐ๋ณด๋‹ค ๊ดœ์ฐฎ์€๋ฐ์š”?

  • kind๋Š” ์›๋ž˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์šฉ์ธ์ง€๋ผ, e2eํ…Œ์ŠคํŠธ๊นŒ์ง€ ์ œ๊ณตํ•˜๋„ค์š”.
git clone https://github.com/metallb/metallb-operator.git
cd metallb-operator
make deploy
cat << EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: MetalLB
metadata:
  name: metallb
  namespace: metallb-system
EOF
make test
make test-e2e

๊ทธ์ € Quick Start ๋‹ฅ๋Œํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰ ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€๊ฐ€ ์•„๋ž˜์™€ ๊ฐ™์€๋ฐ…
๋ญ ๊ดœ์ฐฎ๊ฒ ์ฃ  Openshift: instruct the cluster network operator to deploy frrk8s

b. Quick Start

# ์•„๋ž˜ ์ปค๋งจ๋“œ ์‘์šฉ
# kubectl apply -f bin/metallb-operator.yaml 
# CRD (๋ฐ ์˜คํผ๋ ˆ์ดํ„ฐ ๋ฐฐํฌ ์ปค๋งจ๋“œ) ๋‹ค์šด๋กœ๋“œ  
curl -LO https://raw.githubusercontent.com/metallb/metallb-operator/refs/heads/main/bin/metallb-operator.yaml 
# CRD ์ ์šฉ. ํ™•์ธํ•ด๋ณด๋‹ˆ ์˜คํผ๋ ˆ์ดํ„ฐ๋„ ํ•จ๊ป˜ ๋ฐฐํฌํ•˜๋Š” ๋ชจ์–‘  
kubectl apply -f metallb-operator.yaml

์•„๋ž˜์™€ ์œ ์‚ฌํ•˜๊ฒŒ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

โฏ curl -LO https://raw.githubusercontent.com/metallb/metallb-operator/refs/heads/main/bin/metallb-operator.yaml 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  233k  100  233k    0     0   799k      0 --:--:-- --:--:-- --:--:--  797k
โฏ kubectl apply -f metallb-operator.yaml
namespace/metallb-system created
customresourcedefinition.apiextensions.k8s.io/bfdprofiles.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgpadvertisements.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgppeers.metallb.io created
customresourcedefinition.apiextensions.k8s.io/communities.metallb.io created
customresourcedefinition.apiextensions.k8s.io/frrconfigurations.frrk8s.metallb.io created
customresourcedefinition.apiextensions.k8s.io/frrnodestates.frrk8s.metallb.io created
customresourcedefinition.apiextensions.k8s.io/ipaddresspools.metallb.io created
customresourcedefinition.apiextensions.k8s.io/l2advertisements.metallb.io created
customresourcedefinition.apiextensions.k8s.io/metallbs.metallb.io created
customresourcedefinition.apiextensions.k8s.io/servicel2statuses.metallb.io created
serviceaccount/manager-account created
role.rbac.authorization.k8s.io/metallb-manager-role created
clusterrole.rbac.authorization.k8s.io/metallb-manager-role created
rolebinding.rbac.authorization.k8s.io/metallb-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/metallb-manager-rolebinding created
secret/metallb-operator-webhook-server-cert created
secret/metallb-webhook-cert created
service/metallb-operator-webhook-service created
service/metallb-webhook-service created
deployment.apps/metallb-operator-controller-manager created
deployment.apps/metallb-operator-webhook-server created
validatingwebhookconfiguration.admissionregistration.k8s.io/metallb-operator-webhook-configuration created
validatingwebhookconfiguration.admissionregistration.k8s.io/metallb-webhook-configuration created
serviceaccount/controller created
serviceaccount/frr-k8s-daemon created
serviceaccount/speaker created
role.rbac.authorization.k8s.io/controller created
role.rbac.authorization.k8s.io/frr-k8s-daemon-role created
role.rbac.authorization.k8s.io/frr-k8s-daemon-scc created
role.rbac.authorization.k8s.io/pod-lister created
role.rbac.authorization.k8s.io/speaker created
clusterrole.rbac.authorization.k8s.io/metallb-system:kube-rbac-proxy created
clusterrole.rbac.authorization.k8s.io/frr-k8s-daemon-role created
clusterrole.rbac.authorization.k8s.io/frr-k8s-metrics-reader created
clusterrole.rbac.authorization.k8s.io/frr-k8s-proxy-role created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/controller created
rolebinding.rbac.authorization.k8s.io/frr-k8s-daemon-rolebinding created
rolebinding.rbac.authorization.k8s.io/frr-k8s-daemon-scc-binding created
rolebinding.rbac.authorization.k8s.io/pod-lister created
rolebinding.rbac.authorization.k8s.io/speaker created
clusterrolebinding.rbac.authorization.k8s.io/kube-rbac-proxy created
clusterrolebinding.rbac.authorization.k8s.io/frr-k8s-daemon-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/frr-k8s-proxy-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created

c. ์˜คํผ๋ ˆ์ดํ„ฐ ๊ด€๋ จ ๋ฆฌ์†Œ์Šค ํ™•์ธ

์˜คํผ๋ ˆ์ดํ„ฐ๊ฐ€ ์ œ๋Œ€๋กœ ์ ์šฉ๋˜์—ˆ๋Š”์ง€ ์ฒดํฌํ•ด๋ด…์‹œ๋‹ค.

CRD์— FRR๊ด€๋ จ ์ •์˜๋„ ๋“ค์–ด๊ฐ„๊ฑฐ ๊ฐ™์€๋ฐ ์ผ๋‹จ ๋ˆˆ์„ ๊ฐ๊ณ  ํ•ด๋ด…์‹œ๋‹ค.

โฏ kubectl get crd | grep metallb
bfdprofiles.metallb.io                2024-10-02T14:18:46Z
bgpadvertisements.metallb.io          2024-10-02T14:18:46Z
bgppeers.metallb.io                   2024-10-02T14:18:46Z
communities.metallb.io                2024-10-02T14:18:46Z
frrconfigurations.frrk8s.metallb.io   2024-10-02T14:18:46Z
frrnodestates.frrk8s.metallb.io       2024-10-02T14:18:46Z
ipaddresspools.metallb.io             2024-10-02T14:18:46Z
l2advertisements.metallb.io           2024-10-02T14:18:46Z
metallbs.metallb.io                   2024-10-02T14:18:46Z
servicel2statuses.metallb.io          2024-10-02T14:18:46Z

์•„๋ž˜๋ถ€ํ„ฐ๋Š” ์ดํ•ด์—†์ด ๋ฌด๋”ฐ๊ธฐ๋กœ ํ•œ๊ฑฐ๋ผ, ์–‘ํ•ด ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

NS, POD(deployment,replicaset), SVC, CM, SECRET, EP ๋‹ค ์…‹ํŒ…๋˜์—ˆ๋„ค์š”.

โฏ kubectl get all,configmap,secret,ep -n metallb-system
NAME                                                       READY   STATUS    RESTARTS   AGE
pod/metallb-operator-controller-manager-5dbc8fd577-bgczj   1/1     Running   0          13m
pod/metallb-operator-webhook-server-77d47cb764-9lcs8       1/1     Running   0          13m

NAME                                       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/metallb-operator-webhook-service   ClusterIP   10.200.1.159   <none>        443/TCP   13m
service/metallb-webhook-service            ClusterIP   10.200.1.149   <none>        443/TCP   13m

NAME                                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/metallb-operator-controller-manager   1/1     1            1           13m
deployment.apps/metallb-operator-webhook-server       1/1     1            1           13m

NAME                                                             DESIRED   CURRENT   READY   AGE
replicaset.apps/metallb-operator-controller-manager-5dbc8fd577   1         1         1       13m
replicaset.apps/metallb-operator-webhook-server-77d47cb764       1         1         1       13m

NAME                         DATA   AGE
configmap/kube-root-ca.crt   1      13m

NAME                                          TYPE     DATA   AGE
secret/metallb-operator-webhook-server-cert   Opaque   4      13m
secret/metallb-webhook-cert                   Opaque   4      13m

NAME                                         ENDPOINTS        AGE
endpoints/metallb-operator-webhook-service   10.10.2.2:9443   13m
endpoints/metallb-webhook-service            10.10.3.3:9443   13m

ํŒŒ๋“œ ๋‚ด์— kube-rbac-proxy ์ปจํ…Œ์ด๋„ˆ๋Š” ํ”„๋กœ๋ฉ”ํ…Œ์šฐ์Šค ์ต์Šคํฌํ„ฐ ์—ญํ•  ์ œ๊ณตํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

โฏ kubectl get pods -n metallb-system -l app=metallb -o jsonpath="{range .items[*]}{.metadata.name}{':\n'}{range .spec.containers[*]}{'  '}{.name}{' -> '}{.image}{'\n'}{end}{end}"
metallb-operator-webhook-server-77d47cb764-9lcs8:
  webhook-server -> quay.io/metallb/controller:main

metallb ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋””ํ”Œ๋กœ์ด๋จผํŠธ๋กœ ๋ฐฐํฌ๋œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

โฏ kubectl get ds,deploy -n metallb-system
NAME                                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/metallb-operator-controller-manager   1/1     1            1           20m
deployment.apps/metallb-operator-webhook-server       1/1     1            1           20m

์—ฌ๊ธฐ์„œ ์‚ด์ง ์‹ธํ•˜๋„ค์š”. ๊ทธ๋ƒฅ ๋‹ค ๋ฐ€์–ด๋ฒ„๋ฆฌ๊ณ  ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ํ• ๊นŒ;
speaker pods(speaker-lorem)๊ฐ€ ๋ณด์ด์ง€ ์•Š๋Š”๋ฐ, ์ด๊ฑฐ BGP ๊ฐ™๊ธฐ๋„…

control-plane ์ด๋ž‘ worker2๋Š” ์–ด๋””๋กœ?

โฏ kubectl get pod -n metallb-system -o wide
NAME                                                   READY   STATUS    RESTARTS   AGE   IP          NODE            NOMINATED NODE   READINESS GATES
metallb-operator-controller-manager-5dbc8fd577-bgczj   1/1     Running   0          21m   10.10.2.2   myk8s-worker3   <none>           <none>
metallb-operator-webhook-server-77d47cb764-9lcs8       1/1     Running   0          21m   10.10.3.3   myk8s-worker    <none>           <none>

๊ฐ€๋‹ค๋“ฌ๊ณ  ๋” ์ž˜ ์ฐพ์•„๋ณด๋‹ˆ, OpenShift Docs์—์„œ ์ •์ƒ์ด๋ผ๊ณ  ํ•˜๋„ค์š”.

์•„์ง ๋๋‚œ๊ฒŒ ์•„๋‹ˆ๋‹ค! ์…‹์—…์ด ๋œ ๋˜์„œ ๋ฐ”๋กœ ์—๋Ÿฌ๋œน๋‹ˆ๋‹ค.

โฏ kubectl logs -n metallb-system -l app=metallb -f
Defaulted container "speaker" out of: speaker, frr, reloader, frr-metrics, cp-frr-files (init), cp-reloader (init), cp-metrics (init)
Defaulted container "speaker" out of: speaker, frr, reloader, frr-metrics, cp-frr-files (init), cp-reloader (init), cp-metrics (init)
Defaulted container "speaker" out of: speaker, frr, reloader, frr-metrics, cp-frr-files (init), cp-reloader (init), cp-metrics (init)
Defaulted container "speaker" out of: speaker, frr, reloader, frr-metrics, cp-frr-files (init), cp-reloader (init), cp-metrics (init)
error: you are attempting to follow 6 log streams, but maximum allowed concurrency is 5, use --max-log-requests to increase the limit

์ž์‹ ๊ฐ์„ ๊ฐ–๊ณ  ์ด์–ด๋ด…์‹œ๋‹ค.

d. MetalLB deployment ์ƒ์„ฑ

GitHub ๋งŒ์œผ๋กœ๋Š” ๋„์ €ํžˆ ์ด๊ฒŒ ๋ญ˜ํ•˜๋Š” ๊ฑด๊ฐ€ ํ–ˆ๋Š”๋ฐ, ์Šคํ”ผ์ปค๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๊ฒƒ ๊ฐ™๋„ค์š”.

โฏ cat << EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: MetalLB
metadata:
  name: metallb
  namespace: metallb-system
EOF
metallb.metallb.io/metallb created

์ œ๋ฐœ…! (๋„คํŠธ์›Œํฌ ์ƒํƒœ๊ฐ€ ์ข‹์ง€ ์•Š์•„์„œ ํ•ซ์ŠคํŒŸ…)

โฏ kubectl get pod -n metallb-system -o wide
NAME                                                   READY   STATUS             RESTARTS   AGE    IP           NODE                  NOMINATED NODE   READINESS GATES
controller-7dd49fb757-rsf9n                            0/1     ImagePullBackOff   0          118s   10.10.2.3    myk8s-worker3         <none>           <none>
metallb-operator-controller-manager-5dbc8fd577-bgczj   1/1     Running            0          36m    10.10.2.2    myk8s-worker3         <none>           <none>
metallb-operator-webhook-server-77d47cb764-9lcs8       1/1     Running            0          36m    10.10.3.3    myk8s-worker          <none>           <none>
speaker-ndwfb                                          0/4     Init:0/3           0          118s   172.18.0.3   myk8s-worker3         <none>           <none>
speaker-vnjlb                                          0/4     Init:0/3           0          118s   172.18.0.5   myk8s-worker          <none>           <none>
speaker-w9946                                          0/4     Init:0/3           0          118s   172.18.0.2   myk8s-worker2         <none>           <none>
speaker-zgf46                                          0/4     Init:0/3           0          118s   172.18.0.4   myk8s-control-plane   <none>           <none>

ํœด

Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  2m30s                default-scheduler  Successfully assigned metallb-system/controller-7dd49fb757-rsf9n to myk8s-worker3
  Warning  Failed     57s                  kubelet            Failed to pull image "quay.io/metallb/controller:main": failed to pull and unpack image "quay.io/metallb/controller:main": failed to copy: read tcp 172.18.0.3:33674->104.18.37.147:443: read: connection reset by peer
  Warning  Failed     57s                  kubelet            Error: ErrImagePull
  Normal   BackOff    56s                  kubelet            Back-off pulling image "quay.io/metallb/controller:main"
  Warning  Failed     56s                  kubelet            Error: ImagePullBackOff
  Normal   Pulling    45s (x2 over 2m29s)  kubelet            Pulling image "quay.io/metallb/controller:main" 
  Normal   Pulled     31s                  kubelet            Successfully pulled image "quay.io/metallb/controller:main" in 13.488s (13.488s including waiting). Image size: 29150053 bytes.
  Normal   Created    31s                  kubelet            Created container controller
  Normal   Started    31s                  kubelet            Started container controller

์ž˜ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค.

โฏ kubectl get pod -n metallb-system -o wide
NAME                                                   READY   STATUS    RESTARTS   AGE    IP           NODE                  NOMINATED NODE   READINESS GATES
controller-7dd49fb757-rsf9n                            1/1     Running   0          4m3s   10.10.2.3    myk8s-worker3         <none>           <none>
metallb-operator-controller-manager-5dbc8fd577-bgczj   1/1     Running   0          38m    10.10.2.2    myk8s-worker3         <none>           <none>
metallb-operator-webhook-server-77d47cb764-9lcs8       1/1     Running   0          38m    10.10.3.3    myk8s-worker          <none>           <none>
speaker-ndwfb                                          4/4     Running   0          4m3s   172.18.0.3   myk8s-worker3         <none>           <none>
speaker-vnjlb                                          3/4     Running   0          4m3s   172.18.0.5   myk8s-worker          <none>           <none>
speaker-w9946                                          3/4     Running   0          4m3s   172.18.0.2   myk8s-worker2         <none>           <none>
speaker-zgf46                                          4/4     Running   0          4m3s   172.18.0.4   myk8s-control-plane   <none>           <none>

์•„์ง ๋๋‚œ๊ฒŒ ์•„๋‹ˆ๋‹ค! ์…‹์—…์ด ๋œ ๋˜์„œ ๋ฐ”๋กœ ์—๋Ÿฌ๋œน๋‹ˆ๋‹ค.
๋ผ๊ธฐ ๋ณด๋‹ค๋Š” concurrency ์—๋Ÿฌ์˜€๊ตฐ์š”. --max-log-requests 6๋กœ ๋Š˜๋ฆฌ๊ณ  ํ™•์ธํ•ด๋ณด๋‹ˆ ์ž˜ ๋‚˜์˜ต๋‹ˆ๋‹ค.

โฏ kubectl logs -n metallb-system -l app=metallb -f
Defaulted container "speaker" out of: speaker, frr, reloader, frr-metrics, cp-frr-files (init), cp-reloader (init), cp-metrics (init)
Defaulted container "speaker" out of: speaker, frr, reloader, frr-metrics, cp-frr-files (init), cp-reloader (init), cp-metrics (init)
Defaulted container "speaker" out of: speaker, frr, reloader, frr-metrics, cp-frr-files (init), cp-reloader (init), cp-metrics (init)
Defaulted container "speaker" out of: speaker, frr, reloader, frr-metrics, cp-frr-files (init), cp-reloader (init), cp-metrics (init)
error: you are attempting to follow 6 log streams, but maximum allowed concurrency is 5, use --max-log-requests to increase the limit

e. MetalLB ConfigMap ์ƒ์„ฑ

๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ์ด์ œ kind์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ธŒ๋ฆฌ์ง€(docker bridge)๋ฅผ ํ™•์ธํ•˜๊ณ  ์ด ๋Œ€์—ญ์„ ์žก์•„์ค˜์•ผํ•ฉ๋‹ˆ๋‹ค.

  • kind๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ kind๋ผ๋Š” ์ด๋ฆ„์˜ ๋ธŒ๋ฆฌ์ง€๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
docker network ls
# (sol0) kind alias ๋Œ€์‹  Id(SHA256)์œผ๋กœ ํ™•์ธํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค. ๋‹ค ๊ฐ™์€ ์กฐํšŒ๋ฐฉ๋ฒ•์ผ ๋ฟ.  
docker network inspect kind
# (sol1) docker inspect kind
# (sol2) docker inspect <Id>
# (sol3) docker network inspect <Id>
  • IP CIDR ์กฐํšŒ
docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
# ์œ„์˜ ๊ฒƒ ์น˜๊ธฐ ๊ท€์ฐฎ์œผ๋ฉด?  
# ์ด๊ฑด ๊ฐ Node์— ํ• ๋‹น ๋œ IP
# docker inspect e8 | grep IPv4Address
# ์ด๊ฑด ๋ธŒ๋ฆฌ์ง€์— ์ •์˜๋œ IP ๋Œ€์—ญ
# docker inspect e8 | grep Subnet

๊ทธ๋ ‡๊ตฐ์š”

โฏ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
9a356b80a908   bridge    bridge    local
d2f5be011872   host      host      local
e8e5256f1aa7   kind      bridge    local
439c3626705a   none      null      local
โฏ docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
/myk8s-worker 172.18.0.2
/myk8s-control-plane 172.18.0.5
/myk8s-worker2 172.18.0.4
/myk8s-worker3 172.18.0.3
โฏ docker inspect e8 | grep Subnet
                    "Subnet": "172.18.0.0/16",
                    "Subnet": "fc00:f853:ccd:e793::/64",
โฏ docker inspect e8 | grep IPv4Address
                "IPv4Address": "172.18.0.3/16",
                "IPv4Address": "172.18.0.4/16",
                "IPv4Address": "172.18.0.2/16",
                "IPv4Address": "172.18.0.5/16",

์•ž์„œ CRD๋ฅผ ์กฐํšŒํ•ด๋ณด๋ฉด,
ipaddresspools.metallb.io์™€
l2advertisements.metallb.io๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
์™œ ์กฐํšŒํ–ˆ์—ˆ๋Š”์ง€ ์•Œ๊ฒ …์ฃ ?

์ž‘์„ฑ ๋ฐฉ๋ฒ• ํ™•์ธ ๋ฐฉ๋ฒ•

kubectl explain ipaddresspools.metallb.io
kubectl explain l2advertisements.metallb.io
  • MetalLB ConfigMap: (1) IPAddressPool

ํ•ด๋‹น ์„œ๋ธŒ๋„ท์—์„œ ์„ค๋งˆ ์ด ๋Œ€์—ญ๊นŒ์ง€๋Š” ์“ฐ์ง€ ์•Š๊ฒ ์ง€?๋ž€
๊ฒฝ๊ฑดํ•œ ๋งˆ์Œ์œผ๋กœ MetalLB์šฉ ์„œ๋น„์Šค IP ๋Œ€์—ญ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

cat << EOF > metallb-ipaddresspool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: kkumtree-ippool
  namespace: metallb-system
spec:
  addresses:
  - 172.18.255.200-172.18.255.254
EOF
kubectl apply -f metallb-ipaddresspool.yaml
  • MetalLB ConfigMap: (2) L2Advertisement

์œ„์—์„œ ์„ค์ •ํ•œ IPPool์„ Layer2 Advertisement์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

cat << EOF > metallb-l2advertisement.yaml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: kkumtree-l2adv
  namespace: metallb-system
spec:
  ipAddressPools:
  - kkumtree-ippool
EOF
kubectl apply -f metallb-l2advertisement.yaml

๊ทธ๋ ‡๊ตฐ์š”

โฏ kubectl get ipaddresspools,l2advertisements -n metallb-system
NAME                                       AUTO ASSIGN   AVOID BUGGY IPS   ADDRESSES
ipaddresspool.metallb.io/kkumtree-ippool   true          false             ["172.18.255.200-172.18.255.254"]

NAME                                        IPADDRESSPOOLS        IPADDRESSPOOL SELECTORS   INTERFACES
l2advertisement.metallb.io/kkumtree-l2adv   ["kkumtree-ippool"] 

6. ํ…Œ์ŠคํŠธ ์„œ๋น„์Šค ์ƒ์„ฑ

MetalLB์— ๋Œ€ํ•œ ์ค€๋น„๊ฐ€ ๋๋‚ฌ์œผ๋‹ˆ, LB๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋น„์Šค์™€ ํŒŒ๋“œ๋ฅผ ์ƒ์„ฑํ•ด๋ด…์‹œ๋‹ค.

cat <<EOF> metallb-test-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc1
spec:
  ports:
    - name: svc1-webport
      port: 80
      targetPort: 80
  selector:
    app: webpod
  type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
  name: svc2
spec:
  ports:
    - name: svc2-webport
      port: 80
      targetPort: 80
  selector:
    app: webpod
  type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
  name: svc3
spec:
  ports:
    - name: svc3-webport
      port: 80
      targetPort: 80
  selector:
    app: webpod
  type: LoadBalancer
EOF
kubectl apply -f metallb-test-svc.yaml
  • ์ƒ์„ฑ ์ดํ›„ ์ƒˆ๋กœ์šด ํ„ฐ๋ฏธ๋„์—์„œ ARP ์Šค์บ”์„ ์ผœ๋‘ก๋‹ˆ๋‹ค.
โฏ docker exec -it myk8s-control-plane arp-scan --interfac=eth0 --localnet
Interface: eth0, type: EN10MB, MAC: 02:42:ac:12:00:05, IPv4: 172.18.0.5
Starting arp-scan 1.10.0 with 65536 hosts (https://github.com/royhills/arp-scan)
172.18.0.1	02:42:01:57:ec:6f	(Unknown: locally administered)
172.18.0.2	02:42:ac:12:00:02	(Unknown: locally administered)
172.18.0.3	02:42:ac:12:00:03	(Unknown: locally administered)
172.18.0.4	02:42:ac:12:00:04	(Unknown: locally administered)
  • LoadBalancer ํƒ€์ž…์˜ ์„œ๋น„์Šค๊ฐ€ NodePort์™€ ClusterIP๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • (default) allocateLoadBalancerNodePorts : true
โฏ kubectl get svc,ep
NAME                 TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
service/kubernetes   ClusterIP      10.200.1.1     <none>           443/TCP        2d20h
service/svc1         LoadBalancer   10.200.1.66    172.18.255.200   80:31865/TCP   2m42s
service/svc2         LoadBalancer   10.200.1.133   172.18.255.201   80:30199/TCP   2m42s
service/svc3         LoadBalancer   10.200.1.175   172.18.255.202   80:30462/TCP   2m42s

NAME                   ENDPOINTS                   AGE
endpoints/kubernetes   172.18.0.5:6443             2d20h
endpoints/svc1         10.10.1.2:80,10.10.3.2:80   2m42s
endpoints/svc2         10.10.1.2:80,10.10.3.2:80   2m42s
endpoints/svc3         10.10.1.2:80,10.10.3.2:80   2m42s
  • ๊ทธ ์‚ฌ์ด์— ARP ์Šค์บ” ์ค‘์ธ ํ„ฐ๋ฏธ๋„์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ๋ฐ”๋€Œ์–ด์žˆ๋„ค์š”
โฏ docker exec -it myk8s-control-plane arp-scan --interfac=eth0 --localnet
Interface: eth0, type: EN10MB, MAC: 02:42:ac:12:00:05, IPv4: 172.18.0.5
Starting arp-scan 1.10.0 with 65536 hosts (https://github.com/royhills/arp-scan)
172.18.0.1	02:42:01:57:ec:6f	(Unknown: locally administered)
172.18.0.2	02:42:ac:12:00:02	(Unknown: locally administered)
172.18.0.3	02:42:ac:12:00:03	(Unknown: locally administered)
172.18.0.4	02:42:ac:12:00:04	(Unknown: locally administered)
172.18.0.1	02:42:01:57:ec:6f	(Unknown: locally administered) (DUP: 2)
172.18.0.2	02:42:ac:12:00:02	(Unknown: locally administered) (DUP: 2)
172.18.0.3	02:42:ac:12:00:03	(Unknown: locally administered) (DUP: 2)
172.18.0.4	02:42:ac:12:00:04	(Unknown: locally administered) (DUP: 2)
172.18.0.1	02:42:01:57:ec:6f	(Unknown: locally administered) (DUP: 3)
172.18.255.200	02:42:ac:12:00:02	(Unknown: locally administered)
172.18.255.201	02:42:ac:12:00:02	(Unknown: locally administered)
172.18.255.202	02:42:ac:12:00:03	(Unknown: locally administered)
172.18.0.1	02:42:01:57:ec:6f	(Unknown: locally administered) (DUP: 4)
172.18.0.1	02:42:01:57:ec:6f	(Unknown: locally administered) (DUP: 5)

14 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.10.0: 65536 hosts scanned in 263.158 seconds (249.04 hosts/sec). 7 responded
  • ๊ทธ๋Ÿผ ์–ด๋–ค ๋…ธ๋“œ์—์„œ Leader ์—ญํ• ์„ ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
โฏ kubectl describe svc | grep Events: -A5
Events:                   <none>


Name:                     svc1
Namespace:                default
Labels:                   <none>
--
Events:
  Type    Reason        Age   From                Message
  ----    ------        ----  ----                -------
  Normal  IPAllocated   11m   metallb-controller  Assigned IP ["172.18.255.200"]
  Normal  nodeAssigned  11m   metallb-speaker     announcing from node "myk8s-worker" with protocol "layer2"

--
Events:
  Type    Reason        Age                From                Message
  ----    ------        ----               ----                -------
  Normal  IPAllocated   11m                metallb-controller  Assigned IP ["172.18.255.201"]
  Normal  nodeAssigned  11m (x2 over 11m)  metallb-speaker     announcing from node "myk8s-worker" with protocol "layer2"

--
Events:
  Type    Reason        Age   From                Message
  ----    ------        ----  ----                -------
  Normal  IPAllocated   11m   metallb-controller  Assigned IP ["172.18.255.202"]
  Normal  nodeAssigned  11m   metallb-speaker     announcing from node "myk8s-worker3" with protocol "layer2"

๋ฌผ๋ก , ์ด์ฏค๋˜๋ฉด ๋ฐฐํฌ๋œ ์•„๋ฌด ์„œ๋น„์Šค๋งŒ ์žก๊ณ  ์ฐ์–ด๋ณด๋ฉด ๋˜๊ฒ ์ฃ ?

โฏ kubectl describe svc/svc2 | grep metallb-speaker
  Normal  nodeAssigned  13m (x2 over 13m)  metallb-speaker     announcing from node "myk8s-worker" with protocol "layer2"
  • ์ด์ œ ๊ฐ ๋…ธ๋“œ์— ํŒŒ๋“œ๋ฅผ ๋ถ™์—ฌ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
    • ์ƒ๊ฐํ•ด๋ณด๋‹ˆ ์„œ๋น„์Šค ๋ถ™์ด๊ธฐ ์ „์— ํŒŒ๋“œ๋ฅผ ๋จผ์ € ๋ถ™์˜€์–ด์•ผํ–ˆ๋Š”๋ฐ;
cat <<EOT> 3pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: webpod1
  labels:
    app: webpod
spec:
  nodeName: myk8s-worker
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: webpod2
  labels:
    app: webpod
spec:
  nodeName: myk8s-worker2
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: webpod3
  labels:
    app: webpod
spec:
  nodeName: myk8s-worker3
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
EOT
kubectl apply -f 3pod.yaml
  • ์–ด์จŒ๊ฑฐ๋‚˜ ์ €์จŒ๊ฑฐ๋‚˜, kind ๋…ธ๋“œ์—์„œ ์ ‘์†ํ•ด์„œ ํ…Œ์ŠคํŠธ ํ•˜์ง€ ์•Š์•„๋„
    metalLB๋กœ ์ƒ์„ฑ๋œ EXTERNAL-IP๋กœ๋„ ์ž˜ ์ ‘์†๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
โฏ kubectl get svc
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
kubernetes   ClusterIP      10.200.1.1     <none>           443/TCP        3d3h
svc1         LoadBalancer   10.200.1.66    172.18.255.200   80:31865/TCP   6h52m
svc2         LoadBalancer   10.200.1.133   172.18.255.201   80:30199/TCP   6h52m
svc3         LoadBalancer   10.200.1.175   172.18.255.202   80:30462/TCP   6h52m
โฏ curl -s 172.18.255.200
Hostname: webpod2
IP: 127.0.0.1
IP: ::1
IP: 10.10.1.2
IP: fe80::b862:52ff:fed1:7cbb
RemoteAddr: 172.18.0.2:3343
GET / HTTP/1.1
Host: 172.18.255.200
User-Agent: curl/8.5.0
Accept: */*

โฏ curl -s 172.18.255.201
Hostname: webpod1
IP: 127.0.0.1
IP: ::1
IP: 10.10.3.2
IP: fe80::d0fa:f1ff:fe03:49bf
RemoteAddr: 10.10.3.1:20630
GET / HTTP/1.1
Host: 172.18.255.201
User-Agent: curl/8.5.0
Accept: */*

โฏ curl -s 172.18.255.202
Hostname: webpod2
IP: 127.0.0.1
IP: ::1
IP: 10.10.1.2
IP: fe80::b862:52ff:fed1:7cbb
RemoteAddr: 172.18.0.3:6166
GET / HTTP/1.1
Host: 172.18.255.202
User-Agent: curl/8.5.0
Accept: */*

7. ๋ฑ€๋‹ค๋ฆฌ

a. docker bridge network default cidr?

… ๊ฐ€๋งŒ ์ƒ๊ฐํ•ด๋ณด๋‹ˆ, 172.18.0.0 ๋Œ€์—ญ์„ yaml์— ์ง€์ •๋„ ์•ˆํ–ˆ๋Š”๋ฐ ๊ทธ๋ˆ”์˜ Docker ๋ฌธ์„œ์—์„  ๋ˆˆ์— ์ž˜ ์•ˆ ๋„๋„ค?๋ฅผ 2์ฃผ ์ „๋ถ€ํ„ฐ ์ƒ๊ฐํ–ˆ์—ˆ๋Š”๋ฐ์š”

serverfault/916941์„ ๋ณด๊ณ  ๊ธฐ์–ต๋‚ฌ์Šต๋‹ˆ๋‹ค.

๋„์ปค ๋„คํŠธ์›Œํฌ ๋ธŒ๋ฆฟ์ง€ ์„ค์ • ๊ฐ’์„ ๋ณด๋ฉด ๋˜๋Š” ๊ฒƒ … ๋ถ„๋ช… ์ด๊ฑฐ ๋•๋ถ„์— ์‚ฝ์งˆ์„ ์ข€ ํ–ˆ๋˜๊ฑฐ๋กœ ์•„๋Š”๋ฐ ์•ˆ ์ ์–ด๋‘๋‹ˆ ๋˜๋ฅต.
๋„์ปค๊ฐ€ ์ด๋ ‡๊ฒŒ๋‚˜ ์œ„?ํ—˜ํ•ฉ๋‹ˆ๋‹ค.

โฏ docker -v
Docker version 24.0.7, build 24.0.7-0ubuntu4.1
โฏ sudo docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a90c02431872   bridge    bridge    local
d2f5be011872   host      host      local
439c3626705a   none      null      local
โฏ sudo docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "a90c02431872f243e6c3918d0ca4f8875fb070ae0ad1a504891b74485634de14",
        "Created": "2024-10-02T08:22:04.247414071+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

b. docker ๊ถŒํ•œ ์•ˆ ํ’€์–ด๋‘๋ฉด, kind ์—๋Ÿฌ ํ„ฐ์ง€๋Š” ๊ทธ๊ฑฐ

์˜ˆ, ๊ทธ ๋ป”ํ•œ ๊ทธ๊ฑฐ์—์š”. ์ด ๊ธฐ๊ธฐ์—์„œ๋Š” ์„ธํŒ…์„ ์•ˆํ•ด๋’€๋„ค์š”.

ERROR: failed to create cluster: failed to list nodes: command "docker ps -a --filter label=io.x-k8s.kind.cluster=myk8s --format '{{.Names}}'" failed with error: exit status 1
Command Output: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json?all=1&filters=%7B%22label%22%3A%7B%22io.x-k8s.kind.cluster%3Dmyk8s%22%3Atrue%7D%7D": dial unix /var/run/docker.sock: connect: permission denied

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ฐธ ์‰ฝ์ฃ ?

# https://snapcraft.io/docker refer and apply  
sudo addgroup --system docker
sudo adduser $USER docker
newgrp docker
sudo service docker restart
โฏ sudo addgroup --system docker
info: The group `docker' already exists as a system group. Exiting.
โฏ sudo adduser $USER docker
info: Adding user `kkumtree' to group `docker' ...
โฏ newgrp docker
โฏ sudo service docker restart

c. MetalLB ์„ค์น˜ ํ›„ Controller ๋ฐ Speaker ํ™•์ธ

๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

โฏ kubectl get deployment -n metallb-system controller
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
controller   1/1     1            1           2d17h
โฏ kubectl get daemonset -n metallb-system speaker
NAME      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
speaker   4         4         4       4            4           kubernetes.io/os=linux   2d17h

d. Log cuncurrency ์—๋Ÿฌ

โฏ kubectl logs -n metallb-system -l app=metallb -f
error: you are attempting to follow 6 log streams, but maximum allowed concurrency is 5, use --max-log-requests to increase the limit

๊ทธ๋Ÿฌ๋ฉด ํ•˜๋ผ๋Š” ๋Œ€๋กœ ํ•ด์•ผ์ฃ 

โฏ kubectl logs -n metallb-system -l app=metallb -f --max-log-requests 6
# (์ค‘๋žต) ๋กœ๊ทธ๋Š” ์ž˜ ๋‚˜์˜ต๋‹ˆ๋‹ค.
failed to create fsnotify watcher: too many open files

??? ๊ทธ๊ฒƒ ์ฐธ ๊นŒ์น ํ•œ ์นœ๊ตฌ๋„ค์š”. … ์•„ fd ๋ฌธ์ œ์ธ๊ฑฐ ๊ฐ™์€๋ฐ์š”?

# 1024์ธ๋ฐ ๋ถ€์กฑํ•˜๋‹ค๊ณ ?  
# ์•„ ๋‹ค๋ฅธ๊ฑฐ๋„ ์ผœ๊ณ  ํ•˜๊ณ  ์žˆ์—ˆ๊ตฌ๋‚˜
โฏ ulimit -n
1024

/etc/security/limits.conf ๋ฅผ ์ˆ˜์ •ํ•ด์„œ ์•„๋ž˜์ฒ˜๋Ÿผ ๊ฐ’ ๋„ฃ๊ณ  ํ™œ์„ฑํ™” ํ•  ์ˆ˜๋Š” ์žˆ๋Š”๋ฐ, ์ œ ๋…ธํŠธ๋ถ์„ ๊ฑด๋“œ๋ฆฌ๋ ค๋‹ˆ ์ข€ ๊ป˜๋ฆ„์น™ํ•˜๊ตฐ์š”.
์–Œ์ „ํžˆ ๊ธฐ๋ณธ๊ฐ’ ์“ฐ๊ฒ ์Šต๋‹ˆ๋‹ค(?).

#<domain> <type> <item> <value>
* soft nofile 10000
* hard nofile 30000

Reference

๋‹ค ๋๋‚˜๊ฐˆ ์ฏค์— ๋ฐœ๊ฒฌํ•œ ๊ฑด๋ฐ, RedHat OpenShift Docs์— ์„ธ์ƒ ์นœ์ ˆํ•˜๊ฒŒ ์“ฐ์—ฌ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค…

kkumtree

plumber for infra

kkumtree

Source code on GitHub

ยฉ 2025 kkumtree and contributors All rights reserved.
Licensed under
CC BY-NC-ND 4.0