我们要清楚,在 K8s 中有两种创建资源的方式:
(1)命令行方式:如 kubectl create ...
(2)配置文件方式:如 kubectl apply -f nginx.yml
命令行方式:
# 创建Seployment资源
kubectl create deployment nginx --image=nginx:1.20.0 --replicas=2
# 使用Service将Pod暴露出去
kubectl expose deployment nginx --port=8090 --target-port=80 --type=NodePort
配置文件方式:
配置文件采用的是 YAML 格式。
vim nginx.yml
apiVersion: apps/v1 # 指定deployment的api版本
kind: Deployment # 指定创建资源的角色/类型
metadata: # 指定Deployment的元数据
name: nginx # 创建名为nginx的Deployment
labels: # 指定Deployment的标签(可自定义多个),这里的标签不需要与任何地方的标签匹配,根据实际场景随意自定义即可
app: demo
spec: # Deployment的资源规格
replicas: 2 # Deployment将创建2个Pod副本(默认为 1)
selector: # 匹配标签选择器,定义Deployment如何查找要管理的Pod,因此这里必须与Pod的template模板中定义的标签保持一致
matchLabels:
app: demo
template: # 指定Pod模板
metadata: # 指定Pod的元数据
labels: # 指定Pod的标签(可自定义多个)
app: demo
spec: # Pod的资源规格
containers: # 指定Pod运行的容器信息
- name: nginx # 指定Pod中运行的容器名
image: nginx:1.20.0 # 指定Pod中运行的容器镜像与版本(不指定镜像版本号则默认为latest)
ports:
- containerPort: 80 # 指定容器的端口(即Nginx默认端口)
说明:对应资源的apiVersion须在kubectl api-versions中,如下图所示。
kubectl apply -f nginx.yml
查看 Deployment 上线状态
kubectl get deployment
# 字段说明
# NAME:列出namespace中Deployment的名称(不指定namespace则默认为Default)
# READY:显示应用程序的可用的副本数。显示的模式是“就绪个数/期望个数”
# UP-TO-DATE:为了达到期望状态已经更新的副本数
# AVAILABLE:显示应用可供用户使用的副本数
# AGE:显示应用程序运行的总时间
查看 Deployment 创建的 ReplicaSet
kubectl get rs
kubectl get replicaset
# 字段说明
# NAME:列出namespace中ReplicaSet的名称(不指定namespace则默认为Default)
# DESIRED:表示期望状态,显示应用的期望副本个数,即在创建Deployment时所定义的值
# CURRENT:显示当前运行状态中的副本个数
# READY:显示应用中有多少副本可以为用户提供服务
# AGE:显示应用已经运行的总时间
注意:ReplicaSet 的名称始终被格式化为 [Deployment名称]-[哈希]。其中的哈希字符串与 ReplicaSet 上的 pod-template-hash 标签一致。
# 查看ReplicaSet的标签
kubectl get replicaset --show-labels
我们都知道 Pod 副本通过 ReplicaSet 管理,而 ReplicaSet 又是创建于 Deployment,那为什么不直接通过 Deployment 来直接管理,而是 ReplicaSet呢?
原因是每个控制器的标签或者选择算符不能与其他控制器(包括其他 Deployment 和 StatefulSet)重叠,那 K8s 是如何保证各控制器之间标签的唯一性呢?就是通过 ReplicaSet,因为 Pod 的 PodTemplate
标签就是通过 ReplicaSet 进行哈希处理,如下图,可看到 Pod 的 PodTemplate 标签值,此标签可确保了 Deployment 的子 ReplicaSets 不发生重叠现象。
所谓的伸缩容就是在线增加或减少 Pod 的副本数(只须修改 replicas 的值即可,大于当前值为伸,小于当前值为缩)。
1、修改配置文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: demo
spec:
replicas: 5
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx:1.20.0
ports:
- containerPort: 80
2、执行 kubectl apply
kubectl apply -f nginx.yml
3、查看 Pod 运行状态
kubectl get pod -o wide
出于安全考虑,默认情况下 K8s 不会将 Pod 调度到 master 节点,如果希望将 k8s-master
节点也作为 work 节点,可执行以下命令:
kubectl taint node k8s-master node-role.kubernetes.io/master-
再新增一个副本,此时可看到新增的这个 Pod 已经被调度到 Master 节点上了
如果要恢复为原来的 Master Only,执行以下语句即可:
kubectl taint node k8s-master node-role.kubernetes.io/master="":NoSchedule
需要注意的是,动态伸缩容不会触发上线动作,仅当 Deployment Pod 模板(即 .spec.template
)发生改变时,例如模板的标签或容器镜像被更新,才会触发 Deployment 上线,以下为一个更新镜像版本的触发上线案例。
kubectl set image deployment.v1.apps/nginx nginx=nginx:1.20.2
# 或使用下面的命令:
kubectl set image deployment/nginx nginx=nginx:1.20.2
# 或使用下面的命令:直接编辑修改镜像版本即可
kubectl edit deployment/nginx
查看上线状态:
看看 replicaset 状态:
可看到 Deployment 通过创建新的 ReplicaSet 并将其扩容到 2 个副本并将旧 ReplicaSet 缩容到 0 个副本完成了 Pod 的更新操作。
Deployment 可确保在更新时仅关闭一定数量的 Pod。默认情况下,它确保至少所需 Pod 的 75% 处于运行状态(最大不可用比例为 25%)。 默认情况下,它可确保启动的 Pod 个数比期望个数最多多出 125%(最大峰值 25%)。
仔细查看下图的 Pod 变换情况 ,其流程是先创建一个新的 Pod,然后再删除旧的 Pod。但需要注意的是,更新时它不会立即杀死旧的 Pod,直到有足够数量的新 Pod 已经出现才会杀死旧的 Pod,因为它需要确保至少 2 个 Pod 可用, 同时最多总共 3 个 Pod 可用。
也可以查看 Deployment 的详情来看到整体更新流程
kubectl describe deployments
故障前 Pod 运行的节点(work1 节点运行1个、work2 节点运行2个节点)
模拟 k8s-work2 故障(关闭节点)
init 0 # 直接关机
等待 5 分钟后会自动在其他可用 work 节点进行创建并运行,之所以要等待5分钟,这是因为 k8s 的 Taint(污点)与 Toleration(容忍)机制所造成。Taint和Toleration相互配合,可以避免pod被分配到不合适的节点上。每个节点上都可以应用一个或多个Taint,这表示对于那些不能容忍 Taint 的 pod,是不会被该节点接受的。如果将 Toleration 应用于 pod 上,则表示这些 pod 可以(但不要求)被调度到具有匹配 Taint 的节点上。
此时可看到 work2 上原来的两个节点已经 Terminating(终止),并转移到了 work1 上。
因此,此服务中断时间=停机等待5分钟时间+重建时间+服务启动时间+readiness探针检测正常时间
疑问?那当 K8s-work2 节点恢复后,其原来的 Pod 还会被重新调度回来吗?
答案是:不会
此时我恢复 k8s-work2 节点
再看看 Pod 的分布情况
默认情况下,Scheduler 会将 Pod 调度到所有可用的 Work 节点,不过在某些情况下我们需要将 Pod 部署到指定的 Work 节点,比如将有大量磁盘 I/O 的 Pod 部署到有 SSD 的 work 节点上来保证其 I/O。
因此,在 K8s 中可通过 label 来实现这个功能,label 是一对 key-value,各种资源都可以设置 label。具体操作如下。
1、先标记 work 节点
比如:我标记 work2 节点为配置有 SSD 的节点
kubectl label node k8s-work2 disktype=ssd
如何查看节点已设置的标签?
kubectl get node --show-labels
2、修改配置文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: demo
spec:
replicas: 4
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx:1.20.0
ports:
- containerPort: 80
3、执行 kubectl apply
kubectl apply -f nginx.yml
4、查看 这4个副本都只在 work2 节点运行
如何删除标签?
kubectl label node k8s-work2 disktype-
此时 k8s-work2 节点上的 Pod 并不会因为其标签的删除而删除,依然是正常运行的,除非删除 nginx.yml 配置文件中的 nodeSelect 字段,并再次执行 kubectl apply
,master 才会自动调度到其他可用节点(当然也可以包括 work2),如下图所示:
会创建当然也要会删除,K8s 中删除资源的方法有以下两种。
1、直接删除
kubectl delete deployment nginx
2、或通过配置文件删除
kubectl delete -f nginx.yml