Search code examples
python-3.xdockerkubernetes-helm

ARG is never substituted when building Dockerfile


This question has been asked around many times and I've tried so many permutations! But nothing seems to work :(

I have a Dockerfile for a simple Flask app in Python which looks like this:

FROM ubuntu:20.04
ARG env=BOOGA
MAINTAINER First Last "[email protected]"

RUN apt-get update -y && apt-get install -y python3-pip python3.8

# We copy just the requirements.txt first to leverage Docker cache
COPY ./requirements.txt /app/requirements.txt

WORKDIR /app

RUN pip3 install -r requirements.txt

EXPOSE 5000

COPY . /app

ENTRYPOINT [ "python3", "src/app.py", "$env" ]

I try to build this container like this:

docker build . -t try:latest --force-rm --no-cache --build-arg env=LOCAL

We use helm for Kubernetes.

When the pod which houses this container is starting up, the command that's run is

python3 src/app.py $env

and not

python3 src/app.py LOCAL

which is what I specified while building my Docker image. Before incorporating helm I used ENV and a docker run -e env=LOCAL ... command to run my app but with helm I guess I don't have to do that.

How can I get the value of $env into the ENTRYPOINT command correctly substituted?

I have also tried the following to no avail:

ENTRYPOINT ["python3 src/app.py $env"]
ENTRYPOINT ["python3 src/app.py ${env}"]
ENTRYPOINT python3 src/app.py $env

Solution

  • I'd use an environment variable over a positional argument here:

    • In your code, look at os.environ.get('env', 'development') to get the value.
    • In your Dockerfile, don't pass it as a ARG or mention it in the startup-time metadata
      # no ARG
      CMD ["python3", "src/app.py"] # no $env
      
    • Use the exact same Docker image in all environments.
    • In your Helm values.yaml make it configurable
      environment: default
      
    • In your Helm template, set the environment variable
      env:
      - name: env
        value: {{ .Values.environment }}
      
    • At deploy time you can select an alternate environment
      docker run -e env=staging ...
      
      helm install ... --set environment=qa
      

    When you use JSON-array for ENTRYPOINT or CMD (or RUN) nothing is ever substituted at all. In the first form in your question, the script is passed the literal string $env; in the later forms, the container is invoked with a single word containing spaces and also the literal $env, and you'll get a "command not found" error when the whole thing is interpreted as a command name with no arguments. You need to use the shell form to get variable expansion.

    There's a further issue in that ARG values are included in the environment, but only for RUN commands. Dockerfile variable substitution doesn't happen for the command-type directives. So you'd also need to explicitly set an ENV to get the value visible at container startup time.

    ENV env $env                   # Relay ARG to ENV
    CMD python3 src/app.py "$env"  # Not JSON-array form