
나는 docker를 기반으로 한 클러스터 제출 시스템을 가지고 있으며 로컬 실행도 지원하도록 노력하고 있습니다. 로컬로 실행할 때 작업을 시작하는 명령은 기본적으로
docker run /results/src/launcher/local.sh
클러스터 실행을 위해 다른 스크립트가 대신 실행됩니다. 내가 직면한 어려움은 CTRL-C를 올바르게 지원하면서 로컬 사용자로 코드를 실행하는 방법입니다. docker run은 진입점을 uid 0으로 시작하므로 su -c
. 기본적으로 스크립트는 다음 두 가지를 실행해야 합니다.
- 사전 실행 스크립트(루트라고 함)
- Python 프로그램(호출 사용자라고 함)
현재 스크립트의 내용은 다음과 같습니다.
# Run prerun script
$PRERUN &
PRERUN_PID=$!
wait $PRERUN_PID
PRERUN_FINISHED=true
status=$?
if [ "$status" -eq "0" ]; then
echo "Prerun finished successfully."
else
echo "Prerun failed with code: $status"
exit $status
fi
# Run main program dropping root privileges.
su -c '/opt/conda/bin/python /results/src/launcher/entrypoint.py \
> >(tee -a /results/stdout.txt) 2> >(tee -a /results/stderr.txt >&2)' \
$USER &
PYTHON_PID=$!
wait $PYTHON_PID
PYTHON_FINISHED=true
status=$?
if [ "$status" -eq "0" ]; then
echo "Entrypoint finished successfully."
else
echo "Entrypoint failed with code: $status"
exit $status
fi
신호 전파는 다음과 같은 스크립트로 처리됩니다.
_int() {
echo "Caught SIGINT signal!"
if [ "$PRERUN_PID" -ne "0" ] && [ "$PRERUN_FINISHED" = "false" ]; then
echo "Sending SIGINT to prerun script!"
kill -INT $PRERUN_PID
PRERUN_PID=0
fi
if [ "$PYTHON_PID" -ne "0" ] && [ "$PYTHON_FINISHED" = "false" ]; then
echo "Sending SIGINT to Python entrypoint!"
kill -INT $PYTHON_PID
PYTHON_PID=0
fi
}
PRERUN_PID=0
PYTHON_PID=0
PRERUN_FINISHED=false
PYTHON_FINISHED=false
trap _int SIGINT
/results/src/launcher/entrypoint.py
에 의해 실행되는 코드인 에 신호 처리기가 있습니다 su -c
. 그러나 SIGINT를 얻지 못하는 것 같습니다. 나는 문제가 su -c
. 예상대로 PYTHON_PID
bash 스크립트에는 Python 인터프리터의 PID가 할당되지 않고 su
프로그램의 PID가 할당됩니다. os.system("ps xa")
Python 진입점에서 다음을 수행하면 다음이 표시됩니다.
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /bin/bash /results/src/launcher/local.sh user 1000 1000 /results/src/example/compile.sh
61 ? S 0:00 su -c /opt/conda/bin/python /results/src/launcher/entrypoint.py \ > >(tee -a /results/stdout.txt) 2> >(tee -a /results/stderr.txt >&2) user
62 ? Ss 0:00 bash -c /opt/conda/bin/python /results/src/launcher/entrypoint.py \ > >(tee -a /results/stdout.txt) 2> >(tee -a /results/stderr.txt >&2)
66 ? S 0:01 /opt/conda/bin/python /results/src/launcher/entrypoint.py
67 ? S 0:00 bash -c /opt/conda/bin/python /results/src/launcher/entrypoint.py \ > >(tee -a /results/stdout.txt) 2> >(tee -a /results/stderr.txt >&2)
68 ? S 0:00 bash -c /opt/conda/bin/python /results/src/launcher/entrypoint.py \ > >(tee -a /results/stdout.txt) 2> >(tee -a /results/stderr.txt >&2)
69 ? S 0:00 tee -a /results/stdout.txt
70 ? S 0:00 tee -a /results/stderr.txt
82 ? R 0:00 /opt/conda/bin/python /results/src/launcher/entrypoint.py
83 ? S 0:00 /bin/dash -c ps xa
84 ? R 0:00 ps xa
PYTHON_PID
PID 61이 할당됩니다. 그러나 Python 인터프리터를 정상적으로 종료할 수 있기를 원하므로 거기에서 일부 신호를 포착할 수 있어야 합니다. 이와 같은 상황에서 SIGINT를 Python 인터프리터에 전달하는 방법을 아는 사람이 있습니까? 내가 성취하려는 일을 수행하는 더 현명한 방법이 있을까요? docker run
코드가 로컬 실행으로 예약될 때 명령을 함께 작성하는 코드를 완전히 제어할 수 있습니다 .
답변1
여기에는 몇 가지 일이 진행되고 있습니다. 먼저 컨테이너 내부에서 pid 1로 쉘 스크립트를 실행하고 있습니다. 다양한 시나리오에서 해당 프로세스는 cont+c를 보거나 docker stop
신호를 보내는 것이며 이를 트랩하고 처리하는 것은 bash에 달려 있습니다. 기본적으로 pid 1로 실행될 때 bash는 신호를 무시합니다(Linux 서버에서 단일 사용자 모드를 처리한다고 생각합니다). 다음과 같이 해당 신호를 명시적으로 트랩하고 처리해야 합니다.
trap 'pkill -P $$; exit 1;' TERM INT
스크립트 상단에 있습니다. 그러면 SIGTERM 및 SIGINT(cont+c에 의해 생성됨)를 포착하고 하위 프로세스를 종료한 후 즉시 종료됩니다.
su
다음으로, 신호 처리를 중단할 수 있는 프로세스를 포크하는 명령이 있습니다 . 나는 gosu
포크 syscall 대신 exec를 실행하여 프로세스 목록에서 자신을 제거하는 것을 선호합니다. gosu
Dockerfile에 다음을 사용하여 설치할 수 있습니다 .
ARG GOSU_VER=1.10
ARG GOSU_ARCH=amd64
RUN curl -sSL "https://github.com/tianon/gosu/releases/download/${GOSU_VER}/gosu-${GOSU_ARCH}" >/usr/bin/gosu \
&& chmod 755 /usr/bin/gosu \
&& gosu nobody true
마지막으로 분기점에 진입점에 많은 논리가 있고 백그라운드 프로세스가 완료될 때까지 기다립니다. 이는 포그라운드에서 프로세스를 실행하여 단순화할 수 있습니다. 실행하는 마지막 명령은 exec
셸이 계속 실행되는 것을 방지하기 위해 시작될 수 있습니다. 를 사용하여 오류를 포착 set -e
하거나 이를 확장하여 플래그와 함께 실행 중인 명령에 대한 디버깅을 표시할 수 있습니다 -x
. 최종 결과는 다음과 같습니다.
#!/bin/bash
set -ex
# in case a signal is received during PRERUN
trap 'exit 1;' TERM INT
# Run prerun script
$PRERUN
# Run main program dropping root privileges.
exec gosu "$USER" /opt/conda/bin/python /results/src/launcher/entrypoint.py \
> >(tee -a /results/stdout.txt) 2> >(tee -a /results/stderr.txt >&2)
로그 를 제거할 수 있다면 스크립트 상단 에서 으로 /results
전환하여 컨테이너의 결과만 볼 수 있어야 합니다./bin/bash
/bin/sh
docker logs