Skip to content

feat: register container runtime factories after startup - #3953

Open
karaolidis wants to merge 12 commits into
google:masterfrom
karaolidis:factory-registration-retry
Open

karaolidis wants to merge 12 commits into
google:masterfrom
karaolidis:factory-registration-retry

Conversation

@karaolidis

Copy link
Copy Markdown

cAdvisor registers each runtime factory (docker, podman, containerd, CRI-O) once, in Start. If the runtime is down or slow at that moment, the factory never registers until cAdvisor is restarted. Its containers are tracked as raw cgroups with no runtime labels, or not at all with --docker_only. Nothing reports the failure beyond one Info log line.

The usual trigger is boot ordering, e.g. rootless podman whose socket comes up after cAdvisor. This was asked for in #953, and #1968's workaround was "restart cAdvisor".

Changes

  • Each global housekeeping pass retries plugins that failed to register. Retry is opt-in through a new RetryablePlugin interface, so the Plugin contract is unchanged.
  • Containers the raw factory adopted are handed to the runtime factory once it can claim them. This also fixes containers adopted during the normal start race, when the cgroup appears before the runtime reports it.
  • Calls on the register/claim paths now have deadlines, and caches that would defeat a retry are fixed: containerd cached failed dials, and docker/podman cached the storage root.
  • The containerd client namespace was fixed by whichever plugin dialed first, which caused containerd metrics randomly missing when Docker (overlayfs) and containerd run on the same node #3951. It is now applied per call.
  • Registration state is shown on /validate and exported as cadvisor_container_plugin_registered{plugin}.
  • New flags --docker_client_timeout and --podman_client_timeout, defaulting to the current 10s, for socket-activated runtimes with slow cold starts.

A runtime that becomes reachable is picked up within one global housekeeping interval (1m by default). Behaviour when every runtime is up at startup is unchanged, apart from "; will retry" on the first failure log of plugins whose runtime isn't installed.

The commits are meant to be reviewed one at a time.

Factories make runtime API calls. Holding the read lock across them
made RegisterContainerHandlerFactory wait for a slow runtime, and every
lookup queued behind the waiting writer.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
A docker daemon that accepted connections but never answered blocked
container discovery forever.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
Register and handler construction had no deadline, so a runtime that
accepted connections but never answered blocked them forever. CRI-O's
--crio_client_timeout defaults to none.

Each TaskPid retry gets its own deadline, so the backoff sleeps don't
use it up. CrioClient has no context parameters, so the bounded CRI-O
calls are reached through a type assertion; other clients fall back to
the interface methods. Steady-state calls are unchanged.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
The client was dialed in a sync.Once that also cached errors, so a
containerd that started after cAdvisor was never picked up.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
The default namespace was fixed on the shared connection by whichever
caller dialed first. When docker's containerd snapshotter path dialed
first with "moby", the containerd plugin looked for k8s.io containers
in the moby namespace and never claimed them.

Fixes google#3951.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
RootDir() caches its first answer. For docker that is the --docker_root
fallback if docker was down when the FS context was built; for podman
it is "" if its own request fails. Either way a later Register used the
wrong root.

podman's /info always sets the root, so an empty one now fails
Register, before any watcher starts.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
Retry is opt-in so that Plugin's contract doesn't change: plugins that
don't implement RetryablePlugin are still called once.

Nothing calls InitializePlugins more than once yet.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
The previous commits bound every call on their Register paths and make
a failed Register leave nothing behind. systemd's Register can't fail.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
A runtime that was down or slow when cAdvisor started was never
registered until a restart. Its containers were tracked as raw, or with
--docker_only not at all.

Each global housekeeping pass now retries pending plugins before
discovery, so a factory that registers claims new containers in the
same pass. Watchers can only be started from Start, so any returned on
a retry are dropped.

Absent runtimes now fail every pass, so only their first failure is
logged at the default level.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
The raw factory accepts every cgroup by default, so a runtime container
seen before its factory could claim it stayed raw forever.

The new containerData is built before the raw entry is destroyed, so a
failure keeps the raw entry. No deletion event is sent, because the
container didn't exit. The re-check stops after half the interval and
resumes where it stopped, so a slow runtime can't starve discovery.

newContainerData also leaked its CPU load reader when updateSpec failed.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
With registration retried, a restart no longer fixes a missing runtime,
so operators need a signal to alert on.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
Socket-activated podman exits when idle, so every registration retry
pays its cold start. A host where that takes over 10s would never
register. The defaults keep today's 10s.

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant