Home » DevOps » Docker » Docker Best Practices Tutorial

About Shivakumar Ramannavar

Shivakumar Ramannavar
Shivakumar has 16+ years of experience in Java Development, Cloud and also has a lot of passion in Cloud-native Applications and Kubernetes. Shiva has a Bachelor's Degree from Visweswaraiah Technological University, India. Shiva has been involved in the development of IT systems using Java/J2EE, Kubernetes, and has designed tonnes of applications both on-premise and on-cloud. Shiva strongly believes there is a great revolution of cloud-native technologies and that we can create a world of difference through learning, sharing, and caring among the community.

Docker Best Practices Tutorial

This tutorial talks about various best practices and examples around the docker containers.

1. Introduction

Docker containers are an essential part of containerization as we moved towards cloud-native applications. Since it is going to massive implementation of containers, it is vital to follow certain best practices based on our past experience, to bring out qualitative containers. The best practices for the docker are around the below classifications:

  • Dockerfile best practices
  • Docker security best practices

2. Dockerfile best practices

2.1 Keep in mind the caching and the layers

This section explains the tricks which make your builds lightweight using caching mechanisms. You need to write the Dockerfile in an efficient way to support appropriate caching. Each time the docker builds, it caches layers of the images. However, when it encounters the COPY/ADD instructions and if the folder or files under the operation have changed then it invalidates the caches the docker builds all the layer thereafter.

Check this example. Here ADD comes after FROM instruction. Let us run mvn clean install and then docker build . --file Dockerfile.wrong_order --tag jcg_example:0.0.1. Now without running the mvn install command again run the docker build command. It will not take much time as the earlier build.

However, run the install once again and then the docker build command, it would take the same time as new. That is because, the ADD jar file in the ADD command has changed and hence has invalidated the cache.

Dockefile.wrong_order
FROM ubuntu:18.04

ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar

RUN apt-get update
RUN apt-get install -y openjdk-8-jdk

CMD ["java", "-jar", "java-docker.jar"]

Download the code and compile the java code using the instructions given in section 5, below. Execute the below command to generate an example image with version 0.0.1.

time docker build . --file Dockerfile.wrong_order --tag jcg_example:0.0.1

# Run Maven clean install again
mvn clean install

# Run docker image again
time docker build . --file Dockerfile.wrong_order --tag jcg_example:0.0.1

Sample Output:

time docker build . --file Dockerfile.wrong_order --tag jcg_example:0.0.1
Sending build context to Docker daemon    128kB
Step 1/5 : FROM ubuntu:18.04
 ---> d27b9ffc5667
Step 2/5 : ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar
 ---> 37171c094f33
Step 3/5 : RUN apt-get update
 ---> Running in 20d2c01147c6
Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
..
..
Step 5/5 : CMD ["java", "-jar", "java-docker.jar"]
 ---> Running in 469ccf6754a2
Removing intermediate container 469ccf6754a2
 ---> aeb0816e2cdb
Successfully built aeb0816e2cdb
Successfully tagged jcg_example:0.0.1

real    1m55.505s
user    0m0.091s
sys     0m0.141s

~/projects/JCGJavaWithDocker$ mvn clean install
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/usr/share/maven/lib/guice.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------------------------------
[INFO] Building JCGJavaWithDocker 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ JCGJavaWithDocker ---
[INFO] Deleting /home/.../projects/JCGJavaWithDocker/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ JCGJavaWithDocker ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ JCGJavaWithDocker ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /home/.../projects/JCGJavaWithDocker/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ JCGJavaWithDocker ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/.../projects/JCGJavaWithDocker/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ JCGJavaWithDocker ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ JCGJavaWithDocker ---
[INFO] No tests to run.
[INFO] 
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ JCGJavaWithDocker ---
[INFO] Building jar: /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT.jar
[INFO] 
[INFO] --- maven-assembly-plugin:2.2-beta-5:single (default) @ JCGJavaWithDocker ---
[INFO] Building jar: /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ JCGJavaWithDocker ---
[INFO] Installing /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT.jar to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT.jar
[INFO] Installing /home/.../projects/JCGJavaWithDocker/pom.xml to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT.pom
[INFO] Installing /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.708 s
[INFO] Finished at: 2020-07-23T12:27:55+01:00
[INFO] ------------------------------------------------------------------------
~/projects/JCGJavaWithDocker$ time docker build . --file Dockerfile.wrong_order --tag jcg_example:0.0.1
Sending build context to Docker daemon    128kB
Step 1/5 : FROM ubuntu:18.04
 ---> d27b9ffc5667
Step 2/5 : ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar
 ---> d6274dbfcb70
Step 3/5 : RUN apt-get update
 ---> Running in def9d44a2262
 ...
 Removing intermediate container e20b310b89f8
 ---> 78c1ecdfc835
Step 5/5 : CMD ["java", "-jar", "java-docker.jar"]
 ---> Running in 67c9afd2ec59
Removing intermediate container 67c9afd2ec59
 ---> 1aff615dd35d
Successfully built 1aff615dd35d
Successfully tagged jcg_example:0.0.1

real    1m57.550s
user    0m0.041s
sys     0m0.251s

Here is the right way of ordering the layers. In this way even we do change the compiled jar, it does not affect the build time of the docker image.

Dockefile.right_order
FROM ubuntu:18.04

RUN apt-get update
RUN apt-get install -y openjdk-8-jdk

ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar

CMD ["java", "-jar", "java-docker.jar"]

Execute the below commands:

# Run Maven clean install again
mvn clean install

docker build . --file Dockerfile.right_order --tag jcg_example:0.0.2

# Run Maven clean install again
mvn clean install

# Run docker image again
docker build . --file Dockerfile.right_order --tag jcg_example:0.0.2

# Examine it takes the lesser time as previous

Sample Output:

~/projects/JCGJavaWithDocker$ mvn clean install
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/usr/share/maven/lib/guice.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------------------------------
[INFO] Building JCGJavaWithDocker 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] Installing /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT.jar to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT.jar
[INFO] Installing /home/.../projects/JCGJavaWithDocker/pom.xml to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT.pom
[INFO] Installing /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.919 s
[INFO] Finished at: 2020-07-23T12:31:21+01:00
[INFO] ------------------------------------------------------------------------

~/projects/JCGJavaWithDocker$ time docker build . --file Dockerfile.right_order --tag jcg_example:0.0.2
Sending build context to Docker daemon    128kB
Step 1/5 : FROM ubuntu:18.04
 ---> d27b9ffc5667
Step 2/5 : RUN apt-get update
 ---> Using cache
 ---> 8818b118f8d4
Step 3/5 : RUN apt-get install -y openjdk-8-jdk
 ---> Using cache
 ---> 967e3b35c6ee
Step 4/5 : ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar
 ---> 2c9aa1a06388
Step 5/5 : CMD ["java", "-jar", "java-docker.jar"]
 ---> Running in ea68774c0b30
Removing intermediate container ea68774c0b30
 ---> 0e51dc5a27a9
Successfully built 0e51dc5a27a9
Successfully tagged jcg_example:0.0.2

real    0m0.436s
user    0m0.033s
sys     0m0.037s
~/projects/JCGJavaWithDocker$ mvn clean install
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/usr/share/maven/lib/guice.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------------------------------
[INFO] Building JCGJavaWithDocker 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] Installing /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT.jar to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT.jar
[INFO] Installing /home/.../projects/JCGJavaWithDocker/pom.xml to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT.pom
[INFO] Installing /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.919 s
[INFO] Finished at: 2020-07-23T12:31:21+01:00
[INFO] ------------------------------------------------------------------------

~/projects/JCGJavaWithDocker$ time docker build . --file Dockerfile.right_order --tag jcg_example:0.0.2
Sending build context to Docker daemon    128kB
Step 1/5 : FROM ubuntu:18.04
 ---> d27b9ffc5667
Step 2/5 : RUN apt-get update
 ---> Using cache
 ---> 8818b118f8d4
Step 3/5 : RUN apt-get install -y openjdk-8-jdk
 ---> Using cache
 ---> 967e3b35c6ee
Step 4/5 : ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar
 ---> 88eabff6bcb3
Step 5/5 : CMD ["java", "-jar", "java-docker.jar"]
 ---> Running in c44ad221562f
Removing intermediate container c44ad221562f
 ---> ce30673e6e80
Successfully built ce30673e6e80
Successfully tagged jcg_example:0.0.2

real    0m0.430s
user    0m0.014s
sys     0m0.063s

2.2 Avoid wild characters in ADD or COPY command – be specific

Whenever you issue wild charecter like * and dot (.) like the one in the below, it will end copy or adding a lot of files and it would copy a lot of files leading to cache bursts.

Dockefile.cacheburst
FROM ubuntu:18.04

RUN apt-get update
RUN apt-get install -y openjdk-8-jdk

