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/