从0开始搭建一个完整的Kubernetes集群

我们利用Kubernetes来搭建一个完整的Kubernetes集群

这里的完整,指的是这个集群具备一个完整的Kubernetes项目的所有功能,模拟生产环境所有需求,但是并不代表这个集群是生成级别可用的,类似高可用,授权,多租户,灾难备份等生产级别集群的功能暂时不说

首先准备需要的机器

1.满足安装Docker项目所需的要求,是64位的Linux操作系统

2.x86或者ARM架构

3.网络互通

4.主机有外网权限

5.单机2核4G以上

5.30G的可用磁盘

我们的实践目标是

所有的节点安装Docker和kubeadm

部署Kubernetes Master

部署网络插件

部署Kubernetes Worker

部署Dashboard可视化插件

部署容器存储插件

首先关闭swap分区

sudo swapoff -a

我们需要添加kubeadm的源,可以使用apt-get安装

$ 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

如果apt.kubernetes.io网络不行,可以考虑Ubuntu镜像源deb

http://mirrors.ustc.edu.cn/kubernetes/apt kubernetes-xenial main

或者使用kubernetes yum源进行安装

首先添加对应的repo文件

[kubernetes]

name=kubernetes

baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/

gpgcheck=0

enable=1

添加到/etc/yum.repos.d目录下

然后进行

yum clean all

yum repolist

分别安装kubeadm

yum install docker-ce

yum install kubelet-1.11.1

yum install kubeadm-1.11.1

yum install kubectl-1.11.1

yum install kubernetes-cni

确保都是1.11.1版本的

systemctl enable docker

systemctl enable kubectl.service

systemctl start docker

systemctl start kubectl.service

在利用systemctl status kubectl.service的时候,会发现其启动失败了

journalctl查看错误信息

原因是因为没有kubeadm init

这样我们进行init 操作

我们利用kubeadm来进行一键部署Master节点

我们就给kubeadm编写了一个对应的init的YAML

apiVersion: kubeadm.k8s.io/v1alpha1

kind: MasterConfiguration

controllerManagerExtraArgs:

horizontal-pod-autoscaler-use-rest-clients: “true”

horizontal-pod-autoscaler-sync-period: “10s”

node-monitor-grace-period: “10s”

apiServerExtraArgs:

runtime-config: “api/all=true”

kubernetesVersion: “stable-1.11”

最后我们指定了kubernetesVersion为1.11,不过我写为了kubernetesVersion:v1.11.1

而且由于我们无法在国内连入谷歌的镜像仓库

我们可以借助国内的镜像仓库,来进行相关的镜像拉取

借助一个shell脚本,来进行拉取

vim pullimages.sh

#!/bin/bash

images=(kube-proxy-amd64:v1.11.1 kube-scheduler-amd64:v1.11.1 kube-controller-manager-amd64:v1.11.1

kube-apiserver-amd64:v1.11.1 etcd-amd64:3.2.18 coredns:1.1.3 pause:3.1 )

for imageName in ${images[@]} ; do

docker pull anjia0532/google-containers.$imageName

docker tag anjia0532/google-containers.$imageName k8s.gcr.io/$imageName

docker rmi anjia0532/google-containers.$imageName

done

bash pullimages.sh

拉取完成后,就可以进行master节点的init操作了

kubeadm init –config kubeadm.yaml

这就是Master节点的部署流程,整体的部署流程完成后,就会生成一个行指令

kubeadm join 10.152.240.112:6443 –token f0m0tr.qto4au6bp15l5l47 –discovery-token-ca-cert-hash sha256:75686062786ad9e7b83fa0a93850a4458e271005e2bd823a2d877a234df1e4d2

这样我们就配好了Master节点

然后,kubeadm还会提醒我们进行集群相关的配置命令

mkdir -p $HOME/.kube

sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

sudo chown $(id -u):$(id -g) $HOME/.kube/config

这些配置的命令,是Kubernetes集群默认需要加密访问,我们将加密相关的配置文件,保存在目录的.kube目录下,然后kubectl会默认使用这个目录下的授权信息去访问Kubernetes集群

如果不这么做,每次都需要export KUBECONFIG,来告诉kubectl的安全配置文件位置

然后利用kubectl get来查看唯一的一个节点

图片

Master节点的状态是NotReady,这是为何?

