Merge pull request #890 from unpoller/docker-healthcheck
Add Docker health check support
This commit is contained in:
commit
4e6ebee524
|
|
@ -12,4 +12,7 @@ FROM gcr.io/distroless/static-debian11
|
|||
COPY unpoller /usr/bin/unpoller
|
||||
COPY --from=builder /etc/unpoller /etc/unpoller
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
||||
CMD ["/usr/bin/unpoller", "--health"]
|
||||
|
||||
ENTRYPOINT [ "/usr/bin/unpoller" ]
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ examples and default configurations.
|
|||
|
||||
OPTIONS
|
||||
---
|
||||
`unpoller [-c <config-file>,[config-file]] [-j <filter>] [-e <pass>] [-h] [-v]`
|
||||
`unpoller [-c <config-file>,[config-file]] [-j <filter>] [-e <pass>] [--health] [-d] [-h] [-v]`
|
||||
|
||||
-c, --config <config-file>,[config-file]
|
||||
Provide a configuration file (instead of the default). You may provide
|
||||
|
|
@ -39,6 +39,17 @@ OPTIONS
|
|||
-v, --version
|
||||
Display version and exit.
|
||||
|
||||
--health
|
||||
Run a health check and exit with status 0 (healthy) or 1 (unhealthy).
|
||||
This validates the configuration file, ensures input and output plugins
|
||||
are properly configured, and performs basic connectivity checks. Useful
|
||||
for Docker HEALTHCHECK and container orchestration readiness probes.
|
||||
|
||||
-d, --debugio
|
||||
Debug the inputs and outputs configured and exit. This performs more
|
||||
verbose validation checks than --health and is useful for troubleshooting
|
||||
configuration issues.
|
||||
|
||||
-j, --dumpjson <filter>
|
||||
This is a debug option; use this when you are missing data in your graphs,
|
||||
and/or you want to inspect the raw data coming from the controller. The
|
||||
|
|
|
|||
|
|
@ -9,3 +9,18 @@ in InfluxDB by UniFi Poller.
|
|||
##### HOWTO
|
||||
**Learn more about how and when to use these *Docker Compose* files in the
|
||||
[Docker Wiki](https://unpoller.com/docs/install/dockercompose).**
|
||||
|
||||
## Health Check
|
||||
|
||||
The UniFi Poller Docker image includes a built-in health check that validates
|
||||
the configuration and checks plugin connectivity. The health check runs every
|
||||
30 seconds and marks the container as unhealthy if configuration issues are
|
||||
detected or if enabled outputs cannot be reached.
|
||||
|
||||
You can manually run the health check:
|
||||
```bash
|
||||
docker exec <container_name> /usr/bin/unpoller --health
|
||||
```
|
||||
|
||||
The health check is automatically used by Docker and container orchestration
|
||||
platforms (Kubernetes, Docker Swarm, etc.) to determine container health status.
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ services:
|
|||
unifi-poller:
|
||||
restart: always
|
||||
image: ghcr.io/unpoller/unpoller:${POLLER_TAG}
|
||||
# Health check is built into the Docker image
|
||||
# It validates configuration and checks plugin connectivity
|
||||
depends_on:
|
||||
- grafana
|
||||
- influxdb
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ services:
|
|||
unifi-poller:
|
||||
restart: always
|
||||
image: ghcr.io/unpoller/unpoller:${POLLER_TAG}
|
||||
# Health check is built into the Docker image
|
||||
# It validates configuration and checks plugin connectivity
|
||||
environment:
|
||||
- UP_INFLUXDB_USER=${INFLUXDB_ADMIN_USER}
|
||||
- UP_INFLUXDB_PASS=${INFLUXDB_ADMIN_PASSWORD}
|
||||
|
|
|
|||
|
|
@ -122,3 +122,66 @@ func (u *UnifiPoller) DebugIO() error {
|
|||
|
||||
return allErr
|
||||
}
|
||||
|
||||
// HealthCheck performs a basic health check suitable for Docker HEALTHCHECK.
|
||||
// It validates configuration and checks if inputs/outputs are properly configured.
|
||||
// Returns nil (exit 0) if healthy, error (exit 1) if unhealthy.
|
||||
func (u *UnifiPoller) HealthCheck() error {
|
||||
// Silence output for health checks (Docker doesn't need verbose logs).
|
||||
u.Quiet = true
|
||||
|
||||
// Load configuration.
|
||||
cfile, err := getFirstFile(strings.Split(u.Flags.ConfigFile, ","))
|
||||
if err != nil {
|
||||
return fmt.Errorf("health check failed: config file not found: %w", err)
|
||||
}
|
||||
|
||||
u.Flags.ConfigFile = cfile
|
||||
|
||||
if err := u.ParseConfigs(); err != nil {
|
||||
return fmt.Errorf("health check failed: config parse error: %w", err)
|
||||
}
|
||||
|
||||
inputSync.RLock()
|
||||
defer inputSync.RUnlock()
|
||||
|
||||
outputSync.RLock()
|
||||
defer outputSync.RUnlock()
|
||||
|
||||
// Check that we have at least one input and one output configured.
|
||||
if len(inputs) == 0 {
|
||||
return fmt.Errorf("health check failed: no input plugins configured")
|
||||
}
|
||||
|
||||
if len(outputs) == 0 {
|
||||
return fmt.Errorf("health check failed: no output plugins configured")
|
||||
}
|
||||
|
||||
// Check if at least one output is enabled.
|
||||
hasEnabledOutput := false
|
||||
for _, output := range outputs {
|
||||
if output.Enabled() {
|
||||
hasEnabledOutput = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasEnabledOutput {
|
||||
return fmt.Errorf("health check failed: no enabled output plugins")
|
||||
}
|
||||
|
||||
// Perform basic validation checks on enabled outputs.
|
||||
for _, output := range outputs {
|
||||
if !output.Enabled() {
|
||||
continue
|
||||
}
|
||||
|
||||
ok, err := output.DebugOutput()
|
||||
if !ok || err != nil {
|
||||
return fmt.Errorf("health check failed: output %s validation failed: %w", output.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// All checks passed, application is healthy.
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ type Flags struct {
|
|||
HashPW string
|
||||
ShowVer bool
|
||||
DebugIO bool
|
||||
Health bool
|
||||
*pflag.FlagSet
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ func (u *UnifiPoller) Start() error {
|
|||
return u.PrintPasswordHash()
|
||||
}
|
||||
|
||||
if u.Flags.Health {
|
||||
return u.HealthCheck()
|
||||
}
|
||||
|
||||
cfile, err := getFirstFile(strings.Split(u.Flags.ConfigFile, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -76,6 +80,7 @@ func (f *Flags) Parse(args []string) {
|
|||
f.StringVarP(&f.DumpJSON, "dumpjson", "j", "",
|
||||
"This debug option prints a json payload and exits. See man page for more info.")
|
||||
f.BoolVarP(&f.DebugIO, "debugio", "d", false, "Debug the Inputs and Outputs configured and exit.")
|
||||
f.BoolVarP(&f.Health, "health", "", false, "Run health check and exit with status 0 (healthy) or 1 (unhealthy).")
|
||||
f.StringVarP(&f.ConfigFile, "config", "c", DefaultConfFile(),
|
||||
"Poller config file path. Separating multiple paths with a comma will load the first config file found.")
|
||||
f.BoolVarP(&f.ShowVer, "version", "v", false, "Print the version and exit.")
|
||||
|
|
|
|||
Loading…
Reference in New Issue