Docker

Li

Li Wei

November 30, 202510 min read

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:

https://docs.docker.com/engine/reference/commandline/cli/

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 assets
  • conf: 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: conf and html.
  • Inside the Nginx container, the conf and html directories are each linked to a volume.
  • The conf volume maps to the host’s /var/lib/docker/volumes/conf/_data directory, and the html volume maps to the host’s /var/lib/docker/volumes/html/_data directory.

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/volumes is 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 html directory
    (commands omitted)

  • MySQL anonymous volume

    Focus on the .Config.Volumes section: 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 .Mounts section 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 directory
    • Destination: directory inside the container

    The configuration mounts the container’s /var/lib/mysql directory to volume 29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f, 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:

  1. Provision a Linux server (CentOS or Ubuntu)
  2. Install and configure the JDK
  3. Upload the JAR file
  4. Run the JAR

Correspondingly, building an image follows these steps:

  1. Prepare a Linux runtime (the Java app only needs a minimal OS, not a full distribution)
  2. Install and configure the JDK
  3. Copy the JAR into the image
  4. 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:

https://docs.docker.com/engine/reference/builder/

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 -t flag 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.

Keep reading

More related articles from DriftSeas.