KIND and Load Balancing with MetalLB on Mac

KIND and Load Balancing with MetalLB on Mac

在 Mac 上,由于 host 和 docker bridge 网络之间并不是通的,因此当 Service 类型为 Load Balance 时,外部的请求并不能命中到 Docker bridge 网络上,从而导致请求失败。这个问题折磨了我好久,最终从这篇文章上找到了解决办法。

解决这个问题的关键,在于如何把外部的流量打到 docker 的 bridge 网络上。这里使用了一个开源的仓库docker-tuntap-osx,它是一个脚本,执行这个脚本之后,会在 mac 上建立一张网卡,这个网卡和 docker 之间的网络是通的,接下来只需要添加路由,把需要路由到 docker bridge 网络的请求都 route 到这个网卡上,通过这个网卡将请求转发到 docker bridge network 上。

docker-tuntap-osx这个脚本依赖TunTap这个工具,因此第一步是安装 TunTap。当然,在此之前,你需要安装 KIND 和 Docker。

Install KIND and Docker

KIND 地址: https://kind.sigs.k8s.io/

Docker 地址: https://www.docker.com/

安装过程: 略

我电脑上的 KIND 版本以及 Docker 版本分别为

➜  kind-metallb kind --version
kind version 0.11.1
➜  kind-metallb docker --version
Docker version 20.10.8, build 3967b7d

Install TunTap

我的 Mac 上 Homebrew 的版本如下

➜  kind-metallb brew --version
Homebrew 3.2.15

当前的系统版本

➜  kind-metallb uname -a
Darwin helloworld 19.6.0 Darwin Kernel Version 19.6.0: Tue Jun 22 19:49:55 PDT 2021; root:xnu-6153.141.35~1/RELEASE_X86_64 x86_64

可以直接通过 brew 命令来安装 TunTap

brew install --cask tuntap

在安装的过程中需要输入密码。

但输入密码后依然安装失败了。原因是没有给权限。

打开

系统偏好设置 -> 安全性与隐私 -> 通用

允许 Mattias Nissler XXX 打上勾。(Mattias Nissler 是 Tuntap 这个工具的作者)

退出 Docker Desktop For Mac 重新执行一次命令。

brew install --cask tuntap

结果依旧安装失败。

最后重启了一下电脑,再次安装,这次终于安装成功了。

Install tap

接下来就需要利用 tuntap 这个工具来给创建网卡了

docker-tuntap-osx clone 下来

git clone git@github.com:AlmirKadric-Published/docker-tuntap-osx.git

依次执行这两个 shell 脚本就行了

./docker-tuntap-osx/sbin/docker_tap_install.sh
./docker-tuntap-osx/sbin/docker_tap_up.sh

接着检查是否成功了,执行 ifconfig 命令,拖到最下面

tap1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
	ether 8a:4e:85:69:a2:bf
	inet 10.0.75.1 netmask 0xfffffffc broadcast 10.0.75.3
	media: autoselect
	status: active
	open (pid 2309)

会看到增加了一个名为 tap1 的网卡。

Add Route

接下来需要把 docker bridge 的 ip 段都 route 到 tap1 这张网卡上

利用 docker network ls 查看 docker 的 network 情况

➜  kind-metallb docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a72136e0a128   bridge    bridge    local
0f4a261db133   host      host      local
6ca7c686aab0   kind      bridge    local
4e612adeacb8   none      null      local

这里面有一个名为 kind 的 network,这个network 就是 KIND 创建的。

利用 docker network inspect kind 看看 kind 的 ip 区间

➜  kind-metallb docker network inspect kind
[
    {
        "Name": "kind",
        "IPAM": {
            "Driver": "default",
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                },
                {
                    "Subnet": "fc00:f853:ccd:e793::/64",
                    "Gateway": "fc00:f853:ccd:e793::1"
                }
            ]
        },
    }
]

➜  kind-metallb 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[]}]

着重观察 Subnet 的值,可以看到 172.18.0.0/16 被分配给了 kind

172.18.0.0/16 这段 ip 区间全都 route 到 tap1 上

sudo route -v add -net 172.18.0.1 -netmask 255.255.0.0 10.0.75.2

验证一下是否添加成功了

➜  kind-metallb route get 172.18.0.1
   route to: 172.18.0.1
destination: 172.18.0.0
       mask: 255.255.0.0
    gateway: 10.0.75.2
  interface: tap1
      flags: <UP,GATEWAY,DONE,STATIC,PRCLONING>
 recvpipe  sendpipe  ssthresh  rtt,msec    rttvar  hopcount      mtu     expire
       0         0         0         0         0         0      1500         0

