跳到主要内容

自定义镜像与 Dockerfile 实践

这页要解决的不是“Docker 能不能做”,而是“我到底该用哪种方式做才不容易把后面的人坑到”。结论先放前面:

  • 临时快照可以用 docker commit
  • 需要重复构建、交付、协作时,优先写 Dockerfile
  • 不要把“给 root 设密码”当成容器内获取高权限的默认做法

先分清 3 种常见需求

1. 临时调试环境

你只是想先起个容器进去试命令、装包、改文件,这时直接:

docker run --rm -it ubuntu:24.04 bash

2. 我已经在容器里手改好了,想先存个快照

这时可以用:

docker commit my-debug-container my-debug-image:temp

它适合:

  • 临时保存排障现场
  • 保留一次实验状态
  • 快速做一版只给自己用的快照

它不适合:

  • 团队协作
  • 可重复构建
  • 长期维护

3. 我要把环境稳定复现出来

这时就应该上 Dockerfile

一个更推荐的最小 Dockerfile

下面这个例子偏“Ubuntu 工具环境”,比直接在容器里一通手改更可复现:

FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
bash \
ca-certificates \
curl \
git \
vim \
wget \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /workspace

CMD ["bash"]

构建和运行:

docker build -t ubuntu-tools:22.04 .
docker run --rm -it ubuntu-tools:22.04

同时准备 .dockerignore

很多构建慢、镜像上下文过大、误把缓存和大文件带进去的问题,都是因为没写 .dockerignore

一个常见的最小例子:

.git
node_modules
dist
build
__pycache__
*.log
*.tar
*.zip

Dockerfile 里我更愿意默认采用的写法

固定基础镜像版本

比起:

FROM ubuntu:latest

我更推荐:

FROM ubuntu:22.04

这样版本漂移更少,排障也更容易复现。

apt-get update 和安装合到同一个 RUN

RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
&& rm -rf /var/lib/apt/lists/*

这样层次更清楚,也更不容易踩缓存问题。

需要非 root 运行时,用 USER

RUN useradd -m app
USER app
WORKDIR /home/app

如果只是偶尔需要 root 进入正在运行的容器,不要去给 root 配密码,直接:

docker exec -u root -it my-container bash

多阶段构建什么时候值得用

如果“编译环境”和“运行环境”不同,多阶段构建通常很值。

例如静态前端构建:

FROM node:22 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:stable-alpine
COPY --from=build /app/dist /usr/share/nginx/html

好处是:

  • 最终镜像更小
  • 运行镜像里不需要 node_modules
  • 安全面也更干净

docker commit 的正确定位

如果你已经在容器里完成了一轮调试,确实可以先这样留一个快照:

docker commit my-container my-image:debug
docker image ls

但后续最好还是回到 Dockerfile,把真正需要保留的步骤写进构建过程。

否则时间一长,你只会记得“这个镜像能用”,却说不清它到底是怎么做出来的。

导出和导入镜像

导出

docker save -o my-image.tar my-image:1.0

导入

docker load -i my-image.tar

这个流程适合:

  • 离线机器之间传镜像
  • 做一次性备份
  • 在不能直接访问镜像仓库的环境里转移镜像

在主机和容器之间复制文件

docker cp ./local-file.txt my-container:/tmp/local-file.txt
docker cp my-container:/var/log/app.log ./app.log

如果你的目的是“长期保留文件”,优先考虑 volume 或 bind mount,而不是把文件长期留在容器里。

再次进入正在运行的容器

docker exec -it my-container bash

没有 bash 就换:

docker exec -it my-container sh

我自己的默认判断顺序

  1. 只是临时试验:docker run --rm -it
  2. 需要留现场:docker commit
  3. 需要复现和协作:写 Dockerfile
  4. 需要多服务编排:直接上 Compose

关联阅读