使用 run-this-one 运行的 Linux 脚本不适用于 docker run-one#!/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

我遇到了一个问题,我在 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$

Terminal2:

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

Terminal2:

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 回答,我放置了一个陷阱和一个清理函数来移除容器,如下面的脚本所示:

/path/to/test

#!/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 应该成功运行(它失败了,因为第二个 {{ 1}} 在清理中移除容器)。

我尝试将清理放在 cmd1 命令中(从其他 2 个地方删除),以便仅由运行的单个进程调用,以避免上述问题,但奇怪的是清理不是在那里调用,即使陷阱也在那里定义。

iCMS 回答:使用 run-this-one 运行的 Linux 脚本不适用于 docker run-one#!/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

为了简化你的问题,我会使用这个命令来重现问题:

run-one docker run --rm -it alpine sleep 10

可以看出 - 无论是 run-one 还是 run-this-one - 行为绝对不是想要的。

由于该命令创建了一个由 docker 管理的进程,我怀疑 run-one 工具集不是适合该工作的工具,因为不应使用 {{1} 杀死 docker 容器},而是使用 pkill

一个相对简单的解决方案是接受 docker 希望您终止容器的方式,并创建您的简短 docker kill 脚本来正确处理 docker。

run-one

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
本文链接:https://www.f2er.com/133041.html

大家都在问