Sunday, July 3, 2022

Docker 101 - Dockerfile - ENV and ARG

    


Dockerfile Instructions - ENV



The ENV instruction sets the environment variable <key> to the value <value>.
This value will be in the environment for all subsequent instructions in the build stage and can be replaced inline in many as well.

The environment variables set using ENV will persist when a container is run from the resulting image.

Take the following as an example:

Dockerfile:
FROM python:alpine3.16
  ENV MY_ENV='Hello world'

Ex:
$ docker image build -t env-exp .

Result:
Sending build context to Docker daemon  2.048kB
  Step 1/2 : FROM python:alpine3.16
  ---> b22cfbf3bfa6
  Step 2/2 : ENV MY_ENV='Hello world'
  ---> Running in 66ff3946bdae
  Removing intermediate container 66ff3946bdae
  ---> 215b48c4908f
  Successfully built 215b48c4908f
  Successfully tagged env-exp:latest

Ex:
$ docker container run -it env-exp sh
  / # printenv

Result:
...
  MY_ENV=Hello world
  ...

According to the previous example, the env variables will be persisted inside container.


In addition, it can be adjusted when running a container.

Ex:
$ docker container run --env MY_ENV='modified_when_run' -it env-exp sh
  / # printenv

Result:
...
MY_ENV=modified_when_run
  ...


Environment Variables can be used in certain instructions as variables.


Dockerfile:
FROM python:alpine3.16
  ENV MY_APP_DIR='/app/'
  WORKDIR $MY_APP_DIR
  ADD hello-world.py hello-world.py

Ex:
$ docker image build -t env-as-variable .

Result:
Sending build context to Docker daemon  3.072kB
  Step 1/4 : FROM python:alpine3.16
  ---> b22cfbf3bfa6
  Step 2/4 : ENV MY_APP_DIR='/app/'
  ---> Running in 263cd2a9d616
  Removing intermediate container 263cd2a9d616
  ---> 62f999684aaa
  Step 3/4 : WORKDIR $MY_APP_DIR
  ---> Running in 9e70090d46aa
  Removing intermediate container 9e70090d46aa
  ---> 635bf8e45985
  Step 4/4 : ADD hello-world.py hello-world.py
  ---> d0ee09cd29a5
  Successfully built d0ee09cd29a5
  Successfully tagged env-as-variable:latest

Ex:
$ docker container run -it env-as-variable sh
/app # ls -l
  /app # printenv

Result:
-rw-rw-r--    1 root     root            21 Jun  7 11:59 hello-world.py

  MY_APP_DIR=/app/

Environment variable persistence can cause unexpected side effects.
Therefore, if an environment variable is only needed during build, and not in the final image, consider setting a value for a single command instead or using ARG, which is not persisted in the final image.


Dockerfile Instructions - ARG



The ARG instruction defines a variable that users can pass at build-time to the builder with the docker build command using the --build-arg <varname>=<value> flag.

An ARG instruction can optionally include a default value. If an ARG instruction has a default value and if there is no value passed at build-time, the builder uses the default.

Take the following Dockerfile as an example:
* ARG VERSION and build_no include a default value.
* FROM can interact with ARG
* ${user:-default_user} means if ARG user is not set, then we use default_user instead

Dockerfile:
ARG VERSION=alpine3.16
  FROM python:${VERSION}
  ARG build_no=1
  ARG user
  WORKDIR app
  RUN echo ${build_no} ${user:-default_user} > log.txt

Ex:
$ docker image build -t arg-exp .

Result:
Sending build context to Docker daemon  2.048kB
  Step 1/6 : ARG VERSION=alpine3.16
  Step 2/6 : FROM python:${VERSION}
  alpine3.16: Pulling from library/python
  2408cc74d12b: Pull complete
  2f22aa6a21a6: Pull complete
  54cc066f118a: Pull complete
  03624af3d529: Pull complete
  4ae78d2f3e6f: Pull complete
  Digest: sha256:97725c6081f5670080322188827ef5cd95325...
  Status: Downloaded newer image for python:alpine3.16
  ---> 27edb73bd1fc
  Step 3/6 : ARG build_no=1
  ---> Running in cb48df4b6e52
  Removing intermediate container cb48df4b6e52
  ---> c72fda3ca32e
  Step 4/6 : ARG user
  ---> Running in 0f252e5d4c1a
  Removing intermediate container 0f252e5d4c1a
  ---> 90e61bddcab5
  Step 5/6 : WORKDIR app
  ---> Running in aecbaf0b83b3
  Removing intermediate container aecbaf0b83b3
  ---> d9d2e1f5b94a
  Step 6/6 : RUN echo ${build_no} ${user:-default_user} > log.txt
  ---> Running in 9072470ff1b3
  Removing intermediate container 9072470ff1b3
  ---> 9de14e06b7e5
  Successfully built 9de14e06b7e5
  Successfully tagged arg-exp:latest

Ex:
$ docker container run -it --rm arg-exp sh
  /app # ls
  /app # more log.txt

Result:
log.txt

  1 default_user


Impact on build cache?



ARG variables are not persisted into the built image as ENV variables are. However, ARG variables do impact the build cache in similar ways. If a Dockerfile defines an ARG variable whose value is different from a previous build, then a “cache miss” occurs upon its first usage, not its definition. In particular, all RUN instructions following an ARG instruction use the ARG variable implicitly (as an environment variable), thus can cause a cache miss. Reference

Continue with the previous exp, we build the image again.

Ex:
$ docker image build -t arg-exp .

Result:
Sending build context to Docker daemon  2.048kB
  Step 1/6 : ARG VERSION=alpine3.16
  Step 2/6 : FROM python:${VERSION}
  ---> 27edb73bd1fc
  Step 3/6 : ARG build_no=1
  ---> Using cache
  ---> c72fda3ca32e
  Step 4/6 : ARG user
  ---> Using cache
  ---> 90e61bddcab5
  Step 5/6 : WORKDIR app
  ---> Using cache
  ---> d9d2e1f5b94a
  Step 6/6 : RUN echo ${build_no} ${user:-default_user} > log.txt
  ---> Using cache
  ---> 9de14e06b7e5
  Successfully built 9de14e06b7e5
  Successfully tagged arg-exp:latest

As we can see, cache will be used.

Then if we build image again and pass some arg.

Ex:
$ docker image build --build-arg user=frank -t arg-exp .

Result:
Sending build context to Docker daemon  2.048kB
  Step 1/6 : ARG VERSION=alpine3.16
  Step 2/6 : FROM python:${VERSION}
  ---> 27edb73bd1fc
  Step 3/6 : ARG build_no=1
  ---> Using cache
  ---> c72fda3ca32e
  Step 4/6 : ARG user
  ---> Using cache
  ---> 90e61bddcab5
  Step 5/6 : WORKDIR app
  ---> Using cache
  ---> d9d2e1f5b94a
  Step 6/6 : RUN echo ${build_no} ${user:-default_user} > log.txt
  ---> Running in fd126c3a0481
  Removing intermediate container fd126c3a0481
  ---> cd0ce55baeb9
  Successfully built cd0ce55baeb9
  Successfully tagged arg-exp:latest

And we can see on step 4, there is no cache miss.
Only on step 6, since it is the first time to use ARG user, so the cache miss occur.

No comments:

Post a Comment