Docker 基础学习笔记
Docker 用于构建、分发和运行容器化应用。它将应用程序及其依赖打包为镜像,再通过镜像创建相互隔离的容器。
基本概念

| 概念 | 说明 |
|---|---|
| 镜像(Image) | 只读的软件包,包含应用程序、运行环境和依赖 |
| 容器(Container) | 镜像运行后产生的实例 |
| 仓库(Registry) | 用于保存和分发镜像,例如 Docker Hub |
| Dockerfile | 描述镜像构建过程的文本文件 |
| Docker Compose | 使用 YAML 文件统一管理多个容器 |
容器与虚拟机

容器与虚拟机都可以隔离应用,但实现方式不同:
| 对比项 | 容器 | 虚拟机 |
|---|---|---|
| 操作系统 | 多个容器共享宿主机内核 | 每台虚拟机包含完整操作系统 |
| 启动速度 | 通常较快 | 通常较慢 |
| 资源占用 | 较低 | 较高 |
| 隔离方式 | 进程、网络和文件系统隔离 | 硬件虚拟化 |
| 可移植性 | 镜像可以跨环境分发 | 依赖虚拟机镜像和虚拟化平台 |
容器拥有独立的文件系统、进程空间、网络和资源限制,但仍与宿主机共享内核。
镜像管理
常用命令
| 命令 | 作用 |
|---|---|
docker search nginx |
在镜像仓库中搜索 Nginx |
docker pull nginx |
拉取默认标签的 Nginx 镜像 |
docker pull nginx:1.26.0 |
拉取指定版本 |
docker images |
查看本地镜像 |
docker rmi nginx:1.26.0 |
删除指定镜像 |
docker rmi <image-id> |
根据镜像 ID 删除 |
docker run nginx |
使用镜像创建并启动容器 |
没有显式指定标签时,Docker 通常使用 latest:
1
2
docker pull nginx
docker pull nginx:latest
latest 只是一个普通标签,不代表镜像一定是最新稳定版本。生产环境更适合固定明确版本。
将容器保存为镜像
docker commit 可以把容器当前的文件系统修改保存为新镜像:
1
2
3
4
5
docker commit \
-a "zangxin" \
-m "Update index.html" \
my-nginx \
zangxin-nginx:v1.0
这种方式适合学习和临时保存。可重复构建的项目应优先使用 Dockerfile。
导出与导入镜像
将镜像保存为 tar 文件:
1
docker save -o zangxin-nginx.tar zangxin-nginx:v1.0
在另一台机器加载并运行:
1
2
docker load -i zangxin-nginx.tar
docker run -d --name my-nginx -p 22222:80 zangxin-nginx:v1.0
验证服务:
1
curl http://localhost:22222
推送到 Docker Hub
先登录 Docker Hub:
1
docker login
镜像名称需要包含 Docker Hub 用户名或组织名:
1
2
docker tag zangxin-nginx:v1.0 zangxin51/zangxin-nginx:v1.0
docker push zangxin51/zangxin-nginx:v1.0
如果需要提供默认标签,可以额外推送 latest:
1
2
docker tag zangxin51/zangxin-nginx:v1.0 zangxin51/zangxin-nginx:latest
docker push zangxin51/zangxin-nginx:latest
容器管理
常用命令
| 命令 | 作用 |
|---|---|
docker ps |
查看正在运行的容器 |
docker ps -a |
查看全部容器 |
docker start <container> |
启动已停止的容器 |
docker stop <container> |
停止容器 |
docker restart <container> |
重启容器 |
docker rm <container> |
删除已停止的容器 |
docker rm -f <container> |
强制删除容器 |
docker logs <container> |
查看容器日志 |
docker inspect <container> |
查看容器详细信息 |
删除所有容器属于危险操作,执行前应确认没有需要保留的数据:
1
docker rm -f $(docker ps -aq)
运行 Nginx
下面的命令会创建名为 my-nginx 的容器,在后台运行,并将宿主机 27182 端口映射到容器的 80 端口:
1
2
3
4
docker run -d \
--name my-nginx \
-p 27182:80 \
nginx:latest
常用参数:
| 参数 | 作用 |
|---|---|
-d |
在后台运行容器 |
--name |
指定容器名称 |
-p host:container |
将宿主机端口映射到容器端口 |
--rm |
容器停止后自动删除 |
-e KEY=value |
设置环境变量 |
-v source:target |
配置目录或卷挂载 |
--network |
指定容器网络 |
访问服务:
1
curl http://localhost:27182
进入容器
使用 docker exec 在运行中的容器里启动 Shell:
1
docker exec -it my-nginx bash
修改 Nginx 默认页面:
1
2
echo "<h1>Hello, Docker</h1>" \
> /usr/share/nginx/html/index.html
退出容器后再次访问:
1
curl http://localhost:27182
直接修改容器内部文件不利于维护,容器重新创建后修改也会丢失。实际项目应使用挂载或自定义镜像。
数据持久化
Docker 常见的数据持久化方式包括绑定挂载和命名卷。
| 方式 | 示例 | 数据来源 | 适用场景 |
|---|---|---|---|
| 绑定挂载 | -v /host/path:/container/path |
宿主机指定目录 | 配置文件、源码和需要直接编辑的文件 |
| 命名卷 | -v volume-name:/container/path |
Docker 管理的卷 | 数据库数据和容器生成的数据 |
绑定挂载
将宿主机目录映射到 Nginx 的静态文件目录:
1
2
3
4
5
docker run -d \
--name nginx-app \
-p 15000:80 \
-v ~/docker-data/nginx/html:/usr/share/nginx/html \
nginx:latest
挂载后,宿主机与容器访问的是同一份文件。宿主机目录不存在时,使用 -v 通常会自动创建目录。
需要注意:绑定挂载会覆盖容器目标路径中原有的内容。将空目录挂载到 /etc/nginx,可能遮蔽镜像自带配置并导致 Nginx 无法启动。
如果容器需要使用宿主机时区,可以挂载时区文件:
1
-v /etc/localtime:/etc/localtime:ro
命名卷
命名卷由 Docker 统一管理,可以在首次挂载时接收镜像目标目录中的初始内容:
1
2
3
4
5
docker run -d \
--name nginx-app \
-p 15000:80 \
-v nginx-conf:/etc/nginx \
nginx:latest
常用命令:
| 命令 | 作用 |
|---|---|
docker volume ls |
查看所有卷 |
docker volume create app-data |
创建命名卷 |
docker volume inspect app-data |
查看卷的详细信息 |
docker volume rm app-data |
删除未使用的卷 |
docker volume prune |
删除所有未使用的卷,需谨慎 |
Linux 中的卷数据通常位于 /var/lib/docker/volumes/。Docker Desktop 运行在内部虚拟机中,不建议依赖宿主机直接访问该目录。
删除容器默认不会删除命名卷,因此重新创建容器后仍可以复用数据。执行 docker compose down -v 或显式删除卷时,数据才会被移除。
Docker 网络
默认 bridge 网络
Docker 默认会创建 bridge 网络,常见网桥名称为 docker0。容器会获得内部 IP,并通过网桥访问其他容器或外部网络。
查看容器网络信息:
1
docker inspect nginx-app

