k8s deployment

  |  

全部的 K8S学习笔记总目录,请点击查看。

以下主要介绍 k8s 中的 deployment ,deployment 是 k8s 中最常用的资源类型之一,用于管理 pod 的生命周期。

改造之前的 pod

之前我们使用 pod 来管理应用,但是 pod 有如下缺点:

  • pod 无法实现滚动升级
  • pod 无法实现回滚
  • pod 无法实现自动伸缩
  • pod 无法实现自动恢复
  • pod 无法实现自动发布
  • pod 无法实现自动扩容
  • pod 无法实现自动缩容
  • pod 无法实现自动重启

为了解决这些问题,我们需要使用 deployment 来管理应用。

创建 deployment 文件

首先创建数据的文件,将其改造为 deployment 方式 deploy-mysql.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: test
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
hostNetwork: true
volumes:
- name: mysql-data
hostPath:
path: /opt/mysql/data
nodeSelector:
component: mysql
containers:
- name: mysql
image: mariadb:11.1
args:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
ports:
- containerPort: 3306
env:
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: myblog-secret
key: MYSQL_USER
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: myblog-secret
key: MYSQL_PASSWD
- name: MYSQL_DATABASE
valueFrom:
configMapKeyRef:
name: myblog
key: MYSQL_DATABASE
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql

之后创建业务应用的 deployment 文件 deploy-myblog.yaml,为了更好的实验,我把 blog 的应用换成 wordpress 了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: apps/v1
kind: Deployment
metadata:
name: myblog
namespace: test
spec:
replicas: 1
selector:
matchLabels:
app: myblog
template:
metadata:
labels:
app: myblog
spec:
containers:
- name: myblog
image: wordpress:php8.2-alpine
imagePullPolicy: IfNotPresent
env:
- name: WORDPRESS_DB_HOST
valueFrom:
configMapKeyRef:
name: myblog
key: MYSQL_HOST
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: myblog-secret
key: MYSQL_USER
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: myblog-secret
key: MYSQL_PASSWD
- name: WORDPRESS_DB_NAME
valueFrom:
configMapKeyRef:
name: myblog
key: MYSQL_DATABASE
ports:
- containerPort: 80

创建 deployment

1
2
$ kubectl apply -f deploy-mysql.yaml
$ kubectl apply -f deploy-myblog.yaml

查看 deployment

此时可以通过 kubectl get deploy -n test 查看 deployment 的状态:

1
2
3
4
$ kubectl get deploy -n test
NAME READY UP-TO-DATE AVAILABLE AGE
mysql 1/1 1 1 2m
myblog 1/1 1 1 2m
  • NAME 列出了集群中 Deployments 的名称。
  • READY显示当前正在运行的副本数/期望的副本数。
  • UP-TO-DATE显示已更新以实现期望状态的副本数。
  • AVAILABLE显示应用程序可供用户使用的副本数。
  • AGE 显示应用程序运行的时间量。

查看 pod

1
2
3
4
$ kubectl get po -n test
NAME READY STATUS RESTARTS AGE
mysql-7b4f8f8d5f-4j4q4 1/1 Running 0 2m
myblog-7c96c9f76b-qbbg7 1/1 Running 0 2m

查看 replicaSet

1
2
3
4
$ kubectl get rs -n test
NAME DESIRED CURRENT READY AGE
mysql-7b4f8f8d5f 1 1 1 2m
myblog-7c96c9f76b 1 1 1 2m

查看 deployment 详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
$ kubectl describe deploy mysql -n test
Name: mysql
Namespace: test
CreationTimestamp: Fri, 03 Sep 2021 23:59:15 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=mysql
Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=mysql
Containers:
mysql:
Image: mariadb:11.1
Port: 3306/TCP
Host Port: 0/TCP
Args:
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
Environment:
MYSQL_USER: <set to the key 'MYSQL_USER' in secret 'myblog-secret'> Optional: false
MYSQL_ROOT_PASSWORD: <set to the key 'MYSQL_PASSWD' in secret 'myblog-secret'> Optional: false
MYSQL_DATABASE: <set to the key 'MYSQL_DATABASE' of config map 'myblog'> Optional: false
Mounts:
/var/lib/mysql from mysql-data (rw)
Volumes:
mysql-data:
Type: HostPath (bare host directory volume)
Path: /opt/mysql/data
HostPathType:
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: mysql-7f97cb6cc9 (1/1 replicas created)
Events: <none>

