kubeadm安装高可用k8s集群

0 k8s高可用架构

在介绍部署方式前,先介绍一下k8s的高可用架构。

0.1 与keepalive+VIP对比

网上大量的人使用KeepAlive+VIP的形式完成高可用,这个方式有两个不好的地方:

其一,受限于使用者的网络,无法适用于SDN网络,比如Aliyun的VPC。

其二,虽然是高可用的,但是流量还是单点的,所有node的的网络I/O都会高度集中于一台机器上(VIP),一旦集群节点增多,pod增多,单机的网络I/O迟早是网络隐患。

0.2 两种模式

0.2.1 堆叠式etcd拓扑

堆叠式拓扑的意思是存储元数据的etcd与控制屏幕节点安装在同一台服务器上,共同被kubeadm所管理。

每个控制平面节点都运行一组kube-apiserver,kube-scheduler和kube-controller-manager,其中kube-apiserver通过负载均衡暴露给所有worker节点。

每个控制平面节点都创建一个本地etcd,这个本地etcd只与该控制平面节点的kube-apiserver通讯,kube-scheduler和kube-controller-manager也是节点独立的。

介绍一下这种方案的优缺点: 优点:这种控制平面和etcd部署在同一个节点的模式比控制平面和etcd分离部署安装容易并且易于管理副本。 缺点:这种模式存在错误耦合的缺点,当一个节点宕机,etcd和控制平面实例都丢失,只能添加更多的控制平面节点来缓和这个问题。

在高可用集群下需要运行至少3个堆叠控制平面节点。

这是kubeadm高可用的默认实现方案,运行kubeadm init和kubeadm join –control-plane后本地etcd将被自动创建。

0.2.2 分离式etcd拓扑

分离式拓扑的意思是存储元数据的etcd与控制屏幕节点安装在不同服务器上。

类似于堆叠式架构,每个控制平面节点都运行一组kube-apiserver,kube-scheduler和kube-controller-manager,其中kube-apiserver通过负载均衡暴露给所有worker节点。不同的地方在于,etcd在不同的host上运行,每个etcd都会和每个控制平面上的kube-apiserver交互。

这种拓扑结构将etcd与控制平面进行了分离。这种方式与堆叠式架构相比可以在etcd的节点丢失以及控制平面的节点丢失上有更高的容错性。

然而,这种拓扑结构在机器数量上对比堆叠式架构需要两倍的数量。需要至少3个etcd节点以及3个控制平面节点。

1 前置安装

1.1 环境相关

安装docker和kubeadm,在每一台机器上都需要安装。

1:ssh可信
2:hosts
cat>>/etc/hosts<<EOF
10.57.26.15 k8s-01
10.57.26.7 k8s-02
10.57.26.8 k8s-03
10.57.26.26 k8s-04
10.57.26.11 k8s-05
10.57.26.17 k8s-06
10.57.26.33 k8s-07
EOF
host名需要和 hostname /etc/hostname 一致
3:安全相关
setenforce 0
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
systemctl disable firewalld.service
systemctl stop firewalld.service
systemctl disable iptables.service
systemctl stop iptables.service
4:源和代理
echo proxy=http://10.57.22.8:3128/ >> /etc/yum.conf
cat>/etc/yum.repos.d/docker-ce.repo<<EOF
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/docker-ce/linux/centos/gpg
EOF

cat>/etc/yum.repos.d/kubernetes.repo<<EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

5:安装docker和kubeadm
yum install -y docker
yum install -y kubeadm
systemctl start docker
systemctl enable docker 
systemctl enable kubelet
echo KUBELET_EXTRA_ARGS="\"--fail-swap-on=false\"" > /etc/sysconfig/kubelet
systemctl daemon-reload&&systemctl restart kubelet

6:私有仓库以及网络
cat>/etc/sysctl.conf<<EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl -p

cat>/etc/docker/daemon.json<<EOF
{
"registry-mirrors": ["https://a5aghnme.mirror.aliyuncs.com"],
"insecure-registries":["10.57.26.15:4000"]
}
EOF
systemctl daemon-reload
systemctl restart docker

1.2 私有仓库搭建

需要选取一台服务器来安装私仓,因为只是私用,所以只安装最简单的registry,在这里不安装harbor。

1.2.1 设置代理

#create older
mkdir -p /etc/systemd/system/docker.service.d
#add http-proxy.conf
cat>/etc/systemd/system/docker.service.d/http-proxy.conf<<EOF
[Service]
Environment="HTTP_PROXY=http://10.57.22.8:3128/"
EOF
systemctl daemon-reload
systemctl restart docker