如上,对 172.18.0.1 的请求会通过 tap1 网卡转发。

Deploying an Application

现在 host 和 docker bridge network 之间通了,接下来可以按照官网文档的教程来部署应用了。

Create Cluster

第一步是利用 KIND 创建一个 k8s cluster

在这里我创建一个双节点的 cluster

对应的 KIND 配置文件为

# config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
➜  kind-metallb kind create cluster --config config.yaml
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.21.1) 🖼
 ✓ Preparing nodes 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-kind"
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂

ps: 嫌 kubectl 太长, 给它建了一个 alias: alias kb="kubectl"

查看一下 cluster 里的 node 地址

➜  kind-metallb kb 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,master   4m13s   v1.21.1   172.18.0.3    <none>        Ubuntu 21.04   5.10.47-linuxkit   containerd://1.5.2
kind-worker          Ready    <none>                 3m52s   v1.21.1   172.18.0.2    <none>        Ubuntu 21.04   5.10.47-linuxkit   containerd://1.5.2

ping 一下 172.18.0.2 和 172.18.0.3, 看看能不能 ping 通

➜  kind-metallb ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: icmp_seq=0 ttl=63 time=0.838 ms
64 bytes from 172.18.0.2: icmp_seq=1 ttl=63 time=0.367 ms
^C
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.367/0.603/0.838/0.235 ms
➜  kind-metallb ping 172.18.0.3
PING 172.18.0.3 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: icmp_seq=0 ttl=63 time=0.555 ms
64 bytes from 172.18.0.3: icmp_seq=1 ttl=63 time=0.519 ms
^C
--- 172.18.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.519/0.537/0.555/0.018 ms

一定要确保能 ping 通,如果没有 ping 通检查一下前面步骤,看看是不是没有 add route, 或者安装 tap1 失败了。

Installing metallb using default manifests

依次执行

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/master/manifests/namespace.yaml
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)" 
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/master/manifests/metallb.yaml

等待 metallb-system 下面的 pod 变成 running 状态

➜  kind-metallb kubectl get pods -n metallb-system --watch
NAME                          READY   STATUS              RESTARTS   AGE
controller-6cc57c4567-9fhhx   0/1     ContainerCreating   0          8s
speaker-86c62                 0/1     ContainerCreating   0          8s
speaker-m6rb8                 0/1     ContainerCreating   0          8s
speaker-86c62                 1/1     Running             0          19s
speaker-m6rb8                 1/1     Running             0          19s
controller-6cc57c4567-9fhhx   1/1     Running             0          31s
^C%

Setup address pool used by loadbalancers

➜  kind-metallb 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[]}]

从 172.18.0.0/16 中摘出一段 ip 区间作为 loadbalancer 的 ip 池

我选择了 172.18.0.150-172.18.0.200 这段区间,对应的 manifest 文件为

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 172.18.0.150-172.18.0.200

apply it!

➜  kind-metallb kubectl apply -f metallb-config.yaml
configmap/config created

Using LoadBalancer

所有准备工作都就绪了,接下来就是使用 LoadBalancer 了

Pod 和 Service 的 manifest 如下

# usage.yaml
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:
  # Default port used by the image
  - port: 5678

apply it!

➜  kind-metallb kb apply -f usage.yaml
pod/foo-app created
pod/bar-app created
service/foo-service created
➜  kind-metallb kb get pods
NAME      READY   STATUS    RESTARTS   AGE
bar-app   1/1     Running   0          22s
foo-app   1/1     Running   0          22s
➜  kind-metallb kb get svc
NAME          TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)          AGE
foo-service   LoadBalancer   10.96.112.188   172.18.0.150   5678:31857/TCP   41s
kubernetes    ClusterIP      10.96.0.1       <none>         443/TCP          19m

可以看到 LoadBlance 的 ip 为 172.18.0.150, port 为 5678

curl 一下

➜  kind-metallb for _ in $(seq 100);do curl 172.18.0.150:5678 && sleep 1;done
foo
bar
foo
foo
bar
bar
bar
bar
foo

如上, foo 和 bar 是交替出现的。

(PS: 如果电脑开了 ssr 代理的记得关掉,我就是因为开了代理忘记关掉,导致 curl 一直失败! 折磨了我起码半天时间o(╥﹏╥)o)

参考链接

https://kind.sigs.k8s.io/docs/user/loadbalancer/

https://mauilion.dev/posts/kind-metallb/

https://www.thehumblelab.com/kind-and-metallb-on-mac/

posted @ 2021-10-31 16:13  机智的小小帅  阅读(410)  评论(0编辑  收藏  举报