如何在docker overlay网络创建过程中避免竞争条件?

时间:2016-03-18 22:37:57

标签: docker docker-networking

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

我有TestScript.sh来检查网络是否存在以及是否创建网络。 HostA和HostB都提供此脚本。我在A上只有一个MasterScript.sh,它基本上只在每台机器上调用TestScript.sh。运行MasterScript.sh之后,我看到一个令人惊讶的结果,创建了两个同名的网络!这可以说是一个docker daemon同步问题。

[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。所以问题是,我有可能在这个限制下解决这种竞争条件吗?

2 个答案:

答案 0 :(得分:0)

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

下进行跟踪

答案 1 :(得分: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. 利润!

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

#!/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 的网络(由于 run-one 命令,3 个进程中有 2 个最终出错,只有一个创建网络).

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

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

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