阿里slb做为k8s的负载均衡的限制

2020-11-03  本文已影响0人  PENG先森_晓宇

keepalived的限制

如果在本地搭建,我们可以使用haproxy+keepalived方式轻松实现k8s中的负载均衡,但是阿里的ecs不能使用keepalived,所以我们被迫只能使用阿里的 slb了。

理论

既然keepalived的方式不能使用,那我们就使用阿里的slb进行负载均衡呗,由于该负载均衡不需要被外部访问,只提供对k8s集群节点之间的访问,所以我们就使用私网的slb。
[图片上传失败...(image-b02d7-1604545387128)]

实践

我们保证该slb和k8s集群节点的node属于同一个局域网内,具体配置如下

系统角色 ip 节点角色 CPU Memory Hostname」
CentOS 7.6 64位 192.168.0.161 master 2 vCPU 4 GiB master1
CentOS 7.6 64位 192.168.0.162 master 2 vCPU 4 GiB master2
CentOS 7.6 64位 192.168.0.163 master 2 vCPU 4 GiB master3
私网slb 192.168.4.11 负载均衡

slb创建后端服务器

第一步就是监听该slb的6443端口,该端口后端的服务器组分别是3台ecs的6443端口(apiserver服务)。接着我们可以在master1节点上执行如下命令

nc -v 192.168.4.11 6433

由于后端服务器组的 apiserver 都尚未运行,预期会出现一个连接拒绝错误。然而超时意味着负载均衡器不能和控制平面节点通信。 如果发生超时,请重新配置负载均衡器与控制平面节点进行通信。

kubeadm-config.yaml文件

我们在master1节点上创建kubeadm-config.yaml文件,用于初始化控制平面节点,如下。

apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: 1.19.2
controlPlaneEndpoint: 192.168.4.11:6443
networking:
  # This CIDR is a Calico default. Substitute or remove for your CNI provider.
  podSubnet: "172.22.0.0/16"
  #   # 控制平面节点,安装kube-apiserver,kube-cotroller-manager,kube-scheduler,kube-proxy,这些组件的版本最好和k8s的版本相同,还有安装pause,etcd,coredns
imageRepository: phperall

内网slb的限制

接着我们在master1节点上执行如下命令初始化

kubeadm init --config kubeadm-config.yaml --upload-certs

最后结果如下

[kubelet-check] Initial timeout of 40s passed.

        Unfortunately, an error has occurred:
                timed out waiting for the condition

        This error is likely caused by:
                - The kubelet is not running
                - The kubelet is unhealthy due to a misconfiguration of the node in some way (required cgroups disabled)

        If you are on a systemd-powered system, you can try to troubleshoot the error with the following commands:
                - 'systemctl status kubelet'
                - 'journalctl -xeu kubelet'

        Additionally, a control plane component may have crashed or exited when started by the container runtime.
        To troubleshoot, list all containers using your preferred container runtimes CLI.

        Here is one example how you may list all Kubernetes containers running in docker:
                - 'docker ps -a | grep kube | grep -v pause'
                Once you have found the failing container, you can inspect its logs with:
                - 'docker logs CONTAINERID'

error execution phase wait-control-plane: couldn't initialize a Kubernetes cluster
To see the stack trace of this error execute with --v=5 or higher

看上面的日志好像是kubelet的问题。我们先确认kubelet是否运行,发现处于running状态。

[root@master ~]# systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: active (running) since Wed 2020-11-04 10:36:54 CST; 7min ago
     Docs: https://kubernetes.io/docs/
 Main PID: 23284 (kubelet)
    Tasks: 13
   Memory: 29.5M
   CGroup: /system.slice/kubelet.service

接着查看kubelet的日志

[root@master ~]# journalctl -xeu kubelet
Nov 04 10:45:36 master kubelet[23284]: E1104 10:45:36.317572   23284 kubelet.go:2183] node "master" not found
Nov 04 10:45:36 master kubelet[23284]: E1104 10:45:36.360976   23284 controller.go:136] failed to ensure node lease exists, will retry in 7s, error: Get "https://192.168.4.11:6443/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/master?timeout
Nov 04 10:45:36 master kubelet[23284]: E1104 10:45:36.417732   23284 kubelet.go:2183] node "master" not found
Nov 04 10:45:36 master kubelet[23284]: E1104 10:45:36.517936   23284 kubelet.go:2183] node "master" not found
Nov 04 10:45:36 master kubelet[23284]: E1104 10:45:36.618138   23284 kubelet.go:2183] node "master" not found
Nov 04 10:45:36 master kubelet[23284]: E1104 10:45:36.718290   23284 kubelet.go:2183] node "master" not found
Nov 04 10:45:36 master kubelet[23284]: E1104 10:45:36.818500   23284 kubelet.go:2183] node "master" not found
Nov 04 10:45:36 master kubelet[23284]: E1104 10:45:36.918713   23284 kubelet.go:2183] node "master" not found
Nov 04 10:45:36 master kubelet[23284]: E1104 10:45:36.944451   23284 kubelet.go:2103] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
Nov 04 10:45:37 master kubelet[23284]: E1104 10:45:37.018813   23284 kubelet.go:2183] node "master" not found
Nov 04 10:45:37 master kubelet[23284]: E1104 10:45:37.118960   23284 kubelet.go:2183] node "master" not found

