Compare commits

..

2 Commits

Author SHA1 Message Date
Nguyen Minh Phuc 37b297bf5c ci: reclaim dind disk after each image build
ci / lint (push) Successful in 43s
ci / types (push) Successful in 1m29s
ci / unit (push) Successful in 59s
ci / security (push) Successful in 1m10s
ci / dockerfile (push) Successful in 24s
ci / chart (push) Successful in 9s
ci / integration (push) Successful in 1m50s
ci / image (api) (push) Successful in 4m57s
ci / image (reconciler) (push) Successful in 2m55s
ci / image (worker) (push) Successful in 2m58s
ci / bump (push) Successful in 23s
dind's /var/lib/docker is now a hostPath on node2 rather than the container's
writable layer, so it survives restarts — and nothing reclaims it. Kubelet's
image GC does not manage a nested daemon's store, so left alone it grows every
run until node2 hits disk pressure and evicts pods, which reads as a cluster
problem rather than a CI one.

The step is deliberately narrow. `docker image prune` without -a removes
dangling images only; with -a it would delete the act runner image, which no
container references between jobs, and buy back a 1.6GB re-pull on the next
run. The buildx cache is what actually grows without bound, so it is pruned by
age keeping a week, recent enough that --cache-from still hits. Named volumes
are never pruned, since that is where the trivy vuln DB lives.

always(), because a failed build still leaves layers behind, and that is when
disk is most likely to have been the reason it failed.
2026-07-20 04:48:48 +00:00
Nguyen Minh Phuc 5d7f46483e runbook: recovering the queue after a runner restart
Restarting act_runner leaves its in-flight jobs in_progress with nothing behind
them, and at capacity 1 one orphan blocks every later run. Gitea 1.26 has no
cancel endpoint; DELETE .../actions/runs/{index} does the job and takes the run
index, not the database id.
2026-07-20 04:41:15 +00:00
2 changed files with 43 additions and 1 deletions
+23
View File
@@ -439,6 +439,29 @@ jobs:
docker buildx imagetools inspect "${IMAGE}:${GITHUB_SHA}" \ docker buildx imagetools inspect "${IMAGE}:${GITHUB_SHA}" \
--format '{{.Manifest.Digest}}' --format '{{.Manifest.Digest}}'
- name: reclaim dind disk
# dind's /var/lib/docker is a hostPath on node2 (see oci-k8s addons role), so
# nothing reclaims it automatically — kubelet's image GC does not manage a nested
# daemon's store. Left alone it grows every run until node2 hits disk pressure and
# starts evicting pods, which looks like a cluster problem rather than a CI one.
#
# `always()`: a failed build still leaves layers behind, and that is exactly when
# the disk is most likely to be the reason it failed.
#
# Deliberately narrow. `docker image prune` WITHOUT -a removes dangling images
# only; with -a it would delete the act runner image, which no container references
# between jobs, and buy back a 1.6GB re-pull on the very next run. The buildx cache
# is the part that actually grows without bound, so it is pruned by age and keeps a
# week — recent enough that `--cache-from` still hits on normal traffic.
#
# Named volumes are never pruned here: that is where the trivy vuln DB lives.
if: always()
run: |
docker image prune -f
docker buildx prune -af --filter until=168h
echo "--- dind disk after prune ---"
docker system df
# --- stage 11: bump the chart's digests. CI's last act. ------------------------------ # --- stage 11: bump the chart's digests. CI's last act. ------------------------------
bump: bump:
runs-on: ubuntu-latest runs-on: ubuntu-latest
+20 -1
View File
@@ -124,7 +124,26 @@ from the old node. It is pinned to node2 in `oci-k8s/.../addons/tasks/main.yml`
that reason. A dedicated PVC for the image cache would survive restarts outright, but on that reason. A dedicated PVC for the image cache would survive restarts outright, but on
this cluster that volume faulted and blocked the runner, so it is deliberately not used. this cluster that volume faulted and blocked the runner, so it is deliberately not used.
### 6. Verify the whole loop, not just the green checkmarks ### 6. Restarting the runner orphans its in-flight jobs
Any change to the act_runner StatefulSet recreates the pod, and every job it was running is
left `in_progress` forever with nothing behind it. At `capacity: 1` a single orphan blocks
the whole queue: the next run sits in `waiting` indefinitely. One was still `in_progress` at
15m45s, so do not wait for `ZOMBIE_TASK_TIMEOUT` to rescue it.
Gitea 1.26 has no cancel endpoint — `POST .../actions/runs/{id}/cancel` returns 404 for both
the run id and the run index. Delete the run instead, which does exist and takes the **run
index** (`#14`), not the database id (`51`):
```bash
curl -sS -o /dev/null -w '%{http_code}\n' -X DELETE -H "authorization: token $T" \
"$G/api/v1/repos/gitea_admin/svcforge/actions/runs/14" # -> 204
```
The queued run starts within seconds. The two ids are easy to confuse and the wrong one
returns 404 rather than touching the wrong run, which is the safe direction.
### 7. Verify the whole loop, not just the green checkmarks
```bash ```bash
# the digest CI pushed # the digest CI pushed