当我们的服务从启动,到可以对外提供服务,需要经历哪些过程?
如果在服务启动的过程中,外部的服务访问了该服务,会出现什么情况?
当服务在滚动升级中,服务该如何平滑升级而不会影响服务的正常功能?
服务的健康检查功能该如何编写,有哪些需要注意的点?
接下来,我们来逐步讨论这个问题。
一、服务启动不是马上能对外提供服务的
我们在 k8s 中运行的服务,常常会依赖于 Redis、MySQL、MongoDB,业务依赖的第三方服务等。
服务启动的时候,可能需要完成一些操作,比如
- 缓存数据的加载: 比如从 MySQL 中读取缓存数据放到 Redis 中
- 数据库表结构的初始化或者表记录变更:
根据代码转化成对应的 表结构,比如自动生成 table
业务变更需要执行的 sql 语句,比如 django 的 migration
- 和第三方服务 API 进行交互: 比如将自己注册到 nacos 中
服务的启动时间,从 1s以内,到几十s,都有可能。
当我们的服务还没有准备好,但是服务在 k8s 中已经显示为 Ready 状态(表示已经正常运行了),那么外部的流量就会进来,可能造成服务报错或者业务故障,这不是我们期望的。
二、健康检查功能的设计
2.1 健康检查功能应该是全局的
2.2 健康检查功能应该是非阻塞的
不能因为执行了健康检查功能而影响自身业务服务
2.3 健康检查功能应该要能体现服务的健康状况
2.4 健康检查功能最好是无验证的
2.5 健康检查功能应该只体现自身服务的健康状况
三、使用健康检查
apiVersion: v1
kind: Pod
metadata:
name: pod-http-readiness
spec:
containers:
- name: app
image: nginx
readinessProbe: # 设置就绪探针
httpGet:
path: /hello # 检查/hello路径的可访问性
port: 80
initialDelaySeconds: 5 # 容器启动后首次探测等待5秒
timeoutSeconds: 2 # 探测超时时间2秒
periodSeconds: 10 # 每10秒探测一次
apiVersion: v1
kind: Pod
metadata:
name: pod-exec-readiness
spec:
containers:
- name: app
image: nginx
readinessProbe: # 设置就绪探针
exec:
command: ["/bin/cat", "/app/start.txt"] # 执行cat命令检查文件存在性
initialDelaySeconds: 5 # 容器启动后首次探测等待5秒
timeoutSeconds: 2 # 探测超时时间2秒
periodSeconds: 10 # 每10秒探测一次
apiVersion: v1
kind: Pod
metadata:
name: pod-tcp-readiness
spec:
containers:
- name: app
image: nginx
readinessProbe: # 设置就绪探针
tcpSocket:
port: 80
initialDelaySeconds: 5 # 容器启动后首次探测等待5秒
timeoutSeconds: 2 # 探测超时时间2秒
periodSeconds: 10 # 每10秒探测一次
四、编写健康检查接口
package main
import (
"fmt"
"log"
"net/http"
"os"
"time"
)
func main() {
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(5 * time.Second)
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "ok")
})
http.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "ok")
})
log.Println("listen on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}