summaryrefslogtreecommitdiff
path: root/deploy/buildbot/worker/RECOVERY.txt
blob: 5d12b48b67dadfa50417be3c097886a389916e45 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# Worker recovery — FreeBSD + Linuxulator podman

This host runs the buildbot **worker**; the **master** is 192.168.3.110
(Debian armv7l, "dangofactory.net"). The worker is a container, and two host
level facts decide how it must be started. Both were measured, not guessed.

## Fact 1 — `pf` must be enabled

Without it the container will not start at all:

```
Error: unable to start container: plugin type="bridge" failed (add):
cni plugin bridge failed: The pf kernel module must be loaded to support
ipMasq networks
```

`/etc/pf.conf` already carries the matching NAT rule for podman's subnet, so
enabling is all that is needed. The ruleset is NAT-only (no `block` rules), so
this does not filter traffic:

```
nat on em0 inet from 10.88.0.0/16 to any -> (em0)   # podman
nat on em0 inet from 10.0.0.0/24  to any -> (em0)
```

```sh
pfctl -n -f /etc/pf.conf      # dry run the ruleset first
pfctl -f /etc/pf.conf -e      # load + enable
```

## Fact 2 — the CNI bridge path does not work; use `--network=host`

Every outbound `connect()` from a bridge-networked container fails with
**EACCES**, including to the container's own gateway. Measured from a *fresh*
container with no volumes, so it is not a per-container problem:

| destination | `--network=podman` (bridge) | `--network=host` |
|---|---|---|
| `10.88.0.1:22` (gateway)   | EACCES (13) | OK |
| `192.168.3.110:9989` (master) | EACCES (13) | OK |
| `8.8.8.8:53`               | EACCES (13) | OK |
| `1.1.1.1:443`              | EACCES (13) | OK |

The container's route table is correct, so this is ocijail refusing the
connect, not a routing problem. `--network=host` loses nothing here: the worker
only makes **outbound** connections (master `:9989`, github, the FTP deploy).

`--network=slirp4netns` is not an alternative — it fails outright with
`not implemented GetSlirp4netnsIP`.

## The caches are in the container's writable layer — extract them first

`GRADLE_USER_HOME=/var/jenkins_home/.gradle` (6.3 GB) and `.konan` (1.7 GB)
live in the container's **writable layer**. There is no named volume
(`podman volume ls` is empty, and `run-worker.sh`'s `-v jenkins-data:...` was
never real). So **`podman rm` destroys them**, and the next build re-downloads
everything.

`podman commit` is the obvious way to preserve them and **it fails**:

```
Error: copying layers and metadata for container "6b6421f1...":
on copy: write /var/tmp/buildah3539187385/layer: no space left on device
```

buildah stages the layer into `/var/tmp`, which is on the root zfs pool —
1.5 GB free against a 6 GB layer. Point its tmpdir at `/ROM` (451 GB):

```sh
podman --tmpdir /ROM/tmp_probe commit ...   # still needs 6+GB of scratch
```

Copying just the cache directories out is smaller and is what
`extract-caches.sh` does:

```sh
sh deploy/buildbot/worker/extract-caches.sh
# -> /ROM/worker-cache/{.gradle,.konan,.kotlin}
```

## The recovery

```sh
sh deploy/buildbot/worker/extract-caches.sh        # once, before any rm
sh deploy/buildbot/worker/recreate-worker-hostnet.sh
```

Verify with the **master**, not the container — a container that is merely
`Up` may still be retrying:

```sh
curl -s http://192.168.3.110:8010/api/v2/workers | python3 -m json.tool
#   "connected_to": [{"masterid": 1}]      <- attached
tail -3 /ROM/worker/twistd.log
#   [Broker,client] Connected to buildmaster; worker is ready
```

## Traps on this host

- **Background jobs stop.** `terminal(background=true)` wrappers land in `Ts`
  and silently do nothing. A `podman commit` reported "running" for 5 minutes
  while frozen; CPU time stayed at 0:00.03. Check `ps -o stat=` and
  `kill -CONT <pid>` (loop — one CONT is often not enough, and the *child*
  needs it too, not just the wrapper).
- **The root pool is nearly full** (23 G, 1.5 GB free). `podman commit` and
  anything that stages into `/var` will die with ENOSPC. Use
  `--tmpdir /ROM/tmp_probe` and `export TMPDIR=/ROM/tmp_probe`.
- **Secrets live in two places and they differ.** The container mounts
  `/root/buildbot/secrets/*` — that is the correct set. The similarly named
  `/ROM/wiiu/secrets/` has a **different** `worker_pass` and an **empty**
  `git_pass`; copying from there produces a worker that cannot authenticate.
  Container uid 1000 must be able to *read* the secrets, so they are mode 644.
- **The master's secrets must not be echoed.** `master.cfg` interpolates
  `$(cat /run/secrets/...)` inside single-quoted step bodies; tooling that
  redacts `Authorization:` may show you the redacted form and you can mistake it
  for file corruption. Check the bytes, not the rendered output.

## What is NOT wrong here

Checked and confirmed healthy, so do not spend time on them:

- the master (`/api/v2/masters.last_active` tracks the clock; poller is current)
- git HTTP auth (`/root/buildbot/secrets/git_pass` → `info/refs` = 200; no auth = 401)
- the worker passphrase (`buildbot.tac` matches the mounted secret)
- `github_token` (`GET /user` → 200, login `ketsuban152`)
- OOM / crash / network loss — the earlier exit was `143` after a clean
  `SIGTERM`, `OOMKilled:false`, with normal keepalives up to 8 s prior