副本保障机制

controller实时检测pod状态,并保障副本数一直处于期望的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 删除pod,观察pod状态变化
$ kubectl -n test delete pod myblog-7c96c9f76b-qbbg7

# 观察pod
$ kubectl get pods -o wide -n test

# 设置两个副本, 或者通过 kubectl -n test edit deploy myblog 的方式,最好通过修改文件,然后 apply 的方式,这样 yaml 文件可以保持同步
$ kubectl -n test scale deploy myblog --replicas=2
deployment.extensions/myblog scaled

# 观察pod
$ kubectl get pods -o wide -n test
NAME READY STATUS RESTARTS AGE
mysql-7b4f8f8d5f-4j4q4 1/1 Running 0 3m18s
myblog-7c96c9f76b-qbbg7 1/1 Running 0 11m
myblog-7c96c9f76b-s6brm 1/1 Running 0 3m18s

Pod驱逐策略

K8S 有个特色功能叫 pod eviction,它在某些场景下如节点 NotReady,或者资源不足时,把 pod 驱逐至其它节点,这也是出于业务保护的角度去考虑的。

  1. Kube-controller-manager: 周期性检查所有节点状态,当节点处于 NotReady 状态超过一段时间后,驱逐该节点上所有 pod。

    • pod-eviction-timeout:NotReady 状态节点超过该时间后,执行驱逐,默认 5 min,适用于k8s 1.13版本之前

    • 1.13版本后,集群开启 TaintBasedEvictions 与TaintNodesByCondition 功能,即taint-based-evictions,即节点若失联或者出现各种异常情况,k8s会自动为node打上污点,同时为pod默认添加如下容忍设置:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      tolerations:
      - effect: NoExecute
      key: node.kubernetes.io/not-ready
      operator: Exists
      tolerationSeconds: 300
      - effect: NoExecute
      key: node.kubernetes.io/unreachable
      operator: Exists
      tolerationSeconds: 300

      即各pod可以独立设置驱逐容忍时间。

  2. Kubelet: 周期性检查本节点资源,当资源不足时,按照优先级驱逐部分 pod

    • memory.available:节点可用内存
    • nodefs.available:节点根盘可用存储空间
    • nodefs.inodesFree:节点inodes可用数量
    • imagefs.available:镜像存储盘的可用空间
    • imagefs.inodesFree:镜像存储盘的inodes可用数量

服务更新方法

修改服务,重新打tag模拟服务更新至下一个版本。

更新方式:

  1. 修改yaml文件,使用kubectl -n test apply -f deploy-myblog.yaml来应用更新
  2. kubectl -n test edit deploy myblog在线更新
  3. kubectl -n test set image deploy myblog myblog=wordpress:php8.2 --record

更新策略

1
2
3
4
5
6
7
8
9
10
11
12
13
...
spec:
replicas: 2
selector:
matchLabels:
app: myblog
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
#指定更新方式为滚动更新,默认策略,通过get deploy yaml查看
type: RollingUpdate
...

update

策略控制:

  • maxSurge:最大激增数, 指更新过程中, 最多可以比replicas预先设定值多出的pod数量, 可以为固定值或百分比,默认为desired Pods数的25%。计算时向上取整(比如3.4,取4),更新过程中最多会有replicas + maxSurge个pod
  • maxUnavailable: 指更新过程中, 最多有几个pod处于无法服务状态 , 可以为固定值或百分比,默认为desired Pods数的25%。计算时向下取整(比如3.6,取3)

在Deployment rollout时,需要保证Available(Ready) Pods数不低于 desired pods number - maxUnavailable; 保证所有的非异常状态Pods数不多于 desired pods number + maxSurge

