项目介绍

Docker 是一个开源的容器化平台,允许开发者将应用及其所有依赖打包到一个轻量级、可移植的容器中,实现"一次构建,到处运行"。自 2013 年发布以来,Docker 彻底改变了软件的开发、测试和部署方式,成为现代 DevOps 和微服务架构的基石。

Docker 容器与传统虚拟机不同,它共享宿主机的操作系统内核,因此启动速度快(秒级)、资源占用少、密度高。在 GitHub 上,Docker 的核心引擎(Moby 项目)拥有超过 7 万星,是全球使用最广泛的容器运行时。

项目信息详情
开发公司Docker, Inc.(美国旧金山)
GitHub Stars70,000+(Moby/Moby)
官方网站docker.com
开源协议Apache License 2.0
技术栈Go
容器运行时containerd / runc
部署方式Docker Desktop / Docker Engine / Docker CE
一句话理解:Docker 就像一个"标准化集装箱" — 把你的应用和所有依赖装进去,不管运到哪台服务器,打开就能跑,彻底告别"在我机器上能跑"的问题。

Docker vs 其他方案

对比维度 Docker Podman LXC/LXD 虚拟机 (VM)
架构 C/S 架构(守护进程) 无守护进程(Daemonless) 系统级容器 完整虚拟化
启动速度 秒级 秒级 秒级 分钟级
资源占用 极低(共享内核) 极低(共享内核) 高(独占内存/CPU)
隔离性 进程级隔离 进程级隔离 进程级隔离 完全隔离(独立内核)
生态系统 最成熟,Docker Hub 百万镜像 兼容 Docker 镜像 生态较小 成熟但不同体系
Rootless 支持 需额外配置 原生支持 支持 不适用
兼容 K8s 原生支持 支持(通过 CRI-O) 不直接支持 不直接支持
适用场景 应用容器化、微服务、CI/CD 安全敏感环境、替代 Docker 需要完整 OS 环境 强隔离、异构 OS
选择建议:Docker 拥有最成熟的生态和最广泛的社区支持,是容器化的首选方案。如果对安全性有极高要求(如无 root 守护进程),可考虑 Podman。需要完整虚拟操作系统时,选择 VM 或 LXC。

安装部署

方式 1 Ubuntu / Debian 安装 Docker Engine

推荐使用官方仓库安装,确保获取最新版本:
# 1. 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc

# 2. 安装依赖
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg

# 3. 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 4. 添加仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 5. 安装 Docker
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 6. 验证安装
sudo docker run hello-world
免 sudo 使用:将当前用户加入 docker 组:sudo usermod -aG docker $USER,然后重新登录生效。

方式 2 macOS 安装 Docker Desktop

macOS 推荐使用 Docker Desktop:
1. 访问 Docker Desktop for Mac 下载安装包
2. 拖拽到 Applications 文件夹,双击启动
3. 支持 Apple Silicon(M1/M2/M3)和 Intel 芯片

或使用 Homebrew 安装:
brew install --cask docker
启动 Docker Desktop 后,终端即可使用 docker 命令。
注意:Docker Desktop 对个人和小型团队免费,大型企业(250+ 员工或年收入 $10M+)需付费订阅。

方式 3 Windows 安装 Docker Desktop

Windows 10/11 推荐使用 Docker Desktop + WSL 2 后端:
1. 确保已启用 WSL 2:wsl --install
2. 下载 Docker Desktop for Windows
3. 安装时选择 "Use WSL 2 instead of Hyper-V"
4. 安装完成后重启电脑

验证安装:
# PowerShell 或 CMD
docker --version
docker run hello-world
国内加速:Docker Hub 在国内访问较慢,建议配置镜像加速器。在 Docker Desktop 的 Settings → Docker Engine 中添加:{"registry-mirrors": ["https://mirror.ccs.tencentyun.com"]}

方式 4 一键安装脚本(适用于 Linux)

Docker 官方提供了便捷安装脚本,适合快速测试:
# 官方一键安装脚本
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# 国内用户可使用阿里云镜像加速
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh --mirror Aliyun

# 启动 Docker 并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
警告:一键脚本不建议在生产环境使用,仅适合开发和测试。生产环境请使用方式 1 的手动安装步骤。

核心概念

概念 1 镜像(Image)

镜像是容器的只读模板,包含运行应用所需的一切:代码、运行时、库、环境变量、配置文件。镜像采用分层存储机制,每一层都是只读的,多个镜像可以共享相同的底层。
┌──────────────────────────────┐ │ 应用层(你的代码) │ ← 最上层,你自定义的内容 ├──────────────────────────────┤ │ 依赖层(npm install 等) │ ← 安装的依赖包 ├──────────────────────────────┤ │ 运行时层(Node.js 18) │ ← 语言运行环境 ├──────────────────────────────┤ │ 基础层(Alpine Linux) │ ← 精简操作系统 └──────────────────────────────┘
常用镜像操作命令:
# 搜索镜像
docker search nginx

# 拉取镜像
docker pull nginx:latest
docker pull node:18-alpine

# 查看本地镜像
docker images

# 删除镜像
docker rmi nginx:latest

# 查看镜像详情
docker inspect nginx:latest

概念 2 容器(Container)

容器是镜像的运行实例,拥有自己的文件系统、网络和进程空间。你可以把镜像理解为"类",容器理解为"对象实例"。
# 创建并启动容器
docker run -d --name my-nginx -p 8080:80 nginx

# 查看运行中的容器
docker ps

# 查看所有容器(含已停止)
docker ps -a