在调试Kubernetes集群的时候,最重要的就是kubectl describe来查看这个节点的详细信息,状态和事件,我们看一下

 kubectl describe node dev-code2

Name:               dev-code2

Roles:              master

Labels:             beta.kubernetes.io/arch=amd64

beta.kubernetes.io/os=linux

kubernetes.io/hostname=dev-code2

node-role.kubernetes.io/master=

Annotations:        kubeadm.alpha.kubernetes.io/cri-socket=/var/run/dockershim.sock

node.alpha.kubernetes.io/ttl=0

volumes.kubernetes.io/controller-managed-attach-detach=true

CreationTimestamp:  Wed, 02 Sep 2020 15:25:00 +0800

Taints:             node-role.kubernetes.io/master:NoSchedule

Unschedulable:      false

Conditions:

Type             Status    LastHeartbeatTime                 LastTransitionTime                Reason                    Message

—-             ——    —————–                 ——————                ——                    ——-

OutOfDisk        Unknown   Wed, 02 Sep 2020 16:38:01 +0800   Wed, 02 Sep 2020 16:37:58 +0800   NodeStatusUnknown         Kubelet stopped posting node status.

MemoryPressure   Unknown   Wed, 02 Sep 2020 16:38:01 +0800   Wed, 02 Sep 2020 16:37:58 +0800   NodeStatusUnknown         Kubelet stopped posting node status.

DiskPressure     Unknown   Wed, 02 Sep 2020 16:38:01 +0800   Wed, 02 Sep 2020 16:37:58 +0800   NodeStatusUnknown         Kubelet stopped posting node status.

PIDPressure      False     Wed, 02 Sep 2020 16:38:01 +0800   Wed, 02 Sep 2020 15:25:00 +0800   KubeletHasSufficientPID   kubelet has sufficient PID available

Ready            Unknown   Wed, 02 Sep 2020 16:38:01 +0800   Wed, 02 Sep 2020 16:37:58 +0800   NodeStatusUnknown         Kubelet stopped posting node status.

Addresses:

InternalIP:  10.152.240.112

Hostname:    dev-code2

Capacity:

cpu:                4

ephemeral-storage:  51175Mi

hugepages-2Mi:      0

memory:             8009204Ki

pods:               110

Allocatable:

cpu:                4

ephemeral-storage:  48294789041

hugepages-2Mi:      0

memory:             7906804Ki

pods:               110

System Info:

Machine ID:                 5d63f0cbd9934b7691f8249d2f157602

System UUID:                258F4D56-1E72-8D61-D1E8-96E117945DD1

Boot ID:                    ed1a4ca5-76ec-48e4-8693-332adad577bd

Kernel Version:             3.10.0-957.el7.x86_64

OS Image:                   CentOS Linux 7 (Core)

Operating System:           linux

Architecture:               amd64

Container Runtime Version:  docker://18.3.1

Kubelet Version:            v1.11.1

Kube-Proxy Version:         v1.11.1

Non-terminated Pods:         (5 in total)

Namespace                  Name                                 CPU Requests  CPU Limits  Memory Requests  Memory Limits

———                  —-                                 ————  ———-  —————  ————-

kube-system                etcd-dev-code2                       0 (0%)        0 (0%)      0 (0%)           0 (0%)

kube-system                kube-apiserver-dev-code2             250m (6%)     0 (0%)      0 (0%)           0 (0%)

kube-system                kube-controller-manager-dev-code2    200m (5%)     0 (0%)      0 (0%)           0 (0%)

kube-system                kube-proxy-bj826                     0 (0%)        0 (0%)      0 (0%)           0 (0%)

kube-system                kube-scheduler-dev-code2             100m (2%)     0 (0%)      0 (0%)           0 (0%)

Allocated resources:

(Total limits may be over 100 percent, i.e., overcommitted.)

Resource  Requests    Limits

——–  ——–    ——

cpu       550m (13%)  0 (0%)

memory    0 (0%)      0 (0%)

Events:

Type    Reason                   Age                From                Message

—-    ——                   —-               —-                ——-

Normal  NodeHasSufficientMemory  14m (x58 over 1h)  kubelet, dev-code2  Node dev-code2 status is now: NodeHasSufficientMemory

Normal  NodeHasSufficientDisk    3m (x67 over 1h)   kubelet, dev-code2  Node dev-code2 status is now: NodeHasSufficientDisk

因为没有网络插件,所以没法Ready

我们又通过kubectl检查这个节点各个系统Pod的状态,其中kube-system是Kubernetes项目预留的系统Pod的状态,

我们看下主节点相关的Pod的状态都是Pending的状态,就是调度失败的,这是因为Master节点的网络尚未准备就绪

kubectl get pods -n kube-system

NAME                                READY     STATUS    RESTARTS   AGE

coredns-78fcdf6894-85mpn            0/1       Pending   0          1h

coredns-78fcdf6894-kjtmt            0/1       Pending   0          1h

etcd-dev-code2                      1/1       Running   0          1h

kube-apiserver-dev-code2            1/1       Running   0          1h

kube-controller-manager-dev-code2   1/1       Running   0          1h

kube-proxy-bj826                    1/1       Running   0          1h

kube-scheduler-dev-code2            1/1       Running   0          1h

我们进行网络插件的部署

我们部署网络插件相当简单,kubectl apply,我们利用这个命令部署Weave

kubectl apply -f https://git.io/weave-kube-1.6

这时候在看节点的信息

图片

发现已经都启动起来了

刚刚的部署,在kube-system下面启动了一个weave-net-tcg2m的节点

这就是网络插件

Kubernetes支持网络插件,使用的是CNI的通用接口,是容器网络的事实标准,所有容器开源项目通过CNI接入Kubernetes,比如Flannel,Calico,Canal,Romana等,都是利用这个接口接入的

部署完了Master节点,在尝试在Master节点上运行用户Pod之前,我们先部署Worker节点

直接使用join指令

kubeadm join 10.152.240.112:6443 –token f0m0tr.qto4au6bp15l5l47 –discovery-token-ca-cert-hash sha256:75686062786ad9e7b83fa0a93850a4458e271005e2bd823a2d877a234df1e4d2

最后我们说一下如何在Master节点上执行Pod策略

而Kubernetes做到这一点,依靠的是Kubernetes的Taint/Toleration机制

污点机制,一个节点被打上了Taint,就被打上了污点,所有的Pod就不能在这个节点上运行

除非个别Pod声明自己能够容忍这个污点,声明了Toleration,才能在这个节点上运行

为一个节点打上污点的命令是

Kubectl taint nodes nodel foo=bar:Noschedule

这个node1节点上,就会增加一个键值对的Taint

foo=bar:NoSchedule,其中的NoSchedule,意味着这个Taint只会在调度新的Pod的时候使用,对已经存在的Pod,没有任何影响

那么Pod如何声明自己能容忍Toleration呢?

只要在Pod的yaml文件的spec部分,加入tolerations字段即可

apiVersion: v1

kind: Pod

spec:

tolerations:

– key: “foo”

operator: “Equal”

value: “bar”

effect: “NoSchedule”

整体的配置含义是,Pod能够容忍键值对为foo=bar的Taint,operator的含义是 Equal,表示等于操作

这时候我们回到搭建好的集群上,可以通过kubectl describe检查主节点的Taint字段

$ kubectl describe node master

Name:               master

Roles:              master

Taints:             node-role.kubernetes.io/master:NoSchedule

Master节点被默认加上了node-role.kubernetes.io/master:NoSchedule这样的污点,这个污点只有key,没有value

如果直接声明Pod在此节点上运行,可以直接使用Exists操作符,书写的yaml文件如下

apiVersion: v1

kind: Pod

spec:

tolerations:

– key: “foo”

operator: “Exists”

effect: “NoSchedule”

使用Exists操作符即可,说明只要存在key就行

当然,最简单的方式,就是直接删除这个Taint

kubectl taint nodes –all node-role.kuberntes.io/master-

这后面加上了一个 短横线 -,就意味着移除了所有node-role.kuberntes.io/master的Taint

这样就是一个简单的Kubernetes集群的搭建,有了这个原生的管理工具,我们就基本搭建了一个完全可用的Kubernetes,接下来是一下辅助性质的插件

Dashborad和远程存储

部署Dashboard

这是一个可以提供可视化Web界面的插件,对于可以直接连上谷歌镜像仓库,部署非常简单,直接

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc6/aio/deploy/recommended.yaml

但是对于国内用户,我们还是得考虑自定义镜像的方式去拉推镜像

wget https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml

拉取不到,可以考虑直接编写kubernetes-dashboard.yaml

kind: Service

apiVersion: v1

metadata:

labels:

k8s-app: kubernetes-dashboard

name: kubernetes-dashboard

namespace: kube-system

spec:

type: NodePort

ports:

– port: 443

targetPort: 8443

nodePort: 30001

selector:

k8s-app: kubernetes-dashboard

然后进行拉取国内的镜像源

docker pull anjia0532/google-containers.kubernetes-dashboard-amd64:v1.10.0

docker tag anjia0532/google-containers.kubernetes-dashboard-amd64:v1.10.0 k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.0

docker rmi anjia0532/google-containers.kubernetes-dashboard-amd64:v1.10.0

然后直接

kubectl apply -f kubernetes-dashboard.yaml

查看对应的Pods状态

最后部署一下容器的持久化存储

我们需要用数据卷 volume,将外部宿主机的目录或者文件挂载到容器的Mount Namesapce中,达到容器和宿主机共享这些目录或者文件的目的,容器的应用,也就是可以再这些数据卷内新建和写入文件

可是一台机器上启动的一个容器,无法看到其他机器上的容器在数据卷中写入的文件,这就是容器的无状态性

容器的持久化存储,是保存容器状态的重要手段,可以考虑在容器内挂载一个基于网络或者其他机制的远程数据卷,以分布式的方式保存在多个节点上,和当前的宿主机没有任何的绑定,这样,无论在哪个宿主机上启动了新的容器,都可以请求挂载指定的持久化存储卷,从而访问到数据卷内的内容,这就是持久化的含义

由于Kubernetes的松耦合的设计,绝大数存储项目,NFS Ceph,都可以为Kubernetes提供持久化存储能力,这次我们部署Rook

Rook其基于Cepth,不过不同于Ceph的简单封装,Rook在自己的实现中加入了水平扩展,迁移,灾难备份,监控等企业级功能,非常牛皮,其利用了很多诸如Operator,CRD等重要扩展特性,使得其变成了目前社区中基于Kuberntes API构建最完善最成熟容器存储插件

部署的方式,很简单,

$ 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

$ kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/cluster.yaml

部署完成后,可以看到Rook项目将自己的Pod放在自己管理的两个Namespace中

$ kubectl get pods -n rook-ceph-system

NAME                                  READY     STATUS    RESTARTS   AGE

rook-ceph-agent-7cv62                 1/1       Running   0          15s

rook-ceph-operator-78d498c68c-7fj72   1/1       Running   0          44s

rook-discover-2ctcv                   1/1       Running   0          15s

$ kubectl get pods -n rook-ceph

NAME                   READY     STATUS    RESTARTS   AGE

rook-ceph-mon0-kxnzh   1/1       Running   0          13s

rook-ceph-mon1-7dn2t   1/1       Running   0          2s

这样,在Kubernetes上创建的所有Pod都可以通过Persisent Volume PV和 Persistent Volume Claim PVC的方式,在容器中挂载Ceph提供的数据卷

而Rook项目,会负责将这些数据的生命周期管理,灾难备份等运维

其实Kuberetes原生,约等于 云原生这个概念,像是Rook Istio这样的项目,就是这个思路的典范

在本章中,我们从0开始,在Bare-metal环境下使用kubeadm部署了一个完整的kubernetes集群,这个集群,并使用Weave作为容器网络插件,Rook作为容器持久化存储插件,Dashboard插件提供了可视化的Web界面

整个集群的部署过程,靠着Kubeadm,提供的配置证书,二进制文件的准备,集群版本的管理,简化了不少

然后基于万物皆容器的思想,加上良好的扩展机制,插件的部署非常简便,只需要部署一个kubectl即可

我们接下来在部署的时候考虑,工作是否可以容器化

工作是否可以借助kubernetes API和可扩展机制来完成

那么两个思考题

1.其他的工具部署过Kubernetes吗?

2.kubernetes能够有效管理多少个节点

2.对于集群来说,我们将整个集群认为是一个大型的工作节点,那么集群内的可以分为多少个节点首先取决于集群的性能之和,比如我们的性能之和是32个CPU,128G内存,那么可以分为8个 4核16G的节点

发表评论

邮箱地址不会被公开。 必填项已用*标注