Day 20 of 90 Days of DevOps Challenge: Real-World Application Deployment & DockerFile Fundamentals

Vaishnavi DVaishnavi D
4 min read

In yesterday’s blog, I started exploring Docker and learned the basic Docker commands and architecture. how containers work, what Docker images are, and how they help in building isolated environments for applications. Today, I’m diving deeper to learn how to run real-world applications using Docker images and then move on to the fundamentals of Dockerfiles - the blueprints of custom Docker images.

Understanding Docker Networking

  • When we create a Docker container, it acts like a virtual guest OS.

  • The host machine (EC2/Linux) is the main OS.

  • We cannot directly access the guest OS; we need to use port mapping with the -p flag.

Key Points:

  • You can run multiple containers using the same internal port (like 8080), but each must map to different host ports.

  • Two containers cannot use the same host port.

  • To check port and logs:

      docker logs <container_id>
    

Running a Real-World Application with Docker

Let’s try running a hello-world using Docker.

Step-by-step:

  1. Pull the Docker image from Docker Hub:

     docker pull nginxdemos/hello
    
  2. Run the container in detached mode (in the background):

     docker run -d nginxdemos/hello
    

    Expose and map port 8080 to access the app from your host machine:

     docker run -d -p 8080:80 nginxdemos/hello
    

Accessing the application:

If you’re running this on a remote server (like AWS EC2), open your browser:

http://<your-ec2-public-ip>:8080/

Make sure port 8080 is allowed in your security group.

To Stop the Container:

docker ps                      # get the container ID
docker stop <container_id>

Creating Your Own Docker Image Using Dockerfile

A Dockerfile is a plain text file containing instructions to assemble a Docker image. It defines the environment, dependencies, source files, and the commands to run your app.

Syntax to Build an Image:

docker build -t <image-name> .

If using a custom Dockerfile:

docker build -t <image-name> -f Dockerfile.custom .

Dockerfile Keywords & Instructions Explained

Let’s walk through the essential Dockerfile instructions:

1. FROM

Specifies the base image for your application. it’s always the first line in a Dockerfile.

FROM openjdk:17      # pulls the official openjdk 17 image from Docker Hub.
FROM python:3.11     # pulls the official Python 3.11 image from Docker Hub.
FROM node:19.5       # pulls the official node 19.5 image from Docker Hub.

2. MAINTAINER

Defines the author of the Dockerfile (deprecated in favor of LABEL now but still used in many files).

Note: This is deprecated. Use LABEL instead.

Deprecated:

MAINTAINER ZerotoRoot <zerotoroot@example.com>

Recommended:

LABEL maintainer="ZerotoRoot <zerotoroot@example.com>"

3. RUN

Used to execute commands during image build. Typically for installing dependencies or setting up the environment. Every RUN command creates a new layer in the image.

RUN apt-get update
RUN git clone <repo_url>
RUN mvn clean package

NOTE: Combine commands with && to reduce layers.

4. CMD

Used to specify the default command to run when a container is started.

CMD ["java", "-jar", "app.jar"]
CMD ["python", "app.py"]

Only the last CMD will be executed if multiple commands are provided.
It can be overridden via the command line when starting the container.

5. ENTRYPOINT

Specifies the main command that always runs when the container starts.
More rigid than CMD.

ENTRYPOINT ["java", "-jar", "app.jar"]
ENTRYPOINT ["python", "app.py"]

Unlike CMD, ENTRYPOINT cannot be overridden easily by command line arguments.

6. COPY

Used to copy files from host system to container.

NOTE: Doesn't unpack archives. Only supports local files.

COPY target/app.jar /usr/app/app.jar
COPY app.py /usr/app/app.py

7. ADD

Similar to COPY, but with additional features, like fetching files from URLs or extracting tar archives.

ADD target/app.jar /usr/app/app.jar
ADD https://example.com/file.tar.gz /usr/app/

8. WORKDIR

Sets the working directory inside the container.

WORKDIR /usr/app
CMD ["java", "-jar", "app.jar"]

This saves you from using long paths like /usr/app/app.jar repeatedly.

9. USER

Defines which user should run the commands and container processes.

USER appuser

Prevents running processes as root for better security.

10. EXPOSE

Specifies the port on which the containerized application will run.

EXPOSE 8080

This doesn’t actually publish the port. It’s just documentation unless you use -p or --publish.

Example DockerFile:

FROM openjdk:17
WORKDIR /usr/app
COPY target/app.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

Final Thoughts

Today was a big step forward in my Docker journey. I went beyond the basics and learned how to run a real-world Spring Boot application, understood the importance of port mapping, and got hands-on with writing Dockerfiles to create custom images. It was exciting to see how these pieces fit together to make containerized apps work seamlessly. Next, I’m looking forward to exploring multi-stage Dockerfiles, managing data with volumes, and using environment variables to handle configurations more efficiently. Lots more to learn and I’m all in!

0
Subscribe to my newsletter

Read articles from Vaishnavi D directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vaishnavi D
Vaishnavi D