2

我有两台机器 HostA 和 HostB 正确配置了 consul 和 docker 守护进程,以便我可以使用docker network create -d overlay sharednet

我必须TestScript.sh检查网络是否存在,如果不存在则创建网络。这个脚本在 HostA 和 HostB 上都可用。我也有一个MasterScript.shonly on A,它基本上只是TestScript.sh在每台机器上调用。运行后MasterScript.sh,我看到了一个令人惊讶的结果,创建了两个同名的网络!!!这可以说是一个 docker 守护进程同步问题。

[HostA]# docker network ls
NETWORK ID          NAME                 DRIVER
ad492bba9efa        sharednet            overlay
ba53d4e7b739        sharednet            overlay

[HostB]# docker network ls
NETWORK ID          NAME                 DRIVER
ad492bba9efa        sharednet            overlay
ba53d4e7b739        sharednet            overlay

预期的行为是,当我在 HostA 上创建网络testnw时,在 HostB 上我应该看到类似这样的内容

[HostB]# docker network ls
68994f95cd67        testnw               overlay
[HostB]# docker network create -d overlay testnw
Error response from daemon: network with name testnw already exists

由于一些限制,我无法修改.MasterScript.sh但我可以修改我的TestScript.sh. 所以问题是,我有可能在这个限制下解决这个竞争条件吗?

4

2 回答 2

1

此问题已报告给 Docker Github,目前正在https://github.com/docker/docker/issues/20648下跟踪

于 2016-03-28T22:29:53.193 回答
0

这个问题仍然没有解决,但我很容易使用run-one命令避免它(而不是run command,它变成了run-one run command,如果命令仍在运行,则返回错误)。

(您可以验证该run-one命令是否可用which run-one

脚步:

  1. 创建一个创建网络的脚本(它可以接受网络名称作为参数,如docker network create "$1")。
  2. 通过调用脚本来创建网络(无论它应该在哪里创建),run-one以确保它不会为同一个网络执行两次(run /path/to/script network-name)。
  3. ?
  4. 利润!

您可以在下面的(演示)脚本中看到这种方法的实际效果:

#!/bin/bash
set -eou pipefail

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
    "clean")
        sudo docker network prune -f
        ;;
    "test1")
        run-one "$file" "test:concurrent" "test:network"
        ;;
    "test2")
        run-one "$file" "test:concurrent" "test:network:unique"
        ;;
    "test:concurrent")
        echo "===========before==========="
        sudo docker network ls
        echo "============================"

        cmd="$1"

        pids=()

        for i in $(seq 1 3); do
            "$file" "$cmd" &
            pids["${i}"]=$!
        done

        idx=0

        for pid in "${pids[@]}"; do
            wait "$pid" && status="$?" || status="$?"
            idx=$((idx + 1))

            if [ "$status" != '0' ]; then
                echo "error in process $pid (#$idx)"
            fi
        done

        echo "===========after============"
        sudo docker network ls
        echo "============================"
        ;;
    "test:network:unique")
        run-one "$file" "test:network"
        ;;
    "test:network")
        sudo docker network create "my-network"
        ;;
    *)
        echo -e "${RED}[error] invalid command: $command${NC}"
        exit 1
        ;;
esac

然后:

  1. 运行/path/to/script clean以删除未使用的网络(确保在开发环境中运行此脚本)。
  2. 运行/path/to/script test1,看到有 3 个网络名为my-network.
  3. 再跑/path/to/script clean
  4. 运行/path/to/script test2并看到只有 1 个网络被命名my-network(3 个进程中有 2 个因run-one命令而出现错误,只有一个创建网络)。

该脚本添加了另一个抽象层(如果您打算使用网络选项可能会增加复杂性),除了您必须创建脚本并引用它这一事实之外,这个解决方案最多只能被描述为一种解决方法。

也就是说,这很容易实现,我认为这不应该被标记为 hack,尽管适当的解决方案 IMO 应该在 docker 引擎端(可能在 API 中)。

这可能不是那么容易用docker-compose强硬实现的,除非您从一个可以轻松更改的脚本运行它,并且您事先知道网络的名称。

于 2021-07-19T23:35:31.133 回答