DBAplus线上分享实录|Kubernetes Master High Availability 高级实践

Posted 才云Caicloud

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DBAplus线上分享实录|Kubernetes Master High Availability 高级实践相关的知识,希望对你有一定的参考价值。

才云科技云开源高级工程师唐继元受邀DBAplus社群,在线分享《Kubernetes Master High Availability 高级实践》,介绍如何构建Kubernetes Master High Availability环境。




以下是分享实录:

大家好,我是才云科技的唐继元,今天给大家带来一篇技术分享,本次分享我将为大家介绍如何构建Kubernetes Master High Availability环境。此次分享内容是我在工作中经验总结,如果有不正确的或者需要改进的地方,欢迎各位大神指正。

相信大家对容器、dockerkubernetes这些概念并不陌生。下面进入本次分享的正题。

Kubernetes作为容器编排管理系统,通过SchedulerReplicationController等组件实现了应用层的高可用,但是针对Kubernetes集群,还需要实现Master组件的高可用。

本次分享论述的Master高可用方案主要基于社区的高可用方案()的实践,但是社区的高可用方案中采用的GCEExternal Loadbalancer,并未论述如何实现External Loadbalancer,而且也并没有将Kubernetes集群组件容器化。所以,我们的高可用方案在社区高可用方案的基础之上进行了如下两个方面的提升:

第一,除了kubelet之外,Kubernetes所有组件容器化;

第二,通过haproxykeepalived构建Loadbalancer实现Master的高可用。 

下面我们分四个章节来详细论述Kubernetes Master High Availability环境的搭建。

1. HA Master整体架构

2. 核心技术点和难点

3. 实践中的遇到的那些坑

4. 社区关于HA Master的未来发展

 

1.HA Master整体架构


我们已经成功将支持Master High AvailabilityKubernetes集群部署到企业私有云平台,底层采用的是Ubuntu 14.04操作系统。下面是一个典型的部署环境:

HOST

IP ADDRESS

PROCESS

STATIC PODS

lb-1

192.168.205.252

kubelet

(haproxy, keepalived), flannel

lb-2

192.168.205.253

kubelet

(haproxy, keepalived), flannel

master-1

192.168.205.11

kubelet

apiserver, controller manager, scheduler, kubeproxy, etcd, flannel

master-2

192.168.205.12

kubelet

apiserver, controller manager, scheduler, kubeproxy, etcd, flannel

master-3

192.168.205.13

kubelet

apiserver, controller manager, scheduler, kubeproxy, etcd, flannel

node-1

192.168.205.21

kubelet

kubeproxy, flannel

node-2

192.168.205.22

kubelet

kubeproxy, flannel

node-3

192.168.205.23

kubelet

kubeproxy, flannel

Static Pods是由其所在节点上的kubelet直接管理,而不需要通过Apiserver来监视它们。Static Pods的资源类型只能是Pod,而且不与任何的Replication Controller相关联,它们完全由kubelet来监视,并且当它们异常停止的时候由该kubelet负责重启它们。

(haproxy, keepalived):这里表示我们将haproxykeepalived放置在同一个pod中。


1.1.kubelet对static pod高可用的支持

我们需要为kubelet进程配置一个manifests监视目录:

--config=/etc/kubernetes/manifests

如果有新的yaml/manifest文件添加到该目录,kubelet则根据yaml/manifest文件创建一个新的static pod

如果我们把某个yaml/manifest文件从该目录删除,kubelet则会删除由该yaml/manifest文件所产生的static pod

如果该目录下的yaml/manifest文件有更新,kubelet则会删除原来的static pod,而根据更新后的yaml/manifest文件重新创建一个新的staticpod

如果manifests目录下的文件没有任何变化,但是其下某个yaml/manifest文件所产生的static pod错误退出或者被误删后,kubelet仍然会根据该yaml/manifest文件重新创建一个新的static pod

这样,kubelet在一定程度上保证了static pod的高可用。

 

1.2.kubelet进程的高可用

kubelet通过manifests监视目录保证了staticpod的高可用,但是如果kubelet进程本身错误退出或者被误删后,谁来负责重新启动kubelet进程呢?

Linux系统中,我们可以通过MonitUpstartSystemdSupervisor等工具实现对服务的监控保证服务的高可用。

Ubuntu 14.04操作系统中,我们将kubelet做成系统服务,利用Upstart来保证kubelet服务的高可用,下面是kubelet服务基于Upstart的服务启动脚本/etc/init/kubelet.conf

description "Kubelet service"

author "@jainvipin"

 

respawn

 

# make sure we keep respawn for sometime

respawn limit 600 30

 

start on runlevel [2345]

stop on runlevel [!2345]

 

pre-start script

       # see also  https://github.com/jainvipin/kubernetes-ubuntu-start

       KUBELET=/opt/bin/$UPSTART_JOB

       if [ -f  /etc/default/$UPSTART_JOB ]; then

              .  /etc/default/$UPSTART_JOB

       fi

       if [ -f $KUBELET ]; then

              exit 0

       fi

           exit  22

end script

 

script

       # modify these in  /etc/default/$UPSTART_JOB (/etc/default/docker)

       KUBELET=/opt/bin/$UPSTART_JOB

       KUBELET_OPTS=""

       if [ -f  /etc/default/$UPSTART_JOB ]; then

              .  /etc/default/$UPSTART_JOB

       fi

       exec "$KUBELET"  $KUBELET_OPTS

end script

其中:

respawn: 该命令设置服务或任务异常停止时将自动启动。除stop命令外的停止都是异常停止。

respawn limit: 该命令设置服务或任务异常停止后重启次数和间隔时间。

 

1.3.Master High Availability Kubernetes整体架构图

从架构图中我们可以看到:

1 Upstart保证docker服务和kubelet服务的高可用,而Kubernetes的其他组件将以staticpod的方式由kubelet保证高可用。

2)两台lb节点通过haproxykeepalived构建出一个ExternalLoadbalancer,并提供VIP供客户端访问。

3 Haproxy配置成“SSLTermination”方式,外网client通过HTTPS请求访问集群,而内网client则可以通过HTTPS/HTTP请求访问。

4 Kubernetes高可用集群通过flannelstatic pod构建一个Overlay网络,使集群中的docker容器能够通过Kubernetes Cluster IP进行通信。

 

2.核心技术点和难点


2.1.运行在特权模式的组件

Kubernetes集群中的一些组件需要通过内核模块来为集群提供服务,因此这些组件需要运行在特权模式下,以便能访问相应的内核模块。


2.1.1.开启特权模式

为了支持docker容器在特权模式下运行,我们需要开启Kubernetes集群的特权模式权限:

--allow-privileged=true

这里主要体现在kubelet服务和apiserver服务。 

1 Kubelet service

kubelet服务需要开启特权模式权限,以便允许docker容器向kubelet请求以特权模式运行。 

2 Apiserver static pod

apiserver static pod需要开启特权模式权限,以便运行在特权模式下的docker容器能够访问apiserver服务。

 

2.1.2.运行在特权模式下的docker容器

运行在特权模式下的docker容器,在yaml文件中需要添加如下字段:

securityContext:

     privileged: true

这里主要体现在kubeproxy服务、flannel服务和keepalived服务。 

1 Kubeproxy static pod

kubeproxy需要通过Iptables设置防火墙规则。 

2 Flannel static pod

flannel需要访问vxlanopenvswitch等路由数据报文。 

3 Keepalived static pod

keepalived需要访问IP_VS内核模块来建立VIP

 

2.2.Static pod必须运行在主机网络下

如上所述的这些以static pod形式存在的Kubernetes集群组件,必须工作在主机网络下:

hostNetwork: true

虽然Overlay网络是为了让不同节点间的docker容器进行通信,而上述以staticpod形式存在的组件也都是docker容器,但是它们之间的心跳和信息交流都需要通过主机网络而不是类似于flannel等的Overlay网络。理由如下:

2 kubeproxyflannelhaproxy需要通过主机网络修改路由规则,从而使主机上的服务能被其他主机访问。

3 haproxy需要将外网请求重定向到内网后端服务器上,也必须需要主机网络。

 

2.3.External Loadbalancer部署要点

对于如何配置haproxykeepalived,网络上有非常多的资源,所以这里不在论述。下面我们来分析一下部署过程中的一些要点。

External Loadbalancer由至少两台lb node组成,通过haproxykeepalived pod实现Master的负载均衡,对外提供统一的VIP 

我们可以将haproxykeepalived分别放置在不同的pod中,也可以将它们放置在同一个pod中。考虑到keepalived需要监测haproxy的状态,我们会把haproxykeepalived放在一起做成一个loadbalancerpod


2.3.1.lb node配置

1)使能内核IPVS模块

由于keepalived需要通过IPVS模块实现路由转发,所以我们需要使能内核IPVS模块。

Linux内核版本2.6起,ip_vs code已经被整合进了内核中,因此,只要在编译内核的时候选择了ipvs的功能,Linux即能支持LVS。因此我们只需要配置操作系统启动时自动加载IPVS模块:

echo "ip_vs" >> /etc/modules

echo "ip_vs_rr" >> /etc/modules

echo "ip_vs_wrr" >> /etc/modules

我们可以通过如下命令查看ip_vs模块是否成功加载:

lsmod | grep ip_vs

如果没有加载,我们可以通过modprobe命令加载该模块: 

modprobe ip_vs

modprobe ip_vs_rr

modprobe ip_vs_wrr

 

2)修改内核参数

为了使keepalived将数据包转发到真实的后端服务器,每一个lb node都需要开启IP转发功能

echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf

echo "net.ipv4.ip_nonlocal_bind = 1" >> /etc/sysctl.conf

 

2.3.2.keepalived监测haproxy状态的方法

对于普通进程来说, keepalived进程可以通过“killall -0 haproxy”命令检测haproxy进程是否正常运行(注: Sending the signal 0 to a given PID just checksif any process with the given PID is running)。 

然而在docker容器环境下,各容器都有自己的PidNamespaceNetworkNamespace,我们就需要开启haproxy的健康检查页面,然后keepalived通过健康检查页面的URL来检测haproxy目前是否正常运行。 

haproxy健康检查页面配置: 

listen  admin_stats

       bind  0.0.0.0:80

       log  global

       mode  http

       maxconn  10

       stats  enable

       #Hide  HAPRoxy version, a necessity for any public-facing site

       stats  hide-version

       stats  refresh 30s

       stats  show-node

       stats  realm Haproxy\ Statistics

       stats  auth caicloud:caicloud

       stats  uri /haproxy?stats

keepalivedhaproxy的状态检测: 

vrrp_script check_script {

 script  "/etc/keepalived/check_haproxy.py  http://caicloud:caicloud@127.0.0.1/haproxy?stats"

 interval 5 # check every 5 seconds

 weight 5

 fall 2 # require 2 fail for KO

 rise 1 # require 1 successes for OK

}

 

2.3.3.haproxy SSL配置

haproxy代理ssl配置有两种方式:

1 haproxy本身提供SSL证书,后面的web服务器走正常的http协议;

2 haproxy本身只提供代理,直接转发client端的HTTPS请求到后端的web服务器。注意:这种模式下“mode”必须是“tcp”模式, 即仅支持4层代理。 

考虑到:第一,用户亲和性访问需要7层代理的支持;第二,loadbalancermaster走的都是集群内网。所以本实践采用了第一种方式,配置如下:

frontend frontend-apiserver-https

        #  Haproxy enable SSL

        bind  *:443 ssl crt /etc/kubernetes/master-loadblancer.pem

        option  forwardfor

        default_backend  backend-apiserver-http

 

2.3.4.haproxy配置:haproxy.cfg

global

       log  127.0.0.1    local0

       maxconn  32768

       pidfile  /run/haproxy.pid

       # turn  on stats unix socket

       stats  socket /run/haproxy.stats

       tune.ssl.default-dh-param  2048

 

defaults

       log      global

       mode     http

       option   httplog

       option   dontlognull

       retries  3

       timeout  connect 5000ms

       timeout  client 50000ms

       timeout  server 50000ms

       timeout  check 50000ms

       timeout  queue 50000ms

 

frontend frontend-apiserver-http

      bind *:8080

      option  forwardfor

 

      acl local_net  src 192.168.205.0/24

 

      http-request  allow if local_net

      http-request  deny

 

      default_backend  backend-apiserver-http

      

frontend frontend-apiserver-https

       #  Haproxy enable SSL

       bind  *:443 ssl crt /etc/kubernetes/master-loadbalancer.pem

       option  forwardfor

       default_backend  backend-apiserver-http

 

backend backend-apiserver-http

       balance  roundrobin

       option  forward-for

 

       server  master-3 192.168.205.13:8080 check

       server  master-2 192.168.205.12:8080 check

       server  master-1 192.168.205.11:8080 check

       

listen  admin_stats

       bind  0.0.0.0:80

       log  global

       mode  http

       maxconn  10

       stats  enable

       #Hide  HAPRoxy version, a necessity for any public-facing site

       stats hide-version

       stats  refresh 30s

       stats  show-node

       stats  realm Haproxy\ Statistics

       stats  auth caicloud:caicloud

       stats  uri /haproxy?stats

 

2.3.5.keepalived配置:keepalived.conf

1 lb-1keepalived配置

! Configuration File for keepalived

# Script used to check if HAProxy is running

vrrp_script check_script {

 script  "/etc/keepalived/check_haproxy.py  http://caicloud:caicloud@127.0.0.1/haproxy?stats"

 interval 5 # check every 5 seconds

 # keepalived会定时执行脚本并对脚本执行的结果进行分析,动态调整vrrp_instance的优先级。

 # 如果脚本执行结果为0,并且weight配置的值大于0,则权重相应的增加

 # 如果脚本执行结果非0,并且weight配置的值小于0,则权重相应的减少

 # 其他情况,维持原本配置的优先级,即配置文件中priority对应的值

 # 当进程监控失败时,如果不设置weigth,则 keepalived 进入 FAULT 状态

 weight 5

 fall 2 # require 2 fail for KO

 rise 1 # require 1 successes for OK

}

# Virtual interface

# The priority specifies the order in which the  assigned interface to take over in a failover

vrrp_instance VI_01 {

 # role is MASTER or BACKUP

 state MASTER

 #VRRP实例绑定的网卡

 interface eth1

 track_interface {

   eth1

 }

 vrrp_garp_master_repeat 5

 vrrp_garp_master_refresh 10

 #相同VRID的属于同一组,根据优先级选举出一个Master

 virtual_router_id 51

 priority 100

 #nopreempt

 #VRRP心跳包的发送周期,单位为s

 advert_int 1

 authentication {

   auth_type PASS

   auth_pass caicloud

 }

 # The virtual ip address shared between the  two loadbalancers

 virtual_ipaddress {

   192.168.205.254 dev eth1 label  eth1:vip

 }

 track_script {

   check_script

 }

 # don’t include parameters in the notify  script, but exclude notify_xxxx

 notify "/etc/keepalived/notify_state.sh"

 #notify_master  "/etc/keepalived/notify.sh MASTER"

 #notify_backup  "/etc/keepalived/notify.sh BACKUP"

 #notify_fault  "/etc/keepalived/notify.sh FAULT"

}

 

2 lb-2keepalived配置

lb-2lb-1的配置差不多,除了下面两个字段: 

 state BACKUP

 priority 97

 

2.4.flannel网络设置

2.4.1Master节点flannel网络设置

对于Master节点,需要等待Etcd Pod集群启动完后,先在Master上创建Flannel网络,然后Flannel Pod客户端才可以从Etcd中获取到各个Master节点的IP网段,获取到IP网段后会在主机上产生文件:“/var/run/flannel/subnet.env”,然后根据该文件修改docker启动参数: 

.  /var/run/flannel/subnet.env

DOCKER_OPTS="$DOCKER_OPTS  --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}"

并重启docker服务。


2.4.2非Master节点flannel网络设置

对于非Master节点,待Loadbalancer起来之后,Node节点能够访问Apiserver之后,Flannel Pod客户端才能从Etcd获取到该Node节点的IP网段,并且同样会在主机上产生文件:“/var/run/flannel/subnet.env”。然后修改docker启动参数,并重启docker服务。

 

3.实践中的遇到的那些坑


3.1.官网“haproxy docker image”的坑

Docker Hub“haproxy image”“docker-entrypoint.sh”内容如下:

#!/bin/sh

set -e

 

# first arg is `-f` or `--some-option`

if [ "${1#-}" != "$1" ]; then

   set -- haproxy "$@"

fi

 

if [ "$1" = 'haproxy' ]; then

   # if the user wants "haproxy", let's use  "haproxy-systemd-wrapper"

   # instead so we can have proper reloadability  implemented by upstream

   shift # "haproxy"

   set -- "$(which haproxy-systemd-wrapper)" -p  /run/haproxy.pid "$@"

fi

 

exec "$@"

问题就出在“haproxy-systemd-wrapper”。如果运行命令:“haproxy -f/etc/haproxy/haproxy.cfg”, 而实际上运行的是经过“haproxy-systemd-wrapper”包装后的命令: 

# docker run  -v /etc/kubernetes:/etc/kubernetes -v  /etc/haproxy/haproxy.cfg:/etc/haproxy/haproxy.cfg index.caicloud.io/caicloud/haproxy:v1.6.5  haproxy -f /etc/haproxy/haproxy.cfg

