Docker 是一个客户端/服务器应用程序,由瘦客户端 docker 和服务器 dockerd 组成。当你运行一个容器时,客户端对服务器进行一些 API 调用,一个用于创建容器,另一个用于启动它,由于你没有分离运行它,它运行一个附加 API。当您终止 docker 进程时,它会从容器中分离,不再向您显示日志,并终止该客户端部分。但是 dockerd 服务器仍在运行容器,直到容器内的进程(在容器命名空间内以 pid 1 运行)退出。您从未杀死该进程,因为它是从 dockerd 守护进程而不是直接从 docker 客户端生成的。
要解决此问题,我的建议是使用容器名称或 id 运行 docker stop,作为陷阱处理程序的一部分。我什至不会费心在后台运行 docker,而是通过 -d 分离运行。
跟进,在本地测试脚本,看起来当你像这样运行附加的客户端时,杀死 docker 客户端确实会发送一个 docker stop 信号。但是,有一个竞争条件可能导致在数据库运行之前发生停止。命令:
nc -z localhost 5432
即使在 postgresql 开始侦听端口之前也总是会成功,因为 docker 创建了一个端口转发。例如:
$ nc -z localhost 5432 && echo it works
$ docker run -itd --rm -p 5432:5432 busybox tail -f /dev/null
c72427053124608fe18c31e5d6f3307d74a5cdce018503e9fff85dbc039b4fff
$ nc -z localhost 5432 && echo it works
it works
$ docker stop c72
c72
$ nc -z localhost 5432 && echo it works
但是,如果我在脚本中运行 sleep ,这会迫使它等待足够长的时间以使容器完成启动并完成附加,容器就会停止。
更好的脚本版本如下所示,它通过检查日志等待数据库完全启动,并更改陷阱以运行docker stop 命令:
#!/bin/bash -e
base="$(dirname "$0")"
trap 'kill $(jobs -p)' SIGINT SIGTERM EXIT
cid=$(docker run --rm -d -p 5432:5432 -e POSTGRES_PASSWORD=password postgres:12)
# leaving the kill assuming you have other background processes
trap 'docker stop $cid; kill $(jobs -p)' SIGINT SIGTERM EXIT
# waiting for the db to actually start, assuming later steps need the db to be up
while ! docker logs "$cid" 2>&1 | grep -q "database system is ready to accept connections" ; do
sleep 0.1
done
# uh-oh, error
false