# 进入容器内部
docker exec -it my-nginx /bin/bash

# 查看容器日志
docker logs -f my-nginx

# 停止 / 启动 / 重启
docker stop my-nginx
docker start my-nginx
docker restart my-nginx

# 删除容器
docker rm my-nginx

# 强制删除运行中的容器
docker rm -f my-nginx
关键参数说明:-d 后台运行;-p 8080:80 宿主机 8080 映射到容器 80 端口;-it 交互式终端;--name 指定容器名称。

概念 3 Dockerfile

Dockerfile 是构建镜像的脚本文件,定义了从基础镜像到最终应用镜像的每一步操作。
# 一个典型的 Node.js 应用 Dockerfile # 基础镜像 FROM node:18-alpine # 设置工作目录 WORKDIR /app # 先复制依赖文件(利用缓存层) COPY package*.json ./ # 安装依赖 RUN npm ci --only=production # 复制源代码 COPY . . # 暴露端口 EXPOSE 3000 # 启动命令 CMD ["node", "server.js"]
构建镜像:
# 构建镜像
docker build -t my-app:1.0 .

# 指定 Dockerfile 路径
docker build -f Dockerfile.prod -t my-app:prod .
最佳实践:将不常变化的层(如依赖安装)放在前面,频繁变化的层(如源代码)放在后面,可以最大化利用构建缓存。

概念 4 数据卷(Volume)

容器默认是无状态的 — 容器删除后数据就丢失了。Volume 用于将数据持久化到宿主机,即使容器被删除数据依然保留。
# 方式 1:命名卷(Docker 管理,推荐)
docker run -d -v my-data:/var/lib/mysql mysql:8

# 方式 2:绑定挂载(映射宿主机目录)
docker run -d -v /home/user/data:/var/lib/mysql mysql:8

# 方式 3:tmpfs 挂载(内存中,临时数据)
docker run -d --tmpfs /tmp nginx

# 查看所有卷
docker volume ls

# 删除未使用的卷
docker volume prune
容器 A ──────┐ ├──▶ Volume: mysql_data ──▶ 宿主机磁盘 容器 B ──────┘ (持久化数据) (共享同一卷)

概念 5 网络(Network)

Docker 提供多种网络模式,让容器之间以及容器与外部通信:

bridge(默认):容器通过虚拟网桥通信,适合单机多容器场景
host:容器直接使用宿主机网络,性能最好但无隔离
none:容器无网络,完全隔离
overlay:跨主机容器通信,用于 Docker Swarm / K8s
# 创建自定义网络
docker network create my-network

# 容器加入网络
docker run -d --name app --network my-network my-app
docker run -d --name db --network my-network mysql:8

# 同一网络中的容器可以通过容器名互相访问
# app 容器中可以通过 "db:3306" 连接数据库

# 查看网络
docker network ls

# 查看网络详情
docker network inspect my-network
重要:同一自定义网络中的容器可以通过容器名(而非 IP)直接互相访问,Docker 内置了 DNS 解析。默认的 bridge 网络不支持此功能。

实战场景

场景 1 部署 Node.js Web 应用

将一个 Express/Koa 应用容器化部署:
# Dockerfile FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 3000 USER node CMD ["node", "server.js"]
# 构建并运行
docker build -t my-node-app .
docker run -d --name web -p 3000:3000 --restart unless-stopped my-node-app
配合 .dockerignore 文件排除不必要的文件:
node_modules
.git
.env
npm-debug.log
Dockerfile
docker-compose.yml

场景 2 多阶段构建优化镜像大小

多阶段构建可以显著减小最终镜像体积,例如一个 Go 应用:
# 阶段 1:编译 FROM golang:1.22-alpine AS builder WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -o /app/server . # 阶段 2:运行(仅包含编译后的二进制文件) FROM alpine:3.19 RUN apk --no-cache add ca-certificates COPY --from=builder /app/server /app/server EXPOSE 8080 CMD ["/app/server"]
效果:Go 编译镜像约 1GB,多阶段构建后的运行镜像仅 10-20MB,体积缩小 98%。

场景 3 部署 MySQL + phpMyAdmin

快速搭建 MySQL 数据库和管理界面:
# 创建网络
docker network create db-net

# 启动 MySQL
docker run -d --name mysql \
  --network db-net \
  -e MYSQL_ROOT_PASSWORD=my-secret-pw \
  -e MYSQL_DATABASE=myapp \
  -v mysql_data:/var/lib/mysql \
  mysql:8

# 启动 phpMyAdmin
docker run -d --name phpmyadmin \
  --network db-net \
  -e PMA_HOST=mysql \
  -p 8080:80 \
  phpmyadmin/phpmyadmin
访问 http://localhost:8080,使用 root / my-secret-pw 登录管理数据库。

场景 4 Nginx 反向代理多个应用

使用 Nginx 容器作为反向代理,将不同域名转发到不同的后端容器:
# nginx.conf server { listen 80; server_name api.example.com; location / { proxy_pass http://backend-api:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } server { listen 80; server_name web.example.com; location / { proxy_pass http://frontend:8080; } }
docker run -d --name nginx-proxy \
  --network my-network \
  -p 80:80 -p 443:443 \
  -v ./nginx.conf:/etc/nginx/conf.d/default.conf \
  nginx:alpine

场景 5 搭建本地开发环境

用 Docker 统一团队的开发环境,告别"在我机器上能跑":
# 前端开发(热重载)
docker run -d --name frontend \
  -p 3000:3000 \
  -v $(pwd):/app \
  -w /app \
  node:18-alpine \
  sh -c "npm install && npm run dev"