<7>haproxy-systemd-wrapper: executing /usr/local/sbin/haproxy -p  /run/haproxy.pid -f /etc/haproxy/haproxy.cfg -Ds

[WARNING] 154/070725 (6) : Setting tune.ssl.default-dh-param to 1024 by  default, if your workload permits it you should set it to at least 2048.  Please set a value >= 1024 to make this warning disappear.

执行命令“haproxy -f /etc/haproxy/haproxy.cfg”时,真正执行的是:“/usr/local/sbin/haproxy -p /run/haproxy.pid -f /etc/haproxy/haproxy.cfg -Ds”,对于“-Ds”选项, 官网是这么描述的: 

-Ds passe en daemon systemd

This patch adds a new option "-Ds" which is exactly like  "-D", but instead of

forking n times to get n jobs running and then exiting, prefers to wait  for all the

children it just created. With this done, haproxy becomes more  systemd-compliant,

without changing anything for other systems.

原来,“haproxy”经过“haproxy-systemd-wrapper”包装后在后台执行,而docker container不允许进程后台执行,否则docker容器将该启动命令执行完后就退出了。官网image的这个坑很大。

所以,当我们用官网“haproxy image”的时候,就需要用haproxy的完全路径来执行。比如在yaml文件中:

containers:

 - name: lb-haproxy

   image: index.caicloud.io/caicloud/haproxy:v1.6.5

   command:

   - /usr/local/sbin/haproxy

   - -f

   - /etc/haproxy/haproxy.cfg

   - -p

   - /run/haproxy.pid

 

3.2.haproxy container exited with 137

首先137退出码表示,其他进程向haproxy container发起了“kill”信号,导致haproxy container退出,容器日志如下: 

[WARNING] 155/053036 (1) : Setting tune.ssl.default-dh-param to 1024 by  default, if your workload permits it you should set it to at least 2048.  Please set a value >= 1024 to make this warning disappear.

其次,当通过“docker run”命令执行haproxy container,使用的命令与yaml文件中的一样,而且照样输出上述的“WARNING”,但是容器却不退出。

然后,无奈之下,我试着先将这个“WARNING”解决:这个错误是由于haproxy.cfg中添加了SSL证书导致的, 可以通过设置参数“default-dh-param”解决: 

global

       ...

       # turn  on stats unix socket

       stats  socket /run/haproxy.stats

       tune.ssl.default-dh-param  2048

frontend frontend-apiserver-https

       #  Haproxy enable SSL

       bind  *:443 ssl crt /etc/kubernetes/master-loadbalancer.pem

       ...

当我解决这个“WARNING”之后,奇迹出现了,haproxy container奇迹般的正常运行了。原来在容器的世界,一个“WARNING”也不能疏忽。


4.社区关于HA Master的未来发展


熟悉kubelet配置参数的都知道,我们在给kubelet配置apiserver的时候,可以通过“--api-servers”指定多个:

--api-servers=http://m1b:8080,http://m1c:8080,http://m2a:8080,http://m2b:8080,http://m2c:8080

 这看起来似乎已经做到apiserver的高可用配置了,但是实际上当第一个apiserver挂掉之后, 不能成功的连接到后面的apiserver,也就是说目前仍然只有第一个apiserver起作用。

如果上述问题解决之后, 似乎不需要额外的loadbalancer也能实现master的高可用了,但是,除了kubelet需要配置apiservercontrollermanagerscheduler都需要配置apiserver,目前我们还只能通过“--master”配置一个apiserver,无法支持多个apiserver

社区后续打算支持multi-master配置,实现Kubernetes Master的高可用,而且计划在Kubernetes 1.4版本中合入。

即使将来社区实现了通过multi-master配置的高可用方式,本次分享的MasterHigh Availability仍然非常有意义,因为在私有云场景中,ExternalLoadbalancer除了实现Master的高可用和负载均衡外,还可以针对Worker Node实现Nodeport请求的负载均衡,从而不仅实现了应用的高可用访问,同时也大大提高了应用的访问速度和性能。

参考链接:

好了,以上是本次分享的所有内容,欢迎大家批评指正,同时也希望能为大家带来些收益。


以上是关于DBAplus线上分享实录|Kubernetes Master High Availability 高级实践的主要内容,如果未能解决你的问题,请参考以下文章

Weex线上踩坑实录

DBA深圳宣讲会·FAQ实录

线上技术分享的一些思考

线上技术分享的一些思考

宜信敏捷数据中台建设实践|分享实录

为什么说Prometheus是足以取代Zabbix的监控神器?