Docker 跑得慢?从镜像到资源限制的性能优化实战指南
Docker 性能优化教程|附源码
在现代软件开发与运维体系中,Docker 已经成为应用交付、环境隔离、持续集成和微服务部署的重要基础设施。相比传统虚拟机,Docker 容器启动更快、资源占用更低、部署更灵活,但这并不意味着 Docker 天然就是“高性能”的。
在实际生产环境中,如果镜像构建不合理、容器资源限制不清晰、存储驱动选择不当、日志无限增长、网络模式使用不当,Docker 依然可能出现 CPU 飙高、内存泄漏、磁盘 IO 变慢、镜像体积巨大、容器启动缓慢、应用响应延迟升高 等问题。
本文将系统介绍 Docker 性能优化的核心思路,并提供可直接参考的 Dockerfile、docker-compose、Shell 脚本和配置示例,帮助你从镜像、容器、资源、网络、存储、日志、监控等多个维度优化 Docker 运行效率。
一、Docker 性能优化的核心目标
Docker 性能优化并不是单纯追求某一个指标的极限,而是让容器在稳定、安全、可维护的前提下获得更好的运行效率。
常见优化目标包括:
-
减少镜像体积
- 加快镜像拉取速度;
- 减少磁盘占用;
- 提升 CI/CD 构建效率。
-
缩短容器启动时间
- 更快完成服务扩缩容;
- 提升故障恢复速度。
-
降低 CPU 和内存占用
- 提高宿主机资源利用率;
- 避免某个容器拖垮整个服务器。
-
优化磁盘 IO
- 降低日志、缓存、数据库写入导致的性能损耗;
- 减少 Docker 存储层压力。
-
优化网络性能
- 减少容器间通信延迟;
- 提高高并发场景下的吞吐能力。
-
提升可观测性
- 及时发现资源瓶颈;
- 为后续调优提供数据依据。
二、优化镜像构建
镜像优化是 Docker 性能优化中最基础、最重要的一步。一个臃肿的镜像不仅会占用大量磁盘空间,还会导致构建、推送、拉取和启动变慢。
三、选择更小的基础镜像
很多初学者在编写 Dockerfile 时喜欢使用完整系统镜像,例如:
FROM ubuntu:22.04
这种镜像通用性较好,但体积通常较大。如果应用不依赖完整系统环境,可以使用更轻量的镜像。
例如 Node.js 应用可以使用:
FROM node:20-alpine
Go 应用可以使用:
FROM alpine:3.20
甚至对于静态编译后的 Go 程序,可以使用 scratch:
FROM scratch
COPY app /app
ENTRYPOINT ["/app"]
alpine 镜像通常只有几 MB,远小于 Ubuntu、Debian 等完整发行版镜像。不过需要注意,Alpine 使用的是 musl libc,在某些依赖 glibc 的应用中可能存在兼容性问题。
四、使用多阶段构建减少镜像体积
多阶段构建是 Dockerfile 优化中非常推荐的方式。它可以把“构建环境”和“运行环境”分离,最终镜像只保留运行所需文件。
下面以 Go 应用为例。
1. 示例项目结构
docker-performance-demo/
├── Dockerfile
├── go.mod
└── main.go
2. main.go 源码
package main
import (
"fmt"
"log"
"net/http"
"os"
"runtime"
)
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
}
func infoHandler(w http.ResponseWriter, r *http.Request) {
hostname, _ := os.Hostname()
msg := fmt.Sprintf(
"hostname=%s\nos=%s\narch=%s\ncpu=%d\n",
hostname,
runtime.GOOS,
runtime.GOARCH,
runtime.NumCPU(),
)
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(msg))
}
func main() {
http.HandleFunc("/health", healthHandler)
http.HandleFunc("/info", infoHandler)
port := os.Getenv("APP_PORT")
if port == "" {
port = "8080"
}
log.Printf("server started at :%s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal(err)
}
}
3. go.mod 源码
module docker-performance-demo
go 1.22
4. 未优化 Dockerfile
FROM golang:1.22
WORKDIR /app
COPY . .
RUN go build -o server .
EXPOSE 8080
CMD ["./server"]
这个镜像包含完整 Go 编译环境,体积较大,不适合生产环境。
5. 优化后的多阶段 Dockerfile
# 第一阶段:构建阶段
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags="-s -w" \
-o server .
# 第二阶段:运行阶段
FROM alpine:3.20
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder /src/server /app/server
USER appuser
EXPOSE 8080
ENTRYPOINT ["/app/server"]
这里的优化点包括:
- 使用
golang:alpine缩小构建镜像; - 使用多阶段构建,最终镜像不包含编译工具链;
- 使用
-ldflags="-s -w"减小 Go 二进制体积; - 使用非 root 用户运行应用,兼顾安全与稳定;
- 仅复制最终产物,避免无关文件进入镜像。
五、合理利用 Docker 构建缓存
Docker 构建镜像时会按层缓存。如果 Dockerfile 编写不合理,每次修改业务代码都会导致依赖重新下载,严重影响构建速度。
不推荐写法:
COPY . .
RUN go mod download
RUN go build -o server .
推荐写法:
COPY go.mod ./
RUN go mod download
COPY . .
RUN go build -o server .
这样当业务代码变化但依赖文件未变化时,go mod download 这一层可以复用缓存。
对于 Node.js 项目也是类似:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]
优化原则是:先复制变化少的文件,再复制变化频繁的文件。
六、使用 .dockerignore 减少构建上下文
很多项目在构建镜像时,会把 .git、日志文件、缓存目录、测试报告等无关内容发送给 Docker Daemon,导致构建速度变慢。
推荐在项目根目录添加 .dockerignore:
.git
.gitignore
Dockerfile
docker-compose.yml
*.log
tmp
.cache
node_modules
dist
coverage
.idea
.vscode
.env
注意:是否忽略 dist、node_modules 需要根据项目实际构建方式决定。如果你的构建产物需要复制进镜像,就不要忽略对应目录。
七、控制容器 CPU 资源
如果不限制容器 CPU,一个异常容器可能会占满宿主机资源,影响其他服务。Docker 提供了多种 CPU 限制方式。
1. 使用 docker run 限制 CPU
docker run -d \
--name demo-app \
--cpus="1.5" \
-p 8080:8080 \
docker-performance-demo:latest
表示该容器最多使用 1.5 个 CPU 核心。
也可以使用 CPU shares 设置相对权重:
docker run -d \
--name demo-app \
--cpu-shares=512 \
-p 8080:8080 \
docker-performance-demo:latest
默认值通常是 1024。cpu-shares=512 表示在 CPU 竞争时,该容器获得约一半权重。
2. docker-compose 中限制 CPU
services:
demo-app:
image: docker-performance-demo:latest
container_name: demo-app
ports:
- "8080:8080"
deploy:
resources:
limits:
cpus: "1.5"
reservations:
cpus: "0.5"
需要注意,在非 Swarm 模式下,不同版本的 Compose 对 deploy.resources 支持有所差异。更通用的方式可以使用:
services:
demo-app:
image: docker-performance-demo:latest
container_name: demo-app
ports:
- "8080:8080"
cpus: 1.5
八、控制容器内存资源
内存限制同样重要。如果容器没有内存上限,应用内存泄漏可能拖垮宿主机。
1. docker run 限制内存
docker run -d \
--name demo-app \
--memory=512m \
--memory-swap=512m \
-p 8080:8080 \
docker-performance-demo:latest
参数说明:
--memory=512m:限制容器最多使用 512MB 内存;--memory-swap=512m:限制内存加 swap 总量为 512MB,即基本不允许额外使用 swap。
如果允许使用 swap,可以设置更高:
--memory=512m --memory-swap=1g
2. 避免 Java 应用忽略容器内存限制
Java 应用在容器中运行时,需要确保 JVM 能正确感知容器资源。现代 JDK 已经支持容器感知,但仍建议显式设置:
docker run -d \
--name java-app \
--memory=1g \
-e JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=75.0 -XX:InitialRAMPercentage=50.0" \
java-app:latest
这样 JVM 最大堆内存约为容器内存的 75%,避免堆外内存、线程栈、Metaspace 等导致 OOM。
九、优化容器日志
Docker 默认日志驱动通常是 json-file。如果不限制日志大小,容器长时间运行后日志文件可能非常大,导致磁盘空间耗尽、IO 性能下降。
1. docker run 设置日志限制
docker run -d \
--name demo-app \
--log-driver=json-file \
--log-opt max-size=100m \
--log-opt max-file=3 \
-p 8080:8080 \
docker-performance-demo:latest
表示单个日志文件最大 100MB,最多保留 3 个文件。
2. docker-compose 设置日志限制
services:
demo-app:
image: docker-performance-demo:latest
ports:
- "8080:8080"
logging:
driver: json-file
options:
max-size: "100m"
max-file: "3"
3. Docker Daemon 全局日志配置
可以修改 /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
修改后重启 Docker:
sudo systemctl restart docker
生产环境中,建议将日志采集到 ELK、Loki、ClickHouse、云日志服务等系统,不建议长期依赖宿主机本地日志文件。
十、优化存储性能
Docker 的镜像和容器写入层通常不适合承载高频写入业务数据。数据库、消息队列、搜索引擎等有大量磁盘 IO 的服务,应优先使用 volume 或绑定宿主机目录。
1. 使用 volume 保存数据
docker volume create mysql-data
docker run -d \
--name mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-v mysql-data:/var/lib/mysql \
mysql:8.0
2. docker-compose 中使用 volume
services:
mysql:
image: mysql:8.0
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: "123456"
MYSQL_DATABASE: "demo"
volumes:
- mysql-data:/var/lib/mysql
ports:
- "3306:3306"
volumes:
mysql-data:
3. 避免把高频写入放在容器层
不推荐:
docker run -d mysql:8.0
这样数据库数据会写入容器可写层,容器删除后数据也容易丢失,并且性能和可维护性都较差。
推荐:
docker run -d \
-v mysql-data:/var/lib/mysql \
mysql:8.0
十一、选择合适的存储驱动
在 Linux 环境下,Docker 常见存储驱动包括 overlay2、aufs、devicemapper 等。当前大多数生产环境推荐使用 overlay2。
查看当前存储驱动:
docker info | grep "Storage Driver"
如果显示:
Storage Driver: overlay2
通常就是比较理想的状态。
overlay2 优点包括:
- 性能较好;
- 稳定性高;
- 支持广泛;
- 适合现代 Linux 内核。
如果你的环境仍在使用旧驱动,建议评估迁移。但迁移存储驱动可能影响现有镜像和容器,生产环境需要提前备份并安排维护窗口。
十二、网络性能优化
Docker 默认 bridge 网络适合大多数单机部署场景,但在极致性能需求下,需要根据业务场景选择更合适的网络模式。
1. bridge 网络
默认模式,隔离性较好,适合普通服务:
docker run -d \
--name demo-app \
-p 8080:8080 \
docker-performance-demo:latest
2. host 网络
容器直接使用宿主机网络命名空间,网络性能更好,减少 NAT 开销,但隔离性降低。
docker run -d \
--name demo-app \
--network host \
docker-performance-demo:latest
使用 host 网络后,应用监听的端口就是宿主机端口,不需要 -p 映射。
适用场景:
- 高性能网关;
- 高并发代理;
- 对网络延迟极其敏感的服务;
- 监控 Agent。
不适用场景:
- 多个容器需要监听相同端口;
- 对网络隔离要求较高;
- 需要复杂端口映射的场景。
3. 自定义 bridge 网络
相比默认 bridge,自定义 bridge 支持容器名 DNS 解析,适合服务间通信:
docker network create app-net
docker run -d \
--name app \
--network app-net \
docker-performance-demo:latest
十三、优化 Docker Compose 部署
下面提供一个较完整的 docker-compose.yml 示例,包含资源限制、日志限制、健康检查、网络和 volume。
version: "3.9"
services:
demo-app:
build:
context: .
dockerfile: Dockerfile
image: docker-performance-demo:latest
container_name: demo-app
restart: unless-stopped
ports:
- "8080:8080"
environment:
APP_PORT: "8080"
cpus: 1.5
mem_limit: 512m
memswap_limit: 512m
logging:
driver: json-file
options:
max-size: "100m"
max-file: "3"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 10s
networks:
- app-net
networks:
app-net:
driver: bridge
如果基础镜像中没有 wget,健康检查会失败。可以改用 curl,或者在镜像中安装对应工具。对于极简镜像,也可以直接在应用内部提供轻量检测接口,并使用专门的探针工具。
十四、使用健康检查提升稳定性
健康检查可以帮助 Docker 或编排系统判断应用是否正常运行。需要注意,健康检查不是越频繁越好,过高频率会增加应用压力。
Dockerfile 中也可以添加:
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
优化建议:
- 健康检查接口应尽量轻量;
- 不要在健康检查中执行复杂数据库查询;
- 检查频率应根据服务重要性和负载情况设置;
- 对启动慢的应用设置合理的
start_period。
十五、减少容器内不必要进程
容器最佳实践是:一个容器运行一个主进程。
不推荐在一个容器中同时运行:
- Nginx;
- 后端应用;
- 定时任务;
- 日志采集;
- 数据库。
这样会导致容器难以监控、扩缩容和故障恢复。更推荐的方式是拆分为多个容器,通过 Docker Compose 或 Kubernetes 编排。
如果确实需要多进程,建议使用 tini 或专业进程管理工具,避免僵尸进程。
示例:
FROM alpine:3.20
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/app/server"]
十六、清理无用资源
长期运行 Docker 的服务器会积累大量无用镜像、停止的容器、悬空层、无用 volume 和构建缓存。
查看磁盘占用:
docker system df
清理停止容器、无用网络、悬空镜像和构建缓存:
docker system prune
清理所有未使用镜像:
docker system prune -a
清理未使用 volume:
docker volume prune
生产环境慎用:
docker system prune -a --volumes
因为它可能删除未使用但仍有价值的数据卷。建议在清理前确认:
docker volume ls
docker ps -a
docker images
十七、自动清理脚本源码
下面提供一个相对安全的 Docker 清理脚本,会展示空间占用,并要求用户确认。
docker-clean.sh
#!/usr/bin/env bash
set -e
echo "=============================="
echo " Docker Disk Usage"
echo "=============================="
docker system df
echo
echo "This script will clean:"
echo "1. stopped containers"
echo "2. dangling images"
echo "3. unused networks"
echo "4. build cache"
echo
echo "It will NOT remove volumes by default."
echo
read -r -p "Continue? [y/N] " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "Canceled."
exit 0
fi
echo
echo "Cleaning stopped containers..."
docker container prune -f
echo
echo "Cleaning dangling images..."
docker image prune -f
echo
echo "Cleaning unused networks..."
docker network prune -f
echo
echo "Cleaning build cache..."
docker builder prune -f
echo
echo "=============================="
echo " Docker Disk Usage After Clean"
echo "=============================="
docker system df
echo "Done."
赋予执行权限:
chmod +x docker-clean.sh
执行:
./docker-clean.sh
十八、监控容器性能
没有监控就没有优化。Docker 自带了一些基础命令,可以快速查看容器资源使用情况。
1. 查看实时资源占用
docker stats
输出通常包括:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
如果某个容器 CPU 长期过高,可能存在:
- 死循环;
- 请求量过大;
- 线程池配置不合理;
- GC 频繁;
- 数据库慢查询;
- 日志打印过多。
如果内存持续增长,可能存在:
- 内存泄漏;
- 缓存无上限;
- goroutine/thread 泄漏;
- 大对象未释放;
- 连接池配置不合理。
2. 查看容器进程
docker top demo-app
3. 查看容器详细信息
docker inspect demo-app
4. 查看容器日志
docker logs --tail=100 demo-app
持续观察:
docker logs -f --tail=100 demo-app
十九、压测验证优化效果
优化不能只凭感觉,需要通过压测数据验证。可以使用 wrk、ab、hey 等工具。
以 hey 为例:
hey -n 10000 -c 100 http://127.0.0.1:8080/info
参数说明:
-n 10000:总请求数 10000;-c 100:并发数 100。
如果使用 wrk:
wrk -t4 -c200 -d30s http://127.0.0.1:8080/info
压测时建议关注:
- QPS;
- P95/P99 延迟;
- CPU 使用率;
- 内存使用率;
- 网络吞吐;
- 磁盘 IO;
- 错误率。
不要只看平均响应时间。平均值可能掩盖长尾延迟,生产环境更应该关注 P95、P99。
二十、Docker Daemon 优化配置
Docker Daemon 的全局配置文件通常位于:
/etc/docker/daemon.json
一个常见的优化配置如下:
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"features": {
"buildkit": true
},
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 65535,
"Soft": 65535
}
}
}
配置说明:
storage-driver:指定存储驱动;log-driver:指定默认日志驱动;log-opts:限制日志大小;buildkit:启用 BuildKit,提高构建性能;default-ulimits.nofile:提高文件描述符限制,适合高并发服务。
修改配置后重启 Docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
检查配置是否生效:
docker info
二十一、启用 BuildKit 加速构建
BuildKit 是 Docker 新一代构建后端,支持更高效的缓存、更好的并发构建能力和更安全的 secret 挂载方式。
临时启用:
DOCKER_BUILDKIT=1 docker build -t docker-performance-demo:latest .
长期启用可以在 daemon.json 中配置:
{
"features": {
"buildkit": true
}
}
BuildKit 还支持缓存挂载,例如 Go 项目:
# syntax=docker/dockerfile:1.7
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod ./
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags="-s -w" \
-o server .
FROM alpine:3.20
WORKDIR /app
COPY --from=builder /src/server /app/server
EXPOSE 8080
ENTRYPOINT ["/app/server"]
这种方式可以明显提升 Go 项目的重复构建速度。
二十二、生产环境 Docker 性能优化清单
下面给出一份实用检查清单:
- [ ] 使用轻量基础镜像,例如 Alpine、distroless、scratch;
- [ ] 使用多阶段构建;
- [ ] 配置
.dockerignore; - [ ] 合理利用构建缓存;
- [ ] 启用 BuildKit;
- [ ] 使用非 root 用户运行容器;
- [ ] 设置 CPU 限制;
- [ ] 设置内存限制;
- [ ] 配置日志大小和保留数量;
- [ ] 数据库等高 IO 服务使用 volume;
- [ ] 避免高频写入容器可写层;
- [ ] 使用
overlay2存储驱动; - [ ] 根据场景选择 bridge、host 或自定义网络;
- [ ] 配置健康检查;
- [ ] 设置合理的 restart 策略;
- [ ] 定期清理无用镜像和缓存;
- [ ] 使用
docker stats或监控系统观察资源; - [ ] 压测验证优化效果;
- [ ] 关注 P95/P99 延迟,而不只看平均值;
- [ ] 生产环境谨慎执行
docker system prune -a --volumes。
二十三、完整源码汇总
1. main.go
package main
import (
"fmt"
"log"
"net/http"
"os"
"runtime"
)
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
}
func infoHandler(w http.ResponseWriter, r *http.Request) {
hostname, _ := os.Hostname()
msg := fmt.Sprintf(
"hostname=%s\nos=%s\narch=%s\ncpu=%d\n",
hostname,
runtime.GOOS,
runtime.GOARCH,
runtime.NumCPU(),
)
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(msg))
}
func main() {
http.HandleFunc("/health", healthHandler)
http.HandleFunc("/info", infoHandler)
port := os.Getenv("APP_PORT")
if port == "" {
port = "8080"
}
log.Printf("server started at :%s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal(err)
}
}
2. go.mod
module docker-performance-demo
go 1.22
3. Dockerfile
# syntax=docker/dockerfile:1.7
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod ./
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags="-s -w" \
-o server .
FROM alpine:3.20
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder /src/server /app/server
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
ENTRYPOINT ["/app/server"]
4. .dockerignore
.git
.gitignore
*.log
tmp
.cache
coverage
.idea
.vscode
.env
5. docker-compose.yml
version: "3.9"
services:
demo-app:
build:
context: .
dockerfile: Dockerfile
image: docker-performance-demo:latest
container_name: demo-app
restart: unless-stopped
ports:
- "8080:8080"
environment:
APP_PORT: "8080"
cpus: 1.5
mem_limit: 512m
memswap_limit: 512m
logging:
driver: json-file
options:
max-size: "100m"
max-file: "3"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 10s
networks:
- app-net
networks:
app-net:
driver: bridge
6. docker-clean.sh
#!/usr/bin/env bash
set -e
echo "=============================="
echo " Docker Disk Usage"
echo "=============================="
docker system df
echo
echo "This script will clean stopped containers, dangling images,"
echo "unused networks and build cache."
echo "Volumes will not be removed."
echo
read -r -p "Continue? [y/N] " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "Canceled."
exit 0
fi
docker container prune -f
docker image prune -f
docker network prune -f
docker builder prune -f
echo
echo "=============================="
echo " Docker Disk Usage After Clean"
echo "=============================="
docker system df
echo "Done."
二十四、总结
Docker 性能优化是一项系统工程,不能只关注某个单点。镜像体积、构建缓存、运行资源、日志策略、存储方式、网络模式、健康检查、监控压测都会影响最终表现。
对于大多数项目,可以优先从以下几个方面入手:
- 使用多阶段构建和轻量基础镜像;
- 添加
.dockerignore,减少构建上下文; - 启用 BuildKit,提高构建速度;
- 为容器设置 CPU 和内存限制;
- 限制 Docker 日志文件大小;
- 使用 volume 保存高频写入数据;
- 定期清理无用资源;
- 通过
docker stats和压测工具验证优化效果。
真正有效的优化一定建立在数据之上。建议在每次调整前后记录镜像大小、启动时间、CPU、内存、QPS、P95/P99 延迟等指标,避免盲目调参。只有持续观测、持续验证、持续迭代,才能让 Docker 在生产环境中稳定、高效地运行。