发现一个奇怪的问题,https://192.168.4.11:6443提示timeout。

接着我们在master1节点上首先测试本地的6443端口是否已经启用

[root@master ~]# telnet 192.168.0.161 6443
Trying 192.168.0.161...
Connected to 192.168.0.161.
Escape character is '^]'.

[root@master ~]# netstat -anp | grep 6443     
tcp6       0      0 :::6443                 :::*                    LISTEN      23931/kube-apiserve

看到master1节点的6443端口已经被占用,接着我们在master1节点测试slb的6443端口服务,按理说master1节点的6443服务已经启用,那么slb的6443服务也应该是可用可连通的。

[root@master ~]# telnet 192.168.4.11 6443
Trying 192.168.4.11...

遗憾的是slb的6443端口并没有连通,我们在master2,master3节点上分别连接slb的6443端口,发现都timeout。我们又找了同一局域网内的另一台ecs,该ecs不属于slb的后端服务器,在该ecs上却能连接slb的6443端口,现在问题找到了:

对于私网slb,后端服务器组的服务已经启用,但是在后端节点上连接slb时却报连接超时,也正是这个原因导致控制平面节点初始化失败。

带着这个疑问我们提了阿里工单,客服最后给出结论。

对于四层监听的后端服务器无法访问私网SLB问题,是由于目前负载均衡不支持同时作为客户端和服务端,因为SLB tcp协议监听,是直接转发客户端IP和连接给后端ECS,当后端ECS连接SLB端口,SLB转发该连接时,后端ECS“看到”数据包是来自自己的IP,回包就不会回给SLB了,无法正常建立连接,所以telnet会不通。

而ECS可以telnet 公网SLB端口,是因为使用的是ECS的公网IP,VPC ECS的公网IP是在网络层映射到ECS的内网IP上的,ECS内部并没有该公网IP,所以可以telnet通公网SLB的端口。

私网的slb是不可以使用了,我们换成公网slb之后重新按照上述流传执行一遍,最后初始化控制平面节点成功。

初始化过程

初始化之前slb的6443端口负载的后端服务器组的6443服务肯定都没有启动。
初始化开始后先在本地拉取相关镜像,随后apiserver等服务启动起来,也就是本地的6443服务已经启动。
接着验证slb的6443的连通性,由于master1节点的6443服务已经启动,那么slb的后端组在健康检查中就会发现有master1节点6443端口一起启动,所以slb的6443端口服务也就正常启动了。

通过上面的描述,在控制平面节点上大致需要满足以下俩点才能初始化成功

公网slb方式优缺点

优点:可以将kubeconfig文件复制到你笔记本电脑上,进而可以在你本地访问集群,也正是由于这种方式可能造成安全泄漏的问题。
缺点:apiserver暴露的ip是公网ip,一是各个节点之间沟通的效率变低,二是具有安全性问题。

私网slb+haproxy+node方式

如果公司非得使用私网的话,我们可以采取这种方式,大概拓扑图如下


拓扑图

最上层是一个私网的slb,该slb的后端服务器组为俩个haproxy,使用俩台haproxy可以避免haproxy的单点故障,haproxy的后端服务器为3台k8s的master节点。

估计看到这里有人会有疑问,上面介绍的私网slb方式会导致四层监听的后端服务器无法访问私网SLB问题,那么该种方式就不会有这个问题吗?我们带着疑问进行测试。

环境准备

我们准备6台ecs,配置如下

系统角色 ip 节点角色 CPU Memory Hostname」
CentOS 7.6 64位 192.168.0.161 master 2 vCPU 4 GiB master1
CentOS 7.6 64位 192.168.0.162 master 2 vCPU 4 GiB master2
CentOS 7.6 64位 192.168.0.163 master 2 vCPU 4 GiB master3
CentOS 7.6 64位 192.168.0.164 haproxy 2 vCPU 4 GiB
CentOS 7.6 64位 192.168.0.165 haproxy 2 vCPU 4 GiB
私网slb 192.168.4.11 负载均衡

部署

slb监听6443端口,该端口的后端服务器组为俩台haproxy并监听8888端口。

haproxy监听8888端口,该端口的后端服务器组为3台控制平面节点并监听6443端口,haproxy.cfg文件如下。

global

    # 设置日志文件输出定向
    log 127.0.0.1 local3 info

    # 改变当前工作目录
    chroot /usr/local/haproxy

    # 用户与用户组
    #user haproxy
    #group haproxy

    # 守护进程启动,运维方式为后台工作
    daemon

    # 最大连接数
    maxconn 4000
    #pid文件位置
    pidfile /var/run/haproxy.pid
