Docker
Li Wei
Docker
Introduction
Feishu link: https://b11et3un53m.feishu.cn/wiki/MWQIw4Zvhil0I5ktPHwcoqZdnec?from=from_copylink
Overview
Docker is an open‑source application container engine that lets developers package their apps and dependencies into a portable image, which can then be deployed on any popular Linux or Windows machine. It also provides virtualization. Containers are completely sandboxed; they have no interfaces to each other.
Note: What you download here is not an installer but an image. An image contains not only the dependencies themselves but also the runtime environment, configuration, and system‑level libraries needed to run them. Therefore it has its own isolated environment at runtime and can run across systems without manual re‑configuration. This isolated runtime environment is called a container.
Common Commands
Official documentation:
Frequently used commands
| Command | Description | Documentation |
|---|---|---|
docker pull |
Pull an image | docker pull |
docker push |
Push an image to Docker Registry | docker push |
docker images |
List local images | docker images |
docker rmi |
Remove a local image | docker rmi |
docker run |
Create and run a container (cannot be created twice) | docker run |
docker stop |
Stop a specific container | docker stop |
docker start |
Start a specific container | docker start |
docker restart |
Restart a container | docker restart |
docker rm |
Remove a specific container | docs.docker.com |
docker ps |
List containers | docker ps |
docker logs |
View container logs | docker logs |
docker exec |
Enter a container | docker exec |
docker save |
Save an image to a local archive | docker save |
docker load |
Load an image from a local archive | docker load |
docker inspect |
Show detailed information about a container | docker inspect |
Command relationships
(diagram omitted)
Example with Nginx
Docker commands can be long, so you may create aliases for the most frequently used ones for easier access.
Run the following command to make the aliases effective:
(command omitted)
By default, after a VM reboot you must manually start Docker and the containers inside it. You can enable automatic start on boot with a command:
(command omitted)
Volumes
Overview
Containers are isolated environments; files, configurations, and runtime data live inside the container, making it inconvenient to read or write them from the host. Consider these questions:
- If I need to upgrade MySQL and destroy the old container, will the data be lost?
- After MySQL or Nginx is running, how can I modify their configurations?
- How do I serve static assets through Nginx?
Thus, while containers provide the runtime environment, the data produced by the program and the configuration it depends on should be decoupled from the container.
A volume is a virtual directory that bridges a directory inside the container with a directory on the host.
Take Nginx as an example. It has two important directories:
html: for static assetsconf: for configuration files
To let Nginx serve static assets, you’d place them in html; to edit Nginx’s configuration, you’d edit conf/nginx.conf. Since all files reside inside the container, you must use a volume to map these directories to host directories, as shown below:
(illustration omitted)
In the illustration:
- Two volumes are created:
confandhtml. - Inside the Nginx container, the
confandhtmldirectories are each linked to a volume. - The
confvolume maps to the host’s/var/lib/docker/volumes/conf/_datadirectory, and thehtmlvolume maps to the host’s/var/lib/docker/volumes/html/_datadirectory.
Thus, the container’s conf and html directories are mounted to the host’s corresponding directories. Operating on the host’s /var/lib/docker/volumes/html/_data is effectively operating on the container’s /usr/share/nginx/html/_data. Simply drop static files into the host directory and Nginx will serve them.
Note:
/var/lib/docker/volumesis the default directory where Docker stores all volume data. Sub‑directories are created under it using the volume name, following the format/数据卷名/_data.Why not point the container directory directly to a host directory?
Directly binding to a host directory creates a strong coupling; if the environment changes, the host path may change, and because mount points cannot be altered after a container is created, the container would break. By mounting a volume (a logical name) to the container, and then mapping that volume to a host path, you avoid strong coupling. If the host path changes, you only need to update the volume‑to‑host mapping.
Because volume paths can be deep and hard to locate, Docker also allows mounting a host directory directly without using a named volume; see the “Mount a local directory or file” section for details.
Common Volume Commands
| Command | Description | Documentation |
|---|---|---|
docker volume create |
Create a volume | docker volume create |
docker volume ls |
List all volumes | docs.docker.com |
docker volume rm |
Remove a specific volume | docs.docker.com |
docker volume inspect |
Show details of a volume | docs.docker.com |
docker volume prune |
Delete unused volumes | docker volume prune |
Note: Volume mounts must be specified when creating a container; you cannot add a volume to an already‑created container. However, Docker will automatically create the volume during container creation.
Example Demonstrations
Mounting Nginx’s
htmldirectory
(commands omitted)MySQL anonymous volume
Focus on the
.Config.Volumessection: the container declares a local directory that should be mounted, but no named volume is defined—this is an anonymous volume.In the result’s
.Mountssection you’ll see key attributes:Name: volume name. Because the container was created without an explicit name, Docker generated an anonymous name (a hash string).Source: host directoryDestination: directory inside the container
The configuration mounts the container’s
/var/lib/mysqldirectory to volume29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f, which appears on the host as/var/lib/docker/volumes/29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f/_data. This is the directory backing the anonymous volume; it works just like a named volume.You can now inspect the MySQL data files under that directory.
Tip: For each image, consult its Docker Hub page to see which internal directories are suitable for mounting.
Mount a Local Directory or File
Because volume directories can be deep, it’s often more convenient to mount a host directory directly. The mount syntax is similar to that for volumes:
Note: The host path must start with / or ./; otherwise Docker treats it as a volume name rather than a host path.
Example:
(command omitted)
Images
Image Structure
To build an image you must understand its structure.
As mentioned earlier, an image enables rapid cross‑OS deployment because it bundles the system libraries, environment, configuration, and dependencies required at runtime.
Creating a custom image essentially means preparing, in order, the base runtime environment, dependencies, the application itself, and runtime configuration files, then packaging them.
Example workflow for a Java app from scratch:
- Provision a Linux server (CentOS or Ubuntu)
- Install and configure the JDK
- Upload the JAR file
- Run the JAR
Correspondingly, building an image follows these steps:
- Prepare a Linux runtime (the Java app only needs a minimal OS, not a full distribution)
- Install and configure the JDK
- Copy the JAR into the image
- Add a startup script
Each step produces files (system libraries, configs, etc.) on disk, so an image is simply a collection of files.
However, images are not a flat pile of files; they are built layer by layer. Each layer is packaged separately and given a unique ID called a Layer. If a layer already exists (e.g., an official base image), Docker can reuse it instead of rebuilding.
For instance, the Linux runtime layer is very generic, so Docker provides official images that contain only the OS. When building a Java image you can start from an official CentOS or Ubuntu base image and add your own layers on top. The final Java project image looks like this:
(diagram omitted)
Dockerfile
Because building an image involves many layered steps, Docker offers a way to automate the process. You write a Dockerfile that describes each layer using a fixed syntax, and Docker executes it.
The Dockerfile syntax is documented here:
Common instructions:
| Instruction | Description | Example |
|---|---|---|
FROM |
Specify the base image | FROM centos:6 |
ENV |
Set environment variables for later instructions | ENV KEY value |
COPY |
Copy local files into the image | COPY ./xx.jar /tmp/app.jar |
RUN |
Execute a shell command (usually for installing packages) | RUN yum install -y gcc |
EXPOSE |
Declare the port the container will listen on (informational) | EXPOSE 8080 |
ENTRYPOINT |
Define the command that runs when the container starts | ENTRYPOINT java -jar xx.jar |
Example Dockerfile for a Java app based on Ubuntu:
(Dockerfile content omitted)
Think about it: many Java projects need the same Linux + JDK layers; only the third layer (the JAR) differs. Rebuilding the first two layers each time would be wasteful.
Therefore, many provide a base image that already includes the OS and JDK, so you can skip the JDK setup step.
Building an Image
Once the Dockerfile is ready, build the image with a command:
docker build– builds a Docker image-t docker-demo:1.0– the-tflag specifies the repository name and tag.– the final dot tells Docker to use the current directory (where the Dockerfile resides) as the build context; you could also specify an explicit path.
Networking
You have created a container for a Java project, which typically needs to communicate with other services such as MySQL or Redis. Can containers talk to each other? Let’s test it.
First, inspect the MySQL container and note its network IP address:
(output omitted) – they can reach each other, no problem.
However, the container’s IP is a virtual address that can change when the container is recreated. Hard‑coding an IP in your code would break in production.
Thus, you should use Docker’s networking features. Official docs:
https://docs.docker.com/engine/reference/commandline/network/
Common Network Commands
| Command | Description | Documentation |
|---|---|---|
docker network create |
Create a network | docker network create |
docker network ls |
List all networks | docs.docker.com |
docker network rm |
Remove a specific network | docs.docker.com |
docker network prune |
Delete unused networks | docs.docker.com |
docker network connect |
Connect a container to a network | docs.docker.com |
docker network disconnect |
Disconnect a container from a network | docker network disconnect |
docker network inspect |
Show detailed network information | docker network inspect |
Example: Custom Network
(content truncated)
Originally written by Li Wei (李唯_) and published in Chinese on 后端技术栈全书 (Full-Stack Backend Engineering). Translated and adapted for DriftSeas with permission.