以myblog为例,使用默认的策略,更新过程:

  1. maxSurge 25%,2个实例,向上取整,则maxSurge为1,意味着最多可以有2+1=3个Pod,那么此时会新创建1个ReplicaSet,RS-new,把副本数置为1,此时呢,副本控制器就去创建这个新的Pod
  2. 同时,maxUnavailable是25%,副本数2*25%,向下取整,则为0,意味着,滚动更新的过程中,不能有少于2个可用的Pod,因此,旧的Replica(RS-old)会先保持不动,等RS-new管理的Pod状态Ready后,此时已经有3个Ready状态的Pod了,那么由于只要保证有2个可用的Pod即可,因此,RS-old的副本数会有2个变成1个,此时,会删掉一个旧的Pod
  3. 删掉旧的Pod的时候,由于总的Pod数量又变成2个了,因此,距离最大的3个还有1个Pod可以创建,所以,RS-new把管理的副本数由1改成2,此时又会创建1个新的Pod,等RS-new管理了2个Pod都ready后,那么就可以把RS-old的副本数由1置为0了,这样就完成了滚动更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#查看滚动更新事件
$ kubectl -n test describe deploy myblog
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 11s deployment-controller Scaled up replica set myblog-6cf56fc848 to 1
Normal ScalingReplicaSet 11s deployment-controller Scaled down replica set myblog-6fdcf98f9 to 1
Normal ScalingReplicaSet 11s deployment-controller Scaled up replica set myblog-6cf56fc848 to 2
Normal ScalingReplicaSet 6s deployment-controller Scaled down replica set myblog-6fdcf98f9 to 0
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
myblog-6cf56fc848 2 2 2 16h
myblog-6fdcf98f9 0 0 0 16h

除了滚动更新以外,还有一种策略是Recreate,直接在当前的pod基础上先删后建:

1
2
3
4
...
strategy:
type: Recreate
...

我们的mysql服务应该使用Recreate来管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ kubectl -n test edit deploy mysql
...
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
creationTimestamp: null
labels:
app: mysql
...

服务回滚

通过滚动升级的策略可以平滑的升级Deployment,若升级出现问题,需要最快且最好的方式回退到上一次能够提供正常工作的版本。为此K8S提供了回滚机制。

revision:更新应用时,K8S都会记录当前的版本号,即为revision,当升级出现问题时,可通过回滚到某个特定的revision,默认配置下,K8S只会保留最近的几个revision,可以通过Deployment配置文件中的spec.revisionHistoryLimit属性增加revision数量,默认是10。

查看当前:

1
2
$ kubectl -n test rollout history deploy myblog ##CHANGE-CAUSE为空
$ kubectl delete -f deploy-myblog.yaml ## 方便演示到具体效果,删掉已有deployment

记录回滚:

1
2
$ kubectl apply -f deploy-myblog.yaml --record
$ kubectl -n test set image deploy myblog myblog=wordpress:php8.2 --record=true

查看deployment更新历史:

1
2
3
4
5
$ kubectl -n test rollout history deploy myblog
deployment.extensions/myblog
REVISION CHANGE-CAUSE
1 kubectl create --filename=deploy-myblog.yaml --record=true
2 kubectl set image deploy myblog myblog=wordpress:php8.2-apache --record=true

回滚到具体的REVISION:

1
2
3
4
5
$ kubectl -n test rollout undo deploy myblog --to-revision=1
deployment.extensions/myblog rolled back

# 访问应用测试
curl 10.244.1.150:8002/
文章目录
  1. 1. 改造之前的 pod
    1. 1.1. 创建 deployment 文件
    2. 1.2. 创建 deployment
    3. 1.3. 查看 deployment
      1. 1.3.1. 此时可以通过 kubectl get deploy -n test 查看 deployment 的状态:
      2. 1.3.2. 查看 pod
      3. 1.3.3. 查看 replicaSet
      4. 1.3.4. 查看 deployment 详细信息
    4. 1.4. 副本保障机制
    5. 1.5. Pod驱逐策略
    6. 1.6. 服务更新方法
    7. 1.7. 更新策略
    8. 1.8. 服务回滚