parent
ee3f1e0cdb
commit
19a2cac355
3 changed files with 55 additions and 16 deletions
|
|
@ -50,7 +50,9 @@ func main() {
|
||||||
|
|
||||||
cfg := loadConfig()
|
cfg := loadConfig()
|
||||||
|
|
||||||
logger := zerolog.New(os.Stdout).With().Timestamp().Logger()
|
zerolog.SetGlobalLevel(cfg.LogLevel)
|
||||||
|
|
||||||
|
logger := zerolog.New(os.Stdout).Level(cfg.LogLevel).With().Timestamp().Logger()
|
||||||
cfg.Logger = &logger
|
cfg.Logger = &logger
|
||||||
|
|
||||||
agent, err := dockeragent.New(cfg)
|
agent, err := dockeragent.New(cfg)
|
||||||
|
|
@ -87,6 +89,7 @@ func loadConfig() dockeragent.Config {
|
||||||
envSwarmTasks := utils.GetenvTrim("PULSE_SWARM_TASKS")
|
envSwarmTasks := utils.GetenvTrim("PULSE_SWARM_TASKS")
|
||||||
envIncludeContainers := utils.GetenvTrim("PULSE_INCLUDE_CONTAINERS")
|
envIncludeContainers := utils.GetenvTrim("PULSE_INCLUDE_CONTAINERS")
|
||||||
envCollectDisk := utils.GetenvTrim("PULSE_COLLECT_DISK")
|
envCollectDisk := utils.GetenvTrim("PULSE_COLLECT_DISK")
|
||||||
|
envLogLevel := utils.GetenvTrim("LOG_LEVEL")
|
||||||
|
|
||||||
defaultInterval := 30 * time.Second
|
defaultInterval := 30 * time.Second
|
||||||
if envInterval != "" {
|
if envInterval != "" {
|
||||||
|
|
@ -120,6 +123,11 @@ func loadConfig() dockeragent.Config {
|
||||||
collectDiskDefault = utils.ParseBool(envCollectDisk)
|
collectDiskDefault = utils.ParseBool(envCollectDisk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logLevelDefault := "info"
|
||||||
|
if envLogLevel != "" {
|
||||||
|
logLevelDefault = envLogLevel
|
||||||
|
}
|
||||||
|
|
||||||
urlFlag := flag.String("url", envURL, "Pulse server URL (e.g. http://pulse:7655)")
|
urlFlag := flag.String("url", envURL, "Pulse server URL (e.g. http://pulse:7655)")
|
||||||
tokenFlag := flag.String("token", envToken, "Pulse API token (required)")
|
tokenFlag := flag.String("token", envToken, "Pulse API token (required)")
|
||||||
intervalFlag := flag.Duration("interval", defaultInterval, "Reporting interval (e.g. 30s)")
|
intervalFlag := flag.Duration("interval", defaultInterval, "Reporting interval (e.g. 30s)")
|
||||||
|
|
@ -128,6 +136,7 @@ func loadConfig() dockeragent.Config {
|
||||||
insecureFlag := flag.Bool("insecure", utils.ParseBool(envInsecure), "Skip TLS certificate verification")
|
insecureFlag := flag.Bool("insecure", utils.ParseBool(envInsecure), "Skip TLS certificate verification")
|
||||||
noAutoUpdateFlag := flag.Bool("no-auto-update", utils.ParseBool(envNoAutoUpdate), "Disable automatic agent updates")
|
noAutoUpdateFlag := flag.Bool("no-auto-update", utils.ParseBool(envNoAutoUpdate), "Disable automatic agent updates")
|
||||||
runtimeFlag := flag.String("runtime", envRuntime, "Container runtime to expect (auto, docker, podman)")
|
runtimeFlag := flag.String("runtime", envRuntime, "Container runtime to expect (auto, docker, podman)")
|
||||||
|
logLevelFlag := flag.String("log-level", logLevelDefault, "Log level: debug, info, warn, error")
|
||||||
var targetFlags stringFlagList
|
var targetFlags stringFlagList
|
||||||
flag.Var(&targetFlags, "target", "Pulse target in url|token[|insecure] format. Repeat to send to multiple Pulse instances")
|
flag.Var(&targetFlags, "target", "Pulse target in url|token[|insecure] format. Repeat to send to multiple Pulse instances")
|
||||||
var containerStateFlags stringFlagList
|
var containerStateFlags stringFlagList
|
||||||
|
|
@ -211,6 +220,12 @@ func loadConfig() dockeragent.Config {
|
||||||
interval = 30 * time.Second
|
interval = 30 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logLevel, err := parseLogLevel(*logLevelFlag)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
containerStates := make([]string, 0)
|
containerStates := make([]string, 0)
|
||||||
if len(containerStateFlags) > 0 {
|
if len(containerStateFlags) > 0 {
|
||||||
containerStates = append(containerStates, containerStateFlags.Values()...)
|
containerStates = append(containerStates, containerStateFlags.Values()...)
|
||||||
|
|
@ -235,9 +250,24 @@ func loadConfig() dockeragent.Config {
|
||||||
IncludeTasks: *includeTasksFlag,
|
IncludeTasks: *includeTasksFlag,
|
||||||
IncludeContainers: *includeContainersFlag,
|
IncludeContainers: *includeContainersFlag,
|
||||||
CollectDiskMetrics: *collectDiskFlag,
|
CollectDiskMetrics: *collectDiskFlag,
|
||||||
|
LogLevel: logLevel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseLogLevel(value string) (zerolog.Level, error) {
|
||||||
|
normalized := strings.ToLower(strings.TrimSpace(value))
|
||||||
|
if normalized == "" {
|
||||||
|
return zerolog.InfoLevel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
level, err := zerolog.ParseLevel(normalized)
|
||||||
|
if err != nil {
|
||||||
|
return zerolog.InfoLevel, fmt.Errorf("invalid log level %q: must be debug, info, warn, or error", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return level, nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseTargetSpecs(specs []string) ([]dockeragent.TargetConfig, error) {
|
func parseTargetSpecs(specs []string) ([]dockeragent.TargetConfig, error) {
|
||||||
targets := make([]dockeragent.TargetConfig, 0, len(specs))
|
targets := make([]dockeragent.TargetConfig, 0, len(specs))
|
||||||
for _, spec := range specs {
|
for _, spec := range specs {
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,8 @@ docker run -d \
|
||||||
ghcr.io/rcourtman/pulse-docker-agent:latest
|
ghcr.io/rcourtman/pulse-docker-agent:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Add `-e LOG_LEVEL=warn` when your container logs feed into journald or another centralized collector and you want quieter steady-state logging.
|
||||||
|
|
||||||
> **Note**: Official images for `linux/amd64` and `linux/arm64` are published to `ghcr.io/rcourtman/pulse-docker-agent`. To test local changes, run `docker build --target agent_runtime -t pulse-docker-agent:test .` from the repository root.
|
> **Note**: Official images for `linux/amd64` and `linux/arm64` are published to `ghcr.io/rcourtman/pulse-docker-agent`. To test local changes, run `docker build --target agent_runtime -t pulse-docker-agent:test .` from the repository root.
|
||||||
|
|
||||||
`--pid=host`, `--uts=host`, and the `/etc/machine-id` bind keep host metadata stable so Pulse doesn’t think the container itself is the Docker host. Auto-update is disabled in the image by default; rebuild or override `PULSE_NO_AUTO_UPDATE=false` only if you manage upgrades outside of your orchestrator. Expect to grant the container the same level of Docker socket access as the systemd service—running inside Docker doesn’t sandbox the agent from the host.
|
`--pid=host`, `--uts=host`, and the `/etc/machine-id` bind keep host metadata stable so Pulse doesn’t think the container itself is the Docker host. Auto-update is disabled in the image by default; rebuild or override `PULSE_NO_AUTO_UPDATE=false` only if you manage upgrades outside of your orchestrator. Expect to grant the container the same level of Docker socket access as the systemd service—running inside Docker doesn’t sandbox the agent from the host.
|
||||||
|
|
@ -232,6 +234,7 @@ docker run -d \
|
||||||
| `--token`, `PULSE_TOKEN`| Pulse API token with `docker:report` scope (required). | — |
|
| `--token`, `PULSE_TOKEN`| Pulse API token with `docker:report` scope (required). | — |
|
||||||
| `--target`, `PULSE_TARGETS` | One or more `url|token[|insecure]` entries to fan-out reports to multiple Pulse servers. Separate entries with `;` or repeat the flag. | — |
|
| `--target`, `PULSE_TARGETS` | One or more `url|token[|insecure]` entries to fan-out reports to multiple Pulse servers. Separate entries with `;` or repeat the flag. | — |
|
||||||
| `--interval`, `PULSE_INTERVAL` | Reporting cadence (supports `30s`, `1m`, etc.). | `30s` |
|
| `--interval`, `PULSE_INTERVAL` | Reporting cadence (supports `30s`, `1m`, etc.). | `30s` |
|
||||||
|
| `--log-level`, `LOG_LEVEL` | Log verbosity (`debug`, `info`, `warn`, `error`). Use `debug` only while actively troubleshooting to avoid noisy journals. | `info` |
|
||||||
| `--runtime`, `PULSE_RUNTIME` | Container runtime to target (`docker`, `podman`, `auto`). | `docker` |
|
| `--runtime`, `PULSE_RUNTIME` | Container runtime to target (`docker`, `podman`, `auto`). | `docker` |
|
||||||
| `--container-socket`, `PULSE_CONTAINER_SOCKET` / `CONTAINER_HOST` | Explicit runtime socket path or `unix://` URI. | Runtime default |
|
| `--container-socket`, `PULSE_CONTAINER_SOCKET` / `CONTAINER_HOST` | Explicit runtime socket path or `unix://` URI. | Runtime default |
|
||||||
| `--rootless`, `PULSE_RUNTIME_ROOTLESS` | Install/manage the agent as a user service (Podman). | Auto (rootful) |
|
| `--rootless`, `PULSE_RUNTIME_ROOTLESS` | Install/manage the agent as a user service (Podman). | Auto (rootful) |
|
||||||
|
|
@ -260,6 +263,7 @@ Need the alerts but at a different tone? The same Containers tab exposes global
|
||||||
## Testing and troubleshooting
|
## Testing and troubleshooting
|
||||||
|
|
||||||
- Run with `--interval 15s --insecure` in a terminal to see log output while testing.
|
- Run with `--interval 15s --insecure` in a terminal to see log output while testing.
|
||||||
|
- Adjust verbosity with `--log-level` or `LOG_LEVEL` (`info` by default). Drop to `warn` for quiet steady-state logs; use `debug` only when actively debugging.
|
||||||
- Ensure the Pulse API token has not expired or been regenerated.
|
- Ensure the Pulse API token has not expired or been regenerated.
|
||||||
- If `pulse-docker-agent` reports `Cannot connect to the Docker daemon`, verify the socket path and permissions.
|
- If `pulse-docker-agent` reports `Cannot connect to the Docker daemon`, verify the socket path and permissions.
|
||||||
- Check Pulse (`/containers` tab) for the latest heartbeat time. Hosts are marked offline if they stop reporting for >4× the configured interval.
|
- Check Pulse (`/containers` tab) for the latest heartbeat time. Hosts are marked offline if they stop reporting for >4× the configured interval.
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ type Config struct {
|
||||||
IncludeTasks bool
|
IncludeTasks bool
|
||||||
IncludeContainers bool
|
IncludeContainers bool
|
||||||
CollectDiskMetrics bool
|
CollectDiskMetrics bool
|
||||||
|
LogLevel zerolog.Level
|
||||||
Logger *zerolog.Logger
|
Logger *zerolog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,20 +82,20 @@ const (
|
||||||
|
|
||||||
// Agent collects Docker metrics and posts them to Pulse.
|
// Agent collects Docker metrics and posts them to Pulse.
|
||||||
type Agent struct {
|
type Agent struct {
|
||||||
cfg Config
|
cfg Config
|
||||||
docker *client.Client
|
docker *client.Client
|
||||||
daemonHost string
|
daemonHost string
|
||||||
runtime RuntimeKind
|
runtime RuntimeKind
|
||||||
runtimeVer string
|
runtimeVer string
|
||||||
supportsSwarm bool
|
supportsSwarm bool
|
||||||
httpClients map[bool]*http.Client
|
httpClients map[bool]*http.Client
|
||||||
logger zerolog.Logger
|
logger zerolog.Logger
|
||||||
machineID string
|
machineID string
|
||||||
hostName string
|
hostName string
|
||||||
cpuCount int
|
cpuCount int
|
||||||
targets []TargetConfig
|
targets []TargetConfig
|
||||||
allowedStates map[string]struct{}
|
allowedStates map[string]struct{}
|
||||||
stateFilters []string
|
stateFilters []string
|
||||||
hostID string
|
hostID string
|
||||||
prevContainerCPU map[string]cpuSample
|
prevContainerCPU map[string]cpuSample
|
||||||
preCPUStatsFailures int
|
preCPUStatsFailures int
|
||||||
|
|
@ -158,8 +159,12 @@ func New(cfg Config) (*Agent, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := cfg.Logger
|
logger := cfg.Logger
|
||||||
|
if zerolog.GlobalLevel() == zerolog.DebugLevel && cfg.LogLevel != zerolog.DebugLevel {
|
||||||
|
zerolog.SetGlobalLevel(cfg.LogLevel)
|
||||||
|
}
|
||||||
|
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
defaultLogger := zerolog.New(os.Stdout).With().Timestamp().Str("component", "pulse-docker-agent").Logger()
|
defaultLogger := zerolog.New(os.Stdout).Level(cfg.LogLevel).With().Timestamp().Str("component", "pulse-docker-agent").Logger()
|
||||||
logger = &defaultLogger
|
logger = &defaultLogger
|
||||||
} else {
|
} else {
|
||||||
scoped := logger.With().Str("component", "pulse-docker-agent").Logger()
|
scoped := logger.With().Str("component", "pulse-docker-agent").Logger()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue