e071084ebe
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
241 lines
5.6 KiB
Markdown
241 lines
5.6 KiB
Markdown
---
|
|
layout: default
|
|
---
|
|
|
|
# Health Checks
|
|
|
|
The `health` package provides health check functionality for microservices, including Kubernetes-style liveness and readiness probes.
|
|
|
|
## Quick Start
|
|
|
|
```go
|
|
import "go-micro.dev/v6/health"
|
|
|
|
func main() {
|
|
// Register health checks
|
|
health.Register("database", health.PingCheck(db.Ping))
|
|
health.Register("cache", health.TCPCheck("localhost:6379", time.Second))
|
|
|
|
// Add health endpoints
|
|
mux := http.NewServeMux()
|
|
health.RegisterHandlers(mux) // Registers /health, /health/live, /health/ready
|
|
|
|
http.ListenAndServe(":8080", mux)
|
|
}
|
|
```
|
|
|
|
## Endpoints
|
|
|
|
| Endpoint | Purpose | Returns 200 when |
|
|
|----------|---------|------------------|
|
|
| `/health` | Overall health status | All critical checks pass |
|
|
| `/health/live` | Kubernetes liveness probe | Service is running |
|
|
| `/health/ready` | Kubernetes readiness probe | All critical checks pass |
|
|
|
|
## Response Format
|
|
|
|
```json
|
|
{
|
|
"status": "up",
|
|
"checks": [
|
|
{
|
|
"name": "database",
|
|
"status": "up",
|
|
"duration": 1234567
|
|
},
|
|
{
|
|
"name": "cache",
|
|
"status": "up",
|
|
"duration": 567890
|
|
}
|
|
],
|
|
"info": {
|
|
"go_version": "go1.22.0",
|
|
"go_os": "linux",
|
|
"go_arch": "amd64",
|
|
"version": "1.0.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
When unhealthy:
|
|
- HTTP status: 503 Service Unavailable
|
|
- `status`: `"down"`
|
|
- Failed checks include an `error` field
|
|
|
|
## Built-in Checks
|
|
|
|
### PingCheck
|
|
|
|
For database connections with a `Ping()` method:
|
|
|
|
```go
|
|
health.Register("postgres", health.PingCheck(db.Ping))
|
|
health.Register("mysql", health.PingContextCheck(db.PingContext))
|
|
```
|
|
|
|
### TCPCheck
|
|
|
|
Verify TCP connectivity:
|
|
|
|
```go
|
|
health.Register("redis", health.TCPCheck("localhost:6379", time.Second))
|
|
health.Register("kafka", health.TCPCheck("kafka:9092", 2*time.Second))
|
|
```
|
|
|
|
### HTTPCheck
|
|
|
|
Verify an HTTP endpoint returns 200:
|
|
|
|
```go
|
|
health.Register("api", health.HTTPCheck("http://api.internal/health", time.Second))
|
|
```
|
|
|
|
### DNSCheck
|
|
|
|
Verify DNS resolution:
|
|
|
|
```go
|
|
health.Register("dns", health.DNSCheck("api.example.com"))
|
|
```
|
|
|
|
### CustomCheck
|
|
|
|
Any function returning an error:
|
|
|
|
```go
|
|
health.Register("disk", health.CustomCheck(func() error {
|
|
var stat syscall.Statfs_t
|
|
if err := syscall.Statfs("/", &stat); err != nil {
|
|
return err
|
|
}
|
|
freeGB := stat.Bavail * uint64(stat.Bsize) / 1e9
|
|
if freeGB < 1 {
|
|
return fmt.Errorf("low disk space: %dGB free", freeGB)
|
|
}
|
|
return nil
|
|
}))
|
|
```
|
|
|
|
### RegistryCheck
|
|
|
|
Verifies the service registry is still reachable. A go-micro service can keep running while it has silently lost its connection to the registry (etcd, Consul, …) — the process looks healthy, but other services can no longer discover it. `RegistryCheck` surfaces that state so a readiness probe can take the pod out of rotation.
|
|
|
|
```go
|
|
svc := micro.NewService("orders")
|
|
|
|
health.Register("registry", health.RegistryCheck(svc.Options().Registry))
|
|
```
|
|
|
|
Registered checks are [critical](#critical-vs-non-critical-checks) by default, so when the registry connection is lost, `/health/ready` returns 503 and Kubernetes stops routing to the pod:
|
|
|
|
```yaml
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /health/ready
|
|
port: 8080
|
|
periodSeconds: 5
|
|
```
|
|
|
|
The check lists services under the configured probe timeout, so an unreachable registry is reported as `down` rather than hanging the probe. It works with any registry implementation — the connectivity is exercised through the standard `ListServices` call.
|
|
|
|
## Critical vs Non-Critical Checks
|
|
|
|
By default, all checks are critical. A critical check failure marks the service as not ready.
|
|
|
|
For non-critical checks (monitoring only):
|
|
|
|
```go
|
|
health.RegisterCheck(health.Check{
|
|
Name: "external-api",
|
|
Check: health.HTTPCheck("https://api.external.com/status", 5*time.Second),
|
|
Critical: false, // Won't affect readiness
|
|
Timeout: 5 * time.Second,
|
|
})
|
|
```
|
|
|
|
## Timeouts
|
|
|
|
Default timeout is 5 seconds. Override per-check:
|
|
|
|
```go
|
|
health.RegisterCheck(health.Check{
|
|
Name: "slow-db",
|
|
Check: health.PingCheck(db.Ping),
|
|
Timeout: 10 * time.Second,
|
|
})
|
|
```
|
|
|
|
## Adding Service Info
|
|
|
|
Include metadata in health responses:
|
|
|
|
```go
|
|
health.SetInfo("version", "1.0.0")
|
|
health.SetInfo("commit", "abc123")
|
|
health.SetInfo("service", "users")
|
|
```
|
|
|
|
## Kubernetes Configuration
|
|
|
|
```yaml
|
|
apiVersion: v1
|
|
kind: Pod
|
|
spec:
|
|
containers:
|
|
- name: app
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /health/live
|
|
port: 8080
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /health/ready
|
|
port: 8080
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 5
|
|
```
|
|
|
|
## Integration with micro run
|
|
|
|
When using `micro run` with a `micro.mu` config that specifies ports, the runner waits for `/health` to return 200 before starting dependent services:
|
|
|
|
```
|
|
service database
|
|
path ./database
|
|
port 8081
|
|
|
|
service api
|
|
path ./api
|
|
port 8080
|
|
depends database
|
|
```
|
|
|
|
The `api` service won't start until `database`'s `/health` endpoint is ready.
|
|
|
|
## Programmatic Usage
|
|
|
|
```go
|
|
// Check readiness in code
|
|
if health.IsReady(ctx) {
|
|
// Service is healthy
|
|
}
|
|
|
|
// Get full health status
|
|
resp := health.Run(ctx)
|
|
fmt.Printf("Status: %s\n", resp.Status)
|
|
for _, check := range resp.Checks {
|
|
fmt.Printf(" %s: %s (%v)\n", check.Name, check.Status, check.Duration)
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Keep checks fast** - Health endpoints are called frequently
|
|
2. **Use timeouts** - Don't let slow dependencies block health checks
|
|
3. **Non-critical for optional deps** - External APIs, caches that have fallbacks
|
|
4. **Critical for required deps** - Databases, message queues
|
|
5. **Include version info** - Helps debugging in production
|