5. Docker 核心概念
5.1 镜像 (Image)
Docker 镜像是一个轻量级、可执行的独立软件包,包含运行某个软件所需的一切:代码、运行时、系统工具、库和设置。
- 镜像是只读的模板
- 镜像由多层(Layer)组成,每层对应 Dockerfile 中的一条指令
- 镜像层可以被多个镜像共享,节省存储空间
- 镜像通过 Dockerfile 构建或从仓库拉取
5.2 容器 (Container)
容器是镜像的可运行实例。可以将容器理解为镜像的"运行时快照"。
- 容器在镜像层之上增加了可写层(容器层)
- 容器可以被启动、停止、删除、暂停
- 容器内的修改在容器删除后会丢失(除非使用数据卷持久化)
- 多个容器可以基于同一个镜像创建
5.3 数据卷 (Volume)
数据卷用于持久化容器产生的数据,以及容器间共享数据。
- 独立于容器的生命周期
- 存储在宿主机文件系统中(
/var/lib/docker/volumes/) - 支持多种挂载方式:Volume、Bind Mount、tmpfs Mount
5.4 网络 (Network)
Docker 网络管理容器之间的通信,以及容器与外部网络的通信。
- 默认提供:bridge、host、none、overlay 等网络驱动
- 容器可以通过网络名称互相访问(自定义网络支持 DNS 解析)
- 端口映射实现容器与宿主机之间的通信
5.5 Docker 架构图
+------------------+ +------------------+
| Docker Client | REST | Docker Daemon |
| (docker CLI) |--------->| (dockerd) |
+------------------+ +--------+---------+
|
+------------+------------+
| |
+------v------+ +-------v--------+
| containerd | | Docker Registry |
| (运行时) | | (镜像仓库) |
+------+------+ +----------------+
|
+------v------+
| runc |
| (OCI 运行层) |
+------+------+
|
+-----v-----+
| 容器实例 |
+-----------+
6. 镜像管理
6.1 搜索镜像
bash
# 在 Docker Hub 上搜索镜像
docker search nginx
# 搜索并限制星标数量
docker search --stars=1000 nginx
# 搜索官方镜像
docker search --filter "is-official=true" nginx
# 搜索已启用自动化构建的镜像
docker search --filter "is-automated=true" nginx
# 限制搜索结果数量
docker search --limit 10 nginx
6.2 拉取镜像
bash
# 拉取最新版本(默认 tag 为 latest)
docker pull nginx
# 拉取指定版本
docker pull nginx:1.25
# 拉取指定架构的镜像
docker pull --platform linux/amd64 nginx
# 从第三方仓库拉取
docker pull quay.io/prometheus/node-exporter
# 从私有仓库拉取
docker pull registry.example.com:5000/myapp:v1.0
# 拉取所有 tag(仅适用于某些仓库)
docker pull -a nginx
6.3 查看镜像
bash
# 列出所有镜像
docker images
# 或使用等价的命令
docker image ls
# 列出所有镜像(包括中间镜像层)
docker images -a
# 只显示镜像 ID
docker images -q
# 以指定格式输出
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
# 按仓库名过滤
docker images --filter "reference=nginx"
# 过滤悬空镜像(dangling images,即 <none>:<none>)
docker images --filter "dangling=true"
# 按时间过滤(before、since)
docker images --filter "before=nginx:1.25"
docker images --filter "since=nginx:1.24"
6.4 标记镜像
bash
# 为镜像添加标签(格式:仓库名:标签)
docker tag nginx:latest mynginx:1.0
# 为镜像添加完整的仓库地址标签(用于推送到私有仓库)
docker tag nginx:latest registry.example.com:5000/mynginx:1.0
6.5 删除镜像
bash
# 删除指定镜像(通过镜像名:标签 或 镜像 ID)
docker rmi nginx:1.25
docker rmi abc123def456
# 强制删除(即使有容器正在使用)
docker rmi -f nginx:1.25
# 删除全部悬空镜像
docker image prune
# 删除所有未使用的镜像(无容器引用的)
docker image prune -a
# 删除所有镜像
docker rmi $(docker images -q)
# 清理所有未使用的 Docker 对象(镜像、容器、网络、构建缓存)
docker system prune -a
6.6 导出和导入镜像
bash
# 将镜像导出为 tar 文件
docker save -o nginx.tar nginx:latest
# 将多个镜像导出到一个 tar 文件
docker save -o multiple-images.tar nginx:latest ubuntu:22.04
# 从 tar 文件加载镜像
docker load -i nginx.tar
# 或使用管道(常用于传输)
# 把镜像文件打包+压缩为一个压缩包
docker save nginx:latest | gzip > nginx.tar.gz
# 把压缩包解压并加载镜像
gunzip -c nginx.tar.gz | docker load
6.7 查看镜像详细信息
bash
# 查看镜像的详细信息(JSON 格式)
docker inspect nginx:latest
# 查看镜像的层级信息
docker history nginx:latest
# 查看镜像的创建时间、大小等
docker history --no-trunc nginx:latest
# 查看镜像的具体配置(如暴露端口、环境变量等)
docker inspect --format='{{.Config.ExposedPorts}}' nginx:latest
docker inspect --format='{{.Config.Env}}' nginx:latest
6.8 构建镜像
bash
# 使用 Dockerfile 构建镜像
docker build -t myapp:1.0 .
# 指定 Dockerfile 路径和构建上下文
docker build -f /path/to/Dockerfile -t myapp:1.0 /path/to/context
# 构建时不使用缓存
docker build --no-cache -t myapp:1.0 .
# 为镜像添加多个标签
docker build -t myapp:1.0 -t myapp:latest .
# 构建时传递构建参数
docker build --build-arg VERSION=1.0 --build-arg ENV=production -t myapp:1.0 .
# 指定目标构建阶段(多阶段构建时)
docker build --target builder -t myapp-builder:1.0 .
7. 容器管理
7.1 创建和启动容器
bash
# 创建并启动容器(前台运行)
docker run nginx
# 创建并启动容器(后台运行,-d = detached)
docker run -d nginx
# 为容器指定名称
docker run -d --name my-nginx nginx
# 映射端口(宿主机端口:容器端口)
docker run -d --name my-nginx -p 8080:80 nginx
# 映射多个端口
docker run -d --name my-nginx -p 8080:80 -p 443:443 nginx
# 随机映射端口(宿主机自动分配端口)
docker run -d --name my-nginx -P nginx
# 挂载数据卷
docker run -d --name my-nginx -v /host/path:/container/path nginx
# 设置环境变量
docker run -d --name my-mysql \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
-e MYSQL_DATABASE=myapp \
mysql:8.0
# 设置资源限制
docker run -d --name my-app \
--memory="512m" \
--cpus="1.5" \
--memory-swap="1g" \
nginx
# 以交互模式运行容器(常用于测试)
docker run -it --name test-container ubuntu:22.04 /bin/bash
# 容器退出后自动删除
docker run --rm -it ubuntu:22.04 /bin/bash
# 指定容器主机名
docker run -d --name my-nginx --hostname web01 nginx
# 添加 DNS 配置
docker run -d --name my-nginx \
--dns 8.8.8.8 \
--dns 114.114.114.114 \
nginx
# 使容器使用宿主机网络
docker run -d --name my-nginx --network host nginx
# 设置容器重启策略
docker run -d --name my-app \
--restart always \
myapp:1.0
--restart 策略说明:
| 策略 | 说明 |
|---|---|
no |
不自动重启(默认) |
on-failure |
容器因错误退出时重启(退出码非 0) |
on-failure:3 |
最多重启 3 次 |
always |
总是重启(除非手动停止) |
unless-stopped |
总是重启,除非手动停止 |
'docker run' 常用的选项
docker run [选项] 镜像名[:标签]
-d/--detach------ 后台运行(最常用)-p/--publish------ 端口映射, 把容器内部端口暴露给本机--name------ 给容器起名字-v/--volume------ 目录挂载(数据持久化)- -v "/tmp/.X11-unix:/tmp/.X11-unix:rw" :让容器里的 GUI 程序,能把画面显示到我电脑屏幕上!
- -v /mnt/disk1/chrono_develop:/root/work : 挂载宿主机目录到容器
-it------ 交互式终端(进入容器内部)-e/--env------ 设置环境变量, -e 名字=值--restart------ 开机 / 重启自动运行--rm------ 运行结束自动删除容器,适合临时测试用- --gpus ------ 把宿主机所有的 NVIDIA 显卡,都分配给 Docker 容器使用
- --gpus all : 使用所有显卡
- --gpus '"device=0"' : 只使用第0号显卡
- --gpus all --memory 4g : 限制显卡使用内存
7.2 查看容器
bash
# 列出运行中的容器
docker ps
# 列出所有容器(包括已停止的)
docker ps -a
# 只显示容器 ID
docker ps -q
# 显示最近创建的容器(默认只显示最近的 n 个)
docker ps -n 5
# 显示容器大小
docker ps -s
# 按状态过滤
docker ps --filter "status=exited"
docker ps --filter "status=running"
docker ps --filter "status=created"
docker ps --filter "status=paused"
# 按名称过滤
docker ps --filter "name=my-nginx"
# 按暴露端口过滤
docker ps --filter "expose=80"
# 以自定义格式输出
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}"
# 只输出运行中的容器名
docker ps --format "{{.Names}}"
7.3 停止和启动容器
bash
# 停止容器(发送 SIGTERM 信号,等待优雅退出)
docker stop my-nginx
# 设置停止前的等待时间(默认 10 秒)
docker stop --time=30 my-nginx
# 强制停止容器(发送 SIGKILL 信号)
docker kill my-nginx
# 启动已停止的容器
docker start my-nginx
# 重启容器(先停止再启动)
docker restart my-nginx
# 暂停容器(冻结进程)
docker pause my-nginx
# 恢复暂停的容器
docker unpause my-nginx
7.4 进入容器
bash
# 在容器内执行命令(交互式)
docker exec -it my-nginx /bin/bash
# 如果容器中没有 bash,尝试 sh
docker exec -it my-nginx /bin/sh
# 在容器内执行命令(非交互)
docker exec my-nginx ls -la /var/log
# 在容器内执行命令并设置工作目录
docker exec -w /var/log my-nginx cat access.log
# 在容器内执行命令并设置环境变量
docker exec -e MY_VAR=hello my-nginx env
# 以指定用户身份在容器内执行命令
docker exec -u root my-nginx whoami
# 查看容器内的目录
docker exec 容器名 ls
# 查看容器内进程
docker exec 容器名 ps aux
| 选项 | 作用 |
|---|---|
-i |
交互式,保持标准输入打开 |
-t |
分配一个终端(能看到光标、能输入) |
-d |
后台执行命令(不输出到屏幕) |
-u |
指定用户(root、www-data 等) |
-w |
指定工作目录 |
-e |
传递环境变量 |
7.5 查看容器日志
bash
# 查看容器日志
docker logs my-nginx
# 实时跟踪日志输出(类似 tail -f)
docker logs -f my-nginx
# 查看最后 N 行日志
docker logs --tail 100 my-nginx
# 查看特定时间范围内的日志
docker logs --since "2024-01-01T00:00:00" --until "2024-01-02T00:00:00" my-nginx
# 查看最近 30 分钟的日志
docker logs --since 30m my-nginx
# 包含时间戳
docker logs -t my-nginx
# 组合使用
docker logs --tail 50 -f -t my-nginx
7.6 容器端口映射
bash
# 查看容器端口映射
docker port my-nginx
# 查看特定协议端口映射
docker port my-nginx 80
docker port my-nginx 80/tcp
7.7 容器资源使用统计
bash
# 实时查看容器资源使用情况
docker stats
# 查看指定容器
docker stats my-nginx
# 只查看一次就退出
docker stats --no-stream
# 查看所有容器(包括已停止的)的资源使用
docker stats --all
7.8 容器与宿主机之间的文件传输
bash
# 从容器复制文件到宿主机
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf
# 从宿主机复制文件到容器
docker cp ./my-config.conf my-nginx:/etc/nginx/conf.d/
# 复制目录
docker cp my-nginx:/var/log/nginx/ ./logs/
7.9 容器的导入和导出
bash
# 将容器导出为 tar 文件(包含文件系统快照)
docker export -o my-container.tar my-nginx
# 从 tar 文件导入为镜像
docker import my-container.tar my-nginx:imported
# 使用管道
docker export my-nginx | gzip > my-container.tar.gz
gunzip -c my-container.tar.gz | docker import - my-nginx:imported
注意 :
docker export/import与docker save/load的区别:
export/import:操作对象是容器,导出的内容不包含镜像的层级信息,历史记录和元数据save/load:操作对象是镜像,保留完整的层级结构和历史记录
7.10 删除容器
bash
# 删除单个已停止的容器
docker rm my-nginx
# 强制删除正在运行的容器
docker rm -f my-nginx
# 删除多个容器
docker rm my-nginx my-nginx2 mysql
# 删除所有已停止的容器
docker container prune
# 删除所有容器(包括运行中的,需要 -f)
docker rm -f $(docker ps -aq)
# 删除所有已退出的容器
docker rm $(docker ps -q -f status=exited)
7.11 容器生命周期图示
+-----------+
| docker |
| create |
+-----+-----+
|
+-----v-----+
| Created |<---------+
+-----+-----+ |
| |
+-----v-----+ +-----+-----+
| Running |--->| Stopped |
+-----+-----+ +-----+-----+
| |
+-----v-----+ |
| Paused | |
+-----+-----+ |
| |
+-----------------+
8. 数据卷管理
8.1 数据卷类型
| 类型 | 说明 | 特点 |
|---|---|---|
| Volume | Docker 管理的数据卷 | 存储在 /var/lib/docker/volumes/,推荐使用 |
| Bind Mount | 挂载宿主机目录/文件 | 可挂载任意宿主机路径,依赖宿主机目录结构 |
| tmpfs Mount | 临时文件系统 | 仅存储在内存中,容器停止后数据消失 |
8.2 Volume 管理
bash
# 创建数据卷
docker volume create my-volume
# 创建带标签的数据卷
docker volume create --label project=myapp my-volume
# 查看所有数据卷
docker volume ls
# 过滤显示
docker volume ls --filter "label=project=myapp"
docker volume ls --filter "dangling=true"
# 查看数据卷详细信息
docker volume inspect my-volume
# 使用数据卷运行容器
docker run -d --name my-nginx \
-v my-volume:/usr/share/nginx/html \
nginx
# 使用匿名数据卷(Docker 自动生成名称)
docker run -d --name my-nginx -v /usr/share/nginx/html nginx
# 以只读方式挂载数据卷
docker run -d --name my-nginx \
-v my-volume:/usr/share/nginx/html:ro \
nginx
# 删除数据卷
docker volume rm my-volume
# 删除所有未使用的数据卷
docker volume prune
# 强制删除所有未使用的数据卷(不询问确认)
docker volume prune -f
# 删除所有数据卷(谨慎!)
docker volume rm $(docker volume ls -q)
8.3 Bind Mount
bash
# 挂载宿主机目录到容器
docker run -d --name my-nginx \
-v /home/user/html:/usr/share/nginx/html \
nginx
# 挂载单个文件
docker run -d --name my-nginx \
-v /home/user/nginx.conf:/etc/nginx/nginx.conf \
nginx
# 使用新的 --mount 语法(推荐,更清晰)
docker run -d --name my-nginx \
--mount type=bind,source=/home/user/html,target=/usr/share/nginx/html \
nginx
# 只读挂载
docker run -d --name my-nginx \
--mount type=bind,source=/home/user/html,target=/usr/share/nginx/html,readonly \
nginx
# 使用 --mount 挂载单个文件
docker run -d --name my-nginx \
--mount type=bind,source=/home/user/nginx.conf,target=/etc/nginx/nginx.conf \
nginx
8.4 tmpfs 挂载
bash
# 挂载 tmpfs(数据存储在内存中)
docker run -d --name my-app \
--tmpfs /app/cache:uid=1000,gid=1000 \
nginx
# 使用 --mount 语法挂载 tmpfs
docker run -d --name my-app \
--mount type=tmpfs,destination=/app/cache,tmpfs-size=100m \
nginx
8.5 容器间共享数据卷
bash
# 创建数据卷
docker volume create shared-data
# 容器 1:写入数据
docker run -d --name writer \
-v shared-data:/data \
alpine sh -c "while true; do echo 'Hello' >> /data/log.txt; sleep 1; done"
# 容器 2:读取数据
docker run -d --name reader \
-v shared-data:/data \
alpine tail -f /data/log.txt
# 使用 --volumes-from 继承另一个容器的挂载
docker run -d --name writer-container -v shared-data:/data alpine ...
docker run -d --name reader-container --volumes-from writer-container alpine ...
8.6 数据卷容器模式
bash
# 步骤 1:创建数据卷容器(仅用于持有数据卷)
docker create -v /data --name data-container alpine /bin/true
# 步骤 2:其他容器使用 --volumes-from 挂载数据
docker run -d --name app1 --volumes-from data-container nginx
docker run -d --name app2 --volumes-from data-container nginx
# 步骤 3:备份数据
docker run --rm --volumes-from data-container \
-v /backup:/backup \
alpine tar czf /backup/data-backup.tar.gz /data
8.7 备份和恢复数据卷
bash
# 备份数据卷
docker run --rm \
-v my-volume:/source:ro \
-v /backup:/destination:rw \
alpine tar czf /destination/my-volume-backup.tar.gz -C /source .
# 恢复数据卷
docker volume create my-volume
docker run --rm \
-v my-volume:/target \
-v /backup:/source:ro \
alpine tar xzf /source/my-volume-backup.tar.gz -C /target
9. 网络管理
9.1 Docker 网络驱动
| 网络驱动 | 说明 | 适用场景 |
|---|---|---|
| bridge | 默认网络驱动,容器通过网桥互相通信 | 单机容器通信 |
| host | 容器使用宿主机网络栈,无需端口映射 | 需要网络性能的场景 |
| none | 容器无网络接口 | 安全场景、只需本地访问 |
| overlay | 跨主机容器网络 | Swarm 集群、多机部署 |
| macvlan | 为容器分配 MAC 地址 | 需要直连物理网络的场景 |
| ipvlan | 为容器分配 IP 地址 | 类似 macvlan,但 MAC 地址可控 |
9.2 查看和管理网络
bash
# 列出所有网络
docker network ls
# 查看网络详细信息
docker network inspect bridge
# 创建自定义 bridge 网络
docker network create my-network
# 创建指定子网的网络
docker network create \
--subnet=172.20.0.0/16 \
--gateway=172.20.0.1 \
my-network
# 创建带 IP 范围限制的网络
docker network create \
--subnet=172.20.0.0/16 \
--ip-range=172.20.10.0/24 \
my-network
# 创建 overlay 网络(需要 Swarm 模式)
docker network create -d overlay my-overlay-network
# 创建 macvlan 网络
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
my-macvlan-network
# 删除网络
docker network rm my-network
# 删除所有未使用的网络
docker network prune
9.3 容器连接到网络
bash
# 运行容器并连接到指定网络
docker run -d --name my-nginx --network my-network nginx
# 将运行中的容器连接到网络
docker network connect my-network my-nginx
# 连接时指定容器 IP
docker network connect \
--ip 172.20.10.5 \
my-network my-nginx
# 将容器从网络断开
docker network disconnect my-network my-nginx
# 断网并清理网络配置
docker network disconnect -f my-network my-nginx
9.4 默认网络详解
bridge 网络
bash
# 查看默认 bridge 网络
docker network inspect bridge
# 默认 bridge 网络中的容器通过 IP 通信
docker run -d --name container1 alpine sleep 3600
docker run -d --name container2 alpine sleep 3600
# 获取 container1 的 IP
docker inspect container1 | grep IPAddress
# container2 可以通过 IP 访问 container1
docker exec container2 ping <container1-ip>
限制 :默认 bridge 网络中容器不能通过容器名通信(需要使用
--link,但已废弃)。自定义网络才支持 DNS 自动解析。
host 网络
bash
# 使用 host 网络(容器共享宿主机网络栈)
docker run -d --name my-nginx --network host nginx
# 此时不需要 -p 端口映射,直接通过宿主机 IP 访问
# 注意:host 网络仅适用于 Linux
none 网络
bash
# 容器无网络
docker run -d --name isolated --network none alpine sleep 3600
# 验证:容器内没有网络接口
docker exec isolated ifconfig # 或无此命令
docker exec isolated ip addr # 只有 lo 接口
9.5 自定义 bridge 网络(推荐)
自定义 bridge 网络相比默认 bridge 网络的优势:
- 自动 DNS 解析:容器间可以通过容器名通信
- 更好的隔离性:只有连接到同一网络的容器才能通信
- 动态连接:容器可以在运行中连接和断开网络
bash
# 创建自定义网络
docker network create --driver bridge app-network
# 启动两个容器在同一个自定义网络中
docker run -d --name web --network app-network nginx
docker run -d --name app --network app-network alpine sleep 3600
# 通过容器名通信(自动 DNS 解析)
docker exec app ping web
# 查看容器在自定义网络中的 DNS 配置
docker exec app cat /etc/resolv.conf
docker exec app cat /etc/hosts
9.6 端口映射详解
bash
# 基础端口映射(宿主机随机端口 -> 容器端口)
docker run -d -p 80 nginx
# 宿主机指定端口 -> 容器端口
docker run -d -p 8080:80 nginx
# 指定 IP 和端口
docker run -d -p 127.0.0.1:8080:80 nginx
# 指定 UDP 端口
docker run -d -p 53:53/udp dns-container
# 同时映射 TCP 和 UDP
docker run -d -p 53:53/tcp -p 53:53/udp dns-container
# 映射端口范围
docker run -d -p 8000-8010:8000-8010 nginx
# 将所有暴露端口映射到宿主机随机端口
docker run -d -P nginx
9.7 容器间通信示例
bash
# 方案一:使用自定义网络(推荐)
docker network create backend-network
docker run -d --name redis --network backend-network redis:7
docker run -d --name app \
--network backend-network \
-e REDIS_HOST=redis \
myapp:1.0
# app 容器中可以直接通过主机名 "redis" 访问 Redis
# 方案二:端口映射(外部访问)
docker run -d --name postgres \
-p 5432:5432 \
-e POSTGRES_PASSWORD=secret \
postgres:15
10. Dockerfile 编写指南
10.1 Dockerfile 指令速查
| 指令 | 用途 | 示例 |
|---|---|---|
FROM |
指定基础镜像 | FROM ubuntu:22.04 |
LABEL |
添加元数据标签 | LABEL version="1.0" |
RUN |
执行命令(构建时) | RUN apt-get update && apt-get install -y nginx |
CMD |
容器启动命令 | CMD ["nginx", "-g", "daemon off;"] |
ENTRYPOINT |
容器入口点 | ENTRYPOINT ["docker-entrypoint.sh"] |
COPY |
复制文件/目录 | COPY ./app /app |
ADD |
复制文件/目录(支持 URL 和自动解压) | ADD app.tar.gz /app |
WORKDIR |
设置工作目录 | WORKDIR /app |
ENV |
设置环境变量 | ENV NODE_ENV=production |
ARG |
构建参数 | ARG VERSION=1.0 |
EXPOSE |
声明暴露端口 | EXPOSE 80 |
VOLUME |
声明数据卷挂载点 | VOLUME /data |
USER |
指定运行用户 | USER appuser |
HEALTHCHECK |
健康检查 | HEALTHCHECK CMD curl -f http://localhost/ |
SHELL |
指定 shell | SHELL ["/bin/bash", "-c"] |
ONBUILD |
触发器(子镜像构建时执行) | ONBUILD COPY . /app |
STOPSIGNAL |
停止信号 | STOPSIGNAL SIGQUIT |
10.2 Dockerfile 最佳实践示例
dockerfile
# ================================================
# 多阶段构建示例:Go 应用
# ================================================
# ---- 第一阶段:构建 ----
FROM golang:1.21-alpine AS builder
# 设置工作目录
WORKDIR /app
# 安装构建依赖
RUN apk add --no-cache git ca-certificates
# 先复制 go.mod 和 go.sum 以利用 Docker 缓存
COPY go.mod go.sum ./
RUN go mod download
# 复制源代码并构建
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o myapp .
# ---- 第二阶段:运行 ----
FROM alpine:3.19
# 添加元数据
LABEL maintainer="developer@example.com"
LABEL version="1.0"
LABEL description="Production-ready Go application"
# 安装运行时依赖(根据需要)
RUN apk add --no-cache tzdata ca-certificates && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone
# 使用非 root 用户运行(安全最佳实践)
RUN adduser -D -u 1000 appuser
# 创建工作目录并设置权限
WORKDIR /app
RUN chown appuser:appuser /app
# 从构建阶段复制编译好的二进制文件
COPY --from=builder /app/myapp .
# 复制配置文件(如果需要)
# COPY --from=builder /app/config.yaml .
# 声明端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
# 切换为用户
USER appuser
# 设置默认命令
ENTRYPOINT ["./myapp"]
CMD ["--config", "/app/config.yaml"]
10.3 通用 Dockerfile 模板
Python 应用
dockerfile
FROM python:3.11-slim
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# 安装 Python 依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 创建非 root 用户
RUN useradd --create-home --shell /bin/bash appuser && \
chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["python", "app.py"]
Node.js 应用
dockerfile
FROM node:20-alpine
WORKDIR /app
# 复制 package 文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production && \
npm cache clean --force
# 复制应用代码
COPY . .
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
EXPOSE 3000
CMD ["node", "server.js"]
Java 应用
dockerfile
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 添加非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 复制 JAR 包
COPY target/*.jar app.jar
# 设置 JVM 参数
ENV JAVA_OPTS="-Xmx512m -Xms256m"
USER appuser
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
10.4 .dockerignore 文件
创建 .dockerignore 文件可以排除不必要的文件进入构建上下文,提高构建速度:
dockerignore
# .dockerignore
.git
.gitignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
*.md
.env
.env.local
.gitlab-ci.yml
.cache
dist
*.log
.gitkeep
.idea
.vscode
*.swp
*.swo
*~
.DS_Store
Thumbs.db
docker-compose*.yml
10.5 构建缓存优化技巧
dockerfile
# 不符合缓存优化:每次修改源代码都需要重新下载依赖
FROM node:20-alpine
WORKDIR /app
COPY . . # 源代码变更导致缓存失效
RUN npm install # 每次都要重新执行
CMD ["npm", "start"]
# 符合缓存优化:利用 Docker 层缓存
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./ # 单独复制依赖文件
RUN npm install # 只有 package.json 变更时才重新执行
COPY . . # 源代码变更不影响依赖层
CMD ["npm", "start"]
10.6 减少镜像体积的技巧
dockerfile
# 技巧 1:使用更小的基础镜像
# 不推荐:python:3.11(~900MB)
# 推荐:python:3.11-slim(~120MB)或 python:3.11-alpine(~50MB)
# 技巧 2:合并 RUN 指令并清理缓存
RUN apt-get update && \
apt-get install -y --no-install-recommends \
package1 \
package2 \
&& rm -rf /var/lib/apt/lists/*
# 技巧 3:多阶段构建
FROM golang:1.21 AS builder
# ... 构建阶段 ...
FROM alpine:3.19
COPY --from=builder /app/myapp .
# 最终镜像只有几十 MB
# 技巧 4:不要安装不必要的包
# 不推荐
RUN apt-get update && apt-get install -y curl vim git wget
# 推荐:只安装需要的包
RUN apt-get update && apt-get install -y --no-install-recommends curl
11. Docker Compose
11.1 Docker Compose 简介
Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。通过一个 compose.yaml(或 docker-compose.yml)文件来配置应用的服务、网络和数据卷。
注意:Ubuntu 22.04 Docker 安装推荐使用
docker compose(子命令形式,插件版本),而非传统的docker-compose(独立二进制版本)。
11.2 Compose 文件结构
yaml
# compose.yaml (Docker Compose V3 格式)
---
name: myapp # 项目名称(Compose V2+ 支持)
services:
web:
image: nginx:alpine
container_name: my-web
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
networks:
- frontend
depends_on:
- api
restart: unless-stopped
api:
build:
context: ./api
dockerfile: Dockerfile
args:
- NODE_ENV=production
container_name: my-api
environment:
- DB_HOST=db
- DB_PORT=5432
- NODE_ENV=production
env_file:
- ./api/.env
volumes:
- api-data:/app/data
networks:
- backend
- frontend
depends_on:
db:
condition: service_healthy
restart: always
db:
image: postgres:15-alpine
container_name: my-db
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: myapp
volumes:
- pg-data:/var/lib/postgresql/data
- ./init-db:/docker-entrypoint-initdb.d
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: my-redis
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
networks:
- backend
restart: unless-stopped
volumes:
pg-data:
driver: local
redis-data:
driver: local
api-data:
driver: local
networks:
frontend:
driver: bridge
backend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
11.3 Compose 文件核心配置详解
services 配置
yaml
services:
myservice:
# 使用已存在的镜像
image: nginx:latest
# 或从 Dockerfile 构建
build:
context: ./dir # 构建上下文
dockerfile: Dockerfile # Dockerfile 路径(默认 Dockerfile)
args: # 构建参数
- BUILD_VAR=value
target: builder # 多阶段构建的目标阶段
cache_from: # 缓存来源
- myimage:latest
labels: # 镜像标签
- "com.example.label=value"
# 容器名
container_name: my-service
# 端口映射
ports:
- "80:80" # host:container
- "8080:80" # 指定 host 端口
- "443:443/tcp" # 指定协议
- "127.0.0.1:8000:8000" # 绑定到特定 IP
# 环境变量
environment:
- ENV_VAR=value # 直接指定
- SECRET_VAR # 从宿主机环境变量继承
environment:
ENV_VAR: value # 也可以使用这种格式
SECRET_VAR:
# 从文件加载环境变量
env_file:
- ./common.env
- ./app.env
# 数据卷挂载
volumes:
- ./host/path:/container/path # bind mount
- myvolume:/container/path # named volume
- /absolute/host/path:/container/path # 绝对路径 bind mount
- ./data:/data:ro # 只读挂载
# 依赖关系
depends_on:
- db
- redis
# 带条件的依赖
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
# 网络
networks:
- frontend
networks:
frontend:
aliases: # 网络别名
- web-frontend
backend:
ipv4_address: 172.20.0.10 # 固定 IP
# 重启策略
restart: always # no | always | on-failure | unless-stopped
# 资源限制
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
# 健康检查
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# 命令
command: ["nginx", "-g", "daemon off;"]
# 或
command: nginx -g daemon off;
# 入口点
entrypoint: ["/entrypoint.sh"]
# 用户
user: "1000:1000"
# 工作目录
working_dir: /app
# DNS
dns:
- 8.8.8.8
- 114.114.114.114
# 额外主机映射(类似 /etc/hosts)
extra_hosts:
- "host.docker.internal:host-gateway"
# 日志配置
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# 安全配置
security_opt:
- no-new-privileges:true
# cap_add / cap_drop(Linux capabilities)
cap_add:
- NET_ADMIN
cap_drop:
- ALL
# 特权模式
privileged: false
# sysctl 设置
sysctls:
- net.core.somaxconn=1024
# ulimit 设置
ulimits:
nofile:
soft: 65535
hard: 65535
变量替换与 .env 文件
bash
# .env 文件(与 compose.yaml 同目录)
DB_PASSWORD=SuperSecretPassword123
REDIS_PASSWORD=RedisSecret456
NGINX_PORT=8080
yaml
# compose.yaml 中引用变量
services:
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
web:
image: nginx
ports:
- "${NGINX_PORT}:80"
变量引用语法:
| 语法 | 说明 |
|---|---|
${VAR} |
使用变量值,未设置时为空字符串 |
${VAR:-default} |
未设置时使用默认值 |
${VAR:?error} |
未设置时报错 |
${VAR:+value} |
设置时使用替代值 |
11.4 Compose 常用命令
bash
# 启动所有服务(前台模式)
docker compose up
# 启动所有服务(后台模式)
docker compose up -d
# 启动指定服务
docker compose up -d web api
# 构建并启动
docker compose up -d --build
# 停止所有服务
docker compose down
# 停止并删除卷
docker compose down -v
# 停止并删除所有内容(卷、网络、镜像)
docker compose down --rmi all -v
# 查看运行中的服务
docker compose ps
# 查看日志
docker compose logs
docker compose logs -f
docker compose logs -f web api
# 查看最后 N 行日志
docker compose logs --tail 100 -f
# 在服务中执行命令
docker compose exec web /bin/sh
docker compose exec db pg_isready
# 查看服务状态
docker compose top
# 构建/重建服务
docker compose build
docker compose build --no-cache api
# 拉取服务镜像
docker compose pull
# 推送服务镜像
docker compose push
# 列出服务
docker compose config --services
# 校验 Compose 文件格式
docker compose config
# 输出渲染后的 Compose 配置
docker compose config -o output.yaml
# 滚动重启服务
docker compose restart web
# 暂停/恢复服务
docker compose pause web
docker compose unpause web
# 启动已停止的服务
docker compose start web
# 停止服务(保留容器)
docker compose stop web
# 删除服务容器
docker compose rm web
docker compose rm -f -v web
11.5 完整实战示例:WordPress + MySQL
yaml
# compose.yaml
---
name: wordpress
services:
db:
image: mysql:8.0
container_name: wp-db
command:
- --default-authentication-plugin=mysql_native_password
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
volumes:
- db-data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword}
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-wppassword}
networks:
- wp-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
wordpress:
image: wordpress:6.4-php8.2-apache
container_name: wp-app
ports:
- "${WP_PORT:-8080}:80"
volumes:
- wp-data:/var/www/html
- ./wp-content/plugins:/var/www/html/wp-content/plugins
- ./wp-content/themes:/var/www/html/wp-content/themes
- ./wp-content/uploads:/var/www/html/wp-content/uploads
- ./php.ini:/usr/local/etc/php/conf.d/custom.ini
restart: always
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD:-wppassword}
WORDPRESS_DB_NAME: wordpress
WORDPRESS_TABLE_PREFIX: wp_
WORDPRESS_DEBUG: ${WP_DEBUG:-0}
networks:
- wp-network
depends_on:
db:
condition: service_healthy
phpmyadmin:
image: phpmyadmin:latest
container_name: wp-pma
ports:
- "8081:80"
environment:
PMA_HOST: db
PMA_PORT: 3306
UPLOAD_LIMIT: 64M
networks:
- wp-network
depends_on:
- db
restart: always
volumes:
db-data:
driver: local
wp-data:
driver: local
networks:
wp-network:
driver: bridge
11.6 Compose 配置文件使用配置文件扩展
yaml
# compose.base.yaml - 基础配置
services:
app:
image: myapp:latest
networks:
- app-net
networks:
app-net:
# compose.prod.yaml - 生产环境扩展
---
name: myapp-prod
services:
app:
image: myapp:${VERSION:-latest}
environment:
- NODE_ENV=production
- DEBUG=0
deploy:
replicas: 3
# compose.dev.yaml - 开发环境扩展
---
name: myapp-dev
services:
app:
build:
context: .
environment:
- NODE_ENV=development
- DEBUG=1
- HOT_RELOAD=true
volumes:
- .:/app
# 使用方式:
# docker compose -f compose.base.yaml -f compose.dev.yaml up -d
# docker compose -f compose.base.yaml -f compose.prod.yaml up -d
12. Docker 仓库与镜像仓库
12.1 Docker Hub
bash
# 登录 Docker Hub
docker login
# 使用指定用户名登录
docker login -u username
# 登录到私有仓库
docker login registry.example.com:5000
# 登出
docker logout
# 推送镜像到 Docker Hub
docker push username/myapp:1.0
# 推送镜像到私有仓库
docker push registry.example.com:5000/myapp:1.0
12.2 搭建私有镜像仓库(Registry)
bash
# 使用 Docker 运行 Registry
docker run -d \
--name registry \
-p 5000:5000 \
-v /data/registry:/var/lib/registry \
registry:2
# 配置 HTTPS(推荐生产环境)
docker run -d \
--name registry \
-p 443:443 \
-v /data/registry:/var/lib/registry \
-v /etc/ssl/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2
# 使用 Compose 搭建带认证的 Registry
# compose.yml
yaml
# compose.yml - 完整的 Registry 部署
---
name: docker-registry
services:
registry:
image: registry:2
container_name: registry
restart: always
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_DELETE_ENABLED: "true"
REGISTRY_HTTP_HEADERS_X-Content-Type-Options: nosniff
# 如果使用 HTTPS(取消注释以下行)
# REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
# REGISTRY_HTTP_TLS_KEY: /certs/domain.key
# 如果启用认证(取消注释以下行)
# REGISTRY_AUTH: htpasswd
# REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
# REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
volumes:
- registry-data:/var/lib/registry
# - /etc/ssl/certs:/certs:ro
# - ./auth:/auth:ro
networks:
- registry-net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/v2/"]
interval: 30s
timeout: 10s
retries: 3
# Web UI 管理界面(可选)
registry-ui:
image: joxit/docker-registry-ui:latest
container_name: registry-ui
restart: always
ports:
- "8080:80"
environment:
REGISTRY_TITLE: "Private Docker Registry"
REGISTRY_URL: "http://registry:5000"
DELETE_IMAGES_ENABLED: "true"
networks:
- registry-net
depends_on:
- registry
volumes:
registry-data:
networks:
registry-net:
driver: bridge
12.3 Registry 常用操作
bash
# 查看仓库列表
curl http://localhost:5000/v2/_catalog
# 查看镜像标签列表
curl http://localhost:5000/v2/myapp/tags/list
# 删除镜像(需要启用 REGISTRY_STORAGE_DELETE_ENABLED)
# 先获取镜像的 digest
curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
http://localhost:5000/v2/myapp/manifests/1.0 | jq -r '.config.digest'
# 使用 digest 删除镜像
curl -X DELETE http://localhost:5000/v2/myapp/manifests/<digest>
# 垃圾回收(清理已删除镜像的存储)
docker exec registry /bin/registry garbage-collect /etc/docker/registry/config.yml
12.4 配置使用不安全的 Registry(HTTP)
如果私有仓库未启用 HTTPS,需要在 Docker Daemon 配置中将其加入不安全仓库列表:
bash
# 编辑 /etc/docker/daemon.json
{
"insecure-registries": [
"registry.example.com:5000",
"192.168.1.100:5000"
]
}
# 重启 Docker
sudo systemctl daemon-reload
sudo systemctl restart docker
12.5 Harbor 企业级镜像仓库
Harbor 是一个开源的云原生制品仓库,比 Docker Registry 功能更丰富。
bash
# 下载 Harbor 安装包
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
tar xzf harbor-offline-installer-v2.10.0.tgz
cd harbor
# 复制配置模板并修改
cp harbor.yml.tmpl harbor.yml
# 编辑 harbor.yml 配置
vim harbor.yml
# 安装 Harbor
sudo ./install.sh
# 带 Trivy 漏洞扫描安装
sudo ./install.sh --with-trivy
# 带 Chart Museum(Helm 仓库)安装
sudo ./install.sh --with-chartmuseum
# 访问 Harbor Web UI:http://<host-ip>
# 默认管理账号:admin / Harbor12345
13. 容器监控与日志
13.1 内置监控命令
bash
# 实时查看所有容器的 CPU、内存、网络、磁盘 I/O
docker stats
# 每 2 秒刷新一次
docker stats --no-stream # 只输出一次
# 查看指定容器
docker stats my-nginx my-mysql
# 查看容器进程
docker top my-nginx
# 查看容器资源使用详情
docker inspect my-nginx | grep -A 20 "HostConfig"
# 查看容器网络性能指标
# 需要在容器内安装工具
docker exec my-nginx apt-get update
docker exec my-nginx apt-get install -y iftop
docker exec -it my-nginx iftop
13.2 日志管理
配置日志驱动
bash
# 全局日志驱动配置(/etc/docker/daemon.json)
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# 容器级别的日志驱动配置
docker run -d \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
--name my-nginx nginx
# 使用 syslog 日志驱动
docker run -d \
--log-driver syslog \
--log-opt syslog-address=tcp://192.168.1.1:514 \
--log-opt syslog-facility=daemon \
--name my-nginx nginx
# 使用 journald 日志驱动
docker run -d \
--log-driver journald \
--name my-nginx nginx
支持的日志驱动
| 日志驱动 | 说明 | 适用场景 |
|---|---|---|
none |
禁用日志 | 不需要日志 |
json-file |
JSON 格式文件(默认) | 单机部署 |
syslog |
Syslog | 集中式日志收集 |
journald |
systemd journal | systemd 环境 |
gelf |
Graylog Extended Log Format | Graylog 收集 |
fluentd |
Fluentd | Fluentd 收集 |
awslogs |
Amazon CloudWatch | AWS 环境 |
splunk |
Splunk HTTP Event Collector | Splunk |
etwlogs |
Event Tracing for Windows | Windows 环境 |
gcplogs |
Google Cloud Logging | GCP 环境 |
logentries |
Logentries | Logentries 服务 |
logz |
Logz.io | Logz.io 服务 |
13.3 使用 cAdvisor 监控
bash
# 运行 cAdvisor 容器
docker run -d \
--name cadvisor \
--restart always \
-p 8080:8080 \
-v /:/rootfs:ro \
-v /var/run:/var/run:ro \
-v /sys:/sys:ro \
-v /var/lib/docker/:/var/lib/docker:ro \
-v /dev/disk/:/dev/disk:ro \
--privileged \
--device=/dev/kmsg \
gcr.io/cadvisor/cadvisor:latest
# 访问 cAdvisor Web 界面:http://localhost:8080
13.4 Docker 事件监控
bash
# 实时查看 Docker 事件
docker events
# 按类型过滤事件
docker events --filter "type=container"
docker events --filter "type=image"
docker events --filter "type=volume"
docker events --filter "type=network"
# 按事件过滤
docker events --filter "event=start"
docker events --filter "event=stop"
docker events --filter "event=destroy"
# 按容器名过滤
docker events --filter "container=my-nginx"
# 组合过滤
docker events --filter "type=container" --filter "event=die"
# 查看特定时间范围内的事件
docker events --since "2024-01-01T00:00:00"
docker events --until "2024-12-31T23:59:59"
14. 安全最佳实践
14.1 容器安全原则
- 最小权限原则:容器只拥有完成任务所需的最小权限
- 非 root 用户运行:不要在容器内以 root 身份运行应用
- 只读文件系统:如非必要,将文件系统设为只读
- 镜像安全扫描:定期扫描镜像中的漏洞
- 资源限制:限制容器的 CPU、内存使用量
- 网络隔离:使用自定义网络,限制容器间通信
14.2 安全运行容器
bash
# 以非 root 用户运行
docker run -d \
--user 1000:1000 \
--name my-app \
myapp:1.0
# 只读根文件系统
docker run -d \
--read-only \
--tmpfs /tmp \
--tmpfs /var/run \
--name my-app \
myapp:1.0
# 删除所有 Linux capabilities
docker run -d \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--name my-app \
myapp:1.0
# 禁止权限提升
docker run -d \
--security-opt no-new-privileges:true \
--name my-app \
myapp:1.0
# 限制系统调用
docker run -d \
--security-opt seccomp=/path/to/seccomp-profile.json \
--name my-app \
myapp:1.0
# 使用 AppArmor
docker run -d \
--security-opt apparmor=docker-default \
--name my-app \
myapp:1.0
# 组合使用
docker run -d \
--user 1000:1000 \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
--tmpfs /var/run:rw,noexec,nosuid \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--security-opt no-new-privileges:true \
--security-opt seccomp=default \
-p 8080:8080 \
--memory="256m" \
--cpus="0.5" \
--name secure-app \
myapp:1.0
14.3 镜像安全扫描
bash
# 使用 Docker Scout(内置)
docker scout quickview myapp:1.0
docker scout cves myapp:1.0
docker scout recommendations myapp:1.0
# 使用 Trivy(开源)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:latest \
image nginx:latest
# 使用 Clair(开源)
# 需要运行 Clair 服务
14.4 敏感信息管理
bash
# 不推荐:在 Dockerfile 中硬编码密码
ENV DB_PASSWORD=123456
# 不推荐:在 docker run 命令中明文传递
docker run -e DB_PASSWORD=123456 ...
# 推荐:使用 Docker Secrets(Swarm 模式)
echo "my-secret-password" | docker secret create db_password -
docker service create \
--secret db_password \
--name my-service \
myapp:1.0
# 推荐:使用 .env 文件(Compose)
14.5 Docker Daemon 安全
bash
# 开启 TLS 认证(生产环境必须)
# 生成 CA 证书和服务器证书
# 配置 dockerd 使用 TLS
# 限制 Docker API 访问
# 方案 1:只监听本地 socket
# /etc/docker/daemon.json
{
"hosts": ["unix:///var/run/docker.sock"]
}
# 方案 2:使用 iptables 限制 API 访问
sudo iptables -A INPUT -p tcp --dport 2375 -j DROP
sudo iptables -A INPUT -p tcp --dport 2376 -s 10.0.0.0/8 -j ACCEPT
14.6 Docker Socker 安全
bash
# 不要将 /var/run/docker.sock 挂载到容器中
# 除非绝对必要(如 CI/CD runner、Portainer 等)
# 如果需要挂载 Docker Socket,使用只读模式
docker run -v /var/run/docker.sock:/var/run/docker.sock:ro ...
# 考虑使用 Docker Socket Proxy 替代直接挂载
docker run -d \
--name docker-socket-proxy \
-v /var/run/docker.sock:/var/run/docker.sock \
-e CONTAINERS=1 \
-e POST=0 \
-p 2375:2375 \
tecnativa/docker-socket-proxy
15. 性能优化
15.1 镜像优化
bash
# 1. 使用合适的基础镜像
# 不推荐:ubuntu:22.04(~77MB)
# 推荐:alpine:3.19(~7MB)或 debian:stable-slim(~25MB)
# 2. 多阶段构建
# 详见 Dockerfile 章节
# 3. 减少镜像层数
# 不推荐(产生多个层):
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2
RUN rm -rf /var/lib/apt/lists/*
# 推荐(合并为一层):
RUN apt-get update && \
apt-get install -y --no-install-recommends package1 package2 && \
rm -rf /var/lib/apt/lists/*
# 4. 使用 .dockerignore 排除不必要的文件
# 5. 利用构建缓存
# 将不常变更的指令放在前面
15.2 容器运行时优化
bash
# 1. 资源限制
docker run -d \
--memory="512m" \
--memory-swap="1g" \
--memory-reservation="256m" \
--cpus="1.5" \
--cpuset-cpus="0-1" \
myapp:1.0
# 2. 使用 host 网络获得最佳网络性能
docker run -d --network host myapp:1.0
# 3. I/O 性能优化
# 使用数据卷而非存储驱动
docker run -d -v /data:/data myapp:1.0
# 4. 调整 ulimit
docker run -d \
--ulimit nofile=65535:65535 \
--ulimit nproc=1024:2048 \
myapp:1.0
# 5. 使用 --oom-score-adj 防止 OOM kill
docker run -d \
--oom-score-adj=-500 \
myapp:1.0
15.3 Docker Daemon 优化
json
{
"storage-driver": "overlay2",
"exec-opts": ["native.cgroupdriver=systemd"],
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 5,
"default-shm-size": "64m",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"live-restore": true,
"selinux-enabled": false
}
15.4 磁盘空间清理
bash
# 查看 Docker 磁盘使用情况
docker system df
# 以更详细的方式显示
docker system df -v
# 清理所有未使用的对象
docker system prune
# 清理所有未使用的对象(包括未使用的镜像)
docker system prune -a
# 清理指定资源
docker image prune # 清理未使用的镜像
docker container prune # 清理已停止的容器
docker volume prune # 清理未使用的数据卷
docker network prune # 清理未使用的网络
docker builder prune # 清理 BuildKit 构建缓存
# 不带确认的清理
docker system prune -a -f
# 查看 Docker 数据目录大小
sudo du -sh /var/lib/docker
sudo du -sh /var/lib/docker/containers
sudo du -sh /var/lib/docker/volumes
sudo du -sh /var/lib/docker/overlay2
15.5 BuildKit 优化
bash
# 启用 BuildKit(设置环境变量)
export DOCKER_BUILDKIT=1
docker build -t myapp:1.0 .
# 永久启用 BuildKit
# 在 /etc/docker/daemon.json 中设置
{
"features": {
"buildkit": true
}
}
# BuildKit 特性
# 1. 并发构建 - 多阶段构建的并行执行
# 2. 缓存优化 - 更好的缓存命中率
# 3. 密钥传递 - 安全的构建时密钥传递
# 4. 仅在需要时拉取基础镜像
# 使用 BuildKit 的缓存功能
# 在 Dockerfile 中使用 --mount=type=cache
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && apt-get install -y package1
# 使用 --mount=type=secret 传递密钥
RUN --mount=type=secret,id=mysecret \
cat /run/secrets/mysecret
16. 常见问题与故障排除
16.1 权限问题
问题: permission denied while trying to connect to the Docker daemon socket
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
解决方法:
bash
# 方案 1:将用户加入 docker 组
sudo usermod -aG docker $USER
newgrp docker
# 方案 2:使用 sudo(不推荐长期使用)
sudo docker ps
# 方案 3:修改 socket 权限(不推荐)
sudo chmod 666 /var/run/docker.sock
16.2 磁盘空间不足
问题: no space left on device
bash
# 查看磁盘使用情况
docker system df
# 清理所有未使用的 Docker 对象
docker system prune -a -f
# 如果还是空间不足,检查 Docker 数据目录
sudo du -sh /var/lib/docker/*
sudo du -sh /var/lib/docker/overlay2/* | sort -rh | head -10
# 移动 Docker 数据目录到更大的磁盘
# 编辑 /etc/docker/daemon.json
{
"data-root": "/mnt/large-disk/docker"
}
# 重启 Docker
sudo systemctl daemon-reload
sudo systemctl restart docker
16.3 容器无法启动
bash
# 查看容器日志
docker logs my-container
# 检查容器状态和退出码
docker ps -a | grep my-container
docker inspect my-container --format='{{.State.ExitCode}}'
# 常见退出码
# 0 - 正常退出
# 1 - 一般错误
# 137 - SIGKILL(OOM 或被 docker kill)
# 139 - SIGSEGV(段错误)
# 143 - SIGTERM(优雅停止)
# 以调试模式启动容器
docker run -it --entrypoint /bin/sh myimage:tag
docker run -it --entrypoint /bin/bash myimage:tag
# 检查容器资源限制
docker inspect my-container | grep -A 10 "HostConfig"
# 查看系统资源使用
free -h
df -h
16.4 端口冲突
问题: port is already allocated
Error: failed to start container: port is already allocated
解决方法:
bash
# 查看哪个进程占用了端口
sudo lsof -i :8080
sudo netstat -tlnp | grep 8080
sudo ss -tlnp | grep 8080
# 查看哪个容器占用了端口
docker ps | grep 8080
# 停止占用端口的容器
docker stop conflicting-container
# 或使用不同的端口
docker run -d -p 8081:80 nginx
16.5 网络问题
问题: 容器无法访问外部网络或 DNS 解析失败
bash
# 检查容器 DNS 配置
docker exec my-container cat /etc/resolv.conf
# 检查宿主机网络
ping google.com
nslookup google.com
# 测试容器网络连接
docker exec my-container ping 8.8.8.8
docker exec my-container ping google.com
# 修改容器的 DNS
docker run -d --dns 8.8.8.8 --dns 114.114.114.114 myimage
# 重启 Docker 网络(可能解决某些网络问题)
sudo systemctl restart docker
# 重置 iptables 规则(谨慎,会影响所有 Docker 网络)
sudo iptables -P FORWARD ACCEPT
16.6 OOM(Out of Memory)
问题: 容器被系统 OOM Killer 杀死
bash
# 检查容器是否被 OOM 杀掉
docker inspect my-container --format='{{.State.OOMKilled}}'
# 查看容器退出码
docker inspect my-container --format='{{.State.ExitCode}}'
# OOM 的退出码通常是 137
# 增加容器内存限制
docker run -d --memory="1g" --memory-swap="2g" myapp:1.0
# 调整 OOM 优先级(越小越不容易被 OOM kill)
docker run -d --oom-score-adj=-500 myapp:1.0
# 查看系统 OOM 日志
sudo dmesg | grep -i oom
sudo journalctl -k | grep -i oom
16.7 镜像拉取慢
bash
# 1. 配置镜像加速器(推荐)
# 编辑 /etc/docker/daemon.json
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com"
]
}
# 2. 使用代理(如果配置了代理)
# 创建 /etc/systemd/system/docker.service.d/proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:8080"
Environment="HTTPS_PROXY=http://proxy.example.com:8080"
Environment="NO_PROXY=localhost,127.0.0.1"
# 重新加载并重启
sudo systemctl daemon-reload
sudo systemctl restart docker
# 3. 预先拉取常用镜像
docker pull nginx:alpine
docker pull alpine:latest
16.8 时间同步问题
问题: 容器内时间与宿主机不一致
bash
# 方案 1:挂载宿主机时区文件
docker run -d \
-v /etc/localtime:/etc/localtime:ro \
-v /etc/timezone:/etc/timezone:ro \
nginx
# 方案 2:设置环境变量
docker run -d \
-e TZ=Asia/Shanghai \
nginx
# 方案 3:在 Dockerfile 中设置
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone
16.9 Docker Daemon 无法启动
bash
# 检查 Docker 服务状态
sudo systemctl status docker
# 查看详细错误日志
sudo journalctl -u docker.service -n 100
# 检查配置文件语法
dockerd --config-file /etc/docker/daemon.json --validate
# 尝试手动启动 Docker Daemon 查看错误
sudo dockerd --debug
# 常见原因及解决方法
# 1. daemon.json 语法错误
# 2. 端口冲突(默认 API 端口 2375/2376)
# 3. 存储驱动问题
# 4. 磁盘空间不足
# 重置 Docker 配置(备份后)
sudo mv /etc/docker/daemon.json /etc/docker/daemon.json.bak
sudo systemctl restart docker
16.10 overlay2 存储驱动问题
bash
# 检查是否使用了 overlay2
docker info | grep "Storage Driver"
# 如果 overlay2 不支持,可以尝试修改存储驱动
# 编辑 /etc/docker/daemon.json
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
# 如果 overlay2 仍不工作,可以切换到 overlay 或其他驱动
# 注意:切换存储驱动会丢失所有现有镜像和容器
17. 附录:常用命令速查
17.1 镜像操作
| 命令 | 说明 |
|---|---|
docker search <name> |
搜索镜像 |
docker pull <name>:<tag> |
拉取镜像 |
docker images |
列出镜像 |
docker rmi <id> |
删除镜像 |
docker build -t <name>:<tag> . |
构建镜像 |
docker tag <src> <target> |
标记镜像 |
docker push <name>:<tag> |
推送镜像 |
docker save -o <file> <image> |
导出镜像 |
docker load -i <file> |
导入镜像 |
docker history <image> |
查看镜像构建历史 |
docker inspect <image> |
查看镜像详情 |
17.2 容器操作
| 命令 | 说明 |
|---|---|
docker run <image> |
创建并启动容器 |
docker ps |
列出运行中的容器 |
docker ps -a |
列出所有容器 |
docker start <container> |
启动容器 |
docker stop <container> |
停止容器 |
docker restart <container> |
重启容器 |
docker kill <container> |
强制停止容器 |
docker rm <container> |
删除容器 |
docker logs <container> |
查看日志 |
docker logs -f <container> |
跟踪日志 |
docker exec -it <container> sh |
进入容器 |
docker cp <src> <dest> |
文件复制 |
docker stats |
资源监控 |
docker top <container> |
查看进程 |
17.3 网络操作
| 命令 | 说明 |
|---|---|
docker network ls |
列出网络 |
docker network create <name> |
创建网络 |
docker network rm <name> |
删除网络 |
docker network connect <name> <container> |
连接网络 |
docker network disconnect <name> <container> |
断开网络 |
docker network inspect <name> |
查看网络详情 |
docker network prune |
清理未使用的网络 |
17.4 数据卷操作
| 命令 | 说明 |
|---|---|
docker volume ls |
列出数据卷 |
docker volume create <name> |
创建数据卷 |
docker volume rm <name> |
删除数据卷 |
docker volume inspect <name> |
查看数据卷详情 |
docker volume prune |
清理未使用的数据卷 |
17.5 Compose 操作
| 命令 | 说明 |
|---|---|
docker compose up -d |
启动服务 |
docker compose down |
停止并删除服务 |
docker compose ps |
列出服务状态 |
docker compose logs -f |
跟踪服务日志 |
docker compose exec <service> sh |
进入服务容器 |
docker compose build |
构建服务镜像 |
docker compose pull |
拉取服务镜像 |
docker compose restart |
重启服务 |
docker compose stop |
停止服务 |
docker compose start |
启动已停止的服务 |
docker compose config |
校验并查看配置 |
docker compose top |
查看服务进程 |
17.6 系统维护
| 命令 | 说明 |
|---|---|
docker info |
查看 Docker 系统信息 |
docker version |
查看 Docker 版本 |
docker system df |
查看磁盘使用情况 |
docker system prune |
清理未使用的资源 |
docker system prune -a |
清理所有未使用的资源 |
docker events |
实时监控 Docker 事件 |
systemctl start docker |
启动 Docker 服务 |
systemctl stop docker |
停止 Docker 服务 |
systemctl restart docker |
重启 Docker 服务 |
systemctl status docker |
查看 Docker 服务状态 |
systemctl enable docker |
设置 Docker 开机自启 |
systemctl disable docker |
禁用 Docker 开机自启 |
17.7 日志管理命令
| 命令 | 说明 |
|---|---|
journalctl -u docker.service -n 100 |
查看 Docker Daemon 日志 |
journalctl -u docker.service -f |
实时跟踪 Docker Daemon 日志 |
journalctl -u docker.service --since "1 hour ago" |
查看最近 1 小时日志 |
docker logs --tail 100 -f <container> |
查看容器日志后 100 行并跟踪 |
版权声明 :本文档仅供学习参考,如有疏漏或错误,欢迎指正。
建议定期访问 Docker 官方文档 获取最新信息。