容器可以使用内部 IP 和容器端口互相访问:
1
curl http://172.17.0.3:80
但容器 IP 可能在重新创建后变化,不应将它写死在应用配置中。
自定义网络
自定义 bridge 网络支持通过容器名进行 DNS 解析,适合容器之间稳定访问:
1
2
docker network create mynet
docker network ls
创建容器时加入网络:
1
2
3
4
docker run -d \
--name app1 \
--network mynet \
nginx:latest
同一网络中的其他容器可以直接使用容器名访问:
1
curl http://app1
只有需要从宿主机或外部网络访问的服务才需要配置 -p。容器之间通信可以直接使用容器端口。
Redis 主从示例

创建 Redis 主节点:
1
2
3
4
5
6
7
8
docker run -d \
--name redis-master \
--network mynet \
-p 6379:6379 \
-v ~/docker-data/redis/master:/bitnami/redis/data \
-e REDIS_REPLICATION_MODE=master \
-e REDIS_PASSWORD=root \
bitnami/redis:latest
创建 Redis 从节点:
1
2
3
4
5
6
7
8
9
10
11
docker run -d \
--name redis-replica \
--network mynet \
-p 6380:6379 \
-v ~/docker-data/redis/replica:/bitnami/redis/data \
-e REDIS_REPLICATION_MODE=slave \
-e REDIS_PASSWORD=root \
-e REDIS_MASTER_HOST=redis-master \
-e REDIS_MASTER_PORT_NUMBER=6379 \
-e REDIS_MASTER_PASSWORD=root \
bitnami/redis:latest
从节点通过自定义网络中的容器名 redis-master 连接主节点。可以在主节点写入数据,再到从节点验证复制结果。
环境变量与中间件
-e 用于向容器传递环境变量:
1
2
3
docker run --rm \
-e APP_ENV=production \
alpine env
环境变量经常用于传递数据库名称、运行模式和服务地址。密码等敏感信息不应硬编码到公开脚本或仓库中,生产环境应使用 Secret 或安全的配置管理方式。
MySQL 示例
1
2
3
4
5
6
7
docker run -d \
--name mysql-app \
-p 3307:3306 \
-v ~/docker-data/mysql/conf:/etc/mysql/conf.d \
-v ~/docker-data/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=example-password \
mysql:8.0
运行中间件前应检查:
| 问题 | 建议 |
|---|---|
| 是否需要从宿主机访问 | 需要时才配置端口映射 |
| 是否需要修改配置 | 使用绑定挂载或配置文件 |
| 是否需要保存数据 | 使用命名卷或绑定挂载 |
| 是否需要运行参数 | 使用环境变量或启动命令 |
| 是否包含敏感信息 | 不要提交真实密码和密钥 |
Docker Compose
Docker Compose 使用 compose.yaml 描述多个服务、网络和卷,再通过一组命令统一管理应用。
参考文档:Compose 文件规范。
常用命令
| 命令 | 作用 |
|---|---|
docker compose up -d |
创建并后台启动服务 |
docker compose down |
停止并删除容器和 Compose 网络 |
docker compose start |
启动已存在的服务容器 |
docker compose stop |
停止服务,但不删除容器 |
docker compose restart |
重启服务 |
docker compose ps |
查看服务状态 |
docker compose logs -f |
持续查看日志 |
docker compose up -d --scale app=3 |
将指定服务扩容为三个实例 |
docker compose down 默认不会删除命名卷。使用 docker compose down -v 会同时删除卷,应谨慎执行。
当 Compose 文件或镜像配置发生变化时,再次执行:
1
docker compose up -d
Compose 会重新创建发生变化的服务,未变化的服务通常保持不动。
WordPress 与 MySQL 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
name: myblog
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: example-root-password
MYSQL_DATABASE: wordpress
volumes:
- mysql-data:/var/lib/mysql
- ./mysql-conf:/etc/mysql/conf.d
restart: unless-stopped
networks:
- myblog
wordpress:
image: wordpress:latest
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: example-root-password
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress-data:/var/www/html
restart: unless-stopped
networks:
- myblog
depends_on:
- mysql
volumes:
mysql-data:
wordpress-data:
networks:
myblog:
启动指定 Compose 文件:
1
docker compose -f compose.yaml up -d
停止并删除服务容器和网络:
1
docker compose -f compose.yaml down
depends_on 只控制基本启动顺序,不等同于等待数据库完全可用。需要严格控制就绪状态时,应增加 healthcheck 或应用侧重试机制。
Dockerfile
Dockerfile 将镜像构建过程写成可重复执行的步骤。
参考文档:Dockerfile reference。

