Docker 是一个开源的容器化平台,允许开发者将应用及其所有依赖打包到一个轻量级、可移植的容器中,实现"一次构建,到处运行"。自 2013 年发布以来,Docker 彻底改变了软件的开发、测试和部署方式,成为现代 DevOps 和微服务架构的基石。
Docker 容器与传统虚拟机不同,它共享宿主机的操作系统内核,因此启动速度快(秒级)、资源占用少、密度高。在 GitHub 上,Docker 的核心引擎(Moby 项目)拥有超过 7 万星,是全球使用最广泛的容器运行时。
| 项目信息 | 详情 |
|---|---|
| 开发公司 | Docker, Inc.(美国旧金山) |
| GitHub Stars | 70,000+(Moby/Moby) |
| 官方网站 | docker.com |
| 开源协议 | Apache License 2.0 |
| 技术栈 | Go |
| 容器运行时 | containerd / runc |
| 部署方式 | Docker Desktop / Docker Engine / Docker CE |
| 对比维度 | Docker | Podman | LXC/LXD | 虚拟机 (VM) |
|---|---|---|---|---|
| 架构 | C/S 架构(守护进程) | 无守护进程(Daemonless) | 系统级容器 | 完整虚拟化 |
| 启动速度 | 秒级 | 秒级 | 秒级 | 分钟级 |
| 资源占用 | 极低(共享内核) | 极低(共享内核) | 低 | 高(独占内存/CPU) |
| 隔离性 | 进程级隔离 | 进程级隔离 | 进程级隔离 | 完全隔离(独立内核) |
| 生态系统 | 最成熟,Docker Hub 百万镜像 | 兼容 Docker 镜像 | 生态较小 | 成熟但不同体系 |
| Rootless 支持 | 需额外配置 | 原生支持 | 支持 | 不适用 |
| 兼容 K8s | 原生支持 | 支持(通过 CRI-O) | 不直接支持 | 不直接支持 |
| 适用场景 | 应用容器化、微服务、CI/CD | 安全敏感环境、替代 Docker | 需要完整 OS 环境 | 强隔离、异构 OS |
# 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 usermod -aG docker $USER,然后重新登录生效。brew install --cask docker启动 Docker Desktop 后,终端即可使用
docker 命令。
wsl --install
# PowerShell 或 CMD docker --version docker run hello-world
{"registry-mirrors": ["https://mirror.ccs.tencentyun.com"]}# 官方一键安装脚本 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
# 搜索镜像 docker search nginx # 拉取镜像 docker pull nginx:latest docker pull node:18-alpine # 查看本地镜像 docker images # 删除镜像 docker rmi nginx:latest # 查看镜像详情 docker inspect nginx:latest
# 创建并启动容器 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 指定容器名称。# 构建镜像 docker build -t my-app:1.0 . # 指定 Dockerfile 路径 docker build -f Dockerfile.prod -t my-app:prod .
# 方式 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
# 创建自定义网络 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
# 构建并运行 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
# 创建网络 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 登录管理数据库。
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
# 前端开发(热重载) 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 一条命令,几分钟即可搭建完整开发环境,无需手动安装各种依赖。# 带持久化和密码认证 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
# 手动备份 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'
# 启动私有 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
Docker Compose 用于定义和管理多容器应用。通过一个 YAML 文件描述所有服务、网络和卷,一条命令即可启动完整的应用栈。
# 启动所有服务(后台运行) docker compose up -d # 查看服务状态 docker compose ps # 查看日志 docker compose logs -f backend # 停止并删除所有容器 docker compose down # 停止并删除容器 + 卷(清除数据) docker compose down -v # 重新构建镜像 docker compose build --no-cache
docker compose up -d,访问 http://localhost:8080 即可开始 WordPress 安装向导。
# .env 文件 DB_PASSWORD=super-secret-123 DB_NAME=production REDIS_PASSWORD=redis-secret在 docker-compose.yml 中引用:
.env。node:18-alpine(~50MB)而非 node:18(~350MB)
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
USER nonroot
docker run --read-only ...
docker run --memory=512m --cpus=1 ...
docker scout cves my-image 或使用 Trivy
--privileged 模式运行容器,这会赋予容器几乎等同于宿主机 root 的权限。# 查看容器日志
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
# 查看 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 会删除所有未运行容器使用的镜像,请确保不会误删需要的镜像。no(默认不重启)、always(总是重启)、unless-stopped(除非手动停止)、on-failure(异常退出时重启)。
以下收录了 Docker 使用过程中最常遇到的 50 个问题,按类别分组,每个问题都提供了具体的错误信息和解决方案。
# Linux sudo systemctl start docker sudo systemctl enable docker # 开机自启 # macOS / Windows # 启动 Docker Desktop 应用程序如果仍然报错,检查 Docker 是否正确安装:
docker --version
# 将用户加入 docker 组 sudo usermod -aG docker $USER # 重新登录生效(或执行) newgrp docker # 验证 docker ps
# 查看端口占用 sudo lsof -i :80 # 或 sudo netstat -tlnp | grep :80 # 方案 1:停止占用端口的进程 sudo kill <PID> # 方案 2:使用其他端口 docker run -p 8080:80 nginx
wsl --set-default-version 2
wsl --update
# 查看当前存储驱动
docker info | grep "Storage Driver"
# 修改 /etc/docker/daemon.json
{
"storage-driver": "overlay2"
}
# 重启 Docker
sudo systemctl restart docker
推荐使用 overlay2 驱动,需要 Linux 内核 4.0+ 和 ext4/xfs 文件系统。
# 旧版(V1,独立安装) docker-compose up -d # 新版(V2,Docker CLI 插件,推荐) docker compose up -d # 安装 Compose 插件(如未安装) sudo apt-get install docker-compose-plugin
docker-compose(带连字符)变为 docker compose(空格)。# 查看版本 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
# 确保 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
/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"
}
# 移除冲突包 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
# 编辑 /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"
latset 应为 latest):
docker search <image> 搜索镜像
nginx 而非 library/nginx)
.dockerignore 排除不需要的文件:
# .dockerignore node_modules .git .env *.log dist coverage .DS_Store __pycache__
docker build 指定的目录),不是相对于 Dockerfile
# 错误:每次代码变化都会重新 npm install COPY . . RUN npm install # 正确:只有 package.json 变化才重新安装依赖 COPY package*.json ./ RUN npm install COPY . .另外,
docker build --no-cache . 可强制禁用缓存。
# 创建 Buildx 构建器 docker buildx create --name mybuilder --use # 构建多平台镜像并推送 docker buildx build --platform linux/amd64,linux/arm64 -t myuser/myapp:latest --push .
# 正确写法: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 # 可能失败
# 登录 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 创建。
python:3.12-slim 而非 python:3.12)
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
docker run --platform linux/amd64 ...
docker build --platform linux/amd64 .
docker logs <container>
# 查看退出原因 docker logs my-container # 交互式进入容器调试 docker run -it my-image /bin/sh # 保持容器运行(调试用) docker run -d my-image tail -f /dev/null
# 方案 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
# 创建自定义网络 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" 主机名访问数据库
# 方案 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
# 查看容器是否因 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。
# 查看容器内运行用户 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 # 不推荐,仅调试用
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
# /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
# 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
# 错误:没有挂载卷,数据在容器内
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: # 声明命名卷
docker compose down 或 docker rm 后数据将永久丢失。# /etc/docker/daemon.json 自定义地址池
{
"default-address-pools": [
{"base": "10.200.0.0/16", "size": 24}
],
"bip": "10.200.0.1/24"
}
修改后重启 Docker 服务。
sysctl net.ipv4.ip_forward(需要为 1)
# 启用 IP 转发 sudo sysctl -w net.ipv4.ip_forward=1 # 检查 iptables FORWARD 链 sudo iptables -L FORWARD -n # 重启 Docker 重建网络规则 sudo systemctl restart docker
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
或在应用代码中实现连接重试逻辑。
# 命名卷(保留容器原有内容) 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,避免被宿主机空目录覆盖。
# 方案 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/
# 方案 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 system df -v
# 清理悬空镜像层
docker image prune
# 清理所有未使用资源
docker system prune -a
# 查看每个镜像占用
docker images --format "{{.Repository}}:{{.Tag}} {{.Size}}" | sort -k2 -h
定期清理不再使用的镜像和已停止的容器是最有效的方法。
# 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
~/projects/),而非 Windows 磁盘,可获得更好的 I/O 性能。:cached 或 :delegated 挂载选项提升性能
# macOS 优化挂载性能 docker run -v $(pwd):/app:cached my-image
-p 3001:3000,-p 3002:3000
# 不暴露端口到宿主机,通过 Traefik 代理
services:
app:
labels:
- "traefik.http.routers.app.rule=Host(`app.localhost`)"
# 限制使用 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
DOCKER_BUILDKIT=1 docker build .
docker build --cache-from=myapp:latest .
C:\Users\你的用户名\.wslconfig:
[wsl2] memory=2GB processors=2
docker compose up -d backend db
# 只启动 dev 组的服务 docker compose --profile dev up -d
docker compose pull
# 实时资源监控
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
:delegated 挂载选项
# node_modules 使用命名卷避免跨系统挂载
services:
app:
volumes:
- .:/app # 源代码绑定挂载
- node_modules:/app/node_modules # 依赖用命名卷
volumes:
node_modules:
docker stop 先发 SIGTERM,超时后发 SIGKILL:
# Node.js 示例
process.on('SIGTERM', () => {
console.log('收到 SIGTERM,优雅关闭...');
server.close(() => process.exit(0));
});
# 正确:exec 格式,信号直接传递给应用 CMD ["node", "server.js"] # 错误:shell 格式,信号传给 /bin/sh,应用收不到 CMD node server.js
tini 作为 init 进程处理信号转发
--network host
# 创建 macvlan 网络(高性能场景) docker network create -d macvlan \ --subnet=192.168.1.0/24 \ --gateway=192.168.1.1 \ -o parent=eth0 \ high-perf-net
# 方案 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
no 不重启;always 总是重启(含 docker restart 后);unless-stopped 除非手动 stop;on-failure[:max] 仅异常退出时重启。