ADD target/* /
CMD ["java", "-jar", "JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar"]

2.3 Reduce Image size

# Tip 1: Use the right base image required for the container to work.

For the java based containers like the example above, instead of using ubuntu using jdk images such as openjdk:8-jdk-alpine and the size of the image becomes almost 10% the image generated. Check the size of the jcg_example:0.0.3 image;it must be in the order of 100s instead of 400+ for other images.

Dockerfile.lessersize
FROM openjdk:8-jdk-alpine
ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar
CMD ["java", "-jar", "java-docker.jar"]
docker build . --file Dockerfile.lessersize --tag jcg_example:0.0.3

# Check the size by executing the docker image and look for jcg_example
docker image ls | grep jcg_example

# Size jcg_example:0.0.3 should be in the order of 100s instead of 400+ for other images

Sample Output:

~/projects/JCGJavaWithDocker$ docker build . --file Dockerfile.lessersize --tag jcg_example:0.0.3
Sending build context to Docker daemon    128kB
Step 1/3 : FROM openjdk:8-jdk-alpine
 ---> a3562aa0b991
Step 2/3 : ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar
 ---> ce842dafc94c
Step 3/3 : CMD ["java", "-jar", "java-docker.jar"]
 ---> Running in 04dbfd1f9718
Removing intermediate container 04dbfd1f9718
 ---> 80d8aec6f234
Successfully built 80d8aec6f234
Successfully tagged jcg_example:0.0.3


~/projects/JCGJavaWithDocker$ docker image ls | grep jcg_example
jcg_example                                                                                                                                           0.0.3                  80d8aec6f234        57 seconds ago      105MB
jcg_example                                                                                                                                           0.0.2                  ce30673e6e80        5 minutes ago       490MB
jcg_example                                                                                                                                           0.0.1                  1aff615dd35d        About an hour ago   490MB
jcg_example                                                                                                                                           0.0.6                  88614bba2d06        15 hours ago        105MB
jcg_example                                                                                                                                           0.0.5                  9b71834fad7a        19 hours ago        105MB

2.4 Use standard labels for better traceability

It is recomended to use OCI labels for standard images. Refer specification for list of labels that can be used.

Dockerfile.with_labels
FROM openjdk:8-jdk-alpine

LABEL \
    org.opencontainers.image.title=jcg_example \
    org.opencontainers.image.vendor=Java Code Geeks \
    org.opencontainers.image.version=0.0.5

ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar

RUN apt-get update
RUN apt-get install -y openjdk-8-jdk

CMD ["java", "-jar", "java-docker.jar"]
docker build . --file Dockerfile.with_labels--tag jcg_example:0.0.4

# Check the labels in the image
docker inspect jcg_example:0.0.4 | grep Label -A 4

Sample Output:

~/projects/JCGJavaWithDocker$ docker build . --file Dockerfile.with_labels --tag jcg_example:0.0.4
Sending build context to Docker daemon    128kB
Step 1/4 : FROM openjdk:8-jdk-alpine
 ---> a3562aa0b991
Step 2/4 : LABEL     org.opencontainers.image.title=jcg_example     org.opencontainers.image.vendor="Java Code Geeks"     org.opencontainers.image.version=0.0.5
 ---> Using cache
 ---> d61342b6d696
Step 3/4 : ADD target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar java-docker.jar
 ---> Using cache
 ---> 2c433b6d996a
Step 4/4 : CMD ["java", "-jar", "java-docker.jar"]
 ---> Running in 7b04d5c12198
Removing intermediate container 7b04d5c12198
 ---> fdf0bc611bbe
Successfully built fdf0bc611bbe
Successfully tagged jcg_example:0.0.4

~/projects/JCGJavaWithDocker$ docker inspect jcg_example:0.0.4 | grep Label -A 4
            "Labels": {
                "org.opencontainers.image.title": "jcg_example",
                "org.opencontainers.image.vendor": "Java Code Geeks",
                "org.opencontainers.image.version": "0.0.5"
            }
--
            "Labels": {
                "org.opencontainers.image.title": "jcg_example",
                "org.opencontainers.image.vendor": "Java Code Geeks",
                "org.opencontainers.image.version": "0.0.5"
            }

2.5 Use ARGS wherever possible

Use ARGS for the dynamic content such as jar name, build date, commit hash etc.,.

Dockerfile
FROM openjdk:8-jdk-alpine

ARG COMMIT
ARG BUILD_DATE
ARG APP_VERSION
ARG JAR_NAME

LABEL \
    org.opencontainers.image.title=jcg_example \
    org.opencontainers.image.created=$BUILD_DATE \
    org.opencontainers.image.revision=$COMMIT \
    org.opencontainers.image.vendor="Java Code Geeks" \
    org.opencontainers.image.version=$APP_VERSION

ADD target/$JAR_NAME java-docker.jar

CMD ["java", "-jar", "java-docker.jar"]
# Build the docker
time docker build --build-arg COMMIT=b34eab --build-arg BUILD_DATE=22-JUL-2020 --build-arg APP_VERSION=0.0.5 --build-arg JAR_NAME=JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar --file Dockerfile.with_args . --tag jcg_example:0.0.5 

# Check the availability of images
docker image ls | grep jcg_example

Sample Output:

~/projects/JCGJavaWithDocker$ time docker build --build-arg COMMIT=b34eab --build-arg BUILD_DATE=22-JUL-2020 --build-arg APP_VERSION=0.0.5 --build-arg JAR_NAME=JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar --file Dockerfile . --tag jcg_example:0.0.5
Sending build context to Docker daemon    128kB
Step 1/8 : FROM openjdk:8-jdk-alpine
 ---> a3562aa0b991
Step 2/8 : ARG COMMIT
 ---> Using cache
 ---> 71c6a6a00594
Step 3/8 : ARG BUILD_DATE
 ---> Using cache
 ---> fb62a3145fea
Step 4/8 : ARG APP_VERSION
 ---> Using cache
 ---> 8fc4365d0cbb
Step 5/8 : ARG JAR_NAME
 ---> Using cache
 ---> 792d4dbee6b5
Step 6/8 : LABEL     org.opencontainers.image.title=jcg_example     org.opencontainers.image.created=$BUILD_DATE     org.opencontainers.image.revision=$COMMIT     org.opencontainers.image.vendor="Java Code Geeks"     org.opencontainers.image.version=$APP_VERSION
 ---> Using cache
 ---> b6cfa98420eb
Step 7/8 : ADD target/$JAR_NAME java-docker.jar
 ---> 230212c73cee
Step 8/8 : CMD ["java", "-jar", "java-docker.jar"]
 ---> Running in 30d2a52ae47c
Removing intermediate container 30d2a52ae47c
 ---> 9acb3c756cef
Successfully built 9acb3c756cef
Successfully tagged jcg_example:0.0.5

real    0m0.466s
user    0m0.019s
sys     0m0.034s

~/projects/JCGJavaWithDocker$ docker image ls | grep jcg_example
jcg_example                                                                                                                                           0.0.5                  9acb3c756cef        About a minute ago   105MB
jcg_example                                                                                                                                           0.0.4                  fdf0bc611bbe        5 minutes ago        105MB
jcg_example                                                                                                                                           0.0.3                  80d8aec6f234        8 minutes ago        105MB
jcg_example                                                                                                                                           0.0.2                  ce30673e6e80        13 minutes ago       490MB
jcg_example                                                                                                                                           0.0.1                  1aff615dd35d        About an hour ago    490MB
jcg_example

3. Docker security best practices

These are the best practices to be followed while running the containers.

3.1 Mind the memory and CPU usage

Any application needs memory and CPU for running. Applications are prone to programming errors which may lead to over usage of available resources and may hinder the performance of other containers. Hence it is good to cap the resources used by the containers at the time execution. Here is an example of how to cap the resources. For more information check the docker documentation.

Reusing the docker image from the previous section.

docker run -m 10m --cpus 1.5 jcg_example:0.0.5

3.2 Run as non-root user

It is not at all a good practice to run a container as a root. When you do not mention any user to run, the container run by default as root. There are a couple of options to not to run as root.

One of the options is to set the user in the Dockerfile, so that when the image is run, it automatically run as specified user. Or alternatively, specify at the time of execution of the containers as per below example:

docker run --user $(id -u):$(id -g) -m 10m --cpus 1.5 jcg_example:0.0.5

3.3 Use only trusted Docker Registries

It is always good to use containers from a secure and trusted docker registries. In the industry, there are a lot of generally available docker images for commonly used images. For example, to run nginx, the image is available at hub.docker.com with the image name as nginx with label 1.19.1.

However, security experts at the enterprises, do no recommend using the global image. Instead, the image has to be downloaded, duly tested for security vulnerabilities, and to be uploaded into an industry-wide image repository. These repositories are protected by firewalls and role-based authentication mechanisms to percent unauthorized access.

3.4 Do not include sensitive information in the containers

I have seen many developers irresponsibly leave the sensitive information such user names, passwords, security tokens for the REST APIs they use in the containers. Some use AWS resources to upload information/files from the containers and they need AWS CLI tokens. It is bad to burn the image with tokens and distribute across the world.

Since it is not good to bake images with secrets (tokens, passwords, API keys). we have to design containers to accept injected secrets or containers must directly fetch the secret. Here are the various options to inject secrets into the container:

  • Inject as environment variables: This is not a recommended way as the containers might be hacked and environment values might be inspected and easily secrets be retrieved
  • Mount the folder containing files with secrets: This is better way than environmental variables. you can write all secrets into a file and mount that folder as volume as secrets. However, its not good from the maintenability perspective to write sensitive information on machine where the docker containers are run.
  • Fetch the sensitive information from a centralized repository: In this method you can use a predefined side car to fetch the tokens dynamically from a centralised stored password manager such a vault.

4. Setup for Execution

In this section we will execute the programs and see how it is working.

Prerequisites:

  • Java 1.8 installed in the system. Environment variables JAVA_HOME set to the Java location and PATH set to the directory containing javac and java binaries ( %JAVA_HOME%/bin on windows or $JAVA_HOME/bin on Linux machines)
  • Maven 3 installed. Refer documentation for instructions on how to install.
  • Docker engine installed using the documentation from the docker site. For Linux OS, add user into the docker group using the steps (this might require a restart).
  • Source code zip and unzipped to a location (say, C:\JavaCodeGeeks. This would be different for Linux)
  • Command-line is used for example

4.1 Execution using command line

Step 1: Open the command line
Step 2: Go the folder where examples are unzipped  C:\JavaCodeGeeks
Step 3: Compile the project using the command mvn clean install

Sample Output:

~/projects/JCGJavaWithDocker$ mvn clean install
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/usr/share/maven/lib/guice.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------------------------------
[INFO] Building JCGJavaWithDocker 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] Installing /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT.jar to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT.jar
[INFO] Installing /home/.../projects/JCGJavaWithDocker/pom.xml to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT.pom
[INFO] Installing /home/.../projects/JCGJavaWithDocker/target/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar to /home/.../.m2/repository/org/example/JCGJavaWithDocker/1.0-SNAPSHOT/JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.919 s
[INFO] Finished at: 2020-07-23T12:31:21+01:00
[INFO] ------------------------------------------------------------------------

Step 4: Generate the latest image and check if it is created.

# Generate the image
time docker build --build-arg COMMIT=b34eab --build-arg BUILD_DATE=22-JUL-2020 --build-arg APP_VERSION=0.0.5 --build-arg JAR_NAME=JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar --file Dockerfile . --tag jcg_example:latest

# Check the availability of image
docker image ls | grep jcg_example | grep latest

Sample Output:

time docker build --build-arg COMMIT=b34eab --build-arg BUILD_DATE=22-JUL-2020 --build-arg APP_VERSION=0.0.5 --build-arg JAR_NAME=JCGJavaWithDocker-1.0-SNAPSHOT-jar-with-dependencies.jar --file Dockerfile . --tag jcg_example:latest
Sending build context to Docker daemon    128kB
Step 1/8 : FROM openjdk:8-jdk-alpine
 ---> a3562aa0b991
Step 2/8 : ARG COMMIT
 ---> Using cache
 ---> 71c6a6a00594
Step 3/8 : ARG BUILD_DATE
 ---> Using cache
 ---> fb62a3145fea
Step 4/8 : ARG APP_VERSION
 ---> Using cache
 ---> 8fc4365d0cbb
Step 5/8 : ARG JAR_NAME
 ---> Using cache
 ---> 792d4dbee6b5
Step 6/8 : LABEL     org.opencontainers.image.title=jcg_example     org.opencontainers.image.created=$BUILD_DATE     org.opencontainers.image.revision=$COMMIT     org.opencontainers.image.vendor="Java Code Geeks"     org.opencontainers.image.version=$APP_VERSION
 ---> Using cache
 ---> b6cfa98420eb
Step 7/8 : ADD target/$JAR_NAME java-docker.jar
 ---> Using cache
 ---> 230212c73cee
Step 8/8 : CMD ["java", "-jar", "java-docker.jar"]
 ---> Using cache
 ---> 9acb3c756cef
Successfully built 9acb3c756cef
Successfully tagged jcg_example:latest

real    0m0.119s
user    0m0.039s
sys     0m0.012s

# Check the availability of images
~/projects/JCGJavaWithDocker$ docker image ls | grep jcg_example | grep latest
jcg_example                                                                                                                                           latest                 9acb3c756cef        4 minutes ago       105MB

5. Download the Project

Download
You can download the full source code of this example here: Docker Best Practices Tutorial

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

 

1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design

 

and many more ....

 

Receive Java & Developer job alerts in your Area

 

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments