通过环境传递bash代码(用于docker-compose)

时间:2015-07-03 18:30:35

标签: shell docker sh docker-compose

我正在尝试处理容器启动的docker-compose顺序中的已知问题,所以我以一个简单的解决方案结束,有一个脚本来检查环境是否已准备好启动命令。

这个想法是,为容器定义了一些环境变量:

  • WAIT_COMMAND 应该是在sh脚本中定义逻辑测试的字符串,必须返回布尔值
  • START_CMD 一个字符串,其中包含要根据 WAIT_COOMAND 结果运行的命令,否则请再次尝试,直至 LOOPS
  • LOOPS 尝试多少次
  • SLEEP 尝试之间睡多少

在该示例中,我在启动应用程序之前等待elasticSearch响应,这取决于要启动的ES数据。

以下脚本将是docker容器上的ENTRYPOINT。

导出的ENV vars

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

如果上述ENV vars可用,脚本应该等到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

如何进行测试以检查卷曲响应是否正常?

如何将逻辑测试命令指定为:

WAIT_COMMAND="$(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) == 200"

以及如何评估它:

is_ready() {
    eval $WAIT_COMMAND
}

3 个答案:

答案 0 :(得分:3)

这个答案是不好的做法。请考虑一种不涉及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命令同义词[(先前版本不是合法的bash比较语法)。
  • ==(在POSIX [中不是有效的运算符)切换到=(POSIX允许)。见the standards document for the test command
  • 在指定时间从双引号切换为单引号,以便curleval被调用之前不会运行。
  • 引用传递给eval的内容,以防止在代码到达eval命令之前发生字符串拆分和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

答案 1 :(得分:2)

如果要存储代码,请不要使用标量变量 - 使用函数;这就是他们的目的。

如果要通过环境变量传递代码,则答案是导出的函数:

wait_command() {
  [ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ]
}
export -f wait_command

...创建一个如下所示的环境变量(在现代的,官方后的shellshock-patch 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

再次重申,更清楚一点:导出的函数只是具有特殊名称和值的环境变量。如果您创建一个名称以BASH_FUNC_开头且以%%结尾且其值以() {开头的环境变量,则您刚刚创建了一个导出函数,该函数可在shell中运行在环境中开始。

因此,在.yml文件中,您可以使用以下内容:

environment: {BASH_FUNC_wait_command%%: '() {  [ $(curl --write-out %{http_code} --silent
    --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ]'}

答案 2 :(得分:1)

不要使用eval。相反,只需获取状态

status=$(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st)"

然后直接测试。

is_ready () {
    [[ $status -eq 200 ]]
}