# 作用于其后紧跟的listen块,直至下一个defaults 块,下一个default 将替换上一个块作用于以后的listen
defaults

    # 启用每个实例日志记录事件和流量。
    log global

    # 默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK
    mode http
    #option forwardfor    #如果后端服务器需要获得客户端的真实ip,需要配置的参数,记录客户端IP在X-Forwarded-For头域中,这里设置的话在backend段里面则不需要设置

    # maxconn 65535         maxconn 每个进程可用的最大连接数
    # retries 3         当对server的connection失败后,重试的次数  
    # option abortonclose     启用或禁用在队列中挂起的中止请求的早期丢弃
    # option redispatch     启用或禁用在连接故障情况下的会话重新分配
    # option dontlognull     启用和禁用 记录 空连接
    # option httpclose         每次请求完毕后主动关闭http通道,HA-Proxy不支持keep-alive模式
    # option forwardfor     获得客户端IP
    # option httplog        记录HTTP 请求,session 状态和计时器

    option abortonclose #当服务器负载很高的时候,自动结束掉当前队列处理比较久的链接
    option redispatch #当serverid对应的服务器挂掉后,强制定向到其他健康的服务器

    option httplog
    option dontlognull
    timeout connect 5000
    timeout client 50000
    timeout server 50000
listen  k8s-control-plane
    #访问的IP和端口
    bind  0.0.0.0:8888
    #网络协议
    mode  tcp
    #负载均衡算法(轮询算法)
    #轮询算法:roundrobin
    #权重算法:static-rr
    #最少连接算法:leastconn
    #请求源IP算法:source
    balance  roundrobin
    #日志格式
    option  tcplog
    #在MySQL中创建一个没有权限的haproxy用户,密码为空。Haproxy使用这个账户对MySQL数据库心跳检测
    server  master1 192.168.0.161:6443 check weight 1 maxconn 2000
    server  master2 192.168.0.162:6443 check weight 1 maxconn 2000
    server  master2 192.168.0.163:6443 check weight 1 maxconn 2000

我们使用haproxy:1.7镜像,在俩台haproxy所在节点分别执行如下操作:

  1. 创建Dockerfile
FROM haproxy:1.7
RUN mkdir -p /usr/local/haproxy/
COPY ./haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
  1. 构建镜像
docker build -t my-haproxy .
  1. 启动容器
docker run -it --name haproxy -p 8888:8888 --privileged my-haprox

kubeadm-config文件中controlPlaneEndpoint参数应为私网slb+6443端口,配置文件如下

apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: 1.19.2
controlPlaneEndpoint: 192.168.4.11:6443
networking:
  # This CIDR is a Calico default. Substitute or remove for your CNI provider.
  podSubnet: "172.22.0.0/16"
  #   # 控制平面节点,安装kube-apiserver,kube-cotroller-manager,kube-scheduler,kube-proxy,这些组件的版本最好和k8s的版本相同,还有安装pause,etcd,coredns
imageRepository: phperall

执行初始化,发现可以初始化成功

kubeadm init --config kubeadm-config.yaml --upload-certs

以下所有测试在master1节点上测试
我们首先测试master1节点的apserver服务,6443端口是否已经被占用

[root@master ~]# telnet 192.168.0.161 6443
Trying 192.168.0.161...
Connected to 192.168.0.161.
Escape character is '^]'

master1节点的6443端口显示已经被占用,接着我们测试haproxy节点的8888端口是否连通

[root@master ~]# telnet 192.168.0.164 8888
Trying 192.168.0.164...
Connected to 192.168.0.164.
Escape character is '^]'

显示haproxy的8888端口已经连通,接着测试slb的6443端口是否被占用,发现可以连通

[root@master ~]# telnet 192.168.4.11 6443
Trying 192.168.4.11...
Connected to 192.168.4.11.
Escape character is '^]'.

到此说明我们的3层架构都已经连通,说明此方案是可以执行的。



最后我们在haproxy所在节点上测试下是否可以连通slb的6443端口
[root@master ~]# telnet 192.168.4.11 6443
Trying 192.168.4.11...

之前提的那个疑问我们现在得到了答案。 但有一点是需要特别注意的

haproxy所在节点一定不能在master所在节点上,需要另外俩台ecs,至于为什么,相信大家已经有答案了。

优缺点

优点:由于中间多了一层haproxy,所以巧妙的解决了私网slb四层监听的后端服务器无法访问私网SLB问题。
缺点:很显而易见了,中间多了一层haproxy的转发代理,而且也增加了成本。

总结

现在大概有俩中方式可以实现k8s的高可用,一种是使用公网slb的方式,另一种是使用私网+haproxy+node的方式,这俩中方式各有优缺点,结合自己的实际情况选择适合的方案。

上一篇下一篇

猜你喜欢

热点阅读