设置成功后,执行docker search 镜像。就可以看到可以下载的镜像。

1.2.2 私仓安装

执行

# 下载镜像
docker pull registry:2 
# 运行私仓镜像,暴露4000端口
docker run -d -v /opt/registry:/var/lib/registry -p 4000:5000 --restart=always --name registry registry:2
# 在确定selinux关闭后,将已备份的镜像拷贝至/opt/registry目录下。
# 查看私仓镜像
curl -XGET http://10.57.26.15:4000/v2/_catalog

1.3 创建Kube-apiserver的负载均衡

  1. 通过创建DNS名来创建kube-apiserver的负载均衡器。 1) 需要将控制平面节点放置于负责TCP转发的负载均衡器后面。这个负载均衡器会将流量分布到所有配置的控制平面上。kube-apiserver的健康检测的端口是kube-apiserver的启动端口,默认是6443 2) 建议控制平面使用域名。 3) 负载均衡需要能访问所有配置的控制平面域名,ip和端口。 4) 可以使用nginx和haproxy等作为负载均衡器实现。 5) kubeadm中的ControlPlaneEndpoint需要设置成负载均衡器的地址。
  2. 添加第所有控制平面节点到负载均衡中。 使用Nginx-Poxy来作为负载均衡器,配置nginxProxy,kube-servers处写上需要代理的多个kube-apiserver的地址
mkdir -p /etc/nginx
cat > /etc/nginx/nginx.conf << EOF
worker_processes auto;
user root;
events {
    worker_connections  20240;
    use epoll;
}
error_log /var/log/nginx_error.log info;

stream {
    upstream kube-servers {
        hash $remote_addr consistent;
        server server1.k8s.local:6443 weight=5 max_fails=1 fail_timeout=10s;
        server server2.k8s.local:6443 weight=5 max_fails=1 fail_timeout=10s;
        server server3.k8s.local:6443 weight=5 max_fails=1 fail_timeout=10s;
    }

    server {
        listen 8443;
        proxy_connect_timeout 1s;
        proxy_timeout 3s;
        proxy_pass kube-servers;
    }
}
EOF

配置文件写好后,加载ipvs模块,并运行nginxproxy

# 加载模块 <module_name>
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4

# 检查加载的模块
lsmod | grep -e ipvs -e nf_conntrack_ipv4

docker run --restart=always \
-v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf \
--name kube_server_proxy \
--net host \
-it \
-d \
nginx

1.4 镜像拉取

export REGISTRY_HOST=10.57.26.7:4000
curl -XGET http://${REGISTRY_HOST}/v2/_catalog

docker pull ${REGISTRY_HOST}/kube-proxy:v1.15.0
docker pull ${REGISTRY_HOST}/kube-apiserver:v1.15.0
docker pull ${REGISTRY_HOST}/kube-controller-manager:v1.15.0
docker pull ${REGISTRY_HOST}/kube-scheduler:v1.15.0
docker pull ${REGISTRY_HOST}/coredns:1.3.1
docker pull ${REGISTRY_HOST}/etcd:3.3.10
docker pull ${REGISTRY_HOST}/pause:3.1

docker tag ${REGISTRY_HOST}/kube-proxy:v1.15.0 k8s.gcr.io/kube-proxy:v1.15.0
docker tag ${REGISTRY_HOST}/kube-apiserver:v1.15.0 k8s.gcr.io/kube-apiserver:v1.15.0
docker tag ${REGISTRY_HOST}/kube-controller-manager:v1.15.0 k8s.gcr.io/kube-controller-manager:v1.15.0
docker tag ${REGISTRY_HOST}/kube-scheduler:v1.15.0 k8s.gcr.io/kube-scheduler:v1.15.0
docker tag ${REGISTRY_HOST}/coredns:1.3.1 k8s.gcr.io/coredns:1.3.1
docker tag ${REGISTRY_HOST}/etcd:3.3.10 k8s.gcr.io/etcd:3.3.10
docker tag ${REGISTRY_HOST}/pause:3.1 k8s.gcr.io/pause:3.1

2 堆叠模式安装

堆叠模式安装比较简单,至少需要3台机器,安装etcd和控制平面。

2.1 第一个控制平面安装

编写配置文件如下

apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
apiServer:
  certSANs:
  - "10.57.26.15"
controlPlaneEndpoint: "k8s-01:8443"
networking:
    podSubnet: 10.244.0.0/16
    serviceSubnet: 10.96.0.0/12

1)运行 kubeadm init --config kubeadm-config.yaml --upload-certs

2) 记录下join命令。

3)安装网络插件,这里使用flannel。

2.2 其他控制平面安装

1)保证第一个控制平面安装成功。

2)加入第一个控制平面安装成功输出的加入控制平面的命令,需要立即输入,key过2小时就将过期。

2.3 worker节点安装

1)保证控制节点安装成功。

2)加入第一个控制平面安装成功输出的加入工作节点的命令。

3 分离模式安装

分离模式至少需要6台机器,3台安装etcd,3台安装控制平面。

3.1 etcd集群安装

3.1.1 配置kubelet成为etcd的服务管理

设置后可以覆盖老的kubeadm的启动方式

mkdir -p /etc/systemd/system/kubelet.service.d/
cat << EOF > /etc/systemd/system/kubelet.service.d/20-etcd-service-manager.conf
[Service]
ExecStart=
ExecStart=/usr/bin/kubelet --address=127.0.0.1 --pod-manifest-path=/etc/kubernetes/manifests
Restart=always
EOF

systemctl daemon-reload
systemctl restart kubelet

3.1.2 创建etcd集群配置文件

#清理,如果不清理会识别老的member信息导致后续通讯失败 
rm -rf /var/lib/etcd/*

# Update HOST0, HOST1, and HOST2 with the IPs or resolvable names of your hosts
export HOST0=10.57.26.7
export HOST1=10.57.26.8
export HOST2=10.57.26.26

# Create temp directories to store files that will end up on other hosts.
mkdir -p /tmp/${HOST0}/ /tmp/${HOST1}/ /tmp/${HOST2}/

ETCDHOSTS=(${HOST0} ${HOST1} ${HOST2})
NAMES=("k8s-02" "k8s-03" "k8s-04")

for i in "${!ETCDHOSTS[@]}"; do
HOST=${ETCDHOSTS[$i]}
NAME=${NAMES[$i]}
cat << EOF > /tmp/${HOST}/kubeadmcfg.yaml
apiVersion: "kubeadm.k8s.io/v1beta2"
kind: ClusterConfiguration
etcd:
    local:
        serverCertSANs:
        - "${HOST}"
        peerCertSANs:
        - "${HOST}"
        extraArgs:
            initial-cluster: ${NAMES[0]}=https://${ETCDHOSTS[0]}:2380,${NAMES[1]}=https://${ETCDHOSTS[1]}:2380,${NAMES[2]}=https://${ETCDHOSTS[2]}:2380
            initial-cluster-state: new
            name: ${NAME}
            listen-peer-urls: https://${HOST}:2380
            listen-client-urls: https://${HOST}:2379
            advertise-client-urls: https://${HOST}:2379
            initial-advertise-peer-urls: https://${HOST}:2380
EOF
done

3.1.3 生成ca

etcd节点中有一台存储ca。如果不存在ca的话生成方法: kubeadm init phase certs etcd-ca 如果/etc/kubernetes/pki/etcd/ca.crt 和 /etc/kubernetes/pki/etcd/ca.key存在可进入下一步

3.1.4 为每个etcd member创建配置

kubeadm init phase certs etcd-server --config=/tmp/${HOST2}/kubeadmcfg.yaml
kubeadm init phase certs etcd-peer --config=/tmp/${HOST2}/kubeadmcfg.yaml
kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
cp -R /etc/kubernetes/pki /tmp/${HOST2}/
# cleanup non-reusable certificates
find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete

kubeadm init phase certs etcd-server --config=/tmp/${HOST1}/kubeadmcfg.yaml
kubeadm init phase certs etcd-peer --config=/tmp/${HOST1}/kubeadmcfg.yaml
kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
cp -R /etc/kubernetes/pki /tmp/${HOST1}/
find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete

kubeadm init phase certs etcd-server --config=/tmp/${HOST0}/kubeadmcfg.yaml
kubeadm init phase certs etcd-peer --config=/tmp/${HOST0}/kubeadmcfg.yaml
kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
# No need to move the certs because they are for HOST0

# clean up certs that should not be copied off this host
find /tmp/${HOST2} -name ca.key -type f -delete
find /tmp/${HOST1} -name ca.key -type f -delete

3.1.5 拷贝到目录

ca上运行拷贝
scp -r /tmp/${HOST1}/* root@${HOST1}:
scp -r /tmp/${HOST2}/* root@${HOST2}:
 
然后进入每个member中,执行
mv pki /etc/kubernetes/

3.1.6 拷贝后的文件结构

含有ca的etcd节点文件结构:

其他member的节点文件结构:

3.1.7 创建静态pod manifest,并启动

执行后可以看到docker ps中有etcd进程。可见etcd已经被容器化了。

root@HOST0 $ kubeadm init phase etcd local --config=/tmp/${HOST0}/kubeadmcfg.yaml
root@HOST1 $ kubeadm init phase etcd local --config=kubeadmcfg.yaml
root@HOST2 $ kubeadm init phase etcd local --config=kubeadmcfg.yaml

3.1.8 检测etcd状态

docker run --rm -it \
--net host \
-v /etc/kubernetes:/etc/kubernetes quay.io/coreos/etcd:${ETCD_TAG} etcdctl \
--cert-file /etc/kubernetes/pki/etcd/peer.crt \
--key-file /etc/kubernetes/pki/etcd/peer.key \
--ca-file /etc/kubernetes/pki/etcd/ca.crt \
--endpoints https://${HOST0}:2379 cluster-health
...
cluster is healthy

设置${ETCD_TAG}为ETCD的版本

3.2 第一个控制平面安装

3.2.1 添加外部etcd访问证书

在控制平面上执行以下命令将证书拷贝过来,这些证书只需要在第一个控制平面中存在。因为在部署时候指定了–upload-certs,这个参数可以将证书在控制平面中共享。

export ETCD_CA=10.57.26.7
mkdir -p /etc/kubernetes/pki/etcd/
scp root@${ETCD_CA}:/etc/kubernetes/pki/etcd/ca.crt /etc/kubernetes/pki/etcd/
scp root@${ETCD_CA}:/etc/kubernetes/pki/apiserver-etcd-client.crt /etc/kubernetes/pki/
scp root@${ETCD_CA}:/etc/kubernetes/pki/apiserver-etcd-client.key /etc/kubernetes/pki/  

3.2.2 安装控制平面

创建kubeadm-config.yaml文件,可见与堆叠模式相比最大的不同是etcd使用external方式。

apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
apiServer:
  certSANs:
  - "10.57.26.15"
controlPlaneEndpoint: "k8s-01:8443"
etcd:
  external:
    endpoints:
    - https://10.57.26.7:2379
    - https://10.57.26.8:2379
    - https://10.57.26.26:2379
    caFile: /etc/kubernetes/pki/etcd/ca.crt
    certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
    keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key
networking:
    podSubnet: 10.244.0.0/16
    serviceSubnet: 10.96.0.0/12

1)运行 kubeadm init --config kubeadm-config.yaml --upload-certs 2) 记录下join命令。 3)安装网络插件,这里使用flannel。

3.3 其他控制平面安装

1)保证第一个控制平面安装成功。

2)加入第一个控制平面安装成功输出的加入控制平面的命令,需要立即输入,key过2小时就将过期。

3.4 worker节点安装

1)保证控制节点安装成功。

2)加入第一个控制平面安装成功输出的加入工作节点的命令。

4 一些坑

4.1 本地镜像拷贝后不能pull和push

出现permission问题,需要更改selinux权限。 setenforce 0 以及selinux改权限

4.2 kubelet和docker的cgroup冲突问题

报错:failed to create kubelet: misconfiguration: kubelet cgroup driver: “cgroupfs” is different from docker cgroup driver: “systemd”

查看docker的cgroup docker info | grep Cgroup 修改kubelet的cgroup: 在/etc/systemd/system/kubelet.service.d/20-etcd-service-manager.conf配置文件中添加–cgroup-driver=systemd

4.3 环境清空

除了kubeadm reset以及iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X 还需要注意以下几点: 1) kubelet状态 查看systemctl status kubelet查看dropdown,删除不使用的配置,然后重启 systemctl daemon-reload&&systemctl restart kubelet 2) etcd删除 rm -rf /var/lib/etcd

5 一些收获

5.1 日志查看:

1:kubeadm的日志 kubeadm init –config kubeadm-config.yaml -v 256 后面加上-v 2:kubelet日志查看 journalctl -xefu kubelet 3:kubelet的配置信息可以通过 systemctl status kubelet查看

5.2 kubectl自动补全

yum install -y bash-completion
source /usr/share/bash-completion/bash_completion
source <(kubectl completion bash)

6 参考

https://qingmu.io/2018/12/20/Deploy-a-highly-available-cluster-with-kubeadm/ https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/