Skip to content

Read the process table with ps in the box - #110

Merged
splitbrain merged 1 commit into
splitbrain:mainfrom
splitbrain-bot:ps-in-box
Oct 11, 2026
Merged

splitbrain merged 1 commit into
splitbrain:mainfrom
splitbrain-bot:ps-in-box

Conversation

@splitbrain-bot

Copy link
Copy Markdown

Part of #109 (step 1).

Changes

  • docker top is removed. The background probe, the tunnel reconciler and the box-level stop now read the process
    table with one ps -eo pid,ppid,etimes,args in the box (containerProcesses in docker.ts). Before, the probe and
    the reconciler got host pids from docker top, and the stop read the table a second time with ps in the box,
    because a kill in the box needs the box's own pids. containerProcessesFromInside is removed.
  • The fallback to a ps format without etimes is removed. It was there for host ps versions that do not know it.
    The box image has procps, so elapsedSeconds is always a number, in ContainerProcess and in the API type
    BoxWork. The dashboard no longer shows a dash for a missing age.
  • The probe finds the init by pid 1, and the hold by the sleep infinity with parent pid 1. Before, it found both by
    their command anywhere in the tree, because host pids do not start at 1. So a tini or a sleep infinity that the
    agent started was not counted as work. Now it is.
  • doc/threads.md says that the table comes from ps in the box.

Tests

  • Orchestrator: 881 passed. The docker top tests are removed. The ps parser test has the age column. The upstream
    test has one fake process table instead of two. A new test checks that a look-alike tini and sleep infinity
    under the agent are work.
  • Dashboard: npm run check and the unit tests pass. The e2e tests did not run.
  • The ps format and the parser agree with the output of a real box (procps-ng 4.0.4, tini, sleep infinity).

Not tested against a real Docker daemon.

Notes for the next steps

  • Step 4, the file service. The probe counts each process in the box as work, except the init, its
    sleep infinity, the adapters, their direct children, the residentProcesses of a harness and the ps of the
    reading. A connected boxes-files is none of these. Without a change, the idle reaper never stops a box with a
    connected file service, and the box-level stop kills the file service. Step 4 must add a rule for it in
    gateway/background.ts, for example a fixed command pattern as READING_ITSELF is for ps. When the disk usage walk
    starts a child process, for example du, the rule must also cover the children of the file service. This was the
    same with docker top.
  • Step 5 and step 6, setup at the start. boxes-setup, the agent set install and the home seed run in the box
    before it is ready. While they run, the probe reads the box as busy. The entrypoint itself counts as work until it
    replaces itself with sleep infinity. This lasts some seconds and has no effect on the idle reaper, which acts only
    after IDLE_STOP_MINUTES.
  • Step 7, the Runtime interface. The process list is now a plain command in the box through execInContainer.
    It needs no runtime method of its own and becomes run(boxId, ['ps', '-eo', 'pid,ppid,etimes,args']). The call has
    no timeout today. The timeout of run will apply to it.
  • Step 8, conformance tests. The process list needs ps from procps with etimes in the box image. A conformance
    test can check that this command works in a new box.
  • Step 9, Kubernetes. The pid 1 rule needs the init of the box to be pid 1 in the box. In a pod, this is true while
    shareProcessNamespace is off, which is the default. With it on, pid 1 is the pause process, and each box reads as
    busy at all times. The box pod must not set it. A process started by an exec has parent pid 0 on both runtimes. The
    adapter rules do not depend on this.

The background probe and the tunnel reconciler read the process table
with docker top, which ran ps on the host and reported host pids. The
box-level stop needed the container's own pids for its kill, so it read
the table a second time with ps in the box. A process list through the
Docker API also had no counterpart on another runtime.

All three now use one ps -eo pid,ppid,etimes,args in the box. The box
image always has procps, so the age of a process is always known, and the
API reports it as a number.

The pids are now the box's own, so the init is pid 1 and the hold is the
sleep infinity directly under it. Before, both were found by their
command anywhere in the tree, so a tini or a sleep infinity that the
agent started was not counted as work.

Part of splitbrain#109
@splitbrain
splitbrain merged commit 740d579 into splitbrain:main Oct 11, 2026
8 of 9 checks passed
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.

2 participants