k8s Deployment滚动升级可可靠吗

Kubernetes Deployment使用滚动升级策略进行升级,可以保证升级过程中的可靠性和容错性。

滚动升级通过逐步将新版本的Pod逐步添加到服务中,同时逐步停止旧版本的Pod,实现了服务的无停机升级。在升级过程中,Kubernetes会监控新版本Pod的运行状态,并在满足指定的健康检查条件之后才继续升级下一个Pod,确保服务的稳定性和可靠性。
使用Deployment滚动升级虽说可以实现无停机服务,但是它是完全客户端无感吗?
答案是否定的!!!

我们来做个实验,使用go提供一个简单的接口。
超时时间为30s,方便实验。

编写代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func main() {
// 创建一个HTTP服务器
server := &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 处理请求
time.Sleep(time.Second * 10)
fmt.Fprintln(w, "Hello, World!")
}),
}

// 设置超时时间为30秒
server.ReadTimeout = 30 * time.Second
server.WriteTimeout = 30 * time.Second

// 启动服务器
err := server.ListenAndServe()
if err != nil {
fmt.Println("服务器启动失败:", err)
}
}
制作镜像
1
2
3
4
5
6
7
8
9
10
11
12
FROM golang:1.17-alpine AS builder

WORKDIR /app

COPY . .

RUN go build -o main

RUN chmod +x main

EXPOSE 8080
CMD ["./main"]
1
2
➜ curl http://127.0.0.1:8080
curl: (52) Empty reply from server
构建一个镜像

将下面的代码保存为Dockerfile,然后使用nerdctl构建镜像,也可以直接拉取。nerdctl的安装可参考附录。
sudo nerdctl build -t hysyeah/my-curl:v1 .

1
2
3
4
5
6
7
8
9
10
11
12
FROM golang:1.17-alpine AS builder

WORKDIR /app

COPY . .

RUN go build -o main

RUN chmod +x main

EXPOSE 8080
CMD ["./main"]
以Deployment形式启动

设置3个复本,策略为RollingUpdate

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
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-curl
spec:
replicas: 3
selector:
matchLabels:
app: my-curl
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: my-curl
spec:
containers:
- name: my-curl
image: hysyeah/my-curl:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080

---
apiVersion: v1
kind: Service
metadata:
name: my-curl
spec:
selector:
app: my-curl
ports:
- name: http
port: 8080
targetPort: 8080
type: NodePort
---

测试滚动升级
启动Deployment

kubectl apply -f deployment.yaml

访问服务

curl http://127.0.0.1:31918

升级Deployment

在服务返回之前,升级Deployment(直接修改image,然后apply),然后你会得到如下错误。
说明Deployment升级对客户端是有影响。为什么会有影响可以参考一个pod的终止流程

1
2
➜ curl http://127.0.0.1:31918
curl: (52) Empty reply from server

原因是虽说是滚动升级,Deployment只保证在升级过程中服务不会中止并不会保证,已有的请求它并不会保证处理完。

如何对客户端无感
在应用中优雅退出

在退出中使用优雅退出机制,监听SIGTERM信号。可以参考一个pod的终止流程,了解pod是如何退出的。

使用pre-stop hook

Pod钩子是在Pod生命周期的不同阶段中执行的命令或脚本。使用Pod钩子,可以在Pod启动之前或之后,容器启动之前或之后,容器退出之前或之后等时刻执行定制化的逻辑。

Pod钩子支持的钩子类型有以下四种:

PostStart: Pod创建后,容器创建前执行。
PreStop: 容器退出前执行,可用于准备容器退出前的工作,如保存状态、备份数据等。
PreStart: Pod创建后,容器创建前执行,但只在容器是第一个启动的容器时才执行。
PostStop: 容器退出后执行,用于清理容器退出后的工作。

可以使用pre-stop hook在容器退出前执行一段代码或脚本。
添加preStop在容器退出前休眠20s,再次执行curl命令和升级操作发现接口能被正常处理。

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-curl
spec:
replicas: 3
selector:
matchLabels:
app: my-curl
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: my-curl
spec:
containers:
- name: my-curl
image: hysyeah/my-curl:gracefulv2
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command: ["sleep","20"]
ports:
- containerPort: 8080

附录-nerdctl套装安装

下载对应的压缩包,解压执行如下命令。

1
2
3
4
5
6
7
➜ cp lib/systemd/system/buildkit.service /lib/systemd/system/
➜ sudo cp bin/buildkitd /usr/local/bin/
➜ sudo cp bin/nerdctl /usr/local/bin/
➜ sudo cp bin/buildctl /usr/local/bin/

➜ sudo systemctl start buildkit
➜ systemctl status buildkit

REF:
1.https://iximiuz.com/en/posts/containerd-command-line-clients/