Skip to content

Latest commit

 

History

History
174 lines (137 loc) · 7.53 KB

File metadata and controls

174 lines (137 loc) · 7.53 KB

K8S 高可用和 SLB 相关

K8S 高可用相关

etcd 就不说了,奇数个副本,可以坏 (n-1)/2 个,有条件的可以单独 SSD 机器部署 etcd 。K8S 所有组件和客户端,都会使用 kubeconfig 或者集群内 rbac 的 serviceaccount 后通过 kubernetes 这个 service 最终连到实体的 kube-apiserver 进程:

etcd 集群下,一般都是 kube-apiserver/kube-controller-manager/kube-scheduler 部署多份,但是所有的都是连接 kube-apiserver,所以需要一个负载均衡负载到 kube-apiserver 副本上。

K8S 集群内部相关

集群内部自带了第一个 Service CIDR 第一个 IP 的 kubernetes service:

$ kubectl -n default describe svc kubernetes 
Name:              kubernetes
Namespace:         default
Labels:            component=apiserver
                   provider=kubernetes
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.96.0.1
IPs:               10.96.0.1
Port:              https  443/TCP
TargetPort:        6443/TCP
Endpoints:         192.168.2.111:6443,192.168.2.112:6443,192.168.2.113:6443
Session Affinity:  None
Events:            <none>

集群内部访问 https://kubernetes.default 最终解析成 10.96.0.1,然后 DNAT 到 endpoint 里任意一个 kube-apiserver。可能有些聪明的人会问,这看起来是 kube-apiserver 上报的自己网卡 IP,如果是 nat 后的怎么处理,kube-apiserver 提供了 --advertise-address 选项,用于告诉访问自己使用哪个 IP。

使用负载均衡

kube-apiserver 本质上是个 web 七层服务,所以我们要整高可用不要把 k8s 想的太复杂了,保证了 apiserver 的高可用就行了。tcp 和 web 层面,是否软硬件,还是组合使用都行,例如:

  • 硬件 F5 配置转发
  • nginx、caddy、envoy、haproxy
  • 云上 SLB 代理
  • keepalived VRRP
  • keepalived VRRP + haproxy
  • 网络设备 ospf + lvs
  • ...

如果是 7层负载均衡,健康检查可以用应用层面 url /healthz:

$ kubectl get --raw / | grep /healthz
    "/healthz",
    "/healthz/autoregister-completion",
    "/healthz/etcd",
    "/healthz/log",
    "/healthz/ping",
    "/healthz/post
    ...

可以 kube-apiserver 不配置 --anonymous-auth=false/healthz 路由就返回 200 状态码了,或者把证书上传到 SLB 上之类的配置里,7层检查,例如下面是 haproxy 的 7 层检查 tcp 代理:

frontend k8s-api
  bind 0.0.0.0:8443
  bind 127.0.0.1:8443
  mode tcp
  option tcplog
  tcp-request inspect-delay 5s
  default_backend k8s-api

backend k8s-api
  mode tcp
  option tcplog
  option httpchk GET /healthz
  http-check expect string ok
  balance roundrobin
  default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
  server  api1  192.168.2.111:6443  check check-ssl verify none
  server  api2  192.168.2.112:6443  check check-ssl verify none
  server  api3  192.168.2.113:6443  check check-ssl verify none

kubeconfig 文件里包含有 kubernetes 的 URL 地址,想在使用 kubectl 的时候绕过负载均衡,可以使用 -s, --server= 来指定 kube-apiserver url ,另外可能你会遇到 kubectl 报错无法连接:

The connection to the server localhost:8080 was refused - did you specify the right host or port?

这是因为没有 ~/.kube/config 文件和 KUBECONFIG 变量文件不存在或者权限问题下,kubectl 默认认为连接 kube-apiserver 地址是 http://localhost:8080 的不需要授权的匿名地址,而现在 kube-apiserver 都会把 insecure-port 关闭了造成的报错。

local proxy

在一些私有化环境内,客户会无法提供 SLB 和硬件负载均衡,环境也可能是 openstack 环境上的虚拟机而无法使用 VRRP VIP(会因为 IP 和 MAC 不匹配被 drop),所以可以使用 local proxy 方式代理,也就是每个节点上部署一个软件做负载均衡,例如每个节点部署一个 nginx,监听 0.0.0.0:8443 反向代理:

  upstream kube_apiserver {
    least_conn;
    server 192.168.2.111:6443 max_fails=3 fail_timeout=10s;
    server 192.168.2.112:6443 max_fails=3 fail_timeout=10s;
    server 192.168.2.113:6443 max_fails=3 fail_timeout=10s;
    }

  server {
    listen        0.0.0.0:8443;
    proxy_pass    kube_apiserver;
    proxy_connect_timeout 1s;
  }

例如下面是市面上的 sealos 就是使用 kubeadm 部署,staticPod 的目录加了个 yaml,使用的内核 lvs 能力做四层负载。每个 node 跑一个 lvs 的 watch 进程的 pod 去维护 lvs 的规则保持高可用:

ha03

再次重申下,不要局限在自己会的软件和实现手段的顽固思想上,而是看场景和后续升级维护和规模多方面考虑,例如如果客户经常注重漏洞扫描 CVE,那选择 caddy 这种单二进制 web server 做代理更新方便。

SLB 和 hairpin

例如云上,阿里云的 K8S 实例,kube-apiserver 会创建一个 SLB,所有 client 访问:

# SLB 反向代理的机器/服务,我们一般称之为 real server。
                                            +-------+
                                            |       |
                               -+---------->+  rs   |
                              /             +-------+
                             /    
                            /     
                           /                +-------+
+--------+                /                 |       |
| client +----------->   SLB     ---------->+  rs   |
+--------+                \                 +-------+
                           \    
                            \     
                             \              +-------+
                              ------------->+       |
                                            |  rs   |
                                            +-------+

因为阿里云的托管版 K8S 实例不能登录,所以有些人在自己部署 K8S 后创建 SLB 做负载的时候会发现存在问题。master 上也有 kubelet,kubelet 的 kubeconfig 里的 IP 是 SLB 的时候,这样会是下面的流量图:

                   +-------+
                   |       |
      -+-----------+  rs   |
     /             +-------+
    /    
   /     
  /                +-------+
 /                 |       |
SLB  <-------------+  rs   |
 \                 +-------+
  \    
   \     
    \              +-------+
     --------------+       |
                   |  rs   |
                   +-------+

例如上面这种 rs 访问 SLB 你会发现有几率超时,这个在 NAT 行为里有个专门的术语,叫 hairpinning(直译为发卡,意思 是像发卡一样,沿着一边上去,然后从另一边绕回来),很多负载均衡会直接扔掉报文。

hairpin-icon

不单单是 master,例如 ingress controller 访问自己用的 SLB,而自己又是 SLB 的 rs 列表里,以及有些 Pod 的业务 --external-url 写 SLB 的 IP ,由于 CNI 对 Pod 访问非 Pod/Service CIDR 都会做 SNAT,访问到 SLB 来源 IP SNAT 成了 node 的节点 IP ,这个 node 又是 SLB 的 rs 也会出问题。

其实 Pod 不能访问自己的 Service 也是这个相关,但是 kubelet 提供了选项 hairpinMode: promiscuous-bridge 配置。

链接