为了应对 IT 上云趋势,提前学习相关技术栈,以及研究基于 Kubernetes 的 GPU 资源调度及用户隔离,花了 2 天时间,利用 kubeadm 搭建了一个 Kubernetes 集群的学习环境。
在本次部署中,我准备了 3 台公有云上的虚拟机,hostname 分别为 master、worker1、worker2,机器的配置如下:
部署的目标:
安装 kubeadm,只需要添加 kubdadm 的源,然后直接使用 apt-get 安装即可,为了方便,后续会直接在 root 用户下进行操作。
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF > /etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y docker.io kubeadm
在安装 kubeadm 的过程中,kubeadm 和 kubelet、kubectl、kubernetes-cni 这几个二进制文件会被自动安装好。kubeadm、kubelet、kubectl 的版本为 1.17.4-00,Docker 的版本为 18.09.7。
编写一个给 kubeadm 用的 YAML 文件 kubeadm.yaml
mkdir -p Projects/Kubernetes
cd Projects/Kubernetes/
cat <<EOF > 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: "v1.17.0"
EOF
这个配置文件中,给 kube-controller-manager 设置了 horizontal-pod-autoscaler-use-rest-clients: "true"
,将来部署的 kube-controller-manager 能够使用自定义资源进行自动水平扩展。
v1.17.0 是 kubeadm 帮我们部署的 Kubernetes 的版本号。
然后我们执行如下指令完成 Kubernetes Master 的部署,
kubeadm init --config kubeadm.yaml
部署完成后,会有如下信息提示。
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 10.140.0.7:6443 --token jt5wl5.hxg7a82w4tay9u07 \
--discovery-token-ca-cert-hash sha256:c1d5c89113ae42a7e3733e2c841398aac96f4634e7f1457bf3311551340647f1
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 这个安全配置文件的位置。
现在,可以使用 kubectl get
命令查看当前唯一一个节点的状态,
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master NotReady master 9m44s v1.17.4
可以看到,master 节点的状态是 NotReady,我们再用 kubectl describe
查看一下这个节点对象的详细信息、状态和事件,
$ kubectl describe node master
...
Conditions:
Ready False Sat, 21 Mar 2020 11:16:15 +0000 Sat, 21 Mar 2020 11:05:53 +0000 KubeletNotReady runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
通过输出结果,我们看到 NotReady 的原因在于,我们没有部署任何网络插件。而在 kubeadm 部署完成后,其中一条提示信息为部署一个网络 pod 到集群。
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/
另外,我们还可以通过 kubectl 检查这个节点上各个系统 Pod 的状态,其中 kube-system 是 Kubernetes 预留的系统 Pod 的工作空间(Namespace)。
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-6955765f44-6vq56 0/1 Pending 0 23m
coredns-6955765f44-mbxn6 0/1 Pending 0 23m
etcd-master 1/1 Running 0 23m
kube-apiserver-master 1/1 Running 0 23m
kube-controller-manager-master 1/1 Running 0 23m
kube-proxy-bgzwt 1/1 Running 0 23m
kube-scheduler-master 1/1 Running 0 23m
可以看到,CoreDNS 这个依赖于网络的 Pod 处于 Pending 状态,即调度失败。
从 https://kubernetes.io/docs/concepts/cluster-administration/addons/ 中,找到 Weave Net 的部署方法,同时需要注意防火墙的端口设置。
Before installing Weave Net, you should make sure the following ports are not blocked by your firewall: TCP 6783 and UDP 6783/6784. For more details, see the FAQ.
$ 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
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-6955765f44-6vq56 1/1 Running 0 36m
coredns-6955765f44-mbxn6 1/1 Running 0 36m
etcd-master 1/1 Running 0 36m
kube-apiserver-master 1/1 Running 0 36m
kube-controller-manager-master 1/1 Running 0 36m
kube-proxy-bgzwt 1/1 Running 0 36m
kube-scheduler-master 1/1 Running 0 36m
weave-net-dw7hl 2/2 Running 0 3m6s
可以看到,所有的系统 Pod 都成功启动了,而刚刚部署的 Weave 网络插件则在 kube-systeme 下面新建了一个名叫 weave-net-dw7hl 的 Pod。
至此,Kubernetes 的 Master 节点部署完成。
Kubernetes 的 Worker 节点跟 Master 节点几乎是相同的,它们运行着的都是一个 kubelet 组
件。唯一的区别在于,在 kubeadm init
的过程中,kubelet 启动后,Master 节点上还会自动运行
kube-apiserver、kube-scheduler、kube-controller-manger 这三个系统 Pod。
部署 Worker 节点只需要两步即可完成:
第一步,在所有 Worker 节点上执行“安装 kubeadm 和 Docker”一节的所有步骤。
第二步,执行部署 Master 节点时生成的 kubeadm join 指令:
kubeadm join 10.140.0.7:6443 --token jt5wl5.hxg7a82w4tay9u07 \
--discovery-token-ca-cert-hash sha256:c1d5c89113ae42a7e3733e2c841398aac96f4634e7f1457bf3311551340647f1
部署完成后,提示如下信息:
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
默认情况下 Master 节点是不允许运行用户 Pod 的。而 Kubernetes 做到这一
点,依靠的是 Kubernetes 的 Taint/Toleration 机制。
通过 kubectl describe
查看一些 Master 节点的 Trint 字段,会发现:
Name: master
Roles: master
Taints: node-role.kubernetes.io/master:NoSchedule
在这里,我们要一个单节点的 Kubernetes,所以直接删除这个 Taint,
$ kubectl taint nodes --all node-role.kubernetes.io/master-
node/master untainted
taint "node-role.kubernetes.io/master" not found
taint "node-role.kubernetes.io/master" not found
这时,再查看 Trint 字段,
Taints: <none>
因为 Kubernetes 本身更新速度比较快,Kubernetes API 在版本之间差异也是比较大的,这就导致了某些功能在新版的 Dashboard 中不能正常展示,其中 Dashboard v2.0.0-rc6 的兼容性如下表所示:
Kubernetes version | 1.14 | 1.15 | 1.16 | 1.17 |
---|---|---|---|---|
Compatibility | ? | ? | ? | ✓ |
本次部署的 Kubernetes 集群版本为 v1.17.4。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 115m v1.17.4
worker1 Ready <none> 44m v1.17.4
worker2 Ready <none> 28m v1.17.4
参考 https://github.com/kubernetes/dashboard/blob/master/docs/user/installation.md
在新版本中,Dashboard 默认会启用 https 的认证,具体认证方式有:TLS、token 和 username/passwd。
当我们部署完成后,我们用 https 访问 Dashboard 时可能会报证书的相关问题,所以还是建议大家先为 Dashboard 创建自签证书再部署 Dashboard,这里我用 openssl工具生成自签证书,具体过程如下:
$ mkdir $HOME/certs
$ openssl req -nodes -newkey rsa:2048 -keyout $HOME/certs/dashboard.key -out $HOME/certs/dashboard.csr -subj "/C=/ST=/L=/O=/OU=/CN=kubernetes-dashboard"
## 利用 key 和私钥生成证书
$ openssl x509 -req -sha256 -days 365 -in $HOME/certs/dashboard.csr -signkey $HOME/certs/dashboard.key -out certs/dashboard.crt
也可以用集群自有 CA 签发证书:
openssl x509 -req -in $HOME/certs/dashboard.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out $HOME/certs/dashboard.crt -days 365
证书签发完成后,查看 $HOME/certs
:
$ ls $HOME/certs/
dashboard.crt dashboard.csr dashboard.key
在 K8S 集群中创建 kubernetes-dashboard 命名空间并创建相应的 secret:
注意:旧版本的 dashboard 的命名空间默认为 kube-system,而新版本的 dashboard 具有独立的命名空间 kubernetes-dashboard,我们可以提前创建。
kubectl create ns kubernetes-dashboard
kubectl create secret generic kubernetes-dashboard-certs --from-file=$HOME/certs -n kubernetes-dashboard
$ cd ~/Projects/Kubernetes/
$ curl -O https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc6/aio/deploy/recommended.yaml
$ kubectl apply -f recommended.yaml
$ kubectl get pods -n kubernetes-dashboard
NAME READY STATUS RESTARTS AGE
dashboard-metrics-scraper-7b8b58dc8b-xdq25 1/1 Running 0 24m
kubernetes-dashboard-5f5f847d57-s7x49 1/1 Running 0 24m
在 Kubernetes 1.11 版本之后,Heapster 被 Metrics Server 替换后,dashboard 无法从 Heapster 获取集群 Metrics,转而使用 Metrics Server 获取集群 Metrics,而 Dashboard 2.0 为此多了一个 dashboard-metrics-scraper 容器专门用来获取这些指标。
Kubernetes Dashboard 2.0 配置完成后默认采用 HTTPS 方式访问,并配合 kubeconfig 文件或者 token 进行登录的,所以,接下来需要搞一个具有权限的用户登录 dashboard。
具有集群权限的用户清单文件(dashboard-admin-user.yaml):
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: dashboard-admin-sa
namespace: kubernetes-dashboard
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dashboard-admin-sa
namespace: kubernetes-dashboard
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: dashboard-admin-sa
namespace: kubernetes-dashboard
执行该文件,
kubectl create -f dashboard-admin-user.yaml
获取该用户 token:
kubectl -n kubernetes-dashboard get secret -o jsonpath='{range .items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="dashboard-admin-sa")].data}{.token}{end}' | base64 -d
用上述输出的字符就可以登录 dashboard 了,这里配置的是管理员权限。
需要注意的是,由于 Dashboard 是一个 Web Server,很多人经常会在自己的公有云上无意地暴露
Dashboard 的端口,从而造成安全隐患。所以,1.7 版本之后的 Dashboard 项目部署完成后,默
认只能通过 Proxy 的方式在本地访问。具体的操作,你可以查看 Dashboard 项目的官方文档。
而如果你想从集群外访问这个 Dashboard 的话,就需要用到 Ingress。
利用 Rook 把 Ceph 存储后端部署起来:
$ kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/common.yaml
$ kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/operator.yaml
# verify the rook-ceph-operator is in the `Running` state before proceeding
$ kubectl -n rook-ceph get pod
NAME READY STATUS RESTARTS AGE
rook-ceph-operator-85f5b946bd-tjx4m 1/1 Running 0 93s
rook-discover-bgqwt 1/1 Running 0 64s
rook-discover-kxt9t 1/1 Running 0 64s
rook-discover-lh26h 1/1 Running 0 64s
kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/cluster.yaml
$ kubectl -n rook-ceph get pod
NAME READY STATUS RESTARTS AGE
csi-cephfsplugin-4fdbg 3/3 Running 0 4m44s
csi-cephfsplugin-provisioner-d77bb49c6-rnxpm 5/5 Running 0 4m44s
csi-cephfsplugin-provisioner-d77bb49c6-x4qzr 5/5 Running 0 4m44s
csi-cephfsplugin-rnpm8 3/3 Running 0 4m44s
csi-cephfsplugin-wqrqj 3/3 Running 0 4m44s
csi-rbdplugin-29qqp 3/3 Running 0 4m44s
csi-rbdplugin-j7k4n 3/3 Running 0 4m44s
csi-rbdplugin-provisioner-5b5cd64fd-pkfjt 6/6 Running 0 4m44s
csi-rbdplugin-provisioner-5b5cd64fd-sf5cl 6/6 Running 0 4m44s
csi-rbdplugin-wgw56 3/3 Running 0 4m44s
rook-ceph-crashcollector-master-764455b764-s86fj 1/1 Running 0 2m9s
rook-ceph-crashcollector-worker1-7777fbc65-grqnt 1/1 Running 0 3m11s
rook-ceph-crashcollector-worker2-7476949d78-25sdw 1/1 Running 0 89s
rook-ceph-mgr-a-7884d789fc-5lt9q 1/1 Running 0 89s
rook-ceph-mon-a-5ddcb5f47c-nx5l9 1/1 Running 0 2m24s
rook-ceph-mon-b-5f9f57f768-wb7hv 1/1 Running 0 2m9s
rook-ceph-mon-c-6695d658b6-f55sd 1/1 Running 0 108s
rook-ceph-operator-85f5b946bd-tjx4m 1/1 Running 0 7m52s
rook-ceph-osd-prepare-master-bm6m2 0/1 Completed 0 66s
rook-ceph-osd-prepare-worker1-62w7h 0/1 Completed 0 66s
rook-ceph-osd-prepare-worker2-82gzl 0/1 Completed 0 66s
rook-discover-bgqwt 1/1 Running 0 7m23s
rook-discover-kxt9t 1/1 Running 0 7m23s
rook-discover-lh26h 1/1 Running 0 7m23s
为验证集群健康状态,可以连接到 Rook toolbox 运行 ceph status
命令查看,验证结果需满足:
$ kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/toolbox.yaml
$ kubectl get pods -n rook-ceph
rook-ceph-tools-7f96779fb9-mq4qf 1/1 Running 0 37s
$ kubectl -n rook-ceph exec -it $(kubectl -n rook-ceph get pod -l "app=rook-ceph-tools" -o jsonpath='{.items[0].metadata.name}') bash
$ ceph status
cluster:
id: 9fb35b75-0695-4f31-a8b0-208b64c3a6ce
health: HEALTH_WARN
OSD count 0 < osd_pool_default_size 3
services:
mon: 3 daemons, quorum a,b,c (age 35m)
mgr: a(active, since 34m)
osd: 0 osds: 0 up, 0 in
data:
pools: 0 pools, 0 pgs
objects: 0 objects, 0 B
usage: 0 B used, 0 B / 0 B avail
pgs:
在本篇文章中,我们从 0 开始,在公有云环境下使用 kubeadm 工具部署了一个完整的 Kubernetes 集群:这个集群有一个 Master 节点和多个 Worker 节点;使用 Weave 作为容器网络插件;使用 Rook 作为容器持久化存储插件;使用 Dashboard 插件提供了可视化的 Web 界面。
微信公众号「padluo」,分享数据科学家的自我修养,既然遇见,不如一起成长。