kubeadm搭建高可用集群
本地虚拟机环境
虚拟机IP | 节点角色 | 备注 |
192.168.163.252 | 工作节点/master节点 | 版本号 v1.18.2 |
192.168.163.251 | 工作节点/master节点 | 版本号 v1.18.2 |
192.168.162.96 | nginx代理节点 | vip:192.168.162.97 |
192.168.162.98 | nginx代理节点 | vip:192.168.162.97 |
准备工作
- 禁用swap分区和selinux,关闭firewalld。
sudo swapoff -a
sudo systemctl stop firewalld
sudo systemctl disable firewalld
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
- 安装docker-ce
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce
- 修改Docker的Cgroup Driver(可选操作)
mkdir /etc/docker
cat <<EOF > /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
systemctl restart docker
systemctl enable docker
注:Docker在默认情况下使用的Cgroup Driver为cgroupfs而kubernetes1.14之后推荐使用systemd来代替 cgroupfs,详见: https://kubernetes.io/docs/setup/cri
- 添加Yum源(可选操作)
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=KubernetesRepos
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
gpgcheck=0
enabled=1
EOF
注:因为kubeadm默认的源地址背墙,这里使用阿里云yum源。
- 启动bridge-nf方式
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-arptables = 1
EOF
sysctl -p /etc/sysctl.d/k8s.conf
注:在iptables没有开启 bridge-nf时,数据会直接经过网桥转发,会导致FORWARD的设置失效;
centos默认不开启 bridge-nf
安装需要的组件
- 安装kubeadm、kubelet、kubectl(所有节点都需要安装)
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
#因为这里是自己的本地虚拟机测试环境,所以直接安装最新的版本踩坑,如果要安装指定版本可以在后面指定版本号如下:
sudo yum install -y kubelet-${version} kubeadm-${version} kubectl-${version} --disableexcludes=kubernetes
sudo systemctl enable kubelet
注:(为什么要单独部署kubelet)早期用二进制的方式部署kubernets时,不难发现kubernetes的每一个组件都是一个需要单独被执行的二进制文件,我们只需要根据这些组件的特性编写对应的启动脚本或服务单元即可,那么如果把这些组件放在一个docker容器镜像中,启动时用docker run命令启动,这样相比于其他的部署方式而言部署过程就会简单不少,但是有一个比较麻烦的问题:
如何容器化 kubelet组件?
kubelet除了要和docker容器基本交互之外,在进行容器的网络/数据卷管理时都需要直接和宿主机进行交互操作,如果将kubernetes运行在一个容器中那么和宿主机的交互就会比较麻烦,对于网络配置我们还可以使用Docker的host network模式直接共享宿主机的网络,但是让kubelet隔着容器的Mount Namespace和文件系统去操作宿主机的文件系统就显得比较困难了。到目前为止,在容器里运行 kubelet,依然没有很好的解决办法。(有人提出可以使用setns系统调用,没具体研究过)正因为如此,kubeadm 选择了一种妥协方案:
即:kubelet 直接运行在宿主机上,然后使用容器部署其他的 Kubernetes 组件。
- 在两个代理节点安装nginx
#为了方便这里直接使用epel源安装nginx
sudo yum install -y nginx
#添加stream模块配置文件,同时编辑主配置文件添加我们的配置文件所在地址
mkdir -p /etc/nginx/stream.d && cd /etc/nginx
#修改nginx主配置文件 (指令 i 进入插入模式 esc退出到底行模式 键入 :wq保存(w)退出(q))
vi nginx.conf
#添加内容如下:
stream {
log_format kubernetes '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
access_log /opt/logs/nginx/.access.log kubernetes;
include /etc/nginx/stream.d/*.conf;
}
#编辑子配置文件
vi stream.d/k8s.conf
#添加内容如下
upstream stream_apiserver_kube {
server 192.168.163.252:6443 weight=5 max_fails=3 fail_timeout=30s;
server 10.200.163.251:6443 weight=5 max_fails=3 fail_timeout=30s;
}
server {
listen 6443;
proxy_pass stream_apiserver_kube;
proxy_connect_timeout 5s;
proxy_timeout 3s;
}
#最后检测一下我们的配置文件是否正确
nginx -t -c /etc/nginx/nginx.conf
#启动nginx
nginx
#查看端口是否已经监听
ss -tlnup | grep 6443
#如果监听成功 示例输出如下
tcp LISTEN 0 128 *:6443 *:* users:(("nginx",pid=7523,fd=27),("nginx",pid=7522,fd=27),("nginx",pid=7521,fd=27),("nginx",pid=7520,fd=27),("nginx",pid=7519,fd=27),("nginx",pid=7518,fd=27),("nginx",pid=7517,fd=27),("nginx",pid=7516,fd=27),("nginx",pid=871,fd=27))
- 在两个代理节点安装keepalived
#这里直接用yum进行安装
yum install-y keepalived
#编辑配置文件
vi /etc/keepalived/keepalived.conf
#内容示例
! Configuration File for keepalived
global_defs {
router_id host96 ##标识节点的字符串,通常为hostname
}
vrrp_script chk_nginx {
script "/etc/keepalived/check_nginx_pid.sh" ##执行脚本位置
interval 2 ##检测时间间隔
weight -20 ##如果条件成立则权重减20
}
vrrp_instance VI_1 {
state MASTER ## 主节点为MASTER,备份节点为BACKUP
interface ens33 ## 绑定虚拟IP的网络接口(网卡),与本机IP地址所在的网络接口相同(我这里是ens33)
virtual_router_id 110 ## 虚拟路由ID号(主备节点一定要相同)
mcast_src_ip 192.168.162.96 ## 本机ip地址
priority 100 ##优先级配置(0-254的值)
nopreempt
advert_int 1 ## 组播信息发送间隔,俩个节点必须配置一致,默认1s
authentication { ## 认证匹配
auth_type PASS
auth_pass abc
}
track_script {
chk_nginx
}
virtual_ipaddress {
192.168.162.97 ## 虚拟ip,可以指定多个
}
}
#编写检查脚本
vi /etc/keepalived/check_nginx_pid.sh
#内容如下
#!/bin/bash
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ];then
/usr/sbin/nginx -c /etc/nginx/nginx.conf #重启nginx
if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then #nginx重启失败
exit 1
else
exit 0
fi
else
exit 0
fi
#开始启动keepalived
chkconfig keepalived on
service keepalived start
systemctl enable keepalived
#重启网络服务
systemctl restart network
注意啦,如果你是源码安装的nginx那么你需要手动建立软连接到/usr/sbin
并给脚本授权
sudo chmod +x nginx
使用kubeadm init 部署主节点
- 编辑我们的kubeadmin配置文件
#为了防止端口被占用 建议先使用 ss 命令查看6443端口是否被占用
cat <<EOF > kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta2
kind: InitConfiguration
localAPIEndpoint:
bindPort: 6443
---
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v1.18.2
controlPlaneEndpoint: "192.168.162.97:6443"
imageRepository: "registry.aliyuncs.com/google_containers"
networking:
podSubnet: "10.212.0.0/16"
apiServer:
certSANs:
- "k8s.cxpt.com"
EOF
这一块建议参考系统给的默认配置结合自己的实际情况进行修改,查看命令如下:
sudo kubeadm config print init-defaults
sudo kubeadm config print join-defaults
开始初始化我们的集群节点
sudo kubeadm init --config=kubeadm-config.yaml --upload-certs
初始化成功以后打印信息里面包含了我们后面添加master和worker节点的join语句如下
在另外一个节点执行join语句添加另外一个主节点
#具体语句根据实际你的日志打印内容填写
kubeadm join 192.168.162.97:6443 --token crj05r.b380vndwk3pwzb6p \
--discovery-token-ca-cert-hash sha256:5649ba2d8669266977575c1a2ef45fe10dc866251e66aa5acaaf6016acbcf6dd \
--control-plane --certificate-key 8feba8cff94acecbfe128e34ad2f23322821c8c68beba705e0e8fcff096e118e
配置kubectl
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
安装flannled,具体参见addones
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
#如果网络不佳可以下载这个yaml文件之后替换镜像为阿里云,操作如下:
wget -c https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
sed -i s#'quay.io/coreos/flannel:v0.12.0-amd64'#'registry.cn-hangzhou.aliyuncs.com/mygcrio/flannel:v0.12.0-amd64'#g kube-flannel.yml
kubectl apply -f kube-flannel.yml
检查etcd健康状况
docker run --rm -it --net host -v /etc/kubernetes:/etc/kubernetes \
registry.aliyuncs.com/google_containers/etcd:3.4.3-0 etcdctl \
--cert /etc/kubernetes/pki/etcd/peer.crt \
--key /etc/kubernetes/pki/etcd/peer.key \
--cacert /etc/kubernetes/pki/etcd/ca.crt \
--endpoints https://192.168.163.252:2379,https://192.168.163.251:2379 endpoint health
复用master节点作为worker节点
kubectl taint nodes --all node-role.kubernetes.io/master-
#禁止某个主节点作为worker节点
kubectl taint nodes node1 node-role.kubernetes.io/master=true:NoSchedule
查看各组件运行状态
kubectl get componentstatus
当我们运行kubeadm init 时它都为我们做了什么?
1-1. 首先会运行一系列预检代码来检查系统的状态(Preflight Checks),例如:
- Linux的内核版本是否符合要求
- kubeadm 和 kubelet版本是否对应
- 是否安装了Docker
- kubelet的工作端口是否被占用
- ......
你也可以使用–ignore-preflight-errors参数来忽略一些前置检查错误,一般不建议这么做。
1-2. 通过了 Preflight Checks 之后,kubeadm 要为你做的,是生成 Kubernetes 对外提供服务所需的各种证书和对应的目录
kubeadm 为 Kubernetes 项目生成的证书文件默认都放在 Master 节点的 /etc/kubernetes/pki 目录下,可以通过–cert-dir修改,生成的证书如下:
$ ls /etc/kubernetes/pki
apiserver.crt apiserver-etcd-client.key apiserver-kubelet-client.crt ca.crt etcd front-proxy-ca.key front-proxy-client.key sa.pub
apiserver-etcd-client.crt apiserver.key apiserver-kubelet-client.key ca.key front-proxy-ca.crt front-proxy-client.crt sa.key
具体的证书介绍可以从参考这个站点: k8s证书介绍
注意默认生成的证书有效期只有一年
你也可以将自己的证书拷贝到 /etc/kubernetes/pki/ 不让kubeadm为你生成证书
1-3.生成证书完成后,kubeadm会为其他组件生成访问 kube-apiserver 所需的配置文件,这些文件默认生成在 /etc/kubernetes/ 下,查看如下:
ls /etc/kubernetes/*.conf
/etc/kubernetes/admin.conf /etc/kubernetes/controller-manager.conf /etc/kubernetes/kubelet.conf /etc/kubernetes/scheduler.conf
这些文件里面记录着Master节点的服务器地址、监听端口、证书目录等等。对应的客户端节点(如kubelet 等),可以直接加载相应的文件与 kube-apiserver建立安全连接。参数中如果包含–feature-gates=DynamicKubeletConfig,会把kubelet的初始化配置文件写入/var/lib/kubelet/config/init/kubelet这个文件里面。
1-4.接下来,kubeadm 会为 Master 组件生成静态Pod配置文件。而后它们都会被使用 Pod 的方式部署起来。如果没有提供外部etcd,还会另外生成一个etcd的静态Pod配置文件。这些静态pod会被写入/etc/kubernetes/manifests,kubelet进程会加载这个目录的配置,从而创建相关的pod。
关于静态pod:
它允许你把要部署的 Pod 的 YAML 文件放在一个指定的目录里。这样,当这台机器上的 kubelet 启动时,它会自动检查这个目录,加载所有的 Pod YAML 文件,然后在这台机器上启动它们。
1-5.给当前的节点(Master节点)打label和taints,从而防止其他的负载在这个节点运行
1-6.生成一个 bootstrap token。只要持有这个 token,任何一个安装了 kubelet 和 kubadm 的节点,都可以通过 kubeadm join 加入到这个集群当中.默认生成的token有效期是24h。
1-7.进行一些允许节点以 Bootstrap Tokens 和 TLS bootstrapping 方式加入集群的必要的操作
- 设置RBAC规则,同时创建一个用于节点加入集群的ConfigMap
- 让Bootstrap Tokens可以访问CSR签名的API。
- 给新的CSR请求配置自动认证机制。
1-8.通过apiServer安装DNS服务器。
dashBoard安装
1.安装dashBoard2.0
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml
2.暴露端口
kubectl expose svc kubernetes-dashboard --port=443 --target-port=8443 --type=NodePort --name=test-dashboard -n kubernetes-dashboard
kubectl get svc -n kubernetes-dashboard
3.访问dashboard
https://192.168.163.251:32635/
4.获取secret信息
kubectl -n kubernetes-dashboard get secret
5.获取token
kubectl -n kubernetes-dashboard describe secrets kubernetes-dashboard-token-gqf25
创建admin用户进行登录
vi admin-user.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kube-system
kubectl apply -f admin-user.yaml
查看admin秘钥
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')
创建一个单独的namespce并用上述方式为其创建集群的超级管理员
apiVersion: v1
kind: Namespace
metadata:
name: development
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-dev
namespace: development
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-dev
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-dev
namespace: development
kubectl apply -f admin-dev.yaml
kubectl describe secret -n development $(kubectl -n development get secret | grep admin-dev | awk '{print $1}' )
生成kubeconfig文件
#设置token变量
DASHBOARD_LOGIN_TOKEN=`kubectl describe secret -n development $(kubectl -n development get secret | grep admin-dev | awk '{print $1}' ) | grep -E '^token' | awk '{print $2}'`
#设置远端API地址
KUBE_APISERVER="https://192.168.162.97:6443"
# 设置集群参数
kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true --server=${KUBE_APISERVER} --kubeconfig=dashboard.kubeconfig
# 设置客户端认证参数,使用上面创建的 Token
kubectl config set-credentials dashboard_user \
--token=${DASHBOARD_LOGIN_TOKEN} \
--kubeconfig=dashboard.kubeconfig
# 设置上下文参数
kubectl config set-context default \
--cluster=kubernetes \
--user=dashboard_user \
--kubeconfig=dashboard.kubeconfig
# 设置默认上下文
kubectl config use-context default --kubeconfig=dashboard.kubeconfig
附录
kubeadm init 参数解释
--apiserver-advertise-address string 设置 apiserver 绑定的 IP.
--apiserver-bind-port int32 设置apiserver 监听的端口. (默认 6443)
--apiserver-cert-extra-sans strings api证书中指定额外的Subject Alternative Names (SANs) 可以是IP 也可以是DNS名称。 证书是和SAN绑定的。
--cert-dir string 证书存放的目录 (默认 "/etc/kubernetes/pki")
--certificate-key string kubeadm-cert secret 中 用于加密 control-plane 证书的key
--config string kubeadm 配置文件的路径.
--cri-socket string CRI socket 文件路径,如果为空 kubeadm 将自动发现相关的socket文件; 只有当机器中存在多个 CRI socket 或者 存在非标准 CRI socket 时才指定.
--dry-run 测试,并不真正执行;输出运行后的结果.
--feature-gates string 指定启用哪些额外的feature 使用 key=value 对的形式。
-h, --help 帮助文档
--ignore-preflight-errors strings 忽略前置检查错误,被忽略的错误将被显示为警告. 例子: 'IsPrivilegedUser,Swap'. Value 'all' ignores errors from all checks.
--image-repository string 选择拉取 control plane images 的镜像repo (default "k8s.gcr.io")
--kubernetes-version string 选择K8S版本. (default "stable-1")
--node-name string 指定node的名称,默认使用 node 的 hostname.
--pod-network-cidr string 指定 pod 的网络, control plane 会自动将 网络发布到其他节点的node,让其上启动的容器使用此网络
--service-cidr string 指定service 的IP 范围. (default "10.96.0.0/12")
--service-dns-domain string 指定 service 的 dns 后缀, e.g. "myorg.internal". (default "cluster.local")
--skip-certificate-key-print 不打印 control-plane 用于加密证书的key.
--skip-phases strings 跳过指定的阶段(phase)
--skip-token-print 不打印 kubeadm init 生成的 default bootstrap token
--token string 指定 node 和control plane 之间,简历双向认证的token ,格式为 [a-z0-9]{6}\.[a-z0-9]{16} - e.g. abcdef.0123456789abcdef
--token-ttl duration token 自动删除的时间间隔。 (e.g. 1s, 2m, 3h). 如果设置为 '0', token 永不过期 (default 24h0m0s)
--upload-certs 上传 control-plane 证书到 kubeadm-certs Secret.