在国内使用kubeadm搭建k8s集群
k8s是目前最流行的容器编排工具,不过k8s的安装部署一直是国内用户的一大痛点,复杂的部署配置过程使得其学习曲线非常陡峭。而新版本推出的kubeadm工具大大的简化了整个过程,这也是k8s官方推荐的安装工具。可惜,在国内的网络环境下,kubeadm是无法顺利运行的。因此,本文提供了在国内环境下使用kubeadm搭建k8s集群的过程。 本文的步骤基于k8s的官方文档 (https://kubernetes.io/docs/setup) 中的“Bootstrapping Clusters with kubeadm”章节,使用coreos公司的Container Linux作为操作系统进行部署k8s的1.12.1版本。
1. 在VirtualBox中创建Container Linux宿主机。
coreos提供了使用vagrant在virtual创建Container Linux的过程,步骤如下:
# git clone https://github.com/coreos/coreos-vagrant/
# cd coreos-vagrant
# cp config.rb.sample config.rb
# 修改config.rb文件中的$num_instances变量为2,创建两个虚机,core-01作为master节点,core-02作为node节点。
# vagrant up
虚拟机启动后,登陆所有节点并启动docker:
# vagrant ssh core01
# sudo -i
# systemctl enable docker && systemctl start docker
2. 安装kubeadm, kubelet, kubectl软件包
参考文档:https://kubernetes.io/docs/setup/independent/install-kubeadm/
2.1 安装CNI:
# CNI_VERSION="v0.6.0"
# mkdir -p /opt/cni/bin
# curl -L "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-amd64-${CNI_VERSION}.tgz" | tar -C /opt/cni/bin -xz
CNI全称为容器网络接口,用于支持k8s节点间的网络通信,其以插件的形式支持多种网络互联方式。如果无法下载,可使用以下替代地址:
curl -L "http://lprincewhn.github.io/assets/k8s/cni-plugins-amd64-${CNI_VERSION}.tgz" | tar -C /opt/cni/bin -xz
2.2 安装CRICTL
# CRICTL_VERSION="v1.11.1"
# mkdir -p /opt/bin
# curl -L "https://github.com/kubernetes-incubator/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-amd64.tar.gz" | tar -C /opt/bin -xz
CRI全称为容器运行时接口,kubeadm和kubelet通过这个接口去创建k8s本身管理所需的容器。如果无法下载,可使用以下地址代替:
curl -L "http://lprincewhn.github.io/assets/k8s/crictl-${CRICTL_VERSION}-linux-amd64.tar.gz" | tar -C /opt/bin -xz
2.3 安装kubeadm, kubectl, kubelet组件
文档中这几个组件的下载地址国内网络无法访问,因此需要科学上网下载,或者通过以下国内地址下载。
# RELEASE=v1.12.1
# cd /opt/bin
# curl -L http://lprincewhn.github.io/assets/k8s/kubeadm-${RELEASE}-linux-amd64.gz | gunzip > kubeadm
# curl -L http://lprincewhn.github.io/assets/k8s/kubectl-${RELEASE}-linux-amd64.gz | gunzip > kubectl
# curl -L http://lprincewhn.github.io/assets/k8s/kubelet-${RELEASE}-linux-amd64.gz | gunzip > kubelet
# chmod +x {kubeadm,kubelet,kubectl}
2.4 启动kubelet
kubelet是k8s中的在各个节点上的代理,kubeadm也会通过kubelet来创建k8s自身管理所需的容器,如包括api-server,scheduler,controller-manager,proxy,也就是说k8s六大组件,除了kubelet和kubectl外,其他的都已经容器化,并通过kubeadm部署。
# curl -sSL "https://raw.githubusercontent.com/kubernetes/kubernetes/${RELEASE}/build/debs/kubelet.service" | sed "s:/usr/bin:/opt/bin:g" > /etc/systemd/system/kubelet.service
# mkdir -p /etc/systemd/system/kubelet.service.d
# curl -sSL "https://raw.githubusercontent.com/kubernetes/kubernetes/${RELEASE}/build/debs/10-kubeadm.conf" | sed "s:/usr/bin:/opt/bin:g" > /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
# systemctl enable kubelet && systemctl start kubelet
国内访问这两个链接容易失败,注意运行完后检查响应文件的内容是否正确。
3. 下载所需Docker镜像
kubeadm创建集群的时候需要从gcr.io下载镜像,查看所需镜像:
# kubeadm config images list --kubernetes-version=${RELEASE}
这些镜像在国内无法访问,因此我已经通过github将这些镜像转移到docker hub上,可用以下命令直接下载:
# cd ~
# git clone https://github.com/lprincewhn/googlecontainers.git
# export RELEASE=v1.12.1
# bash googlecontainers/get_kubeadm_images.sh
4. 创建集群
# export RELEASE=v1.11.2
# kubeadm init --kubernetes-version=${RELEASE} --apiserver-advertise-address=192.168.56.81 --pod-network-cidr=10.244.0.0/16
# mkdir -p $HOME/.kube
# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# chown $(id -u):$(id -g) $HOME/.kube/config
参数说明:
- –kubernetes-version: 由于在国内无法通过网络获取最新的k8s版本号,因此在这里指定为v1.11.2,这里的版本号需要和上一步下载的镜像版本一致。
- –apiserver-advertise-address: 我们在VirtualBox中创建的宿主机有两个网口,其中第二个网口是用于宿主机之间通信的Host-Only网口,我们应该使用这个网口作为api-server的侦听地址。
- –pod-network-cidr: 由于我们将使用的flannel网络插件默认10.244.0.0/16作为node节点间的overlay网络,该参数将这个ip段给pod分配ip。
5. 安装网络插件
我们选择flannel作为网络插件,安装步骤如下:
5.1 修改系统参数
# sysctl net.bridge.bridge-nf-call-iptables=1
该参数表示启动netfilter对bridge数据包,即二层数据包的处理。这是flannel网络插件的要求。
5.2 下载kube-flannel.yml文件
# curl -o kube-flannel.yml -sSL https://raw.githubusercontent.com/coreos/flannel/v0.10.0/Documentation/kube-flannel.yml
在kube-flannel.yml中,定义了名为kube-flannel-ds的DaemonSet,其容器的command为:
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
可见,其并没有指定使用个网口作为flanneld对外通信的物理网卡,于是在有多个网口的宿主机上,flanneld可能会选择一个不正确的网口,使得节点间无法通信。在vagrant启动的宿主机中就存在这个现象,这些虚拟机第一个网口是NAT网口,负责和外网的通信,虚拟机之间不能通过这个网口通信。第二个网口才是虚拟机之间通信的Host-Only网口。因此需要手动修改kube-flannel.yml文件中kube-flannel的command的值,添加参数–iface=ethX,这里的ethX即为用于虚拟机间通信的Host-Only网口,如eth1.
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
- --iface=eth1
5.3 下载flannel镜像
在国内下载flannel镜像同样很容易失败,因此建议提前下载好,再去启动flannel DaemonSet,避免在启动过程中重试浪费时间。手动下载时镜像名称最好从kube-flannel.yml文件中拷贝,确保下载正确的镜像。
# docker pull quay.io/coreos/flannel:v0.10.0-amd64
也可使用以下步骤从国内地址下载:
# cd ~
# git clone https://github.com/lprincewhn/googlecontainers.git
# bash googlecontainers/get_flannel_image.sh
5.4 启动flannel
# kubectl apply -f kube-flannel.yml
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.extensions/kube-flannel-ds created
网络插件安装成功后,节点状态将变为Ready。
# kubectl get nodes
NAME STATUS ROLES AGE VERSION
core-01 Ready master 11m v1.11.2
6. node节点加入集群
在node节点上重复上述第2,3,5.1,5.3节的所有步骤。 然后使用kubeadm join指令将节点加入集群,
# kubeadm join 192.168.56.81:6443 --token cbxiqq.340w6dxc937glr88 --discovery-token-ca-cert-hash sha256:bef4312a2c3326535bda81bfa5d4be98dc1a0e8f6e7491fe5827e671146b068a
参数说明如下:
- –token,node节点加入集群所需的bootstrap token, kubeadm init命令生成的token默认24小时有效,如果超过了这个时间,需要重新生成token,master节点运行命令如下:
# kubeadm token create
- –discovery-token-ca-cert-hash,根证书的摘要,master节点运行如下命令获取:
# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \ openssl dgst -sha256 -hex | sed 's/^.* //'
成功后在master节点上可查看到新节点为ready状态:
# kubectl get nodes
NAME STATUS ROLES AGE VERSION
core-01 Ready master 3h v1.11.2
core-02 Ready <none> 31s v1.11.2
7. 调整和优化
集群搭建完毕后,建议将master节点的cpu数目调整为2,内存调整为2048M。
8. 安装kubernetes-dashboard
8.1 下载kubernetes-dashboard.yaml文件
# cd ~
# curl -o kubernetes-dashboard.yaml -sSL https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
8.2 下载kubernetes-dashboard镜像
和其他镜像类似,在国内无法直接下载kubernetes-dashboard镜像时,可通过以下步骤下载:
# cd ~
# git clone https://github.com/lprincewhn/googlecontainers.git
# bash googlecontainers/get_kube_dashboard.sh
8.3 启动kubernetes-dashboard
# kubectl apply -f ./kubernetes-dashboard.yaml
secret/kubernetes-dashboard-certs created
serviceaccount/kubernetes-dashboard created
role.rbac.authorization.k8s.io/kubernetes-dashboard-minimal created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard-minimal created
deployment.apps/kubernetes-dashboard created
service/kubernetes-dashboard created
8.4 通过proxy暴露服务
kubernetes-dashboard默认使用自签名证书启动,该证书存在内存当中,无法被外部获取。因此外部无法信任该证书。因此即使将其服务类型修改为NodePort或者LoadBanlancer,也无法被外部访问。此时我们可以通过在api-server上建立proxy的方式访问访问kubernetes-dashboard。
启动api-server的proxy:
# kubectl proxy
Starting to serve on 127.0.0.1:8001
需要注意的是,这个代理相当于把api-server暴露,除了访问集群中的服务之外,同样可以通过这个代理进行k8s集群的管理和控制,因此具有非常大的安全风险。正因如此,默认proxy只侦听127.0.0.1回环地址,该地址在启动代理的的master节点之外是无法访问的。如果我们需要访问,通过ssh建立端口转发,把发送到localhost的数据转发到master节点:
ssh可使用本地端口转发和动态端口转发两种模式:
- 动态端口转发
ssh -D 9001 core@192.168.56.80 -i ~/.vagrant.d/insecure_private_key
转发规则建立后需要设置浏览器socks5代理为localhost:9001,此时在ssh客户端上可通过以下链接访问dashboard: http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
- 本地端口转发
ssh -L 9001:localhost:8001 core@192.168.56.80 -i ~/.vagrant.d/insecure_private_key
此时在ssh客户端上可通过以下链接访问dashboard: http://localhost:9001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
注:两种模式的机制和区别可参考《》,简单来说,如果集群中有多个服务需要通过kube proxy暴露给外部访问,则本地端口转发需要为每个服务添加转发规则,而动态端口转发则不需要。但是使用动态端口转发需要在浏览器中指定socks5代理。
8.5 赋予kubernetes-dashboard管理员权限
第一次访问kubernetes-dashboard时将被重定向到登陆页面,支持kubeconfig和token两种登陆方式,如果跳过登陆步骤,则使用最小权限进入dashboard,没有任何权限。
- 创建新的service account
- 将其与角色cluster-admin关联
- 从service account的secret中获取token
- 使用token登陆kubernetes-dashboard