dev-ops-challenges

Write a Dockerfile

Technical Overview

In containerized environments, building custom images systematically is achieved using a Dockerfile. A Dockerfile is a text document that contains a sequential list of instructions and commands that the Docker daemon executes to assemble an image.

Rather than modifying running containers interactively and saving them (as done in docker commit), writing a Dockerfile provides a transparent, reproducible, and version-controlled approach to image building.

Dockerfile Build Pipeline & Layers

Every instruction in a Dockerfile creates a new read-only layer in the resulting image. Docker caches these layers during the build process. If an instruction has not changed, Docker reuses the cached layer, significantly accelerating subsequent builds.

graph TD
    DF[Dockerfile Instructions] -->|docker build| Engine[Docker Engine]
    Engine -->|FROM ubuntu| Layer1[Layer 1: Base Ubuntu OS]
    Engine -->|RUN apt-get install ...| Layer2[Layer 2: Apache Installed]
    Engine -->|RUN sed -i ...| Layer3[Layer 3: Custom Port Configured]
    Engine -->|EXPOSE 8087| Metadata[Image Metadata: Ports/CMD/Env]
    
    Layer1 --> Layer2
    Layer2 --> Layer3
    Layer3 --> Image[Custom Docker Image]
    Metadata --> Image

Detailed Dockerfile Reference

Below are the core directives used to construct a standard Dockerfile:

1. FROM

Sets the base image for subsequent instructions. A valid Dockerfile must begin with a FROM instruction (e.g., FROM ubuntu:latest).

2. ENV

Defines environment variables that persist during the build process and when the container is run.

3. RUN

Executes shell commands during the image build process. Each RUN instruction creates a new image layer. To minimize the layer count, combine commands using && and run cleanup steps in the same directive.

4. COPY & ADD

Copies files or directories from the Docker host’s build context into the container filesystem.

5. EXPOSE

Informs Docker that the container listens on the specified network ports at runtime. This directive acts as metadata documentation; it does not actually publish or map ports on the host.

6. CMD & ENTRYPOINT

Define the process that runs when the container is started.


Infrastructure & Configuration Requirements


Step-by-Step Implementation

Step 1: Connect to the Application Server

Establish an SSH connection from the Jump Host to App Server 1:

ssh tony@stapp01

Provide the server password when prompted.


Step 2: Create the Build Directory

Create the target build directory where the Dockerfile will reside:

sudo mkdir -p /opt/docker

Step 3: Write the Dockerfile

Create and edit the Dockerfile at /opt/docker/Dockerfile:

sudo vi /opt/docker/Dockerfile

Add the following configuration:

# Use ubuntu as the base image
FROM ubuntu:latest

# Prevent interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive

# Update, install apache2, configure the custom port, and clean cache
RUN apt-get update && \
    apt-get install -y apache2 && \
    sed -i 's/Listen 80/Listen 8087/' /etc/apache2/ports.conf && \
    sed -i 's/:80/:8087/g' /etc/apache2/sites-available/000-default.conf && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# Expose the custom port for documentation
EXPOSE 8087

# Start Apache in the foreground to keep the container active
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

Save and exit the text editor (:wq).


Step 4: Build the Custom Docker Image

Build the Docker image locally from the context of /opt/docker and tag it as custom-apache:v1:

sudo docker build -t custom-apache:v1 /opt/docker

Post-Deployment Verification

1. Verify Local Image

Check if the custom image custom-apache:v1 was built successfully:

docker images

Expected Output:

REPOSITORY      TAG       IMAGE ID       CREATED          SIZE
custom-apache   v1        d1e2f3a4b5c6   10 seconds ago   185MB
ubuntu          latest    2d84a7e9e51c   2 weeks ago      72.8MB

2. Run Container and Verify Listening Port

Start a detached container using the new custom image, mapping host port 8087 to container port 8087:

docker run -d --name test_apache -p 8087:8087 custom-apache:v1

Confirm that the application is running and listening on port 8087:

curl -I http://localhost:8087

Expected Output:

HTTP/1.1 200 OK
Server: Apache/2.4.52 (Ubuntu)
...

3. Cleanup Test Container

Once verified, stop and remove the test container:

docker stop test_apache
docker rm test_apache

Log out of the Application Server:

exit