安装docker

设置存储库
[root@kubeadm ~]# yum install yum-utils -y
[root@kubeadm ~]# yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
安装DOCKER引擎
#安装最新版本
yum install docker-ce docker-ce-cli containerd.io

#安装指定版本,我这里安装18.09.9
[root@kubeadm ~]# yum install docker-ce-18.09.9 docker-ce-cli-18.09.9 containerd.io -y
[root@kubeadm ~]# systemctl start docker
[root@kubeadm ~]# systemctl enable docker

安装kubeadm,kubelet和kubectl

设置存储库
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
EOF
安装
yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
[root@kubeadm ~]# systemctl enable --now kubelet

关闭seliunx和防火墙

为了方便,这里直接关闭了seliunx和防火墙

[root@kubeadm ~]# sed -i s#=enforcing#=disabled#g /etc/selinux/config
[root@kubeadm ~]# setenforce 0

[root@kubeadm ~]# systemctl stop firewalld
[root@kubeadm ~]# systemctl disable firewalld

修改docker的cgroup driver与kubelet一致

cat <<EOF > /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF

#重启docker
systemctl restart docker

#查看修改后状态
docker info | grep Cgroup

部署 Kubernetes 的 Master 节点

#安装master节点最简单的就是:
kubeadm init

#这里稍微提高一下难度:通过配置文件来开启一些实验性功能,编写了一个给 kubeadm 用的 YAML 文件:
[root@kubeadm ~]# vim kubeadm.yaml
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v1.18.3
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"
[root@kubeadm ~]# kubeadm init --config kubeadm.yaml

执行kubeadm init后只需要几分钟就可以完成 Kubernetes Master 的部署了。部署完成后,kubeadm 会生成一行指令:

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 192.168.37.201:6443 --token iq6n9j.sna0v1xtx3z3grem \
    --discovery-token-ca-cert-hash sha256:712d83cf1641bc0417ce2a059ff966c7fd92f0287751d68f6980ce3b1ddf807a

执行一下上面kubeadm 提示我们第一次使用 Kubernetes 集群所需要的配置命令

[root@kubeadm ~]# mkdir -p $HOME/.kube
[root@kubeadm ~]# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@kubeadm ~]# chown $(id -u):$(id -g) $HOME/.kube/config

注:需要这些配置命令的原因是:Kubernetes 集群默认需要加密方式访问。所以,这几条命令,就是将刚刚部署生成的 Kubernetes 集群的安全配置文件,保存到当前用户的.kube 目录下,kubectl 默认会使用这个目录下的授权信息访问 Kubernetes 集群。

使用 kubectl get 命令来查看当前唯一一个节点的状态了:

[root@kubeadm ~]# kubectl get nodes
NAME      STATUS     ROLES    AGE    VERSION
kubeadm   NotReady   master   137m   v1.18.3

可以看到,这个 get 指令输出的结果里,Master 节点的状态是 NotReady,这是为什么呢?
在调试 Kubernetes 集群时,最重要的手段就是用 kubectl describe 来查看这个节点(Node)对象的详细信息、状态和事件(Event),我们来试一下:

[root@kubeadm ~]# kubectl describe node kubeadm
...
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 的工作空间

[root@kubeadm ~]# kubectl get pods -n kube-system
NAME                              READY   STATUS    RESTARTS   AGE
coredns-66bff467f8-dh5wj          0/1     Pending   0          148m
coredns-66bff467f8-lh8r7          0/1     Pending   0          148m
etcd-kubeadm                      1/1     Running   0          148m
kube-apiserver-kubeadm            1/1     Running   0          148m
kube-controller-manager-kubeadm   1/1     Running   0          148m
kube-proxy-9tkrs                  1/1     Running   0          148m
kube-scheduler-kubeadm            1/1     Running   0          148m

可以看到,CoreDNS 等依赖于网络的 Pod 都处于 Pending 状态,即调度失败。这当然是符合预期的:因为这个 Master 节点的网络尚未就绪。

部署网络插件

在 Kubernetes 项目“一切皆容器”的设计理念指导下,部署网络插件非常简单,只需要执行一句 kubectl apply 指令,以 Weave 为例:

kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

部暑完成后,我们过一两分钟再观察一下这个节点上各个系统 Pod 的状态:

[root@kubeadm ~]# kubectl get pods -n kube-system
NAME                              READY   STATUS    RESTARTS   AGE
coredns-66bff467f8-dh5wj          1/1     Running   0          3h6m
coredns-66bff467f8-lh8r7          1/1     Running   0          3h6m
etcd-kubeadm                      1/1     Running   0          3h6m
kube-apiserver-kubeadm            1/1     Running   0          3h6m
kube-controller-manager-kubeadm   1/1     Running   0          3h6m
kube-proxy-9tkrs                  1/1     Running   0          3h6m
kube-scheduler-kubeadm            1/1     Running   0          3h6m
weave-net-tms68                   2/2     Running   0          2m5s

Kubernetes 支持容器网络插件,使用的是一个名叫 CNI 的通用接口,它也是当前容器网络的事实标准,市面上的所有容器网络开源项目都可以通过 CNI 接入 Kubernetes,比如 Flannel、Calico、Canal、Romana 等等,它们的部署方式也都是类似的“一键部署”。

至此,Kubernetes 的 Master 节点就部署完成了。如果你只需要一个单节点的 Kubernetes,现在你就可以使用了。

部署 Kubernetes 的 Worker 节点

1、按照前面的文档安装docker和kubeadm,kubelet,kubectl

2、执行部署 Master 节点时生成的 kubeadm join 指令

kubeadm join 192.168.37.201:6443 --token iq6n9j.sna0v1xtx3z3grem \
    --discovery-token-ca-cert-hash sha256:712d83cf1641bc0417ce2a059ff966c7fd92f0287751d68f6980ce3b1ddf807a

通过 Taint/Toleration 调整 Master 执行 Pod 的策略

默认情况下 Master 节点是不允许运行用户 Pod 的。而 Kubernetes 做到这一点,依靠的是 Kubernetes 的 Taint/Toleration 机制。

原理非常简单:一旦某个节点被加上了一个 Taint,即被“打上了污点”,那么所有 Pod 就都不能在这个节点上运行,因为 Kubernetes 的 Pod 都有“洁癖”。

除非,有个别的 Pod 声明自己能“容忍”这个“污点”,即声明了 Toleration,它才可以在这个节点上运行。
其中,为节点打上“污点”(Taint)的命令是:

$ kubectl taint nodes node1 foo=bar:NoSchedule

这时,该 node1 节点上就会增加一个键值对格式的 Taint,即:foo=bar:NoSchedule。其中值里面的 NoSchedule,意味着这个 Taint 只会在调度新 Pod 时产生作用,而不会影响已经在 node1 上运行的 Pod,哪怕它们没有 Toleration。

那么 Pod 又如何声明 Toleration 呢?

我们只要在 Pod 的.yaml 文件中的 spec 部分,加入 tolerations 字段即可:

apiVersion: v1
kind: Pod
...
spec:
  tolerations:
  - key: "foo"
    operator: "Equal"
    value: "bar"
    effect: "NoSchedule"

这个 Toleration 的含义是,这个 Pod 能“容忍”所有键值对为 foo=bar 的 Taint( operator: “Equal”,“等于”操作)。现在回到我们已经搭建的集群上来。

这时,如果你通过 kubectl describe 检查一下 Master 节点的 Taint 字段,就会有所发现了:


$ kubectl describe node kubeadm

Name:               kubeadm
Roles:              master
Taints:             node-role.kubernetes.io/master:NoSchedule

可以看到,Master 节点默认被加上了 node-role.kubernetes.io/master:NoSchedule这样一个“污点”,其中“键”是node-role.kubernetes.io/master,而没有提供“值”

此时,你就需要像下面这样用“Exists”操作符(operator: “Exists”,“存在”即可)来说明,该 Pod 能够容忍所有以 foo 为键的 Taint,才能让这个 Pod 运行在该 Master 节点上:


apiVersion: v1
kind: Pod
...
spec:
  tolerations:
  - key: "foo"
    operator: "Exists"
    effect: "NoSchedule"

当然,如果你就是想要一个单节点的 Kubernetes,删除这个 Taint 才是正确的选择:

$ kubectl taint nodes --all node-role.kubernetes.io/master-

或者,只是删除master节点taint,让master节点可以运行pod

$ kubectl taint nodes kubeadm node-role.kubernetes.io/master-

部署容器存储插件

Rook 项目是一个基于 Ceph 的 Kubernetes 存储插件(它后期也在加入对更多存储实现的支持)。不过,不同于对 Ceph 的简单封装,Rook 在自己的实现中加入了水平扩展、迁移、灾难备份、监控等大量的企业级功能,使得这个项目变成了一个完整的、生产级别可用的容器存储插件。

git clone --single-branch --branch {{ branchName }} https://github.com/rook/rook.git
cd rook/cluster/examples/kubernetes/ceph
kubectl create -f crds.yaml -f common.yaml -f operator.yaml
kubectl create -f cluster.yaml

在部署完成后,你就可以看到 Rook 项目会将自己的 Pod 放置在由它自己管理的Namespace中

[root@kubeadm ~]# kubectl get pods -n rook-ceph -o wide
NAME                                               READY   STATUS      RESTARTS   AGE    IP               NODE      NOMINATED NODE   READINESS GATES
csi-cephfsplugin-fqbqs                             3/3     Running     0          125m   192.168.37.202   kube02    <none>           <none>
csi-cephfsplugin-lrdn4                             3/3     Running     0          125m   192.168.37.203   kube03    <none>           <none>
csi-cephfsplugin-m2h4m                             3/3     Running     0          92m    192.168.37.201   kubeadm   <none>           <none>
csi-cephfsplugin-provisioner-7459667b67-nntxx      5/5     Running     0          125m   10.36.0.4        kube03    <none>           <none>
csi-cephfsplugin-provisioner-7459667b67-vzqlb      5/5     Running     0          125m   10.44.0.2        kube02    <none>           <none>
csi-rbdplugin-cn4zk                                3/3     Running     0          125m   192.168.37.203   kube03    <none>           <none>
csi-rbdplugin-mvddz                                3/3     Running     0          125m   192.168.37.202   kube02    <none>           <none>
csi-rbdplugin-provisioner-7cbb778994-6tn9h         6/6     Running     0          125m   10.44.0.4        kube02    <none>           <none>
csi-rbdplugin-provisioner-7cbb778994-htgch         6/6     Running     0          125m   10.36.0.3        kube03    <none>           <none>
csi-rbdplugin-tv7sl                                3/3     Running     0          92m    192.168.37.201   kubeadm   <none>           <none>
rook-ceph-crashcollector-kube02-84864475db-f7nt4   1/1     Running     0          92m    10.44.0.5        kube02    <none>           <none>
rook-ceph-crashcollector-kube03-554dd78cbc-sgt6j   1/1     Running     0          88m    10.36.0.8        kube03    <none>           <none>
rook-ceph-crashcollector-kubeadm-c5d646cc6-lfh5z   1/1     Running     0          91m    10.32.0.7        kubeadm   <none>           <none>
rook-ceph-mgr-a-7586874869-wjl5n                   1/1     Running     1          88m    10.36.0.6        kube03    <none>           <none>
rook-ceph-mon-a-855dc46d89-ktk4r                   1/1     Running     0          92m    10.44.0.3        kube02    <none>           <none>
rook-ceph-mon-b-b47dbffcd-zpjdj                    1/1     Running     0          92m    10.36.0.5        kube03    <none>           <none>
rook-ceph-mon-c-76bcbdf47f-v7hwt                   1/1     Running     0          91m    10.32.0.5        kubeadm   <none>           <none>
rook-ceph-operator-567d7945d6-mrr26                1/1     Running     0          147m   10.36.0.1        kube03    <none>           <none>
rook-ceph-osd-0-76fcb76b87-znrcn                   1/1     Running     2          88m    10.44.0.6        kube02    <none>           <none>
rook-ceph-osd-1-6d4cf6d9fb-6c4jj                   1/1     Running     0          88m    10.36.0.9        kube03    <none>           <none>
rook-ceph-osd-2-5556589849-9q6vd                   1/1     Running     0          80m    10.32.0.6        kubeadm   <none>           <none>
rook-ceph-osd-prepare-kube02-sfvq7                 0/1     Completed   0          88m    10.44.0.5        kube02    <none>           <none>
rook-ceph-osd-prepare-kube03-j769j                 0/1     Completed   0          88m    10.36.0.8        kube03    <none>           <none>
rook-ceph-osd-prepare-kubeadm-vj4vl                0/1     Completed   0          88m    10.32.0.6        kubeadm   <none>           <none>
rook-discover-nz6gd                                1/1     Running     0          136m   10.36.0.2        kube03    <none>           <none>
rook-discover-qnspv                                1/1     Running     0          136m   10.44.0.1        kube02    <none>           <none>
rook-discover-r89xz                                1/1     Running     0          92m    10.32.0.4        kubeadm   <none>           <none>

关于kubernetes存储这一块后面将详细介绍。

创建第一个容器化应用

Kubernetes 跟 Docker 等很多项目最大的不同,就在于它不推荐你使用命令行的方式直接运行容器(虽然 Kubernetes 项目也支持这种方式,比如:kubectl run),而是希望你用 YAML 文件的方式,即:把容器的定义、参数、配置,统统记录在一个 YAML 文件中,然后用这样一句指令把它运行起来:

$ kubectl create -f 我的配置文件

跑nignx pod,例:

[root@kubeadm ~]# cat nginx.yaml
apiVersion: apps/v1            # for versions before 1.9.0 use apps/v1beta2
kind: Deployment        #Deployment定义多副本应用的对象,还负责在 Pod 定义发生变化时,对每个副本进行滚动更新
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2           #Pod 副本个数 (spec.replicas) 是:2,集群内永远保持有2个副本
  template:             #定义了一个pod模板,这个模板里面只有一个容器,这个容器镜像是nginx1.18
    metadata:           #API 对象的“标识”,即元数据,也是我们从 Kubernetes 里找到这个对象的主要依据
      labels:           #可以通过这个 Labels 字段从 Kubernetes 中过滤出它所关心的被控制对象。
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.18.0
        ports:
        - containerPort: 80

注:在上面这个 YAML 文件中,Deployment 会把所有正在运行的、携带“app: nginx”标签的 Pod 识别为被管理的对象,并确保这些 Pod 的总数严格等于两个。而这个过滤规则的定义,是在 Deployment 的“spec.selector.matchLabels”字段。我们一般称之为:Label Selector。一个 Kubernetes 的 API 对象的定义,大多可以分为 Metadata 和 Spec 两个部分。前者存放的是这个对象的元数据,对所有 API 对象来说,这一部分的字段和格式基本上是一样的;而后者存放的,则是属于这个对象独有的定义,用来描述它所要表达的功能

查看这个yaml运行起来的状态是否与预期的一致:

[root@kubeadm ~]# kubectl get pods -l app=nginx
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-75ddd4d4b4-mdn6f   1/1     Running   0          17m
nginx-deployment-75ddd4d4b4-tnk6v   1/1     Running   0          17m

还可以使用 kubectl describe 命令,查看一个 API 对象的细节,比如

[root@kubeadm ~]# kubectl describe pod nginx-deployment-75ddd4d4b4-mdn6f
Name:         nginx-deployment-75ddd4d4b4-mdn6f
...
...
...
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-wzkjc (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-wzkjc:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-wzkjc
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  24m   default-scheduler  Successfully assigned default/nginx-deployment-75ddd4d4b4-mdn6f to kube03
  Normal  Pulling    24m   kubelet, kube03    Pulling image "nginx:1.18.0"
  Normal  Pulled     22m   kubelet, kube03    Successfully pulled image "nginx:1.18.0"
  Normal  Created    22m   kubelet, kube03    Created container nginx
  Normal  Started    22m   kubelet, kube03    Started container nginx

如果我们要对这个 Nginx 服务进行升级,把它的镜像版本从 1.18 升级为 1.19,要怎么做呢?
只需要更改一下yaml文件中容器的版本:

[root@kubeadm ~]# vi nginx.yaml
...
    spec:
      containers:
      - name: nginx
        image: nginx:1.19.0 # Update the version of nginx from 1.14.2 to 1.16.1
        ports:
        - containerPort: 80

更新:

[root@kubeadm ~]# kubectl apply -f nginx.yaml

注:作为用户,你不必关心当前的操作是创建,还是更新,你执行的命令始终是 kubectl apply,而 Kubernetes 则会根据 YAML 文件的内容变化,自动进行具体的处理。

在这个 nginx Deployment 中声明一个 Volume
[root@kubeadm ~]# vi nginx.yaml
...
    spec:
      containers:
      - name: nginx
        image: nginx:1.19.0 # Update the version of nginx from 1.14.2 to 1.16.1
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: "/usr/share/nginx/html"     #容器上要被挂载的目录
          name: nginx-vol
      volumes:
      - name: nginx-vol
#      emptyDir: {}      #隐式挂载,在宿主机上创建一个临时目录,这个目录将来就会被绑定挂载到容器所声明的Volume 目录上。
        hostPath:
          path: "/var/data"                      #挂载到物理主机上的目录

在上述修改完成后,我们还是使用 kubectl apply 指令,更新这个 Deployment:

[root@kubeadm ~]# kubectl apply -f nginx.yaml

可以通过 kubectl get 指令,查看两个 Pod 被逐一更新的过程:

[root@kubeadm ~]# kubectl get pods -l app=nginx
NAME                                READY   STATUS        RESTARTS   AGE
nginx-deployment-5b5bc57478-frdqt   1/1     Running       0          3s
nginx-deployment-5b5bc57478-hwxkq   1/1     Running       0          9s
nginx-deployment-7b446869f-98rjb    0/1     Terminating   0          14m
nginx-deployment-7b446869f-k224v    0/1     Terminating   0          15m
[root@kubeadm ~]# kubectl get pods -l app=nginx
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-5b5bc57478-frdqt   1/1     Running   0          3m35s
nginx-deployment-5b5bc57478-hwxkq   1/1     Running   0          3m41s

以使用 kubectl exec 指令,进入到这个 Pod 当中(即容器的 Namespace 中)查看这个 Volume 目录:

[root@kubeadm ~]# touch /var/data/test.log          #在这个目录上创建一个文件,观察容器被挂载目录内容变化
[root@kubeadm ~]# kubectl exec -it nginx-deployment-5b5bc57478-hwxkq -- /bin/bash
root@nginx-deployment-5b5bc57478-hwxkq:/# ls /usr/share/nginx/html/
test.log
root@nginx-deployment-5b5bc57478-hwxkq:/# exit
从 Kubernetes 集群中删除yaml文件所建的pod
kubectl delete -f nginx.yaml
给节点打标签
kubectl label nodes kubeadm node=kubeadm
kubectl get node --show-labels
文档更新时间: 2020-12-09 10:10   作者:子木