繁体   English   中英

使用 run-this-one 运行的 Linux 脚本不适用于 docker

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM