k8s 折腾记:ingress 和 lb

摘要

安装 traefik 作为 ingress controller 和安装 metallb 作为 loadbalancer。

前言

昨天我们成功安装好了集群,并配置了网络插件,成功使用 nodeport 访问了我们在集群上面部署的应用。

但是,nodeport 类型的 service 仍然会有许多限制,我们需要合适的方式来暴露我们的服务。

目标

安装 traefik,使用 ingress 来暴露 nginx 服务。

PS:我之前在 k3s 上实现过,k3s 是预装了很多组件的发行版,我现在想在原版上面自己安装组件然后重现一遍。

前置知识

ingress 的定位

按照 k8s 的网络模型,service 通过对 pod 进行负载均衡,对外提供服务。而 ingress 又可以在 service 上面提供更高一层的访问控制。

整体可能是这样子的三层。

1
2
3
4
5
6
7
8

flowchart TD
    Ingress--> Service1
    Ingress--> Service2
    Service1 --> Pod1
    Service1 --> Pod2       
    Service1 --> ....
    Service2 --> .....

service 更多的是用于集群内部服务的互相访问,而 ingress 更多的是用于外部访问集群内部的服务。

service 的分类

按照 官方文档,service 又能够分为几类(这里就直接引用官方文档了)

对一些应用的某些部分(如前端),你可能希望将其公开于某外部 IP 地址,
也就是可以从集群外部访问的某个地址。

Kubernetes Service 类型允许指定你所需要的 Service 类型。


可用的 `type` 值及其行为有:

