golang实现Kubernetes就绪检测的健康检查插件healthcheck的使用
Golang实现Kubernetes就绪检测的健康检查插件healthcheck的使用
为什么健康检查很重要
健康检查对于构建分布式系统中的弹性、自愈应用程序至关重要。它们提供:
- 自动恢复:在Kubernetes中,失败的健康检查会触发Pod自动重启
- 负载均衡集成:防止流量被路由到不健康的实例
- 优雅降级:当非关键服务失败时,应用程序可以优雅降级
- 操作可见性:健康端点提供系统状态的即时洞察
- 零停机部署:就绪检查确保新部署只有在完全初始化后才会接收流量
功能特性
- 多种检查类型:基本(同步)、手动和后台(异步)检查
- Kubernetes原生:内置
/live
和/ready
端点,遵循k8s约定 - JSON状态报告:详细的健康状态和历史记录
- 线程安全:具有适当同步的并发安全操作
- 优雅关闭:正确清理后台检查和关闭信号
安装
go get -u github.com/kazhuravlev/healthcheck
快速开始
package main
import (
"context"
"errors"
"math/rand"
"time"
"github.com/kazhuravlev/healthcheck"
)
func main() {
ctx := context.TODO()
// 1. 创建healthcheck实例
hc, _ := healthcheck.New()
// 2. 注册一个简单的检查
hc.Register(ctx, healthcheck.NewBasic("redis", time.Second, func(ctx context.Context) error {
if rand.Float64() > 0.5 {
return errors.New("service is not available")
}
return nil
}))
// 3. 启动HTTP服务器
server, _ := healthcheck.NewServer(hc, healthcheck.WithPort(8080))
_ = server.Run(ctx)
// 4. 在http://localhost:8080/ready检查健康状态
select {}
}
健康检查类型
1. 基本检查(同步)
基本检查在调用/ready
端点时按需运行。适用于:
- 快速操作(<1秒)
- 需要新鲜数据的检查
- 低成本操作
// 数据库连接检查
dbCheck := healthcheck.NewBasic("postgres", time.Second, func (ctx context.Context) error {
return db.PingContext(ctx)
})
2. 后台检查(异步)
后台检查在单独的goroutine中定期运行(在后台模式下)。适用于:
- 昂贵的操作(API调用、复杂查询)
- 有速率限制的检查
- 可以使用稍微过时数据的操作
// 外部API健康检查 - 每30秒运行一次
apiCheck := healthcheck.NewBackground(
"payment-api",
nil, // 初始错误状态
5*time.Second, // 初始延迟
30*time.Second, // 检查间隔
5*time.Second, // 每次检查的超时时间
func (ctx context.Context) error {
resp, err := client.Get("https://api.payment.com/health")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("unhealthy")
}
return nil
},
)
3. 手动检查
手动检查由您的应用程序逻辑控制。适用于:
- 初始化状态(缓存预热、数据加载)
- 断路器模式
- 功能标志
// 缓存预热检查
cacheCheck := healthcheck.NewManual("cache-warmed")
hc.Register(ctx, cacheCheck)
// 在启动期间设置为不健康
cacheCheck.SetErr(errors.New("cache warming in progress"))
// 缓存预热完成后
cacheCheck.SetErr(nil)
最佳实践
1. 选择正确的检查类型
场景 | 检查类型 | 原因 |
---|---|---|
数据库ping | 基本 | 快速,需要新鲜数据 |
文件系统检查 | 基本 | 快速,本地操作 |
外部API健康 | 后台 | 昂贵,速率限制 |
消息队列深度 | 后台 | 指标查询,可以是过时的 |
缓存预热状态 | 手动 | 应用程序控制的状态 |
2. 设置适当的超时
// ❌ 不好: 超时太长会阻塞就绪状态。超时应小于k8s中的超时
healthcheck.NewBasic("db", 30*time.Second, checkFunc)
// ✅ 好: 短超时
healthcheck.NewBasic("db", 1*time.Second, checkFunc)
3. 正确使用状态码
-
存活检查(
/live
): 应该几乎总是返回200 OK- 仅当应用程序处于不可恢复状态时才失败
- Kubernetes将在失败时重启Pod
-
就绪检查(
/ready
): 应在以下情况下失败:- 关键依赖项不可用
- 应用程序仍在初始化
- 应用程序正在关闭
4. 为错误添加上下文
func checkDatabase(ctx context.Context) error {
if err := db.PingContext(ctx); err != nil {
// 使用fmt.Errorf添加上下文。它将出现在/ready报告中
return fmt.Errorf("postgres connection failed: %w", err)
}
return nil
}
5. 优雅关闭
// 创建healthcheck实例
hc, _ := healthcheck.New()
// 注册您的常规检查
hc.Register(ctx, healthcheck.NewBasic("database", time.Second, checkDB))
// 启动HTTP服务器
server, _ := healthcheck.NewServer(hc, healthcheck.WithPort(8080))
_ = server.Run(ctx)
// 在您的优雅关闭处理程序中
func gracefulShutdown(hc *healthcheck.Healthcheck) {
// 将应用程序标记为正在关闭 - /ready将返回500
hc.Shutdown()
// 继续正常的关闭过程
// - 停止接受新请求
// - 完成现有请求
// - 关闭数据库连接等
}
6. 监控检查
hc, _ := healthcheck.New(
healthcheck.WithCheckStatusHook(func (name string, status healthcheck.Status) {
// hcMetric可以是prometheus指标 - 这取决于您的基础设施
hcMetric.WithLabelValues(name, string(status)).Set(1)
}),
)
完整示例
package main
import (
"context"
"database/sql"
"fmt"
"log"
"time"
"github.com/kazhuravlev/healthcheck"
_ "github.com/lib/pq"
)
func main() {
ctx := context.Background()
// 初始化依赖项
db, err := sql.Open("postgres", "postgres://localhost/myapp")
if err != nil {
log.Fatal(err)
}
// 创建healthcheck
hc, _ := healthcheck.New()
// 1. 数据库检查 - 同步,关键
hc.Register(ctx, healthcheck.NewBasic("postgres", time.Second, func(ctx context.Context) error {
return db.PingContext(ctx)
}))
// 2. 缓存预热 - 手动控制
cacheReady := healthcheck.NewManual("cache")
hc.Register(ctx, cacheReady)
cacheReady.SetErr(fmt.Errorf("warming up"))
// 3. 外部API - 后台检查
hc.Register(ctx, healthcheck.NewBackground(
"payment-provider",
nil,
10*time.Second, // 初始延迟
30*time.Second, // 检查间隔
5*time.Second, // 超时
checkPaymentProvider,
))
// 启动健康检查服务器
server, _ := healthcheck.NewServer(hc, healthcheck.WithPort(8080))
if err := server.Run(ctx); err != nil {
log.Fatal(err)
}
// 模拟缓存预热完成
go func() {
time.Sleep(5 * time.Second)
cacheReady.SetErr(nil)
log.Println("Cache warmed up")
}()
// 优雅关闭示例
go func() {
time.Sleep(30 * time.Second)
log.Println("Initiating graceful shutdown...")
hc.Shutdown() // /ready现在将返回500,停止新流量
log.Println("Application marked as shutting down")
}()
log.Println("Health checks available at:")
log.Println(" - http://localhost:8080/live")
log.Println(" - http://localhost:8080/ready")
select {}
}
func checkPaymentProvider(ctx context.Context) error {
// 支付提供者检查的实现
return nil
}
与Kubernetes集成
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
livenessProbe:
httpGet:
path: /live
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
响应格式
/ready
端点返回带有检查历史记录的详细JSON:
健康应用:
{
"status": "up",
"checks": [
{
"name": "postgres",
"state": {
"status": "up",
"error": "",
"timestamp": "2024-01-15T10:30:00Z"
},
"history": [
{
"status": "up",
"error": "",
"timestamp": "2024-01-15T10:29:55Z"
}
]
}
]
}
正在关闭的应用:
{
"status": "down",
"checks": [
{
"name": "postgres",
"state": {
"status": "up",
"error": "",
"timestamp": "2024-01-15T10:30:00Z"
}
},
{
"name": "__shutting_down__",
"state": {
"status": "down",
"error": "The application in shutting down process",
"timestamp": "2024-01-15T10:30:05Z"
},
"history": null
}
]
}
更多关于golang实现Kubernetes就绪检测的健康检查插件healthcheck的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复