通过环境传递bash代码(用于docker-compose)
我试图处理docker-compose命令的一个已知问题,因此我以一个简单的解决scheme结束了脚本,检查脚本是否准备好启动命令。
这是这个想法,有一些环境variables定义到容器:
- WAIT_COMMAND应该是一个在sh脚本中定义一个逻辑testing的string,它必须返回一个布尔值
- START_CMD根据WAIT_COOMAND结果运行命令的string,否则请重试,直到到达LOOPS 。
- LOOPS多less次尝试
- 睡眠之间多less次睡眠
在那个例子中,我正在等待elasticSearch在启动我的应用程序之前作出响应,这取决于ES数据的启动。
以下脚本将是我在docker集装箱上的入口点。
导出ENVvariables
WAIT_COMMAND="$(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) == 200" LOOPS=3 START_CMD="python my_script_depending_on_elastic.py" SLEEP=2
具有上述ENVvariables可用的脚本应等到ES请求返回代码200
#!/bin/bash is_ready() { eval $WAIT_COMMAND } # wait until is ready i=0 while ! is_ready; do i=`expr $i + 1` if [ $i -ge $LOOPS ]; then echo "$(date) - still not ready, giving up" exit 1 fi echo "$(date) - waiting to be ready" sleep $SLEEP done #start the script exec $START_CMD
问题是代码在is_ready函数行中不起作用 ,它不返回布尔值,而是试图执行200作为命令
# ./wait_elastic.sh ./wait_elastic.sh: line 9: 200: command not found Fri Jul 3 18:26:43 UTC 2015 - waiting to be ready ./wait_elastic.sh: line 9: 200: command not found Fri Jul 3 18:26:45 UTC 2015 - waiting to be ready ./wait_elastic.sh: line 9: 200: command not found Fri Jul 3 18:26:47 UTC 2015 - still not ready, giving up
我该如何做testing来检查curl响应是否正常?
如何指定一个逻辑testing命令为:
WAIT_COMMAND="$(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) == 200"
以及如何评估它:
is_ready() { eval $WAIT_COMMAND }
?
这个答案是不好的做法。 请考虑一个不涉及eval
的方法。
wait_command='[ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ]' is_ready() { eval "$wait_command" }
与原始代码的差异:
- 其实包括
test
命令同义词[
在被评估的环境variables(之前的版本不是合法的bash比较语法)内部)。 - 从
==
(不是POSIX[
]中的有效运算符)切换到=
(POSIX允许)。 请参阅test
命令的标准文档 。 - 在分配时从双引号切换到单引号,这样
curl
不会运行,直到调用eval
。 - 引用传递给
eval
的内容,以防止在代码到达eval
命令之前发生string拆分和glob扩展。
在docker-compose
的上下文中实现它可能如下所示:
app: command: docker/wait environment: - wait_command=[ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ] - wait_loops=10 - wait_sleep=30
…用脚本如下所示:
#!/bin/bash s_ready() { eval "$wait_command"; } # wait until is ready i=0 while ! is_ready; do if (( ++i >= wait_loops )); then echo "$(date) - still not ready, giving up" exit 1 fi echo "$(date) - waiting to be ready" sleep $wait_sleep done
如果你想存储代码,不要使用标量variables – 使用函数; 这就是他们的目的。
如果你想通过一个环境variables传递代码,那么答案是一个导出的函数:
wait_command() { [ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ] } export -f wait_command
…创build一个如下所示的环境variables(在现代的官方shellshock补丁bash中):
BASH_FUNC_wait_command%%=() { [ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ]
…你可以像这样运行,没有eval
:
wait_command
再次重申,更清楚一点: 导出的函数只是具有特殊名称和值的环境variables 。 如果您创build一个名称以BASH_FUNC_
并以%%
结尾且其值以() {
开头的环境variables,则您刚刚创build了一个导出函数,该函数将可用于在环境中随其启动的shell中运行。
因此,在你的.yml
文件中,你可以使用像这样的东西:
environment: {BASH_FUNC_wait_command%%: '() { [ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ]'}
不要使用eval
。 相反,只是得到状态
status=$(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st)"
然后直接进行testing。
is_ready () { [[ $status -eq 200 ]] }