container: Stop gracefully on podman stop
Containers used `sleep infinity` as PID 1, which never reacts to SIGTERM: the kernel does not apply default signal dispositions to PID 1, and sleep has no SIGTERM handler, so every `podman stop` silently ignored the signal, waited out the full 10-second grace period, and fell back to SIGKILL. PID 1 now traps TERM and INT itself and exits on request, so one-off manual stops of a still-running agent container end immediately instead of after the kill timeout. The PID 1 command is baked in at create time, so containers predating this change are detected and recreated automatically on the next run.
This commit is contained in:
parent
43dc757b92
commit
fcbfa32545
2 changed files with 53 additions and 1 deletions
|
|
@ -179,6 +179,12 @@ class AgentContainer:
|
|||
)
|
||||
self.remove_container()
|
||||
|
||||
if self.container_exists() and not self._container_cmd_matches():
|
||||
logger.info(
|
||||
"Recreating container: it predates the SIGTERM-aware PID 1"
|
||||
)
|
||||
self.remove_container()
|
||||
|
||||
if not self.container_exists():
|
||||
self.create_container()
|
||||
|
||||
|
|
@ -513,7 +519,11 @@ class AgentContainer:
|
|||
self.IMAGE,
|
||||
"sh",
|
||||
"-c",
|
||||
"sleep infinity",
|
||||
# PID 1 ignores signals with default dispositions, so a bare
|
||||
# `sleep infinity` never reacts to SIGTERM and every
|
||||
# `podman stop` times out into SIGKILL. Trapping TERM (and
|
||||
# INT) makes stop requests end the container immediately.
|
||||
'trap "exit 0" TERM INT; sleep infinity & wait',
|
||||
]
|
||||
)
|
||||
|
||||
|
|
@ -555,6 +565,26 @@ class AgentContainer:
|
|||
).stdout
|
||||
return "AGENT_CONTAINER=1" in env
|
||||
|
||||
def _container_cmd_matches(self) -> bool:
|
||||
"""Whether the container runs the current PID 1 command.
|
||||
|
||||
The PID 1 entrypoint is baked in at create time and can only change
|
||||
by recreating the container. Comparing against Config.Cmd catches
|
||||
containers created before the SIGTERM-aware fix."""
|
||||
cmd = subprocess.run(
|
||||
[
|
||||
self.RUNTIME,
|
||||
"container",
|
||||
"inspect",
|
||||
"-f",
|
||||
"{{json .Config.Cmd}}",
|
||||
self.container_name,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip()
|
||||
return "TERM INT" in cmd
|
||||
|
||||
def start_container(self) -> bool:
|
||||
subprocess.run(
|
||||
[self.RUNTIME, "start", self.container_name],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue