Last active
August 10, 2023 13:37
-
-
Save LLFourn/70b70b7e26b5e57de7894eefee3c97e1 to your computer and use it in GitHub Desktop.
Docker, Getting around PID 1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# So you want to pipe shell scripts to docker. But docker runs the container's command as PID 1. | |
# Linux doesn't set up signal handlers for PID 1. | |
# This gives you the following problems: | |
# 1. The script can't kill itself: | |
echo 'kill $$; echo "Still alive"' | docker run -i --rm alpine sh | |
# 2. You can't kill the script from outside with a normal kill: | |
echo "sleep 1000" | docker run -i --rm alpine sh & sleep 1 && kill $! | |
docker ps # it didn't die | |
#==== Solving problem 1 ==== | |
# We need to run out script as a PID other than PID 1. This usually works: | |
echo 'kill $$; echo "Still alive!"'| docker run -i --rm alpine sh -c sh | |
# But for things with bash as /bin/sh you need another trick: | |
echo 'kill $$; echo "Still alive"'| docker run -i --rm centos sh -c ':;sh' #<-- take note | |
#==== Solving problem 2 ==== | |
# In order to make it killable from the outside we need to set up a signal hanlder in PID 1 | |
echo 'trap "exit 0" TERM; while true; do sleep 1; done' | docker run -i --rm alpine sh & | |
kill $! | |
docker ps # it should be dead (soon at least) | |
# But this only kinda works... | |
# It won't respond to the signal while it's doing sleep(1) or stuck in any other command | |
#==== Solving problem 1 & 2 togther properly ==== | |
# We need to have the script running as non-PID1 and we need to have a signal handler that will | |
# run immediately | |
# Solution: | |
docker run -i --rm alpine sh -c 'trap "exit 0" TERM; sh <&0 & wait $!' | |
# Problem 1 | |
echo 'kill $$; echo "Still alive"' | docker run -i --rm alpine sh -c 'trap "exit 0" TERM; sh <&0 & wait $!' | |
# Problem 2 | |
echo 'sleep 10000' | docker run -i --rm centos sh -c 'trap "exit 0" TERM; sh <&0 & wait $!' & | |
kill $! | |
docker ps # it's dead | |
# If you want to you can even forward the signal inside the container to your script's process: | |
echo 'sleep 10000' | docker run -i --rm centos sh -c 'trap "kill \$!" TERM; sh <&0 & wait $!' & | |
#==== 💀FINAL BOSS💀 ==== | |
#...But apparently sh <&0 only works on bash ie centos 🤦😩. | |
# So we need to create a fifo and delete it at EXIT. This works on all platforms I've tested: | |
echo 'sleep 10000' | docker run -i --rm alpine sh -c \ | |
'mkfifo stdin; trap "kill \$!" TERM; trap "rm stdin" EXIT; sh<stdin & cat>stdin; wait $!' & | |
kill $! | |
docker ps | |
# It's gone!! 🎉 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment