使用 Nginx Docker 容器发布 运行 初始化脚本

Issue Running an Initialization Script with Nginx Docker Container

我正在创建一个 Nginx docker 映像,我将在 AWS ECS/Fargate 中将其用作反向代理组件。我使用官方 Nginx 图像作为基础图像 (1.17.5)。

当容器启动时,我正在尝试从 ENTRYPOINT 运行 bash 脚本转到 AWS Parameter Store 并检索证书信息。这工作正常,但是当我尝试添加一个参数以传递给 bash 脚本时(例如 ENTRYPOINT ["installcerts.sh", "AppName"] 它执行脚本但容器终止而没有错误。

我希望容器在参数化批处理脚本后继续启动Nginx。

这是我的 Docker 文件:

FROM nginx:1.17.5

# Install AWS CLI/BOTO3, JQ
RUN apt-get update &&  apt-get install -y &&  apt-get install awscli -y && apt-get install jq -y

# Copy Nginx config to etc/nginx
COPY proxy_ssl.conf /etc/nginx/conf.d/

VOLUME ["/etc/nginx/conf/d"]

# Copy entrypoint bash script to install certs from the AWS Parameter Store
COPY installcerts.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/installcerts.sh

#Pull certs from Parameter Store
ENTRYPOINT ["/usr/local/bin/installcerts.sh", "AppName"]

CMD ["nginx", "-g", "daemon off;"]

这是我的 "installcerts.sh" 脚本,展示了如何利用从 ENTRYPOINT 传入的参数。

#!/usr/bin/env bash
-e
echo Installing certs...
aws ssm get-parameters --name /Certificate//CRT | jq '.Parameters[0].Value' -r > /etc/nginx/conf.d/app.crt
aws ssm get-parameters --name /Certificate//KEY | jq '.Parameters[0].Value' -r > /etc/nginx/conf.d/app.key
echo Exiting script.
exec "$@"

bash 脚本中的 "exec "$@" 是必需的,但老实说,即使经过数小时的追踪,我仍不完全理解它是如何或为什么起作用的。

短篇小说是:

如果我使用它,容器会执行我希望它执行的操作,但我无法将参数发送到 bash 脚本。

ENTRYPOINT ["/usr/local/bin/installcerts.sh"]

但是如果我使用这个,脚本会 运行 成功地使用参数,但是容器退出并且 Nginx 不会启动。

ENTRYPOINT ["/usr/local/bin/installcerts.sh", "AppName"]

我做错了什么?

当您在图像上设置 ENTRYPOINT 时,docker 会向该脚本传递 CMD 的值(或您在命令行中图像名称后传递的任何值)。例如,如果您有:

ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/myprogram"]

然后 docker 有效运行:

/entrypoint.sh /usr/bin/myprogram

也就是说,Docker 本身从不运行 /usr/bin/myprogram:这完全取决于 ENTRYPOINT 脚本。这就是 exec "$@" 的用途。这是一个 shell 变量,计算结果为:

Expands to the positional parameters, starting from one. (bash(1) man page, in the "Special Parameters" section)

在我们的示例中,这将计算为:

exec /usr/bin/myprogram

...用 /usr/bin/myprogram 替换当前脚本。但是,如果我们像您在问题中那样设置 ENTRYPOINT

ENTRYPOINT ["/entrypoint.sh", "appName"]

那么 exec "$@" 实际上会计算为:

exec appName /usr/bin/myprogram

并且由于 appName 不是一个有效的命令,容器将会失败。

有几种方法可以解决这个问题:

  1. 您真的需要将参数传递给您的 ENTRYPOINT 脚本吗?改用环境变量怎么样?
  2. 如果您 总是 将参数传递给您的脚本,您可以使用 shift shell 命令从位置使用 $@ 之前的参数。例如,对于需要两个参数的脚本:

    param1=
    param2=
    shift 2
    
    ...do stuff here...
    
    exec "$@"
    

    ...但这仅在您 总是 传递两个参数时有效。

  3. 您可以在脚本中实现命令行选项处理,例如使用getopts 命令:

    while getopts a:b: ch; do
      case $ch in
        (a) param1=$OPTARG
            ;;
        (b) param2=$OPTARG
            ;;
      esac
    done
    shift $(( $OPTIND - 1 ))
    
    ...do stuff here...
    
    exec "$@"
    

话虽如此:我会选择选项 1(使用环境变量)作为最简单的解决方案:

docker run -e PARAM1="some value" ...

然后在您的 ENTRYPOINT 脚本中,您可以在需要的地方使用 $PARAM1 变量。