zhangguanzhang's Blog

k8s高可用涉及到ip填写的相关配置和一些坑

字数统计: 2k阅读时长: 8 min
2019/03/11 Share

etcd就不说了,奇数个副本,可以坏(n-1)/2个,但是不可能同时坏那么多,这里不讨论etcd单独不单独跑。推荐个文档 https://github.com/etcd-io/etcd/tree/master/Documentation/op-guide

管理组件

      先说说k8s组件,kubelet和kube-proxy啥的肯定写LB或者VIP:HA_port,如官方的图
ha_pic1

scheduler和controller连接的apiserver地址我看网上有写127的也有写LB或者VIP:HA_port的,刚看到官方文档说两种都行 https://kubernetes.io/zh/docs/admin/high-availability/#%E8%BF%9B%E8%A1%8Cmaster%E9%80%89%E4%B8%BE%E7%9A%84%E7%BB%84%E4%BB%B6

      官方文档这说得基本有疑问的都写清楚了,前期scheduler和controller不支持ha,后来增加了选举功能,他俩连apiserver的ip写127.0.0.1和负载均衡器都可以

apiserver

      我们要明白的一点是work上有cni(calico flannel …),kube-proxy,kubelet这些都会连接apiserver,通过向apiserve发http请求来反馈自身信息和同步要做的事情来保证node处于预期状态。
      apiserver本质上是个web七层服务,所以我们要整高可用不要把k8s想的太复杂了,保证了apiserver的高可用就行了。高可用实现的手段和工具看个人掌握的,例如上图官方的只说了SLB,市面上也有其他的local proxy的实现方案。

流量入口单点的高可用

  • SLB,
  • 硬件LB和
  • 基于VIP

这三种高可用都是同一时间多台apiserver下,所有流向apiserver的流量是全部涌向SLB/LB/VIP上的,SLB和LB稳定性高于VIP,VIP一般是keepalived+haproxy的方式,也就是所有流量同时流向拥有VIP的那台机器。硬件LB和SLB出故障了可以联系厂商,VIP的HA最好是懂keepalived+haproxy。不过一般的节点数量超过20台公司规模很大了,HA方面肯定会有懂的人调优,也可以ospf+lvs. 另外很多人不懂网络,云上用不了VIP的,一般都关闭了组播并且还有网卡ip和mac的绑定关系(青云测试了没有绑定关系),见博客 https://zhangguanzhang.github.io/2018/07/18/ecs-vip/

阿里的SLB四层有个问题(snat模式)

client访问SLB流程

下面是client访问SLB,没有问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                                            +-------+
| |
-+---------->+ rs |
/ +-------+
/
/
/ +-------+
+--------+ / | |
| client +-----------> SLB ---------->+ rs |
+--------+ \ +-------+
\
\
\ +-------+
------------->+ |
| rs |
+-------+

snat下,client访问SLB包从client出去未到SLB上,源目IP为

1
src: clientIP dest: SLBIP

SLB做SNAT访问rs:

1
src: SLBIP dest: rsIP

SLB给client回包,把包里的源IP替换成自己的ip

1
src: SLBIP dest: clientIP

客户端感知的是SLB回包

real server访问SLB流程

apiserver也会去访问scheduler和controller,也就是下面这样,rs访问SLB,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                   +-------+
| |
-+-----------+ rs |
/ +-------+
/
/
/ +-------+
/ | |
SLB <-------------+ rs |
\ +-------+
\
\
\ +-------+
--------------+ |
| rs |
+-------+

SLB是snat模式下,rs访问SLB,发出去的包源目IP为

1
src: ownIP dest: SLBIP

到了SLB上做了snat后

1
src: SLBIP dest: SLBIP

然后就环了,rs收不到回包

kubeadm的方案

根据官方的文档,kubeadm的HA在init的时候执行配置就可以达到了,如下:
https://kubernetes.io/docs/setup/independent/high-availability/

1
2
3
4
5
6
7
apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
kubernetesVersion: stable
apiServer:
certSANs:
- "LOAD_BALANCER_DNS"
controlPlaneEndpoint: "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT"

controlPlaneEndpoint:为负载均衡器(SLB or 硬件LB)的地址或DNS + 端口
      而这个controlPlaneEndpoint实际上最终会取ip(注意不带端口)写到kube-apiserver的选项--advertise-address作为值。
      默认情况下--advertise-address不配置下它的值将会和--bind-address一样。它的作用就是宣告,在etcd启动后kube-apiserver初次起来后会创建一个svc名叫kubernetes

1
2
3
$ kubectl get svc kubernetes 
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24h

      这个svc的endpoints(ep)就是选项--advertise-address的ip,port则是apiserver的--secure-port。假设用户配置的--secure-port为6443,所以一般云上SLB的话那这个宣告可以填写LB的ip,然后默认的kubernetes的endpoints是<SLB_IP>:6443。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ kubectl describe ep kubernetes 
Name: kubernetes
Namespace: default
Labels: <none>
Annotations: <none>
Subsets:
Addresses: <SLB_IP>
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
https 6443 TCP

Events: <none>

vip单点的坑之 –advertise-address

      截止到现在似乎都没有啥坑,但是这几天写生产环境的部署方案的时候,因为考虑到多网卡,而kubeadm部署的默认很多组件是bind 0.0.0.0的会导致所有网卡的ip的请求都会监听。

      我测试的机器ip信息为

IP Hostname CPU Memory
172.16.1.2 k8s-m1 4 8G
172.16.1.3 k8s-m2 4 8G
172.16.1.4 k8s-m3 4 8G

      于是我--bind-address写网卡ip例如三台分别是172.16.1.2、3、4,HA用的keepalived+haproxy,vip是5。因为master上6443被apiserver监听了,所以haproxy是另一个端口我是用的8443。

      而坑就是宣告,我仿照官方意见--advertise-address写了VIP(带不了端口,否则报错)。启动了apiserver后默认宣告kubernetes的ep是VIP:6443,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ kubectl describe ep kubernetes 
Name: kubernetes
Namespace: default
Labels: <none>
Annotations: <none>
Subsets:
Addresses: 172.16.1.5
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
https 6443 TCP

Events: <none>

      发现后面例如flannel的pod要请求apiserver的时候走它根本不通。因为apiserver是bind的网卡ip,而haproxy是bind 0.0.0.0:8443,没有任何进程是bind vip:6443。

1
2
3
4
5
6
$ netstat -nlpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:8443 0.0.0.0:* LISTEN 40545/haproxy
tcp 0 0 0.0.0.0:8443 0.0.0.0:* LISTEN 40545/haproxy
tcp 0 0 100.64.2.62:6443 0.0.0.0:* LISTEN 258252/kube-apiserv

      这里并没有0.0.0.0:6443的bind,所以请求走vip:6443是不通的。
于是我patch了ep改为了8443。但是后面发现只要一重启kube-apiserver就宣告的ep的port就成了6443。
      之前apiserver是bind 0.0.0.0是没问题的,所以如果是多网卡还用的VIP这种ha下,宣告还是写node自己的网卡ip或者不写,虽然集群内pod访问apiserver走的是svc的负载均衡,但是你要是不想负载在svc那就花钱上LB宣告LB的ip呗。
      写node的网卡ip后,apiserver存活期间会去更新ep的ttl,只要apiserver 宕了它的ep会因为没有刷新ttl而被自动剔除。这个想法是issue里看到别人提到的想法,也就是现在的工作原理。

在m3上停掉apiserver

1
2
3
4
5
6
7
8
9
10
[root@k8s-m3 ~]# systemctl stop kube-apiserver
[root@k8s-m3 ~]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 172.16.1.2:6443,172.16.1.3:6443 24h
[root@k8s-m3 ~]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 172.16.1.2:6443,172.16.1.3:6443 24h
[root@k8s-m3 ~]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 172.16.1.2:6443,172.16.1.3:6443 24h

在m3上启动apiserver

1
2
3
4
[root@k8s-m3 ~]# systemctl restart kube-apiserver
[root@k8s-m3 ~]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 172.16.1.2:6443,172.16.1.3:6443,172.16.1.4:6443 24h

其他实现手段

      软件实现方案不仅仅是keepalived+haproxy,还有其他的例如nginx,envoy都能做到,HA是思想而不是依赖于具体的软件工具

Local Proxy

      前面的都是流量从node出去的后全部涌向HA的点上,然后再分散调度到所有的apiserver上,如果一开始从node上分散开来呢。例如第一次向apiserver发起请求是发到第一个rs上,第二次请求是发到第二个rs上。
      worker节点相关的组件都是只能写一个ip地址,所以我们可以定一个VIP(虚拟IP),流量发到VIP,vip在node自己上,然后rs就是apiserver,从起点就开始分散开了。市面上比较火的一个sealos就是魔改了kubeadm,staticPod的目录加了个yaml,使用的四层lvs做负载。每个node跑一个lvs的watch进程pod去维护lvs的规则保持高可用:
ha_pic1
这里不单单是sealos实现,我们自己用nginx和haproxy以及envoy都能实现,主要是思想

混合

例如下面,单独的两个LB的machine上,然后proxy带apiserver上,vip也可以lvs直接分别调度到L1或者L2上,然后再proxy到apiserver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                  keepalived+haproxy        +-------+
| |
+---------+---------->+ rs |
| +-----+ | +-------+
| | | |
| | L1 | |
| +-----+ | +-------+
+--------+ VIP | | | |
| client +----------->+ +---------->+ rs |
+--------+ | | +-------+
| +-----+ |
| | | |
| | L2 | | +-------+
| +-----+ +---------->+ |
+---------+ | rs |
+-------+

CATALOG
  1. 1. 管理组件
  2. 2. apiserver
    1. 2.1. 流量入口单点的高可用
      1. 2.1.1. client访问SLB流程
      2. 2.1.2. real server访问SLB流程
      3. 2.1.3. kubeadm的方案
      4. 2.1.4. vip单点的坑之 –advertise-address
      5. 2.1.5. 其他实现手段
        1. 2.1.5.1. Local Proxy
        2. 2.1.5.2. 混合