# Python 开发
docker run -it --rm \
  -v $(pwd):/app \
  -w /app \
  python:3.12-slim \
  bash
优势:新成员入职只需 docker compose up 一条命令,几分钟即可搭建完整开发环境,无需手动安装各种依赖。

场景 6 CI/CD 流水线中使用 Docker

在 GitHub Actions 中构建并推送 Docker 镜像:
# .github/workflows/docker.yml name: Build and Push on: push: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_TOKEN }} - uses: docker/build-push-action@v5 with: push: true tags: myuser/myapp:latest

场景 7 部署 Redis 缓存服务

一行命令启动 Redis:
# 带持久化和密码认证
docker run -d --name redis \
  -p 6379:6379 \
  -v redis_data:/data \
  redis:7-alpine \
  redis-server --appendonly yes --requirepass "your-password"

# 连接测试
docker exec -it redis redis-cli -a "your-password" ping
# 输出: PONG

场景 8 容器化 Python Flask/Django 应用

# Dockerfile (Flask) FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 5000 CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]
注意:生产环境不要使用 Flask 自带的开发服务器,务必使用 Gunicorn 或 uWSGI 作为 WSGI 服务器。

场景 9 定时备份容器数据

使用 Docker 容器执行定时数据库备份:
# 手动备份 MySQL
docker exec mysql mysqldump -u root -pmy-secret-pw myapp > backup_$(date +%Y%m%d).sql

# 自动化备份:创建备份容器
docker run -d --name backup \
  --network db-net \
  -v /backups:/backups \
  -e MYSQL_HOST=mysql \
  -e MYSQL_PWD=my-secret-pw \
  mysql:8 \
  sh -c 'while true; do
    mysqldump -h $MYSQL_HOST -u root myapp > /backups/backup_$(date +%Y%m%d_%H%M).sql
    echo "Backup completed at $(date)"
    sleep 86400
  done'

场景 10 搭建私有 Docker Registry

部署私有镜像仓库,团队内部共享镜像:
# 启动私有 Registry
docker run -d --name registry \
  -p 5000:5000 \
  -v registry_data:/var/lib/registry \
  --restart always \
  registry:2

# 推送镜像到私有仓库
docker tag my-app:latest localhost:5000/my-app:latest
docker push localhost:5000/my-app:latest

# 从私有仓库拉取
docker pull localhost:5000/my-app:latest

# 查看仓库中的镜像列表
curl http://localhost:5000/v2/_catalog
进阶:生产环境建议使用 Harbor(带 UI 和权限管理的企业级 Registry)替代基础 Registry。

Docker Compose

Docker Compose 用于定义和管理多容器应用。通过一个 YAML 文件描述所有服务、网络和卷,一条命令即可启动完整的应用栈。

示例 1 完整 Web 应用栈(前端 + 后端 + 数据库 + 缓存)

# docker-compose.yml version: "3.8" services: frontend: build: ./frontend ports: - "3000:3000" depends_on: - backend environment: - API_URL=http://backend:8080 backend: build: ./backend ports: - "8080:8080" depends_on: - db - redis environment: - DATABASE_URL=postgres://user:pass@db:5432/myapp - REDIS_URL=redis://redis:6379 db: image: postgres:16-alpine restart: always environment: - POSTGRES_DB=myapp - POSTGRES_USER=user - POSTGRES_PASSWORD=pass volumes: - pg_data:/var/lib/postgresql/data redis: image: redis:7-alpine restart: always volumes: pg_data:
常用命令:
# 启动所有服务(后台运行)
docker compose up -d

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs -f backend

# 停止并删除所有容器
docker compose down

# 停止并删除容器 + 卷(清除数据)
docker compose down -v

# 重新构建镜像
docker compose build --no-cache

示例 2 WordPress + MySQL 一键部署

version: "3.8" services: wordpress: image: wordpress:latest restart: always ports: - "8080:80" environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: wp WORDPRESS_DB_PASSWORD: wp-pass WORDPRESS_DB_NAME: wordpress volumes: - wp_data:/var/www/html db: image: mysql:8 restart: always environment: MYSQL_DATABASE: wordpress MYSQL_USER: wp MYSQL_PASSWORD: wp-pass MYSQL_ROOT_PASSWORD: root-pass volumes: - db_data:/var/lib/mysql volumes: wp_data: db_data:
执行 docker compose up -d,访问 http://localhost:8080 即可开始 WordPress 安装向导。

示例 3 使用 .env 文件管理配置

将敏感配置从 docker-compose.yml 中分离:
# .env 文件
DB_PASSWORD=super-secret-123
DB_NAME=production
REDIS_PASSWORD=redis-secret
在 docker-compose.yml 中引用:
services: db: image: postgres:16 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: ${DB_NAME}
警告:永远不要将 .env 文件提交到 Git 仓库。确保 .gitignore 中包含 .env

运维与安全

运维 1 镜像瘦身最佳实践

减小镜像体积可以加速部署和降低存储成本:
1. 使用 Alpine 基础镜像:node:18-alpine(~50MB)而非 node:18(~350MB)
2. 多阶段构建:编译阶段用完整镜像,运行阶段用精简镜像
3. 合并 RUN 指令:减少层数,每个 RUN 都会创建新层
4. 清理缓存:RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
5. 使用 .dockerignore:排除 node_modules、.git 等不需要的文件
6. 使用 distroless 镜像:Google 的 distroless 镜像不含 shell 和包管理器,极致精简

运维 2 容器安全加固


1. 不使用 root 用户:在 Dockerfile 中添加 USER nonroot
2. 只读文件系统:docker run --read-only ...
3. 限制资源:docker run --memory=512m --cpus=1 ...
4. 扫描镜像漏洞:docker scout cves my-image 或使用 Trivy
5. 不暴露不必要的端口:只映射必需的端口
6. 使用 secrets 管理敏感数据:不要在环境变量或 Dockerfile 中硬编码密码
7. 定期更新基础镜像:及时修复安全漏洞
严禁:不要在生产环境使用 --privileged 模式运行容器,这会赋予容器几乎等同于宿主机 root 的权限。

运维 3 日志管理

Docker 默认使用 json-file 日志驱动,日志文件会无限增长:
# 查看容器日志
docker logs --tail 100 -f my-container

# 限制日志文件大小(daemon.json)
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
/etc/docker/daemon.json 配置后重启 Docker:sudo systemctl restart docker
进阶方案:生产环境建议将日志输出到 ELK(Elasticsearch + Logstash + Kibana)或 Loki + Grafana 集中管理。

运维 4 磁盘空间清理

Docker 长期运行会积累大量无用数据,定期清理很重要:
# 查看 Docker 磁盘占用
docker system df

# 清理所有未使用的资源(慎用)
docker system prune -a

# 分别清理
docker container prune   # 删除已停止的容器
docker image prune -a    # 删除未使用的镜像
docker volume prune      # 删除未使用的卷
docker network prune     # 删除未使用的网络

# 删除所有悬空(dangling)镜像
docker image prune
注意:docker system prune -a 会删除所有未运行容器使用的镜像,请确保不会误删需要的镜像。

运维 5 健康检查与自动重启

为容器配置健康检查,确保服务异常时自动恢复:
# Dockerfile 中定义健康检查 HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1
或在 docker-compose.yml 中配置:
services: web: image: my-app restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s
restart 策略说明:no(默认不重启)、always(总是重启)、unless-stopped(除非手动停止)、on-failure(异常退出时重启)。

常见问题(50 个)

以下收录了 Docker 使用过程中最常遇到的 50 个问题,按类别分组,每个问题都提供了具体的错误信息和解决方案。

一、安装与启动问题 (10 个)

#1 Docker 守护进程未启动

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Docker 服务未运行,启动它:
# Linux
sudo systemctl start docker
sudo systemctl enable docker  # 开机自启

# macOS / Windows
# 启动 Docker Desktop 应用程序
如果仍然报错,检查 Docker 是否正确安装:docker --version

#2 权限不足,需要 sudo

Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
当前用户不在 docker 组中:
# 将用户加入 docker 组
sudo usermod -aG docker $USER

# 重新登录生效(或执行)
newgrp docker

# 验证
docker ps
安全提示:docker 组的用户拥有等同于 root 的权限,只给可信用户添加此权限。

#3 端口已被占用

Error response from daemon: driver failed programming external connectivity: Bind for 0.0.0.0:80 failed: port is already allocated
宿主机端口 80 已被其他进程占用:
# 查看端口占用
sudo lsof -i :80
# 或
sudo netstat -tlnp | grep :80

# 方案 1:停止占用端口的进程
sudo kill <PID>

# 方案 2:使用其他端口
docker run -p 8080:80 nginx

#4 Docker Desktop 启动失败(Windows)

Docker Desktop - WSL distro terminated abruptly / Hardware assisted virtualization and data execution protection must be enabled in the BIOS
Windows 上的常见问题:
1. 确保 BIOS 中已启用虚拟化(VT-x / AMD-V)
2. 确保 WSL 2 已安装并设置为默认版本:wsl --set-default-version 2
3. 更新 WSL:wsl --update
4. 在 Windows 功能中启用"虚拟机平台"和"适用于 Linux 的 Windows 子系统"
5. 重启电脑

#5 存储驱动不兼容

Error starting daemon: error initializing graphdriver: driver not supported
文件系统与 Docker 存储驱动不兼容:
# 查看当前存储驱动
docker info | grep "Storage Driver"

# 修改 /etc/docker/daemon.json
{
  "storage-driver": "overlay2"
}

# 重启 Docker
sudo systemctl restart docker
推荐使用 overlay2 驱动,需要 Linux 内核 4.0+ 和 ext4/xfs 文件系统。

#6 Docker Compose 命令不存在

docker-compose: command not found / docker compose: unknown command
Docker Compose V2 已集成到 Docker CLI 中,命令格式有变化:
# 旧版(V1,独立安装)
docker-compose up -d

# 新版(V2,Docker CLI 插件,推荐)
docker compose up -d

# 安装 Compose 插件(如未安装)
sudo apt-get install docker-compose-plugin
说明:Docker Compose V1 已停止维护,请迁移到 V2。主要区别是命令从 docker-compose(带连字符)变为 docker compose(空格)。

#7 Docker 版本过旧

Error response from daemon: client version 1.40 is too new. Maximum supported API version is 1.39
Docker CLI 与 Engine 版本不匹配:
# 查看版本
docker version

# 升级 Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

# 或临时降级 API 版本
export DOCKER_API_VERSION=1.39

#8 iptables/nftables 冲突

Error starting daemon: Error initializing network controller: error creating default "bridge" network: Failed to Setup IP tables
Docker 需要 iptables 来管理网络规则:
# 确保 iptables 已安装
sudo apt-get install iptables

# 如果使用 nftables,切换到 iptables 兼容模式
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy

# 重启 Docker
sudo systemctl restart docker

#9 磁盘空间不足导致启动失败

