Think Deep,Work Lean

本地直接访问kind集群中的服务

Posted on By zack

背景

在Mac本地安装完Kind集群后,由于这个K8s集群所有的服务都跑在了一个docker容器中。由于从本地既无法ping通Kind docker的IP,也无法Ping通集群中Pod的IP。因此从本地访问集群中的服务成为了一个问题。

那么有没有办法来解决呢?

对于自己不能解决的问题,其他人肯定也遇到过,Google是个好东西。

https://github.com/wenjunxiao/mac-docker-connector/blob/master/README.md

打通本地网络与Docker网络

首先分析为什么本地无法访问Docker网络?

对于一个简单的docker的服务,如nginx,如果要访问这个服务,需要把它的端口expose出来。

 docker run -p 8000:80 nginx

此时就可以正常的访问主机端口

curl localhost:8000 # ok

为什么要expose?

对于在docker中运行的一个服务,本质上它是一个进程,该进程会绑定该docker的namespace的网络层(IP)及传输层(端口)。进程通常绑定的IP为127.0.0.10.0.0.0或是这个这个进程宿主机的IP,如果这个进程是跑在容器中的,它也有可能是绑定这个容器的IP。

通常docker run <image>跑起来的容器,它的网络使用default bridge模式。它是一个独立的网络,在这个桥中的容器,网络可以互通。

➜  ~ docker inspect `docker ps | grep nginx | awk '{print $1}'` | less -i

       "Networks": {
         "bridge": {         
             "IPAddress": "172.17.0.2",
             "Gateway": "172.17.0.1",

可以看到是bridge网络,且是172.17段

➜  ~ docker network ls
NETWORK ID          NAME                        DRIVER              SCOPE
cec614647447        bridge                      bridge              local

可以通过 docker network inspect cec614647447 查看该network详细信息。

docker网络分为三种:host、bridge、none。

无法直接访问通该docker ip的原因就是因为没有连接到host网络。使用expose,本质上就是把容器ns上的port映射到了host网络的port上。

那么问题来了:对于kind这个docker,默认安装时,只expose出了一个apiserver的port,那现在要访问服务中的Port,要如何做呢?

通过上面的文章,我们可以默认打通mac本地到所有bridge中的网络。

$ brew tap wenjunxiao/brew
$ brew install docker-connector

$ docker network ls --filter driver=bridge --format "" | xargs docker network inspect --format "route " >> /usr/local/etc/docker-connector.conf

$ sudo brew services start docker-connector

$ docker pull wenjunxiao/mac-docker-connector

$ docker run -it -d --restart always --net host --cap-add NET_ADMIN --name connector wenjunxiao/mac-docker-connector

这几步完成后,已经可以ping所有bridge网络中的IP

➜  ~ docker inspect `docker ps | grep kind | awk '{print $1}'` | grep IPAddress

                    "IPAddress": "172.20.0.2",

➜  ~ ping 172.20.0.2
PING 172.20.0.2 (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: icmp_seq=0 ttl=63 time=1.472 ms

现在已经可以访问kind容器的IP了,那么如何访问kind 集群中的服务呢?

访问kind集群中的服务

集群内部的网络与kind容器的网络是两个不同的网络。

就好比是在虚机上搭建的一个k8s集群,容器的网络其实跟宿主机的网络,本质上不是同一个网络。

在虚机中能Ping通Pod IP,因此可以直接访问Pod中的服务。虚机Host网络与集群中的网络,不过是通过cni插件额外做了一些工作,将它们打通。

但是Kind集群的网络与Kind容器所在的网络并没有打通。其实对于k8s集群而言,也有类似于docker expose的概念,即将这个服务改成NodePort,这样就可以使流量从外部进入到集群。

本质上也是两上不通的Namespace,通过NodePort TargetPort做了下映射。

因此此时,只需要将该服务改成NodePort即可访问成功。

➜  zackzhangkai.github.io git:(master) ✗ k -n kubesphere-system get svc ks-console
NAME         TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
ks-console   NodePort   10.99.226.191   <none>        80:30880/TCP   21h

➜  zackzhangkai.github.io git:(master) ✗ docker ps | grep kind
c46d6ca06e9f        kindest/node:v1.19.1              "/usr/local/bin/entr…"   28 hours ago        Up 28 hours         127.0.0.1:53079->6443/tcp   kind-control-plane

➜  zackzhangkai.github.io git:(master) ✗ docker inspect c46d6ca06e9f | grep -i ipadd
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "172.20.0.2",

➜  zackzhangkai.github.io git:(master) ✗ curl -I 172.20.0.2:30880
HTTP/1.1 302 Found
Vary: Accept-Encoding
Location: /login
Content-Type: text/html; charset=utf-8
Content-Length: 43
Date: Fri, 19 Mar 2021 07:56:34 GMT
Connection: keep-alive
Keep-Alive: timeout=5

使用配置文件来暴露端口

其实在创建集群的时候,可以直接在配置文件中定义好要暴露的端口

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  # WARNING: It is _strongly_ recommended that you keep this the default
  # (127.0.0.1) for security reasons. However it is possible to change this.
  apiServerAddress: "127.0.0.1"
  # By default the API server listens on a random open port.
  # You may choose a specific port but probably don't need to in most cases.
  # Using a random port makes it easier to spin up multiple clusters.
  apiServerPort: 6443

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