[英]Linux script run with run-this-one doesn't work with docker
我遇到了一个问题,我在 cronjob 中运行命令并希望确保它尚未被执行。 我实现了作为run-one [command]
( man-page ) 运行的目标。
如果我想取消已经运行的命令并强制运行新命令,我会作为run-this-one [command]
。
至少这是我所期望的,但是如果命令运行 docker 容器,则另一个进程似乎已终止(但不是),终端显示Terminated
,但继续显示在容器中运行的命令输出(但容器结束运行后的命令不会被执行)。 在这种情况下,不会执行运行run-this-one
的命令(不是预期的)。
例子:
/path/to/file.sh
#!/bin/bash
set -eou pipefail
echo "sleep started..." >&2
docker run --rm alpine /bin/sh -c 'echo "sleep started inside..." && sleep 5 && echo "sleep ended inside..."'
echo "sleep ended..." >&2
如果我在终端窗口中sudo run-one /path/to/file.sh
,然后在另一个终端中运行(在上一个命令结束运行之前)命令sudo run-one /path/to/file.sh
,这命令未按预期执行,并且该命令成功结束。
1号航站楼:
user@host:/path$ sudo run-one /path/to/file.sh
sleep started...
sleep started inside...
sleep ended inside...
sleep ended...
user@host:/path$
航站楼2:
user@host:/path$ sudo run-one /path/to/file.sh
user@host:/path$
但是,如果我在终端窗口中sudo run-one /path/to/file.sh
,然后在另一个终端中运行(在上一个命令结束运行之前)命令sudo run-this-one /path/to/file.sh
,此命令未执行,这是不期望的,并且该命令显示在终端Terminated
,终端显示user@host:/path$
,但容器中的输出仍然显示(该命令仍在运行在第一个终端中创建的容器)。
1号航站楼:
user@host:/path$ sudo run-one /path/to/file.sh
sleep started...
sleep started inside...
Terminated
user@host:/path$ sleep ended inside...
# terminal doesn't show new input from the keyboard, but I can run commands after
航站楼2:
user@host:/path$ sudo run-this-one /path/to/file.sh
user@host:/path$
如果文件更改为:
/path/to/file.sh
#!/bin/bash
set -eou pipefail
echo "sleep started..." >&2
sleep 5
echo "sleep ended..." >&2
上面带有 docker 的脚本文件只是一个例子,在我的情况下它是不同的,但问题是相同的,并且独立于运行或不运行容器的-it
。
有人知道为什么会这样吗? 这个问题有(不是很复杂,也不是很hackish)的解决方案吗? 我已经在 VirtualBox 机器(使用 vagrant)内的 Ubuntu 20.04 中执行了上述命令。
更新 (2021-07-15)
根据@ErikMD评论和@DannyB 的回答,我放置了一个陷阱和一个清理函数来删除容器,如下面的脚本所示:
/路径/到/测试
#!/bin/bash
set -eou pipefail
trap 'echo "[error] ${BASH_SOURCE[0]}:$LINENO" >&2; exit 3;' ERR
RED='\033[0;31m'
NC='\033[0m' # No Color
function error {
msg="$(date '+%F %T') - ${BASH_SOURCE[0]}:${BASH_LINENO[0]}: ${*}"
>&2 echo -e "${RED}${msg}${NC}"
exit 2
}
file="${BASH_SOURCE[0]}"
command="${1:-}"
if [ -z "$command" ]; then
error "[error] no command entered"
fi
shift;
case "$command" in
"cmd1")
function cleanup {
echo "cleaning $command..."
sudo docker rm --force "test-container"
}
trap 'cleanup; exit 4;' ERR
args=( "$file" "cmd:unique" )
echo "$command: run-one ${args[*]}" >&2
run-one "${args[@]}"
;;
"cmd2")
function cleanup {
echo "cleaning $command..."
sudo docker rm --force "test-container"
}
trap 'cleanup; exit 4;' ERR
args=( "$file" "cmd:unique" )
echo "$command: run-this-one ${args[*]}" >&2
run-this-one "${args[@]}"
;;
"cmd:unique")
"$file" "cmd:container"
;;
"cmd:container")
echo "sleep started..." >&2
sudo docker run --rm --name "test-container" alpine \
/bin/sh -c 'echo "sleep started inside..." && sleep 5 && echo "sleep ended inside..."'
echo "sleep ended..." >&2
;;
*)
echo -e "${RED}[error] invalid command: $command${NC}"
exit 1
;;
esac
如果我在另一个终端中运行/path/to/test cmd1
( run-one
) 和/path/to/test cmd2
( run-this-one
),它会按预期工作( cmd1
进程停止并删除容器,并且cmd2
进程成功运行)。
如果我在 2 个终端中运行/path/to/test cmd2
,它也会按预期工作(第一个cmd2
进程停止并删除容器,第二个cmd2
进程成功运行)。
但不是那么好:在上述 2 种情况下,有时第二个进程在第一个删除容器之前停止并出现错误(这可能间歇性发生,可能是由于竞争条件)。
情况变得更糟:如果我在 2 个终端中运行/path/to/test cmd1
,两个命令都会失败,尽管第一个cmd1
应该成功运行(它失败是因为第二个cmd1
在清理中删除了容器)。
我试图将清理放在cmd:unique
命令中(从其他 2 个地方删除),以便仅由运行的单个进程调用,以避免上述问题,但奇怪的是清理没有在那里调用,即使陷阱也在那里定义。
只是为了简化您的问题,我将使用此命令来重现问题:
run-one docker run --rm -it alpine sleep 10
可以看出 - 无论是run-one
还是run-this-one
- 行为绝对不是想要的。
由于该命令创建了一个由docker
管理的进程,我怀疑run-one
工具集不是适合该工作的工具,因为 docker 容器不应该用pkill
杀死,而应该用docker kill
。
一种相对简单的解决方案是接受 docker 希望您终止容器的方式,并创建您的短期run-one
脚本来正确处理 docker。
run-one-docker.sh
#!/usr/bin/env bash
if [[ "$#" -lt 2 ]]; then
echo "Usage: ./run-one-docker.sh NAME COMMAND"
echo "Example: ./run-one-docker.sh temp alpine sleep 10"
exit 1
fi
name="$1"
command=("${@:2}")
container_is_running() {
[ "$( docker container inspect -f '{{.State.Running}}' "$1" 2> /dev/null)" == "true" ]
}
if container_is_running "$name"; then
echo "$name is already running, aborting"
exit 1
else
docker run --rm -it --name "$name" "${command[@]}"
fi
run-this-one-docker.sh
#!/usr/bin/env bash
if [[ "$#" -lt 2 ]]; then
echo "Usage: ./run-this-one-docker.sh NAME COMMAND"
echo "Example: ./run-this-one-docker.sh temp alpine sleep 10"
exit 1
fi
name="$1"
command=("${@:2}")
container_is_running() {
[ "$( docker container inspect -f '{{.State.Running}}' "$1" 2> /dev/null)" == "true" ]
}
if container_is_running "$name"; then
echo "killing old $name"
docker kill "$name" > /dev/null
fi
docker run --rm -it --name "$name" "${command[@]}"
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.