Error response from daemon: no space left on device
Docker 数据目录(默认 /var/lib/docker)磁盘已满:
# 查看磁盘使用
df -h /var/lib/docker

# 清理 Docker 无用数据
docker system prune -a --volumes

# 迁移 Docker 数据目录到大磁盘
# 编辑 /etc/docker/daemon.json
{
  "data-root": "/mnt/large-disk/docker"
}

#10 CentOS 8 / RHEL 8 安装 Docker 冲突

Problem: package docker-ce requires containerd.io >= 1.6.0, but none of the providers can be installed
CentOS 8 / RHEL 8 自带的 Podman/containerd 与 Docker 冲突:
# 移除冲突包
sudo yum remove podman buildah

# 安装 containerd.io
sudo yum install https://download.docker.com/linux/centos/8/x86_64/stable/Packages/containerd.io-1.6.28-3.1.el8.x86_64.rpm

# 然后安装 Docker
sudo yum install docker-ce docker-ce-cli

二、镜像与构建问题 (10 个)

#11 镜像拉取超时(国内网络)

Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection
国内访问 Docker Hub 速度慢或超时,配置镜像加速器:
# 编辑 /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://mirror.ccs.tencentyun.com",
    "https://docker.nju.edu.cn",
    "https://docker.mirrors.ustc.edu.cn"
  ]
}

# 重载配置并重启
sudo systemctl daemon-reload
sudo systemctl restart docker

# 验证配置
docker info | grep -A 5 "Registry Mirrors"

#12 镜像不存在或标签错误

