上一篇 下一篇 分享链接 返回 返回顶部

Docker 跑得慢?从镜像到配置一次性优化到位

发布人:慈云数据-客服中心 发布时间:20小时前 阅读量:2

Docker 性能优化教程|附配置文件

Docker 已经成为现代应用交付、微服务部署和 DevOps 流水线中的核心工具。它能让应用以容器形式快速运行、迁移和扩展,但如果 Docker 使用不当,也可能出现 CPU 抢占严重、内存溢出、磁盘 I/O 过高、镜像体积过大、网络延迟增加等问题。

本文将从 镜像构建、容器运行参数、Docker Daemon 配置、存储驱动、日志管理、网络优化、资源限制、Compose 配置、监控排查 等多个角度,系统讲解 Docker 性能优化方法,并附带可直接参考的配置文件示例。


一、Docker 性能优化的核心思路

Docker 性能优化并不是单纯修改某一个参数,而是一个系统性工程。通常需要从以下几个层面入手:

  1. 镜像层优化
    减小镜像体积,减少不必要依赖,加快拉取、启动和部署速度。

  2. 容器资源限制
    合理限制 CPU、内存、磁盘 I/O,避免单个容器拖垮宿主机。

  3. Docker Daemon 优化
    调整日志、存储驱动、镜像加速、并发下载等参数。

  4. 存储优化
    选择合适的存储驱动,合理使用数据卷,避免容器层频繁写入。

  5. 网络优化
    根据业务场景选择 bridge、host、overlay 等网络模式。

  6. 日志优化
    控制日志大小,避免日志无限增长占满磁盘。

  7. 监控与排查
    通过工具持续观察 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 性能优化的关键在于:减少不必要的消耗、限制不可控的资源占用、提升镜像和容器运行效率,并通过监控持续验证优化效果

对于生产环境,建议优先做好以下几件事:

  1. 使用小体积镜像和多阶段构建;
  2. 配置 daemon.json,限制日志大小并启用 overlay2
  3. 为容器设置 CPU、内存、文件句柄等资源限制;
  4. 使用 volume 管理持久化数据;
  5. 避免容器日志和临时文件写爆磁盘;
  6. 建立完整的监控与告警体系。

Docker 本身的性能损耗通常并不高,真正的问题大多来自不合理的镜像构建、无限制的资源使用、日志失控和缺乏监控。只要按照本文的方法逐步优化,就可以显著提升 Docker 在开发、测试和生产环境中的稳定性与运行效率。

目录结构
全文