Docker Build Basics
Table of Contents
Introduction to Docker Images
Docker images are the foundation of containerization. They are lightweight, standalone, executable packages that include everything needed to run an application: code, runtime, libraries, environment variables, and configuration files.
A Docker image is a read-only template that contains instructions for creating a Docker container. Images are created with a series of layered instructions that are executed in order. Each instruction creates a new layer in the image, building upon the previous layers.
Before we dive into building images, it's important to understand these key concepts:
- Images: Read-only templates used to create containers
- Containers: Running instances of Docker images
- Layers: Each step in a Dockerfile creates a layer, which are cached to speed up future builds
- Dockerfile: A text file with instructions for building a Docker image
- Registry: A service that stores Docker images (like Docker Hub)
Understanding Dockerfiles
A Dockerfile is a text document that contains the instructions to assemble a Docker image. It specifies the base image to use, commands to execute, and files to add into the image.
Here's a simple example of a Dockerfile for a Node.js application:
# Use an official Node.js runtime as the base image
FROM node:18-alpine
# Set the working directory in the container
WORKDIR /app
# Copy package.json and package-lock.json to the working directory
COPY package*.json ./
# Install the application dependencies
RUN npm install
# Copy the application code to the working directory
COPY . .
# Expose port 3000
EXPOSE 3000
# Define the command to run the application
CMD ["npm", "start"]
This Dockerfile includes several commands, each creating a new layer in the image. Let's understand what each of these commands does.
Essential Dockerfile Commands
FROM
The FROM
instruction initializes a new build stage and sets the base image. All Dockerfiles must start with a FROM
instruction. It's recommended to use official images from Docker Hub.
FROM node:18-alpine
WORKDIR
The WORKDIR
instruction sets the working directory for any subsequent RUN
, CMD
, ENTRYPOINT
, COPY
, and ADD
instructions.
WORKDIR /app
COPY
The COPY
instruction copies files or directories from the source (host) to the destination (container filesystem).
COPY package*.json ./
COPY . .
RUN
The RUN
instruction executes commands in a new layer on top of the current image. It's used for installing packages, compiling code, etc.
RUN npm install
EXPOSE
The EXPOSE
instruction informs Docker that the container listens on the specified network ports at runtime. It does not actually publish the port; you need to use the -p
flag with docker run
for that.
EXPOSE 3000
CMD
The CMD
instruction provides defaults for executing a container. There can only be one CMD
instruction in a Dockerfile.
CMD ["npm", "start"]
Other Important Commands
- ENV: Sets environment variables
- ARG: Defines build-time variables
- ENTRYPOINT: Configures a container to run as an executable
- VOLUME: Creates a mount point for external volumes
- USER: Sets the user name or UID to use when running the image
Best Practices
Following best practices when creating Dockerfiles ensures your images are efficient, secure, and easy to maintain:
1. Use Official and Specific Base Images
Always use official images from trusted sources. Specify the exact version to avoid unexpected changes.
# Good
FROM node:18.16.0-alpine3.17
# Avoid
FROM node:latest
2. Optimize Layer Caching
Order your Dockerfile instructions from least to most frequently changing to take advantage of Docker's caching mechanism. For example, copy your package.json files before copying your source code.
3. Minimize the Number of Layers
Combine related commands in a single RUN instruction using the shell's &&
operator and \
line continuation.
# Good
RUN apt-get update && \
apt-get install -y --no-install-recommends \
package1 \
package2 && \
rm -rf /var/lib/apt/lists/*
# Avoid
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2
RUN rm -rf /var/lib/apt/lists/*
4. Remove Unnecessary Files
Don't include files that aren't needed in your Docker image. Use .dockerignore
to exclude files and directories.
5. Use Multi-stage Builds for Smaller Images
Use multi-stage builds to create leaner production images, especially for compiled languages.
6. Use Non-root Users
Avoid running containers as root for security best practices.
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
The Docker Build Command
Once you've created your Dockerfile, you can build an image using the docker build
command. Here's the basic syntax:
docker build [OPTIONS] PATH | URL | -
Common options include:
-t, --tag
: Name and optionally a tag in the 'name:tag' format--no-cache
: Do not use cache when building the image--pull
: Always attempt to pull a newer version of the base image-f, --file
: Path to the Dockerfile (default: 'PATH/Dockerfile')
Example:
docker build -t myapp:1.0 .
This command builds an image from the Dockerfile in the current directory and tags it as "myapp:1.0".
Basic Image Optimization
Optimizing your Docker images is crucial for improving build times, reducing image size, and enhancing security.
1. Use Smaller Base Images
Alpine-based images are significantly smaller than their Debian/Ubuntu counterparts.
# ~140MB
FROM node:18-alpine
# ~900MB
FROM node:18
2. Clean Up in the Same Layer
When installing packages, clean up in the same RUN instruction to avoid creating extra layers with unnecessary files.
RUN apt-get update && \
apt-get install -y package1 package2 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
3. Use .dockerignore
Create a .dockerignore
file to exclude files and directories that aren't needed in the image, similar to .gitignore
.
# Example .dockerignore
node_modules
npm-debug.log
.git
.dockerignore
Dockerfile
.env
*.md
tests/
4. Minimize Installed Dependencies
Only install what's necessary for your application to run. For example, use --no-install-recommends
with apt-get.
Conclusion
In this tutorial, we've covered the basics of Docker image building, including understanding Dockerfiles, essential commands, best practices, and basic optimization techniques.
Building efficient Docker images is a skill that develops with practice. Start simple and gradually incorporate more advanced techniques as you become comfortable with the basics.
Ready to Take Your Docker Skills Further?
Check out our tutorial on Multi-stage Builds to learn how to create even smaller and more efficient Docker images.
Next Tutorial: Multi-stage Builds