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.
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
Below are the core directives used to construct a standard Dockerfile:
FROMSets the base image for subsequent instructions. A valid Dockerfile must begin with a FROM instruction (e.g., FROM ubuntu:latest).
ENVDefines environment variables that persist during the build process and when the container is run.
ENV DEBIAN_FRONTEND=noninteractive prevents interactive prompts during package installs.RUNExecutes 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.
RUN apt-get update && apt-get install -y apache2COPY & ADDCopies files or directories from the Docker host’s build context into the container filesystem.
COPY: The preferred instruction for simple file transfers.ADD: Has advanced capabilities, such as extracting local tar archives or downloading files from remote URLs.EXPOSEInforms 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.
CMD & ENTRYPOINTDefine the process that runs when the container is started.
CMD: Sets default commands or parameters that can be easily overridden by appending arguments to docker run.ENTRYPOINT: Configures the container to run as a command-line executable. Arguments passed to docker run append to the entrypoint command rather than overriding it.apache2ctl -D FOREGROUND) to prevent the container from instantly exiting.stapp01) (can vary in labs, e.g., stapp01, stapp02, stapp03)tony (associated with stapp01; steve for stapp02, banner for stapp03)/opt/docker/Dockerfileubuntu (or ubuntu:latest)apache28087 (Apache default port 80 must be updated to 8087)Establish an SSH connection from the Jump Host to App Server 1:
ssh tony@stapp01
Provide the server password when prompted.
Create the target build directory where the Dockerfile will reside:
sudo mkdir -p /opt/docker
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).
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
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
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)
...
Once verified, stop and remove the test container:
docker stop test_apache
docker rm test_apache
Log out of the Application Server:
exit