fix(recovery): quiet the fleet logs — skip running containers, back off failed companion repairs
Some checks failed
Demo images / Build & push demo images (push) Failing after 32s
Some checks failed
Demo images / Build & push demo images (push) Failing after 32s
Fleet log sweep (2026-07-22): (1) recovery paths podman-start'ed containers that were already running, producing hundreds of benign but scary conmon 'Failed to create container' + cgroup Permission-denied pairs per node per day — both paths now check state first; (2) the 30s companion-reconcile loop retried registry pulls/builds forever against unreachable registries (~174×/image/day on an offline node) — failing rounds now back off exponentially to a 1h cap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b193e64ffb
commit
6347d16a2f
@ -108,17 +108,33 @@ impl BootReconciler {
|
||||
let orchestrator = self.orchestrator.clone();
|
||||
let interval = self.interval;
|
||||
Some(tokio::spawn(async move {
|
||||
let mut failure_rounds: u32 = 0;
|
||||
loop {
|
||||
let installed = orchestrator.manifest_ids().await;
|
||||
for (companion, err) in crate::container::companion::reconcile(&installed).await
|
||||
{
|
||||
let failures =
|
||||
crate::container::companion::reconcile(&installed).await;
|
||||
for (companion, err) in &failures {
|
||||
tracing::warn!(
|
||||
companion = %companion,
|
||||
error = %err,
|
||||
"companion reconcile failed"
|
||||
);
|
||||
}
|
||||
time::sleep(interval).await;
|
||||
// A failed repair can involve registry pulls and full
|
||||
// image builds; retrying every 30s hammered unreachable
|
||||
// registries ~174×/image/day on an offline node
|
||||
// (archy-x250-dev log sweep, 2026-07-22). Back off
|
||||
// exponentially while rounds keep failing — 30s doubling
|
||||
// to a 1h cap — and reset the moment a round is clean.
|
||||
failure_rounds = if failures.is_empty() {
|
||||
0
|
||||
} else {
|
||||
failure_rounds.saturating_add(1)
|
||||
};
|
||||
let backoff = interval
|
||||
.saturating_mul(2u32.saturating_pow(failure_rounds.min(7)))
|
||||
.min(Duration::from_secs(3600));
|
||||
time::sleep(backoff).await;
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
|
||||
@ -403,6 +403,14 @@ pub async fn recover_containers(containers: &[RunningContainerRecord]) -> Recove
|
||||
pending_boot_starts_add(containers.iter().map(|r| r.name.clone()));
|
||||
|
||||
for (i, record) in containers.iter().enumerate() {
|
||||
// Skip containers that are already up — `podman start` on a running
|
||||
// container produces the noisy benign conmon "Failed to create
|
||||
// container" + cgroup Permission-denied journal pair (fleet log
|
||||
// sweep 2026-07-22).
|
||||
if container_state(&record.name).await.as_deref() == Some("running") {
|
||||
report.recovered += 1;
|
||||
continue;
|
||||
}
|
||||
info!(
|
||||
"Recovering container: {} (image: {})",
|
||||
record.name, record.image
|
||||
@ -936,14 +944,21 @@ async fn container_state(container: &str) -> Option<String> {
|
||||
}
|
||||
|
||||
async fn start_existing_container(container: &str) -> bool {
|
||||
info!("Recovering stack container: {}", container);
|
||||
let timeout = match container {
|
||||
"immich_server" | "netbird-server" => Duration::from_secs(120),
|
||||
_ => Duration::from_secs(90),
|
||||
};
|
||||
if container_state(container).await.as_deref() == Some("initialized") {
|
||||
cleanup_container_runtime_state(container).await;
|
||||
match container_state(container).await.as_deref() {
|
||||
// Already up — `podman start` on a running container makes conmon
|
||||
// try to join the live cgroup and fail with a scary "Failed to
|
||||
// create container: exit status 1" + cgroup.procs Permission denied
|
||||
// pair in the journal. Fleet log sweep 2026-07-22 found hundreds of
|
||||
// these per day per node, all benign. Skip instead.
|
||||
Some("running") => return true,
|
||||
Some("initialized") => cleanup_container_runtime_state(container).await,
|
||||
_ => {}
|
||||
}
|
||||
info!("Recovering stack container: {}", container);
|
||||
match podman_output(&["start", container], timeout).await {
|
||||
Ok(output) if output.status.success() => {
|
||||
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user