常用指令
| 指令 | 作用 |
|---|---|
FROM |
指定基础镜像 |
LABEL |
添加镜像元数据 |
WORKDIR |
设置后续命令的工作目录 |
COPY |
将构建上下文中的文件复制到镜像 |
RUN |
构建镜像时执行命令 |
ENV |
设置环境变量 |
EXPOSE |
声明容器监听端口 |
CMD |
设置默认启动命令,可被运行参数覆盖 |
ENTRYPOINT |
设置容器入口程序 |
Java 应用示例:
1
2
3
4
5
6
7
8
9
10
FROM eclipse-temurin:17-jre
LABEL author="zangxin"
WORKDIR /app
COPY app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
构建镜像:
1
docker build -f Dockerfile -t my-java-app:v1.0 .
最后的 . 表示使用当前目录作为构建上下文。
镜像分层机制

Docker 镜像由多个只读层组成。容器启动后,会在镜像层之上增加一个可写层。
分层机制带来的特点:
- 不同镜像可以复用相同基础层,减少磁盘占用。
- Dockerfile 中未变化的层可以使用构建缓存。
- 删除容器后,容器可写层中的数据会丢失。
- 经常变化的构建步骤应尽量放在 Dockerfile 后部,提高缓存命中率。
查看容器可写层大小:
1
docker ps -s
查看镜像构建历史:
1
docker history my-java-app:v1.0
日志与排查
查看容器日志:
1
2
docker logs my-nginx
docker logs -f --tail 100 my-nginx
常用排查命令:
| 命令 | 作用 |
|---|---|
docker stats |
实时查看 CPU、内存和网络使用情况 |
docker top <container> |
查看容器中的进程 |
docker inspect <container> |
查看容器配置和状态 |
docker exec -it <container> sh |
进入容器排查 |
docker port <container> |
查看端口映射 |
docker system df |
查看 Docker 磁盘占用 |
其他中间件安装示例可参考:Docker 常用中间件安装。
远程 Docker 与 IDE 集成
Docker CLI 可以通过 Context 管理本地和远程 Docker Engine。优先使用 SSH 或启用 TLS 的 TCP 连接,不要把未加密的 Docker API 暴露到公网。
使用 SSH Context
创建远程 Context:
1
2
docker context create remote-host \
--docker "host=ssh://docker-user@example-host"
查看并切换 Context:
1
2
3
docker context ls
docker context use remote-host
docker ps
切回本地:
1
docker context use default
TCP 2375 的风险
2375 通常表示未启用 TLS 的 Docker API。能够访问该端口的客户端几乎等同于获得宿主机管理员权限,因此只适合隔离的实验环境。
如果只是本机测试,可以将 Docker Engine 绑定到回环地址,而不是 0.0.0.0:
1
2
3
4
5
6
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd \
-H fd:// \
-H tcp://127.0.0.1:2375 \
--containerd=/run/containerd/containerd.sock
重新加载 systemd 配置:
1
2
sudo systemctl daemon-reload
sudo systemctl restart docker.service
本机测试:
1
docker -H tcp://127.0.0.1:2375 ps
需要远程 TCP 连接时,应参考 Docker 官方文档配置双向 TLS,并使用防火墙限制来源。IDEA 中添加 Docker 连接时,可以选择 SSH 或安全的 TCP Socket,然后填写对应 Engine API 地址。
参考文档: