搭建 k8s 集群网上很多教程,如果是手工部署或者实验环境可以直接使用 MiniKube 或者 Kind,来在本地启动简单的 Kubernetes 集群进行后面的学习即可。如果是使用 MiniKube 的话,阿里云还维护了一个国内版的 MiniKube,这对于在国内的同学来说会比较友好。
下面介绍使用 kubeadm 这个 Kubernetes 半官方管理工具的工作原理。kubeadm 的初衷就是让 Kubernetes 集群的部署不再让人头疼,而且发展比较成熟,关于各种问题网上都有对应的文章来解决,我们就来使用它部署一个完整的 Kubernetes 集群。
准备工作
首先准备三台虚拟机,或者是三天试服务器,我是从企鹅☁️上买的三个轻量服务器,还挺便宜,但是后来看文档才知道,这所谓的轻量服务器原来就是k8s上的容器,所以我这属于是在 k8s 的容器里上搭建 k8s 集群了,搁这搁这呢。 为什么我肯定这个提供的就是一个有官方镜像 linux 的容器呢,具体是因为我买了发现他们所谓的内网在不同账号下是不能互通的,也就是说不同账号的轻量服务器分配到了不同的 node 上,内网ip只能在同一个 node 上互通,不同的node之间想互通只能去配置路由,并且配置属于自己的二层网络。所以这听起来就知道是容器了。
在本次部署中,我准备的机器配置如下:
- 2 核 CPU、 4 GB 内存(三台);
- 80 GB 磁盘;
- Centos 7.6;
- 内网互通;
- 外网访问权限不受限制。
这里的内网互通我是直接通过 iptables 来进行配置的,因为三台服务器公网ip可以互通,但是内网ip是不互通的,在 k8s 中默认是用内网ip来交互的,所以我只能用如下命令来进行配置。
sudo iptables -t nat -A OUTPUT -d <另外服务器的内网ip> -j DNAT --to-destination <另外服务器的外网ip>
保证每个节点能内网ip都 ping 通其他节点就行了。
安装 kubeadm 和 Docker
所有节点安装Docker/kubeadm/kubelet
安装 docker
$ wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo $ yum -y install docker-ce-18.06.1.ce-3.el7 $ systemctl enable docker && systemctl start docker $ docker --version Docker version 18.06.1-ce, build e68fc7a
配置一下镜像地址
# cat > /etc/docker/daemon.json << EOF { "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"] } EOF
添加 k8s 的 yum 源
$ 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
安装kubeadm,kubelet和kubectl
yum install -y kubelet-1.18.0 kubeadm-1.18.0 kubectl-1.18.0 systemctl enable kubelet
K8S集群还未拉起,故这里的kubelet是无法启动的,等master初始化时会自动拉起
替换 k8s 所需组件的 docker 镜像
因为国内的镜像都拉不回来关于 k8s 所需组件的,所以需要替换一下镜像
查看 kubeadm 所需要的镜像列表:
$ kubeadm config images list I1108 19:54:40.085559 2997 version.go:252] remote version is much newer: v1.22.3; falling back to: stable-1.18 W1108 19:54:40.592794 2997 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io] k8s.gcr.io/kube-apiserver:v1.18.20 k8s.gcr.io/kube-controller-manager:v1.18.20 k8s.gcr.io/kube-scheduler:v1.18.20 k8s.gcr.io/kube-proxy:v1.18.20 k8s.gcr.io/pause:3.2 k8s.gcr.io/etcd:3.4.3-0 k8s.gcr.io/coredns:1.6.7
编写拉取shell,这里脚本里替换成所需要的组件版本就行了
$ vim ./pull_k8s_images.sh set -o errexit set -o nounset set -o pipefail ##这里定义版本,按照上面得到的列表自己改一下版本号 KUBE_VERSION=v1.18.20 KUBE_PAUSE_VERSION=3.2 ETCD_VERSION=3.4.3-0 DNS_VERSION=1.6.7 ##这是原始仓库名,最后需要改名成这个 GCR_URL=k8s.gcr.io ##这里就是写你要使用的仓库 DOCKERHUB_URL=registry.aliyuncs.com/google_containers ##这里是镜像列表,新版本要把coredns改成coredns/coredns images=( kube-proxy:${KUBE_VERSION} kube-scheduler:${KUBE_VERSION} kube-controller-manager:${KUBE_VERSION} kube-apiserver:${KUBE_VERSION} pause:${KUBE_PAUSE_VERSION} etcd:${ETCD_VERSION} coredns:${DNS_VERSION} ) ##这里是拉取和改名的循环语句 for imageName in ${images[@]} ; do docker pull $DOCKERHUB_URL/$imageName docker tag $DOCKERHUB_URL/$imageName $GCR_URL/$imageName docker rmi $DOCKERHUB_URL/$imageName done
执行shell
$ chmod +x ./pull_k8s_images.sh $ ./pull_k8s_images.sh
部署 k8s master 节点
因为我的版本是 1.18.20 的版本,所以关于 kubeadm 的配置文件 config,内容如下
vim ./kubeadm/kubeadm.yaml apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration controllerManager: extraArgs: horizontal-pod-autoscaler-use-rest-clients: "true" horizontal-pod-autoscaler-sync-period: "10s" node-monitor-grace-period: "10s" apiServer: extraArgs: runtime-config: "api/all=true" kubernetesVersion: "stable-1.18"
这里的文件中 apiVersion 字段要根据不同的 k8s 进行调整,具体可以看看官网,因为不同的版本api不同,会报错~
具体可以看看这个:https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta3/
kubeadm 初始化安装 k8s
kubeadm init --config ./kubeadm/kubeadm.yaml
最后安装完毕后会有提示:
[addons] Applied essential addon: CoreDNS [addons] Applied essential addon: kube-proxy Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join: --token --discovery-token-ca-cert-hash
这个 kubeadm join 命令,就是用来给这个 Master 节点添加更多工作节点(Worker)的命令。我们在后面部署 Worker 节点的时候马上会用到它,所以找一个地方把这条命令记录下来。
此外,kubeadm 还会提示我们第一次使用 Kubernetes 集群所需要的配置命令:
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
而需要这些配置命令的原因是:Kubernetes 集群默认需要加密方式访问。所以,这几条命令,就是将刚刚部署生成的 Kubernetes 集群的安全配置文件,保存到当前用户的.kube 目录下,kubectl 默认会使用这个目录下的授权信息访问 Kubernetes 集群。如果不这么做的话,我们每次都需要通过 export KUBECONFIG 环境变量告诉 kubectl 这个安全配置文件的位置。
我个人更倾向于使用 export KUBECONFIG 环境变量告诉 kubectl 这个安全配置文件的位置,因为当 k8s 发生错误重新启动的时候,可会找不到配置文件
所以
$ vim /etc/profile 在文件最后添加 export KUBECONFIG=/etc/kubernetes/admin.conf $ source /etc/profile
现在,我们就可以使用 kubectl get 命令来查看当前唯一一个节点的状态了
$ kubectl get nodes NAME STATUS ROLES AGE VERSION master NotReady master 1d v1.18.0
在调试 Kubernetes 集群时,最重要的手段就是用 kubectl describe 来查看这个节点(Node)对象的详细信息、状态和事件(Event),我们来试一下:
$ kubectl describe node master ... Conditions: ... Ready False ... KubeletNotReady runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
通过 kubectl describe 指令的输出,我们可以看到 NodeNotReady 的原因在于,我们尚未部署任何网络插件。另外,我们还可以通过 kubectl 检查这个节点上各个系统 Pod 的状态,其中,kube-system 是 Kubernetes 项目预留的系统 Pod 的工作空间(Namepsace,注意它并不是 Linux Namespace,它只是 Kubernetes 划分不同工作空间的单位):
$ kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE coredns-78fcdf6894-j9s52 0/1 Pending 0 1h coredns-78fcdf6894-jm4wf 0/1 Pending 0 1h etcd-master 1/1 Running 0 2s kube-apiserver-master 1/1 Running 0 1s kube-controller-manager-master 0/1 Pending 0 1s kube-proxy-xbd47 1/1 NodeLost 0 1h kube-scheduler-master 1/1 Running 0 1s
可以看到,CoreDNS、kube-controller-manager 等依赖于网络的 Pod 都处于 Pending 状态,即调度失败。这当然是符合预期的:因为这个 Master 节点的网络尚未就绪。
部署网络插件
在 Kubernetes 项目“一切皆容器”的设计理念指导下,部署网络插件非常简单,只需要执行一句 kubectl apply 指令,以 Weave 为例:
# 安装网络插件
$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
serviceaccount/weave-net created
clusterrole.rbac.authorization.k8s.io/weave-net created
clusterrolebinding.rbac.authorization.k8s.io/weave-net created
role.rbac.authorization.k8s.io/weave-net created
rolebinding.rbac.authorization.k8s.io/weave-net created daemonset.apps/weave-net created
部署完成后查看所有的 pod 状态
$ kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE coredns-78fcdf6894-j9s52 1/1 Running 0 1d coredns-78fcdf6894-jm4wf 1/1 Running 0 1d etcd-master 1/1 Running 0 9s kube-apiserver-master 1/1 Running 0 9s kube-controller-manager-master 1/1 Running 0 9s kube-proxy-xbd47 1/1 Running 0 1d kube-scheduler-master 1/1 Running 0 9s weave-net-cmk27 2/2 Running 0 19s
可以看到,所有的系统 Pod 都成功启动了,而刚刚部署的 Weave 网络插件则在 kube-system 下面新建了一个名叫 weave-net-cmk27 的 Pod,一般来说,这些 Pod 就是容器网络插件在每个节点上的控制组件。
除此之外我想让pod被三个节点调度,因为k8s中默认master是不进行调度pod的,也就是此节点上不会部署pod,这个挺浪费
$ kubectl describe node master
Name: master
Roles: master
Taints: node-role.kubernetes.io/master:NoSchedule
可以看到,Master 节点默认被加上了node-role.kubernetes.io/master:NoSchedule这样一个“污点”,其中“键”是node-role.kubernetes.io/master,而没有提供“值”。
这个污点是保证 master 不会被pod调度到
所以直接删除这个 taints:
$ kubectl taint nodes --all node-role.kubernetes.io/master-
部署 k8s 的 worker 节点
首先按照上面的部署“安装 kubeadm 和 Docker”
第二步,因为我希望这三个节点随便哪个都能当 master 节点来使用,所以按照“替换 k8s 所需组件的 docker 镜像”把所有master需要的组件镜像都拿来了
第三步,复制master中的 /etc/kubernetes/admin.conf 到 worker 节点的同样路径下,然后 export KUBECONFIG 环境变量
除此之外我发现在 worker join 集群的时候会出现错误:
[ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1
解决方法:
echo "1" >/proc/sys/net/bridge/bridge-nf-call-iptables
最后在 woker 节点添加进入集群命令:
就是之前 master 节点提示的 join 命令
查看节点
$ kubectl get nodes NAME STATUS ROLES AGE VERSION vm-20-4-centos Ready141m v1.18.0 vm-20-6-centos Ready master 7h5m v1.18.0 vm-20-9-centos Ready 169m v1.18.0
# 为 workder 节点添加 rolekubectl label nodes
vm-20-4-centoskubernetes.io/role=worker
kubectl label nodes
vm-20-9-centoskubernetes.io/role=worker
因为在k8s中根据 namespace 请求pod会请求host,我不希望请求 vm-20-4-centos 的时候会找不到解析的域名,所以我在没个node节点中还添加了以下的配置:
添加host
$ cat >> /etc/hosts << EOF 10.0.20.4 vm-20-4-centos 10.0.20.6 vm-20-6-centos 10.0.20.9 vm-20-9-centos EOF
将桥接的IPv4流量传递到iptables的链:
$ cat > /etc/sysctl.d/k8s.conf << EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF $ sysctl --system # 生效
大功告成,enjoy~~~
关于 k8s 的玩法我个人有以下几个目标:
- 关于 k8s 的可视化,可以给用户提供一个可视化的 Web 界面来查看当前集群的各种信息
- 关于 k8s 的网络插件,可以尝试各种常用的网络插件,看看他们二层网络是怎么工作原理,理解 k8s 的 CNI
- 关于 k8s 的持久性存储是怎么实现的,看看 pvc 和 pv
- 关于 k8s 中对象 operator 是怎么工作的,实现细节是如何的
这里先一个小目标