我在docker-compose中定义了docker环境。它有3个容器。两个有数据库,第三个有cli脚本解释器。
第三个容器必须等待2个db容器才能接受连接。
当我只有一个数据库容器时,它非常简单。第3个容器(基于Alpine)entrypoint.sh:
#!/bin/sh
while ! nc -z $MYSQL_HOST $MYSQL_PORT; do
echo "Waiting for MySQL to accept connections ..."
sleep 0.2
done
echo "MYSQL ready"
exec "$@"
现在使用第二个容器我想用嵌套循环来解决这个问题,但由于posix shell一般不支持数组,所以我不确切知道如何。
一般的想法是创建一个字符串,其中host1:port1由空格分隔
#!/bin/sh
SERVERS="DB1_HOST:DB1_PORT DB2_HOST:DB2_PORT"
通过使用空间(我认为IFS ='')创建项目而不是使用DB:PORT来使用nc
检查连接并在连接成为可能时从SERVERS变量中删除DB:PORT来遍历它。
我主要担心的是我不知道如何从SERVERS变量中删除DB:PORT。
这是您发布的答案的重构,这避免了一些特性和低效率。
#!/bin/sh
: ${MYSQL_HOST?must not be unset}
: ${MYSQL_PORT?must not be unset}
: ${REDIS_HOST?must not be unset}
: ${REDIS_PORT?must not be unset}
# avoid uppercase for private variables
# avoid gratuitous non-portable newline
# pad with surrounding spaces
servers=" MYSQL_HOST:MYSQL_PORT REDIS_HOST:REDIS_PORT "
while true; do
for server in $servers; do
db_host=${server%:*}
db_port=${server#*:}
if nc -z "$db_host" "$db_port"; then
# Show diagnostic messages on stderr
echo "$0: Host '$db_host' started accepting connections" >&2
servers="${servers#* $db_host:$db_port } ${servers% $db_host:$db_port *}"
else
echo "$0: Waiting for host '$db_host' to start accepting connections on port '$db_port' ..." >&2
fi
done
case $servers in
*[! ]*) ;;
*) break
esac
sleep 0.4
done
使用单个空格分隔的字符串进行循环是有点hacky,但至少我们避免外部进程来操作这里的服务器列表。在没有数组变量的情况下,没有真正好的方法来选择性地删除某些服务器并保留其他服务器。此外,由于空间填充,当我们删除所有服务器时,servers
字符串不一定是空的,因此我们在循环内检查它是否包含非空格字符。
接受服务器作为命令行参数(可能直接在主机:端口形式)可能更有意义,而不是要求调用者设置多个变量(更不用说那些变量使用大写)。然后循环"$@"
也许会使操作服务器列表变得更简单。
我会指向http://shellcheck.net/,它可以在没有人为干预的情况下诊断出许多这些错误,http://www.iki.fi/era/unix/award.html#echo指出了一些常见的反模式。关于sh
的POSIX parameter expansion规范解释了上面代码中的许多构造。 Bash manual section on parameter expansion显然记录了Bash,但也可能作为辅助来源。
您可以将值存储为函数参数:
#!/bin/sh
sqlup() {
for server do
IFS=: read -r host port
while ! nc -z "$host" "$port"; do
printf 'Waiting for MySQL to accept connections...\n'
sleep 0.2
done
done
printf 'MySQL ready\n'
}
sqlup DB1_HOST:DB1_PORT DB2_HOST:DB2_PORT # ...
exec "$@"
工作方案。使用ASH在Linux Alpine 3.7上进行测试
如果有人知道为什么脚本坏了(检查行以#开头的代码,请发表评论〜谢谢)
#!/bin/sh
MYSQL_HOST=${MYSQL_HOST:-3}
MYSQL_PORT=${MYSQL_PORT:-3}
REDIS_HOST=${REDIS_HOST:-3}
REDIS_PORT=${REDIS_PORT:-3}
REQ_VARS="MYSQL_HOST\nMYSQL_PORT\nREDIS_HOST\nREDIS_PORT"
printf "$REQ_VARS" | while IFS='' read -r VAR
do
value=$(eval "echo \$$VAR")
if [[ ${value} == 3 ]]; then
echo "Error: Variable '$VAR' cannot be undefined"
exit 1
fi
done
SERVERS="MYSQL_HOST:MYSQL_PORT\nREDIS_HOST:REDIS_PORT"
while test ! -z "$SERVERS"
do
for server in $(printf "$SERVERS"); do
while IFS=: read HOST PORT; do
DB_HOST=$(eval "echo \$$HOST")
DB_PORT=$(eval "echo \$$PORT")
if nc -z $DB_HOST $DB_PORT; then
echo "Host '$DB_HOST' started accepting connections"
# When removing last server from SERVERS script was terminating
if [ "$(printf "$SERVERS" | wc -l)" -gt 0 ]; then
SERVERS=$(printf "$SERVERS" | grep -iv "$HOST:$PORT")
else
SERVERS=""
fi
continue
fi
echo "Waiting for host '$DB_HOST' to start accepting connections on port '$DB_PORT' ..."
done <<EOF
$(printf "$server")
EOF
done
# Wait 0.4 of the second before checking again
sleep 0.4
done