0

I am creating a Nginx docker image that I'll be using as a reverse proxy component in ECS/Fargate in AWS. I'm using the official Nginx image as the base image (1.17.5).

When the container starts I'm trying to run a bash script from an ENTRYPOINT to go out to the AWS Parameter Store and retrieve certificate info. This work fine, however when I try to add a parameter to pass to the bash script (e.g. ENTRYPOINT ["installcerts.sh", "AppName"] it executes the script but the container terminates without error.

I want the container to continue on to start up Nginx after the parameterized batch script.

Here is my Docker File:

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;"]

And here is my "installcerts.sh" script showing utilizing the parameter passed in from ENTRYPOINT.

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

The "exec "$@" in the bash script is needed, but honestly, I don't completely understand how or why this works even after hours of trying to track it down.

The short story is:

If I use this, the container does what i want it to do but I can't send a parameter to the bash script.

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

But If I use this, the script will run WITH the parameter successfully, but the container exits and Nginx doesn't start up.

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

What am I doing wrong?

KSS
  • 821
  • 3
  • 10
  • 26

1 Answers1

3

When you set an ENTRYPOINT on your image, then docker passes that script the value of CMD (or whatever you pass on the command line after the image name). For example, if you have:

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

Then docker runs, effectively:

/entrypoint.sh /usr/bin/myprogram

That is, Docker itself never runs /usr/bin/myprogram: it is entirely up to the ENTRYPOINT script to do that. That is what the exec "$@" is for. This is a shell variable the evaluates to:

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

In our example, this would evaluate to:

exec /usr/bin/myprogram

...which replaces the current script with /usr/bin/myprogram. But if we were to set ENTRYPOINT as you have in your question:

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

Then exec "$@" will in fact evaluate to:

exec appName /usr/bin/myprogram

And since appName isn't a valid command, the container will simply fail.

There are a few ways of dealing with this:

  1. Do you really need to pass parameters to your ENTRYPOINT script? What about using environment variables instead?
  2. If you always pass parameters to your script, you can use the shift shell command to drop those from the positional parameters before using $@. For example, for a script that expects two parameters:

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

    ...but this only works if you always pass two parameters.

  3. You can implement command line option processing in your script using e.g. the getopts command:

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

Having said that: I would opt for option 1 (use environment variables) as being the simplest solution:

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

And then in your ENTRYPOINT script you can just use the $PARAM1 variable where you need it.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • That was the most easy to understand description about the use of Entrypoint+CMD+"$@" I have ever read (and believe me, I've read quite a few). Thank you so much! It's still obtuse, but thanks to you I get it now. After I had asked the question I ended up use environment variables which totally works. Still, you have demystified the way this works thanks (again) to you. :) – KSS Nov 12 '19 at 23:46