结论
我们合理的配置好下面的参数就能实现先停止接收流量,等待60s 处理完本身已经接收的流量
spec.template.spec.terminationGracePeriodSeconds
spec.template.spec.containers.lifecycle
完整的yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
replicas: 1
selector:
matchLabels:
app: example
template:
metadata:
labels:
app: example
spec:
nodeSelector:
kubernetes.io/hostname: test-01-aws-001
terminationGracePeriodSeconds: 70 # 总的优雅终止时间
containers:
- name: example-container
image: test:v1.0
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 60"] # 在这里执行摘除操作
ports:
- containerPort: 9001
测试过程
测试程序
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type QueryConcurrency struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
GlobalConcurrency int `json:"globalConcurrency"`
NodeRatio int `json:"nodeRatio"`
DilatationSwitch bool `json:"dilatationSwitch"`
} `json:"data"`
}
func queryConcurrencyHandler(w http.ResponseWriter, r *http.Request) {
// 设置返回数据
response := QueryConcurrency{
Code: 200,
Message: "Success",
Data: struct {
GlobalConcurrency int `json:"globalConcurrency"`
NodeRatio int `json:"nodeRatio"`
DilatationSwitch bool `json:"dilatationSwitch"`
}{
GlobalConcurrency: 100,
NodeRatio: 50,
DilatationSwitch: true,
},
}
// 设置响应头
w.Header().Set("Content-Type", "application/json")
// 将响应数据编码为 JSON 格式
jsonData, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 写入响应数据
w.Write(jsonData)
}
func actuatorInfo(w http.ResponseWriter, r *http.Request) {
hostname, err := os.Hostname()
if err != nil {
fmt.Printf("Error getting hostname: %v\n", err)
return
}
response := QueryConcurrency{
Code: 200,
Message: "Success" + hostname,
}
jsonData, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 写入响应数据
w.Write(jsonData)
count := 0
count++
fmt.Println("/actuator/info", count)
}
// 调用接口20s后才返回结果,主要用于测试停止接收流量,原有已经接收的请求能正常返回
func sleepTime(w http.ResponseWriter, r *http.Request) {
response := QueryConcurrency{
Code: 200,
Message: "Success",
}
jsonData, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
time.Sleep(time.Second * 30)
// 写入响应数据
w.Write(jsonData)
}
func main() {
// 每隔1s打印一行日志;
go func() {
for {
fmt.Println("Logging every second")
time.Sleep(1 * time.Second)
}
}()
//http.HandleFunc("/queryConcurrency", queryConcurrencyHandler)
http.HandleFunc("/actuator/info", actuatorInfo)
http.HandleFunc("/sleep", sleepTime)
http.ListenAndServe(":9001", nil)
fmt.Println(1)
}
构建镜像
FROM busybox:latest
COPY test-http-Query /
CMD /test-http-Query
#docker build -t test:v1.0 .
启动服务
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
replicas: 1
selector:
matchLabels:
app: example
template:
metadata:
labels:
app: example
spec:
nodeSelector:
kubernetes.io/hostname: test-01-aws-001
terminationGracePeriodSeconds: 70 # 总的优雅终止时间
containers:
- name: example-container
image: test:v1.1
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 60"] # 在这里执行摘除操作
ports:
- containerPort: 9001
---
apiVersion: v1
kind: Service
metadata:
name: example-deployment
spec:
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9001
selector:
app: example
测试过程
- 循环请求
/actuator/info
接口
while true; do curl http://example-deployment/actuator/info; sleep 1 ; done
- 打开pod的日志输出
- 请求
/sleep
接口的同时设置pod的副本数为0;
结论
当我们设置完pod的副本数为0 的时候,观察步骤1 输出内容为请求失败;说明这个时候新的HTTP流量已经无法进入
然后 pod的日志还在继续输出我们每隔1s打印的日志;说明这个时候pod还在运行
等到20s 后 /sleep
接口得到返回结果,说明本身已经接收到的流量能处理完成