结论

我们合理的配置好下面的参数就能实现先停止接收流量,等待60s 处理完本身已经接收的流量

spec.template.spec.terminationGracePeriodSeconds
spec.template.spec.containers.lifecycle
  • 1.
  • 2.

完整的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
  • 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.

测试过程

测试程序

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)
}

  • 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.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.

构建镜像

FROM busybox:latest
COPY test-http-Query /
CMD /test-http-Query

#docker build -t test:v1.0 .
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

启动服务

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
  • 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.

测试过程

  1. 循环请求 /actuator/info 接口
 while  true; do curl http://example-deployment/actuator/info; sleep 1 ; done
  • 1.
  1. 打开pod的日志输出
  2. 请求/sleep 接口的同时设置pod的副本数为0;

结论

当我们设置完pod的副本数为0 的时候,观察步骤1 输出内容为请求失败;说明这个时候新的HTTP流量已经无法进入

然后 pod的日志还在继续输出我们每隔1s打印的日志;说明这个时候pod还在运行

等到20s 后 /sleep 接口得到返回结果,说明本身已经接收到的流量能处理完成