Error response from daemon: manifest for nginx:latset not found: manifest unknown
镜像名称或标签拼写错误(示例中 latset 应为 latest):
1. 检查镜像名称拼写
2. 在 Docker Hub 上确认可用标签
3. 使用 docker search <image> 搜索镜像
4. 注意官方镜像不需要用户名前缀(nginx 而非 library/nginx

#13 Dockerfile 构建上下文过大

Sending build context to Docker daemon 2.5GB
构建时 Docker 会将整个目录发送给守护进程。创建 .dockerignore 排除不需要的文件:
# .dockerignore
node_modules
.git
.env
*.log
dist
coverage
.DS_Store
__pycache__
效果:一个 Node.js 项目添加 .dockerignore 后,构建上下文可从 2GB+ 降到几 MB,构建速度显著提升。

#14 Dockerfile 中 COPY 文件不存在

COPY failed: file not found in build context or excluded by .dockerignore
常见原因:
1. 文件路径相对于构建上下文(docker build 指定的目录),不是相对于 Dockerfile
2. 文件被 .dockerignore 排除了
3. 大小写敏感(Linux 文件系统区分大小写)
4. COPY 不支持复制构建上下文之外的文件

#15 构建缓存不生效

每次构建都重新下载依赖,耗时很长
Dockerfile 中指令顺序影响缓存效率。关键原则:不常变化的指令放前面
# 错误:每次代码变化都会重新 npm install
COPY . .
RUN npm install

# 正确:只有 package.json 变化才重新安装依赖
COPY package*.json ./
RUN npm install
COPY . .
另外,docker build --no-cache . 可强制禁用缓存。

#16 多平台镜像构建失败

ERROR: Multi-platform build is not supported for the docker driver
构建多平台镜像(如同时支持 AMD64 和 ARM64)需要 Buildx:
# 创建 Buildx 构建器
docker buildx create --name mybuilder --use

# 构建多平台镜像并推送
docker buildx build --platform linux/amd64,linux/arm64 -t myuser/myapp:latest --push .
应用场景:如果你的团队同时使用 Intel Mac 和 Apple Silicon Mac,多平台构建可以确保一个镜像在两种架构上都能运行。

#17 构建时 apt-get 安装失败

E: Unable to locate package xxx / E: Failed to fetch http://... 404 Not Found
构建镜像时包管理器缓存过期:
# 正确写法:apt-get update 和 install 在同一个 RUN 中
RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*

# 错误写法:分开的 RUN,update 的缓存层可能过期
RUN apt-get update
RUN apt-get install -y curl  # 可能失败

#18 Docker Hub 推送认证失败

denied: requested access to the resource is denied / unauthorized: authentication required
推送镜像前需要登录并正确命名:
# 登录 Docker Hub
docker login

# 镜像名必须包含你的用户名
docker tag my-app:latest username/my-app:latest
docker push username/my-app:latest
如果使用 Access Token(推荐),在 Docker Hub 网站 → Account Settings → Security → New Access Token 创建。

#19 镜像体积过大

构建出的镜像超过 1GB,部署缓慢
镜像瘦身策略:
1. 使用 Alpine 基础镜像(python:3.12-slim 而非 python:3.12
2. 多阶段构建,运行阶段只保留必要文件
3. 合并 RUN 指令并清理临时文件
4. 使用 docker history <image> 查看每层大小,找出"胖层"
# 查看镜像各层大小
docker history my-app:latest

# 使用 dive 工具分析镜像
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive my-app:latest

#20 ARM / Apple Silicon 兼容性问题

WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8)
在 Apple Silicon Mac 上运行 x86 镜像会出现此警告:
1. 优先使用支持 ARM 的镜像(大多数官方镜像已支持)
2. 如果必须使用 x86 镜像,Docker Desktop 会自动通过 QEMU 模拟(速度较慢)
3. 指定平台:docker run --platform linux/amd64 ...
4. 构建时指定目标平台:docker build --platform linux/amd64 .

三、容器运行问题 (10 个)

#21 容器启动后立即退出

容器状态显示 Exited (0) 或 Exited (1),docker ps 看不到
容器的主进程退出后容器就会停止。常见原因:
1. CMD/ENTRYPOINT 执行完就退出了:确保主进程是长期运行的(如 Web 服务器)
2. 脚本执行完毕:如果 CMD 是 shell 脚本,脚本结束容器就退出
3. 应用启动失败:查看日志 docker logs <container>
# 查看退出原因
docker logs my-container

# 交互式进入容器调试
docker run -it my-image /bin/sh

# 保持容器运行(调试用)
docker run -d my-image tail -f /dev/null

#22 容器内无法解析域名

curl: (6) Could not resolve host: api.example.com
容器 DNS 解析失败:
# 方案 1:指定 DNS 服务器
docker run --dns 8.8.8.8 my-image

# 方案 2:全局配置 DNS(/etc/docker/daemon.json)
{
  "dns": ["8.8.8.8", "114.114.114.114"]
}

# 方案 3:检查宿主机 DNS 是否正常
nslookup api.example.com

#23 容器之间无法通信

dial tcp: lookup db on 127.0.0.11:53: no such host
默认 bridge 网络不支持容器名解析。使用自定义网络:
# 创建自定义网络
docker network create app-net

# 将容器加入同一网络
docker run -d --name db --network app-net mysql:8
docker run -d --name app --network app-net my-app

# 现在 app 容器可以通过 "db" 主机名访问数据库
关键:只有自定义网络才支持容器名 DNS 解析。使用 Docker Compose 时会自动创建自定义网络。

#24 容器时区不正确

容器内 date 命令显示 UTC 时间,日志时间戳不对
Docker 容器默认使用 UTC 时区:
# 方案 1:通过环境变量设置(部分镜像支持)
docker run -e TZ=Asia/Shanghai my-image

# 方案 2:挂载宿主机时区文件
docker run -v /etc/localtime:/etc/localtime:ro my-image

# 方案 3:在 Dockerfile 中设置
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone

#25 容器内存溢出被 Kill

Exited (137) / OOMKilled: true
退出码 137 表示容器被 OOM Killer 终止(内存不足):
# 查看容器是否因 OOM 被杀
docker inspect my-container | grep OOMKilled

# 增加内存限制
docker run -m 1g my-image

# Docker Compose 中设置
services:
  app:
    deploy:
      resources:
        limits:
          memory: 1G
同时检查应用本身是否有内存泄漏。对于 Node.js 应用,可设置 --max-old-space-size=512

#26 文件权限问题(Permission denied)

EACCES: permission denied, open '/app/data/file.json'
容器内用户与挂载卷的文件权限不匹配:
# 查看容器内运行用户
docker exec my-container whoami

# 方案 1:在 Dockerfile 中修改目录权限
RUN mkdir -p /app/data && chown -R node:node /app/data
USER node

# 方案 2:运行时指定用户 UID
docker run -u $(id -u):$(id -g) -v ./data:/app/data my-image

# 方案 3:宿主机修改目录权限
chmod -R 777 ./data  # 不推荐,仅调试用

#27 容器无法访问宿主机服务

connect ECONNREFUSED 127.0.0.1:3306(容器内访问宿主机的 MySQL)
容器内的 localhost / 127.0.0.1 指向容器自身,不是宿主机:
# Docker Desktop(Mac/Windows)
# 使用特殊域名
host.docker.internal:3306

# Linux
# 方案 1:使用宿主机 IP
docker run --add-host host.docker.internal:host-gateway my-image

# 方案 2:使用 host 网络模式
docker run --network host my-image

#28 容器日志占满磁盘

/var/lib/docker/containers/xxx/xxx-json.log 文件过大
Docker 默认不限制日志大小。配置全局日志限制:
# /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

# 对单个容器限制
docker run --log-opt max-size=10m --log-opt max-file=3 my-image

# 清空已有容器的日志
truncate -s 0 /var/lib/docker/containers/<container-id>/*-json.log

#29 exec 进入容器报错

OCI runtime exec failed: exec failed: unable to start container process: exec: "/bin/bash": stat /bin/bash: no such file or directory
Alpine 等精简镜像不包含 bash,使用 sh 代替:
# Alpine 镜像使用 sh
docker exec -it my-container /bin/sh

# 或安装 bash
docker exec -it my-container apk add bash

# distroless 镜像没有任何 shell
# 使用 debug 版本:gcr.io/distroless/base:debug

#30 容器重启后数据丢失

数据库容器重建后数据全部丢失
容器的文件系统是临时的,删除容器数据就没了。必须使用 Volume 持久化:
# 错误:没有挂载卷,数据在容器内
docker run -d mysql:8

# 正确:使用命名卷持久化数据
docker run -d -v mysql_data:/var/lib/mysql mysql:8

# Docker Compose 中
services:
  db:
    image: mysql:8
    volumes:
      - mysql_data:/var/lib/mysql
volumes:
  mysql_data:  # 声明命名卷
重要:任何需要持久化的数据(数据库、上传文件、配置等)都必须挂载 Volume,否则 docker compose downdocker rm 后数据将永久丢失。

四、网络与存储问题 (10 个)

#31 Docker 网络 IP 与局域网冲突

Docker 创建的网络与公司内网 IP 段冲突,导致某些内网服务无法访问
Docker 默认使用 172.17.0.0/16 等私有地址段,可能与内网冲突:
# /etc/docker/daemon.json 自定义地址池
{
  "default-address-pools": [
    {"base": "10.200.0.0/16", "size": 24}
  ],
  "bip": "10.200.0.1/24"
}
修改后重启 Docker 服务。

#32 容器内无法访问外网

curl: (7) Failed to connect to xxx port 443: Connection refused
容器无法访问互联网:
1. 检查宿主机网络是否正常
2. 检查 Docker 网络转发:sysctl net.ipv4.ip_forward(需要为 1)
3. 检查防火墙规则是否阻止了 Docker 网络
# 启用 IP 转发
sudo sysctl -w net.ipv4.ip_forward=1

# 检查 iptables FORWARD 链
sudo iptables -L FORWARD -n

# 重启 Docker 重建网络规则
sudo systemctl restart docker

#33 Docker Compose 服务间连接被拒绝

ConnectionRefusedError: [Errno 111] Connection refused(后端连接数据库失败)
depends_on 只保证容器启动顺序,不保证服务就绪:
# docker-compose.yml 使用健康检查等待
services:
  backend:
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
或在应用代码中实现连接重试逻辑。

#34 Volume 挂载后容器内目录为空

挂载宿主机空目录到容器的 /app 后,容器内原有文件消失
绑定挂载(bind mount)会用宿主机目录覆盖容器内目录:
1. 命名卷:首次创建时会复制容器内已有文件到卷中
2. 绑定挂载:直接覆盖,容器内原有文件不可见
# 命名卷(保留容器原有内容)
docker run -v my-vol:/app/node_modules my-image

# 绑定挂载 + 匿名卷保护(常见于 node_modules)
docker run -v $(pwd):/app -v /app/node_modules my-image
上面的技巧用匿名卷保护 node_modules,避免被宿主机空目录覆盖。

#35 Volume 数据迁移

需要将 Docker Volume 数据迁移到另一台服务器
Docker Volume 迁移方法:
# 方案 1:使用 docker cp 导出数据
docker run --rm -v my-vol:/data -v $(pwd):/backup alpine \
  tar czf /backup/volume-backup.tar.gz -C /data .

# 在新服务器上恢复
docker run --rm -v my-vol:/data -v $(pwd):/backup alpine \
  tar xzf /backup/volume-backup.tar.gz -C /data

# 方案 2:使用 rsync 同步命名卷目录
rsync -avz /var/lib/docker/volumes/my-vol/ user@newserver:/var/lib/docker/volumes/my-vol/

#36 Docker 占用了防火墙端口绕过 UFW

UFW 规则设置了拒绝访问,但 Docker 容器的端口仍然可以外部访问
Docker 直接操作 iptables,会绕过 UFW 规则:
# 方案 1:禁止 Docker 修改 iptables
# /etc/docker/daemon.json
{
  "iptables": false
}

# 方案 2:绑定到 127.0.0.1(只允许本机访问)
docker run -p 127.0.0.1:8080:80 nginx

# 方案 3:使用 DOCKER-USER 链添加规则
sudo iptables -I DOCKER-USER -i eth0 -p tcp --dport 8080 -j DROP
警告:这是一个常被忽视的安全隐患。如果不注意,Docker 发布的端口可能对公网完全开放。

#37 overlay2 存储占用异常

/var/lib/docker/overlay2 目录占用数十 GB 甚至上百 GB
overlay2 存储了所有镜像和容器的分层数据:
# 查看占用详情
docker system df -v

# 清理悬空镜像层
docker image prune

# 清理所有未使用资源
docker system prune -a

# 查看每个镜像占用
docker images --format "{{.Repository}}:{{.Tag}} {{.Size}}" | sort -k2 -h
定期清理不再使用的镜像和已停止的容器是最有效的方法。

#38 挂载 Windows 路径到 Linux 容器

docker: Error response from daemon: invalid mount config: invalid mount path 'C:\Users\...'
Windows 路径在 Docker 中需要特殊处理:
# Git Bash / MSYS2 中使用双斜杠
docker run -v //c/Users/me/project:/app my-image

# PowerShell 中使用 ${PWD}
docker run -v ${PWD}:/app my-image

# WSL 2 中使用 Linux 路径(推荐)
docker run -v /mnt/c/Users/me/project:/app my-image
建议:在 Windows 上使用 Docker,项目文件最好放在 WSL 2 文件系统中(如 ~/projects/),而非 Windows 磁盘,可获得更好的 I/O 性能。

#39 容器内挂载卷文件变更不同步

修改宿主机文件后容器内看不到更新(macOS / Windows)
Docker Desktop 在 macOS 和 Windows 上通过虚拟化层同步文件,可能有延迟:
1. Docker Desktop 设置:确认共享目录已在 Resources → File Sharing 中添加
2. 使用 polling:Webpack/Vite 的热重载设置为 polling 模式
3. macOS:使用 :cached:delegated 挂载选项提升性能
# macOS 优化挂载性能
docker run -v $(pwd):/app:cached my-image

#40 多个 Compose 项目端口冲突

同时运行多个 docker-compose.yml 项目时端口冲突
多个项目使用相同端口会冲突:
1. 每个项目使用不同端口:-p 3001:3000-p 3002:3000
2. 使用 Nginx 反向代理统一管理(通过域名区分)
3. 使用 Traefik 自动反向代理(推荐)
# 不暴露端口到宿主机,通过 Traefik 代理
services:
  app:
    labels:
      - "traefik.http.routers.app.rule=Host(`app.localhost`)"

五、性能优化与进阶 (10 个)

#41 Docker 容器 CPU 占用过高

某个容器持续占用 100% CPU,影响其他服务
限制容器 CPU 使用:
# 限制使用 1 个 CPU 核心
docker run --cpus=1 my-image

# 限制 CPU 权重(相对优先级)
docker run --cpu-shares=512 my-image

# 查看容器资源使用情况
docker stats

# Docker Compose 限制
services:
  app:
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M

#42 构建速度太慢

docker build 每次耗时 10 分钟以上
加速 Docker 构建的方法:
1. 优化 Dockerfile 层顺序,最大化缓存命中
2. 使用 BuildKit:DOCKER_BUILDKIT=1 docker build .
3. 使用 .dockerignore 减少构建上下文大小
4. 多阶段构建,减少最终镜像层数
5. 使用构建缓存镜像:docker build --cache-from=myapp:latest .
6. 并行构建:BuildKit 自动并行执行无依赖关系的指令

#43 Docker Desktop 占用大量内存(macOS/Windows)

Docker Desktop 虚拟机占用 4GB+ 内存
Docker Desktop 运行在虚拟机中,默认分配较多内存:
1. Docker Desktop → Settings → Resources:调低 Memory 限制(如 2GB)
2. WSL 2 用户:创建 C:\Users\你的用户名\.wslconfig
[wsl2]
memory=2GB
processors=2

3. 不使用时退出 Docker Desktop
4. 考虑使用 Colima(macOS)或 Rancher Desktop 替代 Docker Desktop

#44 Docker Compose 服务过多启动慢

docker compose up 启动 10+ 服务需要数分钟
优化多服务启动:
1. 只启动需要的服务:docker compose up -d backend db
2. 使用 profiles 分组:
services: backend: profiles: ["dev"] monitoring: profiles: ["ops"]
# 只启动 dev 组的服务
docker compose --profile dev up -d

3. 预拉取镜像:docker compose pull
4. 并行启动:Docker Compose V2 默认并行启动

#45 如何查看容器内部的进程和资源

需要调试容器内部运行状态
常用调试命令:
# 实时资源监控
docker stats my-container

# 查看容器内进程
docker top my-container

# 查看容器详细配置
docker inspect my-container

# 查看容器文件系统变化(与镜像的差异)
docker diff my-container

# 将运行中的容器导出为镜像(调试快照)
docker commit my-container debug-snapshot:latest

# 使用 nsenter 进入容器的网络命名空间
nsenter -t $(docker inspect -f '{{.State.Pid}}' my-container) -n ip addr

#46 容器内文件 I/O 性能差

绑定挂载的文件读写速度远低于预期(macOS/Windows)
Docker Desktop 的文件共享通过虚拟化层,性能较差(尤其是 node_modules):
1. 使用命名卷代替绑定挂载(数据库、node_modules 等)
2. macOS:使用 :delegated 挂载选项
3. Windows WSL 2:将项目放在 WSL 文件系统中
# node_modules 使用命名卷避免跨系统挂载
services:
  app:
    volumes:
      - .:/app              # 源代码绑定挂载
      - node_modules:/app/node_modules  # 依赖用命名卷
volumes:
  node_modules:

#47 如何优雅地停止容器

docker stop 后容器需要 10 秒才停止(默认超时后被 SIGKILL)
docker stop 先发 SIGTERM,超时后发 SIGKILL:
1. 应用需要处理 SIGTERM 信号,实现优雅关闭
# Node.js 示例
process.on('SIGTERM', () => {
  console.log('收到 SIGTERM,优雅关闭...');
  server.close(() => process.exit(0));
});

2. Dockerfile 使用 exec 格式的 CMD:
# 正确:exec 格式,信号直接传递给应用
CMD ["node", "server.js"]

# 错误:shell 格式,信号传给 /bin/sh,应用收不到
CMD node server.js

3. 使用 tini 作为 init 进程处理信号转发

#48 Docker 网络性能优化

容器间网络延迟高、吞吐量低
Docker 网络性能优化方案:
1. host 网络模式:性能最好,无网络隔离:--network host
2. macvlan 网络:容器直接获得物理网络 IP,性能接近原生
3. 减少 NAT 转换:同一网络内的容器间通信不经过 NAT
4. 调整 MTU:确保 Docker 网络 MTU 与物理网络一致
# 创建 macvlan 网络(高性能场景)
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 \
  high-perf-net

#49 如何让容器开机自启动

服务器重启后 Docker 容器没有自动启动
使用 restart 策略:
# 方案 1:创建容器时指定
docker run -d --restart unless-stopped my-image

# 方案 2:修改已有容器
docker update --restart unless-stopped my-container

# 方案 3:Docker Compose
services:
  app:
    restart: unless-stopped
同时确保 Docker 服务本身开机自启:sudo systemctl enable docker
restart 策略对比:no 不重启;always 总是重启(含 docker restart 后);unless-stopped 除非手动 stop;on-failure[:max] 仅异常退出时重启。

#50 Docker 的局限性与何时不该用 Docker

什么场景不适合使用 Docker
Docker 并非万能,以下场景需要注意:
- GUI 应用:Docker 主要面向服务端,运行桌面应用需要额外配置 X11 转发
- 需要内核定制:容器共享宿主机内核,无法运行不同版本的内核
- 极致性能要求:网络和存储有微小开销,高频交易等极端场景考虑裸机部署
- Windows 原生应用:Windows 容器生态远不如 Linux 容器成熟
- 持久化存储密集型:数据库等 I/O 密集应用使用 Docker 需注意存储驱动性能
- 小型单体应用:如果应用很简单,直接部署可能比容器化更省事
总结:Docker 最适合无状态的微服务、Web 应用、API 服务、CI/CD 流水线。有状态服务(数据库、消息队列)可以用 Docker,但要特别注意数据持久化和备份。