mirror of
https://github.com/temporalio/temporal.git
synced 2026-08-30 18:41:49 -07:00
## Summary
- The admin-tools container CMD runs `sleep infinity` in the foreground,
which blocks the shell from processing signal traps
- SIGTERM is never handled, so the container hangs until the kubelet
termination deadline before being force-killed
- Background the sleep and use `wait` as the foreground command instead
-- `wait` is a shell builtin that gets interrupted by signals, allowing
the trap handler to exit immediately
## Test plan
Run the following to compare signal handling before and after:
```bash
#!/usr/bin/env bash
set -euo pipefail
STOP_TIMEOUT=5
echo "=== OLD: sleep infinity in foreground (should hang for ${STOP_TIMEOUT}s) ==="
docker run -d --name admin-tools-old alpine:latest \
sh -c "trap exit INT HUP TERM; sleep infinity" >/dev/null
sleep 1
echo "Container running. Sending SIGTERM..."
start=$(date +%s)
docker stop --timeout "$STOP_TIMEOUT" admin-tools-old >/dev/null
elapsed=$(( $(date +%s) - start ))
echo "Stopped in ${elapsed}s (expected: ${STOP_TIMEOUT}s -- signal was ignored)"
docker rm admin-tools-old >/dev/null
echo ""
echo "=== NEW: sleep infinity & wait (should exit immediately) ==="
docker run -d --name admin-tools-new alpine:latest \
sh -c "trap exit INT HUP TERM; sleep infinity & wait" >/dev/null
sleep 1
echo "Container running. Sending SIGTERM..."
start=$(date +%s)
docker stop --timeout "$STOP_TIMEOUT" admin-tools-new >/dev/null
elapsed=$(( $(date +%s) - start ))
echo "Stopped in ${elapsed}s (expected: 0s -- signal was handled)"
docker rm admin-tools-new >/dev/null
```
Expected output:
```
=== OLD: sleep infinity in foreground (should hang for 5s) ===
Container running. Sending SIGTERM...
Stopped in 5s (expected: 5s -- signal was ignored)
=== NEW: sleep infinity & wait (should exit immediately) ===
Container running. Sending SIGTERM...
Stopped in 0s (expected: 0s -- signal was handled)
```
51 lines
1.6 KiB
Docker
51 lines
1.6 KiB
Docker
ARG ALPINE_TAG
|
|
|
|
FROM alpine:${ALPINE_TAG}
|
|
|
|
ARG TARGETARCH
|
|
|
|
RUN apk add --no-cache \
|
|
ca-certificates \
|
|
tzdata && \
|
|
apk upgrade --no-cache zlib && \
|
|
addgroup -g 1000 temporal && \
|
|
adduser -u 1000 -G temporal -D temporal
|
|
|
|
# Copy all admin tool binaries:
|
|
# - temporal (CLI)
|
|
# - temporal-cassandra-tool
|
|
# - temporal-sql-tool
|
|
# - temporal-elasticsearch-tool
|
|
# - tdbg
|
|
COPY --chmod=755 \
|
|
./build/${TARGETARCH}/temporal \
|
|
./build/${TARGETARCH}/temporal-cassandra-tool \
|
|
./build/${TARGETARCH}/temporal-sql-tool \
|
|
./build/${TARGETARCH}/temporal-elasticsearch-tool \
|
|
./build/${TARGETARCH}/tdbg \
|
|
/usr/local/bin/
|
|
|
|
COPY ./build/temporal/schema /etc/temporal/schema
|
|
|
|
USER temporal
|
|
|
|
# Keep the container running idle so users can exec into it for admin tasks.
|
|
#
|
|
# trap exit INT HUP TERM
|
|
# Register a signal handler so that when the shell receives SIGINT, SIGHUP,
|
|
# or SIGTERM it runs "exit" instead of the default PID 1 behavior (ignore).
|
|
#
|
|
# sleep infinity &
|
|
# Start a never-ending process to keep the container alive. It runs in the
|
|
# background ("&") so the shell remains the foreground process.
|
|
#
|
|
# wait
|
|
# Block the shell until background jobs finish. Unlike a foreground "sleep",
|
|
# "wait" is a shell builtin that gets interrupted when a signal arrives,
|
|
# giving the shell a chance to run the trap handler and exit immediately.
|
|
#
|
|
# Without the "& wait" pattern, the shell is blocked on the foreground sleep and
|
|
# never processes signals, causing the container to hang until the kubelet
|
|
# termination deadline before being force-killed.
|
|
CMD ["sh", "-c", "trap exit INT HUP TERM; sleep infinity & wait"]
|