Docker 跑得慢?从镜像到配置一次性优化到位
Docker 性能优化教程|附配置文件
Docker 已经成为现代应用交付、微服务部署和 DevOps 流水线中的核心工具。它能让应用以容器形式快速运行、迁移和扩展,但如果 Docker 使用不当,也可能出现 CPU 抢占严重、内存溢出、磁盘 I/O 过高、镜像体积过大、网络延迟增加等问题。
本文将从 镜像构建、容器运行参数、Docker Daemon 配置、存储驱动、日志管理、网络优化、资源限制、Compose 配置、监控排查 等多个角度,系统讲解 Docker 性能优化方法,并附带可直接参考的配置文件示例。
一、Docker 性能优化的核心思路
Docker 性能优化并不是单纯修改某一个参数,而是一个系统性工程。通常需要从以下几个层面入手:
-
镜像层优化
减小镜像体积,减少不必要依赖,加快拉取、启动和部署速度。 -
容器资源限制
合理限制 CPU、内存、磁盘 I/O,避免单个容器拖垮宿主机。 -
Docker Daemon 优化
调整日志、存储驱动、镜像加速、并发下载等参数。 -
存储优化
选择合适的存储驱动,合理使用数据卷,避免容器层频繁写入。 -
网络优化
根据业务场景选择 bridge、host、overlay 等网络模式。 -
日志优化
控制日志大小,避免日志无限增长占满磁盘。 -
监控与排查
通过工具持续观察 CPU、内存、I/O、网络等指标。
二、优化 Docker 镜像构建
镜像体积越小,容器启动速度通常越快,镜像传输和部署效率也越高。因此,镜像优化是 Docker 性能优化的第一步。
1. 使用更小的基础镜像
很多初学者喜欢使用完整系统镜像,例如:
FROM ubuntu:22.04
这种镜像功能完整,但体积较大。如果应用本身只需要运行环境,建议选择更轻量的基础镜像。
例如 Go 应用可以使用:
FROM alpine:3.19
Node.js 应用可以使用:
FROM node:20-alpine
Python 应用可以使用:
FROM python:3.11-slim
常见基础镜像体积对比:
| 镜像 | 特点 |
|---|---|
| ubuntu | 功能完整,但体积较大 |
| debian | 稳定,兼容性较好 |
| slim | 精简版 Debian,适合生产环境 |
| alpine | 极小体积,但部分依赖兼容性需要注意 |
| distroless | 极简运行时镜像,安全性高 |
2. 使用多阶段构建
多阶段构建可以将编译环境和运行环境分离,最终镜像只保留运行所需文件。
下面是一个 Go 项目的示例:
# 第一阶段:构建
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o server ./cmd/server
# 第二阶段:运行
FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/server /app/server
EXPOSE 8080
CMD ["./server"]
优化点:
- 构建工具不会进入最终镜像;
- 最终镜像更小;
- 安全风险更低;
- 部署速度更快。
3. 合理利用 Docker 构建缓存
Dockerfile 每一行都会生成一层缓存。应将变化较少的内容放在前面,变化频繁的代码放在后面。
不推荐:
COPY . .
RUN npm install
推荐:
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
这样当代码变化但依赖没有变化时,npm ci 这一层可以复用缓存,构建速度更快。
4. 减少镜像层数
虽然 Docker 的分层机制很强大,但过多的镜像层会增加管理和加载成本。可以将多个命令合并:
RUN apt-get update \
&& apt-get install -y curl vim \
&& rm -rf /var/lib/apt/lists/*
同时要注意在安装依赖后清理缓存,避免把无用文件打入镜像。
5. 使用 .dockerignore 文件
.dockerignore 可以排除不需要复制到镜像中的文件,例如日志、缓存、Git 目录、本地依赖等。
示例:
.git
.gitignore
node_modules
dist
target
*.log
.cache
.idea
.vscode
Dockerfile
docker-compose.yml
README.md
合理配置 .dockerignore 可以减少构建上下文大小,加快构建速度。
三、Docker Daemon 性能优化配置
Docker Daemon 是 Docker 的核心服务,配置文件通常位于:
/etc/docker/daemon.json
如果文件不存在,可以手动创建。
1. 推荐 daemon.json 配置
下面是一份适合多数生产环境的 Docker Daemon 配置示例:
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://hub-mirror.c.163.com"
],
"data-root": "/data/docker",
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"exec-opts": [
"native.cgroupdriver=systemd"
],
"live-restore": true,
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 5,
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 65535,
"Soft": 65535
},
"nproc": {
"Name": "nproc",
"Hard": 65535,
"Soft": 65535
}
},
"dns": [
"223.5.5.5",
"114.114.114.114"
]
}
2. 配置项说明
registry-mirrors
配置镜像加速器,可以提升镜像拉取速度,尤其是在国内网络环境中非常明显。
"registry-mirrors": [
"https://docker.m.daocloud.io"
]
data-root
指定 Docker 数据目录,默认是:
/var/lib/docker
如果系统盘空间较小,建议将 Docker 数据目录迁移到独立磁盘,例如:
"data-root": "/data/docker"
这样可以避免容器、镜像和日志占满系统盘。
storage-driver
推荐使用:
"storage-driver": "overlay2"
overlay2 是目前 Linux 环境下 Docker 推荐的存储驱动,性能稳定,兼容性好。
查看当前存储驱动:
docker info | grep "Storage Driver"
log-driver 与 log-opts
默认情况下,容器日志会持续增长。如果不限制日志大小,很容易占满磁盘。
推荐配置:
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
表示每个日志文件最大 100MB,最多保留 3 个文件。
live-restore
"live-restore": true
开启后,即使 Docker Daemon 重启,正在运行的容器也不会被强制停止,适合生产环境。
default-ulimits
提高文件句柄数,适合高并发服务:
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 65535,
"Soft": 65535
}
}
如果应用是网关、代理、Web 服务或数据库服务,文件句柄数过低可能导致连接失败。
3. 重新加载 Docker 配置
修改 /etc/docker/daemon.json 后,执行:
sudo systemctl daemon-reload
sudo systemctl restart docker
查看配置是否生效:
docker info
注意:重启 Docker 可能影响容器运行,生产环境建议提前评估,或启用 live-restore。
四、容器运行时性能优化
容器运行时参数会直接影响 CPU、内存、I/O 和稳定性。建议生产环境不要让容器无限制地使用宿主机资源。
1. 限制 CPU 使用
示例:
docker run -d \
--name app \
--cpus="2.0" \
nginx:latest
表示该容器最多使用 2 个 CPU 核心。
也可以使用 CPU 权重:
docker run -d \
--name app \
--cpu-shares=1024 \
nginx:latest
cpu-shares 是相对权重,默认值通常为 1024。它只在 CPU 资源竞争时生效。
2. 绑定指定 CPU 核心
对于对延迟敏感的服务,可以绑定 CPU 核心,减少调度开销。
docker run -d \
--name app \
--cpuset-cpus="0,1" \
nginx:latest
表示容器只能运行在 CPU 0 和 CPU 1 上。
适用场景:
- 高性能网关;
- 实时计算服务;
- 对延迟敏感的交易系统;
- 独占核心的数据库实例。
3. 限制内存使用
docker run -d \
--name app \
--memory="1g" \
--memory-swap="1g" \
nginx:latest
参数说明:
| 参数 | 含义 |
|---|---|
--memory |
限制容器最大可用内存 |
--memory-swap |
内存加 Swap 总量 |
--oom-kill-disable |
是否禁用 OOM Kill,不建议随意使用 |
如果设置:
--memory="1g" --memory-swap="1g"
表示容器最多使用 1GB 内存,且不能额外使用 Swap。
4. 设置重启策略
为了提升服务可用性,应配置重启策略:
docker run -d \
--name app \
--restart=unless-stopped \
nginx:latest
常用策略:
| 策略 | 说明 |
|---|---|
| no | 不自动重启 |
| always | 总是自动重启 |
| unless-stopped | 除非手动停止,否则自动重启 |
| on-failure | 失败时重启 |
生产环境一般推荐:
--restart=unless-stopped
5. 使用只读文件系统
如果应用不需要写入容器根文件系统,可以开启只读模式,提高安全性并减少误写入。
docker run -d \
--name app \
--read-only \
--tmpfs /tmp \
nginx:latest
如果程序需要临时目录,可以使用 tmpfs。
五、Docker Compose 性能优化配置
在实际项目中,通常会使用 Docker Compose 管理多个服务。下面是一份较完整的优化版 docker-compose.yml 示例。
version: "3.9"
services:
app:
image: my-app:1.0.0
container_name: my-app
restart: unless-stopped
ports:
- "8080:8080"
environment:
- TZ=Asia/Shanghai
- APP_ENV=production
deploy:
resources:
limits:
cpus: "2.0"
memory: 1024M
reservations:
cpus: "0.5"
memory: 256M
logging:
driver: json-file
options:
max-size: "100m"
max-file: "3"
ulimits:
nofile:
soft: 65535
hard: 65535
volumes:
- app-data:/app/data
- ./logs:/app/logs
networks:
- app-net
redis:
image: redis:7-alpine
container_name: redis
restart: unless-stopped
command:
- redis-server
- /usr/local/etc/redis/redis.conf
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
- redis-data:/data
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
networks:
- app-net
volumes:
app-data:
redis-data:
networks:
app-net:
driver: bridge
需要注意的是,deploy.resources 在普通 docker compose up 场景中部分配置可能不会完全生效,它主要用于 Docker Swarm。如果不是 Swarm,可以结合 mem_limit 等参数:
services:
app:
image: my-app:1.0.0
mem_limit: 1024m
cpus: 2.0
六、存储性能优化
Docker 存储性能对数据库、消息队列、日志服务等 I/O 密集型应用影响很大。
1. 优先使用 volume,而不是容器可写层
容器内部的可写层性能通常不如 Docker Volume 或宿主机目录挂载。对于需要持久化的数据,应使用:
docker volume create app-data
运行容器:
docker run -d \
--name app \
-v app-data:/app/data \
nginx:latest
这样数据不会随着容器删除而丢失,性能和可维护性也更好。
2. 数据库容器避免写入容器层
错误示例:
docker run -d mysql:8
如果没有挂载数据目录,MySQL 数据会写入容器层,不利于性能和持久化。
推荐:
docker run -d \
--name mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-v mysql-data:/var/lib/mysql \
mysql:8
3. 日志目录单独挂载
对于日志量较大的应用,建议将日志目录挂载到宿主机或专用磁盘:
docker run -d \
--name app \
-v /data/logs/app:/app/logs \
my-app:1.0.0
如果日志写入容器层,会增加 overlay2 压力,长期运行可能影响性能。
4. 使用 tmpfs 存放临时文件
临时文件可以放到内存文件系统中,减少磁盘 I/O。
docker run -d \
--name app \
--tmpfs /tmp:size=512m \
my-app:1.0.0
适合场景:
- 临时缓存;
- 中间计算文件;
- 不需要持久化的短生命周期数据。
七、网络性能优化
Docker 网络模式选择会影响服务性能和访问方式。
1. bridge 网络
默认网络模式,适合多数单机部署场景。
docker network create app-net
运行容器:
docker run -d \
--name app \
--network app-net \
nginx:latest
自定义 bridge 网络比默认 bridge 更推荐,因为它支持容器名 DNS 解析,隔离性更好。
2. host 网络
host 网络模式下,容器直接使用宿主机网络栈,性能损耗更低。
docker run -d \
--name app \
--network host \
nginx:latest
优点:
- 网络性能好;
- 延迟低;
- 不需要端口映射。
缺点:
- 隔离性较差;
- 容器端口与宿主机端口可能冲突;
- 不适合多实例端口复用场景。
适合高性能网关、监控采集器、网络代理等场景。
3. 减少不必要的端口暴露
只暴露必要端口,减少网络攻击面,也能降低管理复杂度。
不推荐:
ports:
- "0.0.0.0:3306:3306"
如果 MySQL 只给内部服务访问,应只加入同一 Docker 网络,不对外暴露端口。
八、日志性能优化
日志是 Docker 最容易被忽略的性能问题之一。大量日志可能造成:
- 磁盘空间耗尽;
- I/O 压力升高;
- 容器响应变慢;
- Docker Daemon 管理日志变慢。
1. 限制容器日志大小
全局限制可以在 daemon.json 中配置:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
单个容器限制:
docker run -d \
--name app \
--log-driver=json-file \
--log-opt max-size=100m \
--log-opt max-file=3 \
nginx:latest
2. 定期检查日志大小
查看容器日志路径:
docker inspect --format='{{.LogPath}}' 容器名
查看所有容器日志大小:
sudo find /var/lib/docker/containers/ -name "*-json.log" -exec ls -lh {} \;
清空某个日志文件:
sudo truncate -s 0 /var/lib/docker/containers/容器ID/容器ID-json.log
注意:不要直接删除日志文件,推荐使用 truncate 清空。
九、系统内核参数优化
Docker 运行在宿主机内核之上,宿主机内核参数也会影响容器性能。
可以创建配置文件:
sudo vim /etc/sysctl.d/99-docker-performance.conf
示例配置:
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
fs.file-max = 2097152
vm.swappiness = 10
vm.max_map_count = 262144
加载配置:
sudo sysctl --system
说明:
| 参数 | 作用 |
|---|---|
net.core.somaxconn |
提高监听队列长度 |
tcp_max_syn_backlog |
提高 SYN 队列长度 |
ip_local_port_range |
扩大本地端口范围 |
tcp_tw_reuse |
复用 TIME_WAIT 连接 |
fs.file-max |
提高系统文件句柄上限 |
vm.swappiness |
降低系统使用 Swap 的倾向 |
vm.max_map_count |
Elasticsearch 等服务常用 |
这些参数应结合业务压测结果调整,不建议盲目复制到所有机器。
十、镜像与容器清理优化
Docker 长期运行后,会积累大量无用镜像、停止容器、构建缓存和未使用数据卷。
查看磁盘占用:
docker system df
清理停止的容器:
docker container prune
清理无用镜像:
docker image prune
清理构建缓存:
docker builder prune
清理所有未使用资源:
docker system prune -a
如果要连未使用的数据卷一起清理:
docker system prune -a --volumes
注意:--volumes 可能删除重要数据卷,生产环境务必谨慎。
十一、监控 Docker 性能
优化不能只靠经验,必须通过监控和数据判断。
1. 使用 docker stats
实时查看容器资源使用:
docker stats
输出包括:
- CPU 使用率;
- 内存使用量;
- 网络收发;
- 磁盘 I/O;
- PIDs 数量。
查看指定容器:
docker stats app redis mysql
2. 使用 docker top
查看容器内进程:
docker top app
3. 使用 docker inspect
查看容器详细配置:
docker inspect app
例如查看容器 IP:
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' app
4. 推荐监控方案
生产环境建议使用:
- Prometheus;
- Grafana;
- cAdvisor;
- Node Exporter;
- Loki;
- ELK / OpenSearch。
典型架构:
Docker 容器 -> cAdvisor -> Prometheus -> Grafana
应用日志 -> Loki/ELK -> Dashboard
宿主机指标 -> Node Exporter -> Prometheus
十二、生产环境推荐配置模板
下面给出一个相对完整的生产环境配置组合。
1. /etc/docker/daemon.json
{
"data-root": "/data/docker",
"storage-driver": "overlay2",
"registry-mirrors": [
"https://docker.m.daocloud.io"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
},
"exec-opts": [
"native.cgroupdriver=systemd"
],
"live-restore": true,
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 5,
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Soft": 65535,
"Hard": 65535
}
},
"dns": [
"223.5.5.5",
"119.29.29.29"
]
}
2. docker-compose.yml
version: "3.9"
services:
web:
image: nginx:1.25-alpine
container_name: web
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./html:/usr/share/nginx/html:ro
- ./logs/nginx:/var/log/nginx
logging:
driver: json-file
options:
max-size: "100m"
max-file: "3"
ulimits:
nofile:
soft: 65535
hard: 65535
networks:
- app-net
app:
image: my-app:1.0.0
container_name: app
restart: unless-stopped
environment:
- TZ=Asia/Shanghai
- APP_ENV=production
cpus: 2.0
mem_limit: 1024m
volumes:
- app-data:/app/data
- ./logs/app:/app/logs
logging:
driver: json-file
options:
max-size: "100m"
max-file: "3"
networks:
- app-net
volumes:
app-data:
networks:
app-net:
driver: bridge
3. Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/main.js"]
4. .dockerignore
.git
.gitignore
node_modules
npm-debug.log
Dockerfile
docker-compose.yml
README.md
.env
.env.*
logs
coverage
.vscode
.idea
dist
十三、常见性能问题排查思路
1. 容器 CPU 占用过高
排查命令:
docker stats
docker top 容器名
进入容器查看:
docker exec -it 容器名 sh
top
常见原因:
- 应用死循环;
- 请求量突然增加;
- 线程数配置不合理;
- 容器未限制 CPU;
- 日志输出过多。
2. 内存持续增长
排查命令:
docker stats
docker inspect 容器名
常见原因:
- 应用内存泄漏;
- JVM 堆配置不合理;
- 缓存没有上限;
- 容器内存限制过小;
- 未配置 OOM 监控。
Java 应用建议显式设置 JVM 内存:
docker run -d \
--name java-app \
-e JAVA_OPTS="-Xms512m -Xmx1024m" \
--memory=1200m \
my-java-app:1.0.0
3. 磁盘空间被占满
查看 Docker 占用:
docker system df
查看日志:
sudo du -sh /var/lib/docker/containers/*
常见原因:
- 容器日志无限增长;
- 无用镜像太多;
- 构建缓存过大;
- 数据写入容器层;
- 数据卷未清理。
4. 网络延迟高
排查方向:
- 是否使用了不合适的网络模式;
- DNS 是否解析慢;
- 是否经过多层代理;
- 是否存在端口映射瓶颈;
- 宿主机网络是否异常。
可以测试 DNS:
docker exec -it app nslookup example.com
如果 DNS 慢,可以在 daemon.json 中指定 DNS。
十四、Docker 性能优化最佳实践清单
下面是一份可直接用于生产环境检查的清单:
- [ ] 使用轻量基础镜像,例如 alpine、slim;
- [ ] 使用多阶段构建减少镜像体积;
- [ ] 配置
.dockerignore; - [ ] 不在镜像中保留构建缓存;
- [ ] 使用
overlay2存储驱动; - [ ] Docker 数据目录放到独立磁盘;
- [ ] 限制容器日志大小;
- [ ] 为容器设置 CPU 和内存限制;
- [ ] 高并发服务提高
nofile; - [ ] 持久化数据使用 volume;
- [ ] 临时文件使用 tmpfs;
- [ ] 生产环境启用
restart策略; - [ ] 根据场景选择 bridge 或 host 网络;
- [ ] 不暴露不必要端口;
- [ ] 定期清理无用镜像和缓存;
- [ ] 使用 Prometheus、Grafana 等监控;
- [ ] 对关键服务进行压测后再调参;
- [ ] 修改 Docker 配置前做好备份和回滚方案。
十五、总结
Docker 性能优化的关键在于:减少不必要的消耗、限制不可控的资源占用、提升镜像和容器运行效率,并通过监控持续验证优化效果。
对于生产环境,建议优先做好以下几件事:
- 使用小体积镜像和多阶段构建;
- 配置
daemon.json,限制日志大小并启用overlay2; - 为容器设置 CPU、内存、文件句柄等资源限制;
- 使用 volume 管理持久化数据;
- 避免容器日志和临时文件写爆磁盘;
- 建立完整的监控与告警体系。
Docker 本身的性能损耗通常并不高,真正的问题大多来自不合理的镜像构建、无限制的资源使用、日志失控和缺乏监控。只要按照本文的方法逐步优化,就可以显著提升 Docker 在开发、测试和生产环境中的稳定性与运行效率。