CI runner intermittently fails to provision containers and resolve DNS #153
Labels
No labels
bug
devops
duplicate
enhancement
good first issue
layout
next major release
next minor release
question
research
testing
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lvl0/fedi-feed-router#153
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
CI runs fail for reasons unrelated to the code being tested. Two distinct
symptoms, both on the runner rather than in the workflow.
1. Container provisioning fails
Run #151 on
fix/150-migration-rate-limit:Nearly fifteen minutes in
Set up job, then every step at 0s. The job neverreached checkout, so nothing in the commit was executed. The runner could not
pull or start the job container.
2. DNS resolution times out mid-install
Two separate runs, different packages, each after roughly 118 packages had
already downloaded successfully:
Composer's own message points at the resolver:
Not the container image
The DNS timeouts were initially attributed to musl's resolver in the alpine
image introduced by #147, and the image was changed to Debian on that basis.
That explanation does not hold: the provisioning failure in run #151 happened on
catthehacker/ubuntu:act-latest, the original Debian-based image, on a branchthat does not contain any of #147's changes. The image is not the common factor.
Both symptoms point at the runner host: name resolution and outbound
connectivity to container registries and to
codeload.github.com.Why this matters beyond the noise
These failures are indistinguishable from code failures in the run list. Effort
has already gone into chasing an image-level explanation for what appears to be
a host-level problem, and a red CI check is currently sitting on #151, the PR
carrying the production migration fix.
Where to look
/etc/resolv.confand which resolver it points atforge.lvl0.xyzand Docker Hubcodeload.github.comtake fifteen minutes
Reduces exposure regardless of cause
The Composer cache is not working: every run downloads all 119 packages fresh.
ci.ymlcaches~/.composer/cachewhile Composer 2 actually uses~/.cache/composer, so the cache step stores and restores nothing. #147contains a fix for the path. A working cache removes ~119 network fetches per
run and with them most of the exposure to this failure mode.
Acceptance criteria
consecutive runs
happening on every run
Investigation on the runner host
The runner is
forge-runner, runningforgejo-runner.serviceas a systemd unit(not a container). It spawns job containers from
catthehacker/ubuntu:act-latest.What was ruled out
The runner logs nothing useful. 30 lines since the previous day, all
task N repo is ...andCleaning up network for job .... No errors, nowarnings, and nothing at all for the 14m50s provisioning failure. The runner
did not treat it as an error, which suggests it happened inside the image pull
rather than in runner logic.
DNS is healthy at rest and under burst. Sequential lookups of
codeload.github.comreturned in 100-230ms. Sixty parallel lookups allsucceeded promptly, which is a heavier burst than
composer installproduces.The earlier theory that the router falls over under load does not hold in that
simple form.
What was found
The host resolves through a single nameserver with no fallback:
Job containers inherited exactly that, confirmed with
docker run --rm catthehacker/ubuntu:act-latest cat /etc/resolv.conf.With one nameserver and no secondary, any dropped or delayed query has nothing
to fail over to. It simply waits out Composer's 10-second timeout and fails the
step. That matches the observed shape: ~118 packages download fine, then one
lookup gets no answer and the whole install dies.
Mitigation applied
/etc/docker/daemon.jsoncreated onforge-runner:followed by
systemctl restart docker. Verified that job containers now receiveboth nameservers.
This is a mitigation, not a proven fix. Neither failure could be reproduced
on demand, and both left no trace. What it does is remove a single point of
failure: a transient non-answer from the router is now retried against a second
resolver instead of becoming a hard timeout.
Still open
covers it, and it has not recurred. If it happens again, capture
journalctl -u forgejo-runnerandjournalctl -u dockeraround the timestamp.ci.ymlcaches~/.composer/cachewhile Composer 2 uses
~/.cache/composer, so all 119 packages are downloadedon every run. The path fix is on
ci/147-speed-up(#147). Landing it removesmost of the remaining exposure, because a warm cache does not make 119 DNS
lookups.
How to tell whether this worked
There is no positive test. The evidence will be an absence: no further
curl error 28 ... Resolving timed outfailures over the next several runs. Ifone occurs anyway, the resolver is not the cause and this ticket needs a
different line of investigation.
Closing as mitigated.
/etc/docker/daemon.jsononforge-runnernow gives job containers a fallbackresolver, so a dropped query from the router fails over to
1.1.1.1instead ofwaiting out Composer's 10-second timeout. Verified that containers receive both
nameservers.
Neither failure has recurred since. Several runs have passed cleanly, including
the release branch runs and the v1.4.1 PR.
Two things reduce the remaining exposure independently of the resolver change:
pcovon every run.That removes a large share of the network activity that was exposed to DNS
failures in the first place.
on every run once a cache is warm.
The 14m50s provisioning failure still has no explanation and left nothing in
journalctl -u forgejo-runner. It has not recurred. If it happens again, captureboth the runner and docker journals around the timestamp and reopen — closing
this now rather than holding the milestone open against a wait.