1. Kubernetes实战
本实战案例拟使用Kubernetes平台部署一个WordPress博客系统,具体配置与介绍如下:
需要用到两个容器镜像,分别为wordpress:4.8-apache和mysql:5.6;使用NFS创建PV作为数据库与WordPress的后端存储;使用Secret生成器管理密码;最后使用yaml文件编排部署博客系统。
基础准备:
IP地址 | 主机名 | 节点 |
192.168.200.10 | master | 容器master节点 |
192.168.200.11 | node | 容器worker节点 |
一、基础环境安装与配置
(一)安装与配置NFS
在master节点上安装NFS所需要的软件及依赖,命令如下:
[root@k8s-master-node1 ~]# yum install -y nfs-utils rpcbind
安装完之后,启动RPC与NFS服务,命令如下:
[root@k8s-master-node1 ~]#systemctl start rpcbind
[root@k8s-master-node1 ~]# systemctl enable rpcbind
[root@k8s-master-node1 ~]#systemctl start nfs
[root@k8s-master-node1 ~]# systemctl enable nfs
创建/home/pvdata/mysql,/home/pvdata/wordpress两个目录,作为NFS的共享目录,然后赋予权限,命令如下:
[root@k8s-master-node1 ~]# mkdir -p /home/pvdata/mysql
[root@k8s-master-node1 ~]# mkdir -p /home/pvdata/wordpress
[root@k8s-master-node1 ~]# chmod 777 /home/pvdata/mysql
[root@k8s-master-node1 ~]# chmod 777 /home/pvdata/wordpress
修改NFS配置文件/etc/exports,将创建的两个目录进行共享,命令如下:
[root@k8s-master-node1 ~]# vi /etc/exports
[root@k8s-master-node1 ~]# cat /etc/exports
#在/etc/exports配置文件中,添加如下两行
/home/pvdata/mysql 192.168.200.0/24(rw,no_root_squash,no_all_squash,sync,anonuid=501,anongid=501)
/home/pvdata/wordpress 192.168.200.0/24(rw,no_root_squash,no_all_squash,sync,anonuid=501,anongid=501)
生效NFS配置文件,并查看分享的目录,命令如下:
[root@k8s-master-node1 ~]# exportfs -r
[root@k8s-master-node1 ~]# showmount -e 192.168.200.10
Export list for 192.168.200.10:
/home/pvdata/wordpress 192.168.200.0/24
/home/pvdata/mysql 192.168.200.0/24
可以看创建的两个目录被共享了,NFS配置完毕。
(二)创建pv
在创建PV之前,首先创建一个工作目录,命令如下:
[root@k8s-master-node1 ~]# mkdir wordpress
[root@k8s-master-node1 ~]# cd wordpress/
[root@k8s-master-node1 wordpress]#
在wordpress目录下创建mysql-persistent-storage.yaml文件,该文件用来创建数据库使用的pv,文件内容如下:
[root@k8s-master-node1 wordpress]# cat mysql-persistent-storage.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-persistent-storage
spec:
capacity:
storage: 2Gi
accessModes: ["ReadWriteMany","ReadWriteOnce"]
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /home/pvdata/mysql/
server: 192.168.200.10
在wordpress目录下继续创建wordpress-persistent-storage.yaml文件,该文件用来创建WordPress使用的pv,文件内容如下:
[root@k8s-master-node1 wordpress]# cat wordpress-persistent-storage.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: wordpress-persistent-storage
spec:
capacity:
storage: 2Gi
accessModes: ["ReadWriteMany","ReadWriteOnce"]
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /home/pvdata/wordpress/
server: 192.168.200.10
编辑好yaml文件后,创建pv卷,命令如下:
[root@k8s-master-node1 wordpress]# kubectl apply -f mysql-persistent-storage.yaml
persistentvolume/mysql-persistent-storage created
[root@k8s-master-node1 wordpress]# kubectl apply -f wordpress-persistent-storage.yaml
persistentvolume/wordpress-persistent-storage created
创建完pv后,查看pv,命令如下:
[root@k8s-master-node1 wordpress]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mysql-persistent-storage 2Gi RWO,RWX Recycle Available 11s
wordpress-persistent-storage 2Gi RWO,RWX Recycle Available 5s
可以看到两个pv都是Available的状态,pv创建成功。
(三)设置密码生成器
在/root/wordpress目录下创建kustomization.yaml文件,并在文件中添加一个Secret生成器,具体命令如下:
[root@k8s-master-node1 wordpress]# cat kustomization.yaml
secretGenerator:
- name: mysql-pass
literals:
- password=123456ok
(四)编辑yaml文件
首先在/root/wordpress目录下创建一个mysql-deployment.yaml文件,用来部署数据库服务,MySQL容器将PersistentVolume挂载在/var/lib/mysql。MYSQL_ROOT_PASSWORD环境变量设置来自Secret的数据库密码,mysql-deployment.yaml中的具体内容如下所示:
[root@k8s-master-node1 wordpress]# cat mysql-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
编辑好mysql-deployment.yaml之后,数据库的部署就告一段落了。还需要创建一个wordpress-deployment.yaml用来部署WordPress应用,WordPress容器网站数据文件位于/var/www/html。WORDPRESS_DB_HOST环境变量集上面定义了MySQL的Service名称,WordPress将通过Service访问数据库。WORDPRESS_DB_PASSWORD密码为Secret生成的数据库密码。具体文件内容如下:
[root@k8s-master-node1 wordpress]# cat wordpress-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
nodePort: 31000
selector:
app: wordpress
tier: frontend
type: NodePort
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim
可以注意到,此次部署的WordPress版本为wordpress:4.8-apache,该镜像在配套的软件包中提供,可自行上载到master节点。编辑完之后,保存退出,将以上两个文件补充到kustomization.yaml文件中,命令如下:
[root@k8s-master-node1 wordpress]# vi kustomization.yaml
[root@k8s-master-node1 wordpress]# cat kustomization.yaml
secretGenerator:
- name: mysql-pass
literals:
- password=123456ok
#在配置文件中添加如下代码
resources:
- mysql-deployment.yaml
- wordpress-deployment.yaml
kustomization.yaml包含用于部署WordPress网站的所有资源以及MySQL数据库。
二、部署应用
在/root/wordpress目录下,通过如下方式进行部署,命令如下:
[root@k8s-master-node1 wordpress]# kubectl apply -k ./
secret/mysql-pass-ccht6ddg2m created
service/wordpress-mysql created
service/wordpress created
deployment.apps/wordpress-mysql created
deployment.apps/wordpress created
persistentvolumeclaim/mysql-pv-claim created
persistentvolumeclaim/wp-pv-claim created
可以看到各类资源都被创建了,包括Service、Deployment、PVC等。查看Secret密码是否存在,命令如下:
[root@k8s-master-node1 wordpress]# kubectl get secret
NAME TYPE DATA AGE
mysql-pass-ccht6ddg2m Opaque 1 5h2m
可以看到密码被成功创建,然后验证pv是否被成功挂载了,命令如下:
[root@k8s-master-node1 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pv-claim Bound mysql-persistent-storage 2Gi RWO,RWX 6d9h
wp-pv-claim Bound wordpress-persistent-storage 2Gi RWO,RWX 6d9h
可以看到创建的pv被挂载了。最后验证Service是否成功创建了,命令如下:
[root@k8s-master-node1 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8d
wordpress NodePort 10.96.236.54 <none> 80:31000/TCP 6d9h
wordpress-mysql ClusterIP None <none> 3306/TCP 6d9h
也可以看到Service成功创建。在创建成功后,进行应用的验证。
通过浏览器访问http://192.168.200.10:31000
根据提示选择语言,并单击“Continue”按钮,进入配置信息界面。
根据要求填写相关信息,然后单击下方的“Install WordPress”按钮,进行安装,然后就可以进行登录了,使用自己设置的用户名和密码进行登录。
登录成功,部署wordpress应用成功。
2. Kubernetes运维
基础准备:
IP地址 | 主机名 | 节点 |
192.168.200.10 | master | 容器master节点 |
192.168.200.11 | node | 容器worker节点 |
使用这两台云主机作为实验基础环境。使用Kubeeasy工具先安装Kubernetes平台与Harbor平台。
一、Node的隔离与恢复
在硬件升级、硬件维护等情况下,需要将某些Node隔离。使用kubectl cordon <node_name>命令可禁止Pod调度到该节点上,在其上运行的Pod并不会自动停止,管理员需要手动停止在该Node上运行的Pod。因为此处使用的实操环境是一台,尝试停止将master节点隔离(master也是一个node节点),命令如下:
[root@k8s-master-node1 ~]# kubectl cordon k8s-master-node1
node/k8s-master-node1 cordoned
查看Node的状态,命令如下:
NAME STATUS ROLES AGE VERSION
k8s-master-node1 Ready,SchedulingDisabled control-plane,master,worker 11d v1.22.1
k8s-worker-node1 Ready worker 11d v1.22.1
可以观察到在node的状态中增加了一项SchedulingDisabled,对于后续创建的Pod,系统将不会再向该Node进行调度。
可以通过kubectl uncordon命令可完成对Node的恢复。命令如下:
[root@k8s-master-node1 ~]# kubectl uncordon k8s-master-node1
node/k8s-master-node1 uncordoned
再次查看Node信息,命令如下:
[root@k8s-master-node1 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master-node1 Ready control-plane,master,worker 11d v1.22.1
k8s-worker-node1 Ready worker 11d v1.22.1
可以看到Node节点已恢复调度,STATUS又变成了Ready,允许Pod调度到该节点上。
通过kubectl drain <node_name>命令可实现对Node节点的驱逐,该命令会删除该节点上的所有Pod(DaemonSet除外)。
二、Pod动态扩容和缩放
在实际生产系统中,经常会遇到某个服务需要扩容的场景,也可能会遇到由于资源紧张或者工作负载降低而需要减少服务实例数量的场景。此时可以利用kubectl scale deployment命令来完成这些任务。
以Nginx Deployment为例,创建一个Nginx的Deployment,副本数为1,命令如下(在做该案例时,为了避免Kubernetes实战中创建的资源影响,可自行删除):
[root@k8s-master-node1 ~]# kubectl create deployment nginx --image=192.168.200.43/library/nginx:latest
deployment.apps/nginx created
查看Pod和Deployment,命令如下:
[root@k8s-master-node1 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-67cfb9b95d-sxt58 1/1 Running 0 3s
[root@k8s-master-node1 ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 8s
通过使用scale命令将Nginx Deployment控制的Pod副本数量从初始的1更新为5。命令如下:
[root@k8s-master-node1 ~]# kubectl scale deployment nginx --replicas=5
deployment.apps/nginx scaled
更新副本后,继续查看Pod数量,命令如下:
[root@k8s-master-node1 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-674ff86d-7xgct 1/1 Running 0 66s
nginx-674ff86d-9mzvx 1/1 Running 0 66s
nginx-674ff86d-dw6mb 1/1 Running 0 66s
nginx-674ff86d-n6jqq 1/1 Running 0 4m33s
nginx-674ff86d-ztm8j 1/1 Running 0 66s
可以看到此时Nginx的Pod数量为5。调整副本数成功。如果需要将副本的数量调小,可以将--replicas设置为比当前Pod副本数量更小的数字,系统将会“杀掉”一些运行中的Pod,即可实现应用集群缩容。例如将Nginx的Pod副本数调整为3,命令如下:
[root@k8s-master-node1 ~]# kubectl scale deployment nginx --replicas=3
deployment.apps/nginx scaled
查看Pod数量,命令如下:
[root@k8s-master-node1 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-674ff86d-9mzvx 1/1 Running 0 4m56s
nginx-674ff86d-dw6mb 1/1 Running 0 4m56s
nginx-674ff86d-n6jqq 1/1 Running 0 8m23s
可以看到Pod副本数减少为3,验证Pod动态扩容和缩放实验成功。
三、将Pod调度到指定的Node
Kubernetes的Scheduler服务(kube-scheduler进程)负责实现Pod的调度,整个调度过程通过执行一系列复杂的算法最终为每个Pod计算出一个最佳的目标节点,这一过程是自动完成的,用户无法知道Pod最终会被调度到哪个节点上。有时可能需要将Pod调度到一个指定的Node上。此时,可以通过Node的标签(Label)和Pod的nodeSelector属性相匹配,来达到上述目的。
Label(标签)作为用户可灵活定义的对象属性,在已创建的对象上,仍然可以随时通过kubectl label命令对其进行增加、修改、删除等操作。使用kubectl label给node打标签的用法如下:
# kubectl label nodes <node-name> <label-key>=<label-value>
该案例需要起码两个Node节点。使用当前安装Kubernetes平台的master和node节点(主机名分别为k8s-master-node1和k8s-worker-node1),然后进行下述操作。
为node节点打上一个project=gcxt的标签。命令如下:
[root@k8s-master-node1 ~]# kubectl label nodes k8s-worker-node1 project=gcxt
node/k8s-worker-node1 labeled
如果想删除Label,只需要在命令行最后指定Label的key名,并加一个减号即可。
[root@k8s-master-node1 ~]# kubectl label nodes k8s-worker-node1 project-
node/k8s-worker-node1 labeled
在Pod中加入nodeSelector定义,示例如下。
[root@k8s-master-node1 ~]# cat nginx.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx-gcxt
labels:
name: nginx-gcxt
spec:
replicas: 1
selector:
name: nginx-gcxt
template:
metadata:
labels:
name: nginx-gcxt
spec:
containers:
- name: nginx-gcxt
image: nginx:latest
- containerPort: 80
nodeSelector:
project: gcxt
运行kubectl apply -f命令创建Pod,scheduler就会将该Pod调度到拥有project=gcxt标签的Node上去。
[root@k8s-master-node1 ~]# kubectl apply -f nginx.yaml
replicationcontroller/nginx-gcxt created
查看Pod的详细信息。命令如下:
[root@k8s-master-node1 ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-gcxt-hdt5x 1/1 Running 0 14s 10.24.9.2 k8s-worker-node1 <none> <none>
可以看到,Pod已成功调度到指定的Node节点。
四、应用滚动升级
当集群中的某个服务需要升级时,需要停止目前与该服务相关的所有Pod,然后重新拉取镜像并启动。如果集群规模比较大,这个工作就变成了一个挑战。如果采取先全部停止,然后逐步升级的方式,会导致较长时间的服务不可用。Kubernetes提供了rolling-update(滚动升级)功能来解决上述问题。
滚动升级通过执行kubectl rolling-update命令一键完成,该命令创建了一个新的Deployment,然后自动控制旧的Deployment中的Pod副本数量逐渐减少到0,同时新的Deployment中的Pod副本数量从0逐步增加到目标值,最终实现了Pod的升级。
注意:系统要求新的Deployment需要与旧的Deployment在相同的命名空间(Namespace)内,即不能把别人的资产偷偷转移到自家名下。
下面的示例在第一次部署时使用httpd:2.2.31,然后使用滚动升级更新到httpd:2.2.32。
首先定义yaml文件,在/root目录下创建httpd.yaml文件,文件内容如下所示:
[root@k8s-master-node1 ~]# vi httpd.yaml
[root@k8s-master-node1 ~]# cat httpd.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd
spec:
selector:
matchLabels:
app: httpd
replicas: 3
template:
metadata:
labels:
app: httpd
spec:
containers:
- name: httpd
image: httpd:2.2.31
ports:
- containerPort: 80
创建httpd的Deployment,命令如下:
[root@k8s-master-node1 ~]# kubectl apply -f httpd.yaml
deployment.apps/httpd created
查看Pod和Deployment信息,命令如下:
[root@k8s-master-node1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
httpd-745bddc8fb-7h9cr 1/1 Running 0 9m22s
httpd-745bddc8fb-h2s2p 1/1 Running 0 9m22s
httpd-745bddc8fb-wtx8r 1/1 Running 0 9m22s
[root@k8s-master-node1 ~]# kubectl get deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
httpd 3/3 3 3 16m httpd httpd:2.2.31 app=httpd
可以看到images的版本是2.2.31,修改httpd.yaml文件,把配置文件中的httpd:2.2.31改为httpd:2.2.32,如下所示:
[root@master ~]# vi httpd.yaml
[root@master ~]# cat httpd.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd
spec:
selector:
matchLabels:
app: httpd
replicas: 3
template:
metadata:
labels:
app: httpd
spec:
containers:
- name: httpd
image: httpd:2.2.32
ports:
- containerPort: 80
再次启动Deployment,命令如下:
[root@k8s-master-node1 ~]# kubectl apply -f httpd.yaml
deployment.apps/httpd configured
再次查看Deployment的信息,命令如下:
[root@k8s-master-node1 ~]# kubectl get deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
httpd 3/3 3 3 16m httpd httpd:2.2.32 app=httpd
可以看到当前的httpd版本为2.2.32,查看Deployment的详细信息,命令如下:
[root@k8s-master-node1 ~]# kubectl describe deployment httpd
Name: httpd
Namespace: default
CreationTimestamp: Mon, 27 Sep 2021 11:20:28 -0400
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 2
Selector: app=httpd
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=httpd
Containers:
httpd:
Image: httpd:2.2.32
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: httpd-55d897fbfc (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 10m deployment-controller Scaled up replica set httpd-745bddc8fb to 3
Normal ScalingReplicaSet 18s deployment-controller Scaled up replica set httpd-55d897fbfc to 1
Normal ScalingReplicaSet 16s deployment-controller Scaled down replica set httpd-745bddc8fb to 2
Normal ScalingReplicaSet 16s deployment-controller Scaled up replica set httpd-55d897fbfc to 2
Normal ScalingReplicaSet 14s deployment-controller Scaled down replica set httpd-745bddc8fb to 1
Normal ScalingReplicaSet 14s deployment-controller Scaled up replica set httpd-55d897fbfc to 3
Normal ScalingReplicaSet 13s deployment-controller Scaled down replica set httpd-745bddc8fb to 0
上面的日志信息就描述了滚动升级的过程。