- `ClusterIP` 通过集群的内部 IP 公开 Service,选择该值时 Service 只能够在集群内部访问。
这也是你没有为服务显式指定 `type` 时使用的默认值。
你可以使用 [Ingress](https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/)
或者 [Gateway API](https://gateway-api.sigs.k8s.io/) 向公共互联网公开服务。
- [`NodePort`](https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#type-nodeport) 通过每个节点上的 IP 和静态端口(`NodePort`)公开 Service。
为了让 Service 可通过节点端口访问,Kubernetes 会为 Service 配置集群 IP 地址,
相当于你请求了 `type: ClusterIP` 的服务。
- [`LoadBalancer`](https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#loadbalancer) 使用云平台的负载均衡器向外部公开 Service。Kubernetes 不直接提供负载均衡组件;
你必须提供一个,或者将你的 Kubernetes 集群与某个云平台集成。
- [`ExternalName`](https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#externalname) 将服务映射到 `externalName` 字段的内容(例如,映射到主机名 `api.foo.bar.example`)。
该映射将集群的 DNS 服务器配置为返回具有该外部主机名值的 `CNAME` 记录。
集群不会为之创建任何类型代理。

服务 API 中的 `type` 字段被设计为层层递进的形式 - 每层都建立在前一层的基础上。
但是,这种层层递进的形式有一个例外。
你可以在定义 `LoadBalancer` 服务时 [禁止负载均衡器分配 ](https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#load-balancer-nodeport-allocation)[`NodePort`](https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#load-balancer-nodeport-allocation)。

安装 Helm

首先查看 traefik 的 安装说明,建议使用 helm,那我们就先安装 helm。

helm 是 k8s 的软件的「包管理器」,可以帮助我们更好的管理在 k8s 上面安装的资源。

helm 就是一个简单的二进制可执行文件,读取./kube 下的配置文件和集群进行通信。

查看 helm 的 安装说明,我选择使用包管理器进行安装

1
2
3
4
5
6
 curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
 sudo apt-get install apt-transport-https --yes
 echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
 sudo apt-get update
 sudo apt-get install helm
 helm completion bash | sudo tee /etc/bash_completion.d/helm > /dev/null && sudo chmod a+r /etc/bash_completion.d/helm

安装 traefik

有了 helm,现在可以安装 traefik 了。

1
2
3
helm repo add traefik https://traefik.github.io/charts
helm repo update
helm install traefik traefik/traefik
1
2
3
PS C:\Users\suyiiyii> k get deploy
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
traefik   1/1     1            1           111m

当 traefik ready 了,就可以访问了。

但是,当我尝试访问 master 的 80 端口时,却报错

Untitled.png

很明显,我们还没有配置好。

甚至于,机器连 80 端口都没有监听,这样能访问才有鬼了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
root@ubuntu-71:~# ss -tnlp
State   Recv-Q  Send-Q Local Address:Port   Peer Address:Port  Process
LISTEN  0       4096   127.0.0.53%lo:53          0.0.0.0:*      users:(("systemd-resolve",pid=571,fd=15))
LISTEN  0       3            0.0.0.0:2623        0.0.0.0:*      users:(("mgmtd",pid=32196,fd=15))
LISTEN  0       128        127.0.0.1:6011        0.0.0.0:*      users:(("sshd",pid=33297,fd=8))
LISTEN  0       3          127.0.0.1:2601        0.0.0.0:*      users:(("zebra",pid=32178,fd=25))
LISTEN  0       3          127.0.0.1:2605        0.0.0.0:*      users:(("bgpd",pid=32225,fd=18))
LISTEN  0       3          127.0.0.1:2616        0.0.0.0:*      users:(("staticd",pid=32236,fd=12))
LISTEN  0       3          127.0.0.1:2617        0.0.0.0:*      users:(("bfdd",pid=32239,fd=19))
LISTEN  0       4096     10.21.22.71:2379        0.0.0.0:*      users:(("etcd",pid=1263,fd=8))
LISTEN  0       4096     10.21.22.71:2380        0.0.0.0:*      users:(("etcd",pid=1263,fd=7))
LISTEN  0       4096       127.0.0.1:10248       0.0.0.0:*      users:(("kubelet",pid=983,fd=15))
LISTEN  0       4096       127.0.0.1:10249       0.0.0.0:*      users:(("kube-proxy",pid=1509,fd=9))
LISTEN  0       4096       127.0.0.1:10259       0.0.0.0:*      users:(("kube-scheduler",pid=1270,fd=3))
LISTEN  0       4096       127.0.0.1:10257       0.0.0.0:*      users:(("kube-controller",pid=1253,fd=3))
LISTEN  0       4096       127.0.0.1:2379        0.0.0.0:*      users:(("etcd",pid=1263,fd=9))
LISTEN  0       4096       127.0.0.1:2381        0.0.0.0:*      users:(("etcd",pid=1263,fd=15))
LISTEN  0       4096     10.21.22.71:7472        0.0.0.0:*      users:(("speaker",pid=32071,fd=13))
LISTEN  0       4096      127.0.0.54:53          0.0.0.0:*      users:(("systemd-resolve",pid=571,fd=17))
LISTEN  0       4096       127.0.0.1:40045       0.0.0.0:*      users:(("containerd",pid=775,fd=10))
LISTEN  0       4096     10.21.22.71:7946        0.0.0.0:*      users:(("speaker",pid=32071,fd=8))
LISTEN  0       3               [::]:2623           [::]:*      users:(("mgmtd",pid=32196,fd=16))
LISTEN  0       4096               *:6443              *:*      users:(("kube-apiserver",pid=1277,fd=3))
LISTEN  0       4096               *:10256             *:*      users:(("kube-proxy",pid=1509,fd=8))
LISTEN  0       4096               *:10250             *:*      users:(("kubelet",pid=983,fd=17))
LISTEN  0       4096               *:7473              *:*      users:(("frr-metrics",pid=32206,fd=8))
LISTEN  0       4096               *:22                *:*      users:(("sshd",pid=2263,fd=3),("systemd",pid=1,fd=91))
LISTEN  0       128            [::1]:6011           [::]:*      users:(("sshd",pid=33297,fd=7))

我们看一下当前的 service

1
2
3
4
5
6
root@ubuntu-71:~# k get svc -A
NAMESPACE     NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
default       kubernetes                ClusterIP      10.96.0.1        <none>        443/TCP                      63m
default       whoami                    ClusterIP      10.102.215.208   <none>        80/TCP                       36m
kube-system   kube-dns                  ClusterIP      10.96.0.10       <none>        53/UDP,53/TCP,9153/TCP       63m
traefik       traefik                   LoadBalancer   10.98.132.99     <pending>     80:30686/TCP,443:32627/TCP   43m

可以看到,traefik 有一项一直是 pending 状态。并且,traefik 的 service 被配置成了 LoadBalancer 类型的。

我们通过查阅相关资料,得知,lb 通常是由云服务商提供,而我们在本地搭建的集群,默认是没有 lb 的,所以,我们还要去搞一个 lb 来。(traefik 文档为什么不告诉我要 lb🤬)

安装 metallb

通过网络搜索,在本地集群使用 lb,推荐的比较多的是 metallb。

首先,先了解一下基本概念。

我也不是很懂,也不会讲,所以就直接在这里给出其他参考资料吧:

https://blog.csdn.net/heian_99/article/details/135260444

https://www.qikqiak.com/post/openelb/

https://www.lixueduan.com/posts/cloudnative/01-metallb/

https://atbug.com/load-balancer-service-with-metallb/

我打算使用 L2 的网络,配置只有几个 ip 的小 ip 池来使用。

碎碎念:

怎么和我之前用的 k3s 不一样,k3s 自带 lb,开启了之后是在本机上面开指定端口,和 nodeport 差不多。这个 metallb 是会直接给一个新的 ip 地址,访问要访问这个 ip 地址,就和原来的机器的 ip 没有什么关系了。好像说其他大多数的 lb 都应该是新建一个 ip 地址的?

安装

参考文档

1
2
3
helm repo add metallb https://metallb.github.io/metallb
kubectl create ns metallb-system
helm install metallb metallb/metallb -n metallb-system

然后等相关的 pod 就绪了就可以使用了

1
2
3
4
5
6
NAME                                 READY   STATUS    RESTARTS   AGE
metallb-controller-66fddf5ff-9zzl9   1/1     Running   0          6m29s
metallb-speaker-jjzf8                4/4     Running   0          6m29s
metallb-speaker-ns9w2                4/4     Running   0          6m29s
metallb-speaker-pnswq                4/4     Running   0          6m29s
metallb-speaker-sjwld                4/4     Running   0          6m29s

配置

配置主要分两步:

  • 分配 ip
  • 声明 ip

网络上大部分的教程是基于 configmap 来配置的,但是 metallb 已经支持使用 CR 进行配置,所以我们全部使用 CR,这样可读性更佳,也便于修改。

下面给出的都是 k8s 的 manifest,使用 kubectl apply 即可

分配 ip

这里是配置 mentallb 可以使用哪一些 ip 地址,要用 CIRD 格式,用 - 表示范围也可以。

1
2
3
4
5
6
7
8
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: cheap
  namespace: metallb-system
spec:
  addresses:
  - 10.21.22.80-10.21.22.84

ip 地址要根据实际情况填写。

这里我用的是内网 ip,为了节约使用就只开了/30 的地址范围。

声明 ip

我用的是 L2 模式的,声明很简单,就应用这一段就行了:

1
2
3
4
5
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system

到此,关于 metallb 就配置完成了。我们就有一个本地的 lb 了。

再查看 service,我们可以看到 traefik 已经正常获取到 ip 了

1
2
3
NAME                TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
kubernetes          ClusterIP      10.96.0.1        <none>        443/TCP                      3h49m
traefik             LoadBalancer   10.108.193.80    10.21.22.80   80:32191/TCP,443:31252/TCP   159m

验收

同样的,我们先创建一个 deployment 和对应的 service

1
2
k create deployment nginx --image nginx
k create svc clusterip nginx --tcp=80

然后,再创建一个到 nginx service 的 ingress

1
k create ingress nginx --rule="/=nginx:80"

最后,我们访问 traefik 的地址,就可以打开网页了

Untitled.png

到此,ingress 就可以使用啦。😁

traefik 控制面板

在文档的 这个部分,提供了访问 traefik dashboard 的方法。在本机运行这行命令开启端口转发。

1
kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000

然后,我们就可以在本机打开 http://127.0.0.1:9000/dashboard/ 访问 dashboard 了。

Untitled.png

页面做的还是很美观的,在上面可以直观的看到添加的路由。

Untitled.png

由于 traefik 是一个独立的路由,里面的一些配置和 k8s 的 ingress 有一些不同,还需要学习一会。

总结

搞得比较顺利吧。(怎么每次都这样说~~,搞得不顺利就不会写出来了😓~~)

感觉要学习的东西好多,搞个 k8s,既要懂 linux 懂容器是怎么运行的,又要懂 http 懂请求是怎么路由的,还要懂网络懂数据包是怎么传输的,还有各种奇奇怪怪的。

不管怎么说,现在是在不断学习,不断进步的。🐋