04.Dockerfile

1. 什么是 Dockerfile

Dockerfile 是一个文本文件,包含了构建 Docker 镜像的所有指令。就像是镜像的”菜谱”,告诉 Docker 如何一步步构建出你需要的镜像。

1
2
3
4
5
6
7
8
# 这是一个简单的 Dockerfile 示例
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]

2. 常用指令详解

FROM - 基础镜像

每个 Dockerfile 必须以 FROM 开始,指定基础镜像:

1
2
3
4
5
6
7
8
9
10
11
# 使用官方 Node.js 镜像
FROM node:18-alpine

# 使用官方 Python 镜像
FROM python:3.11-slim

# 使用最小化镜像
FROM alpine:3.18

# 从零开始构建
FROM scratch

RUN - 执行命令

在镜像构建过程中执行命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Shell 格式
RUN apt-get update && apt-get install -y curl

# Exec 格式
RUN ["apt-get", "install", "-y", "curl"]

# 多行命令(推荐,减少层数)
RUN apt-get update && \
apt-get install -y \
curl \
vim \
git && \
rm -rf /var/lib/apt/lists/*

COPY vs ADD

1
2
3
4
5
6
7
# COPY - 简单复制文件
COPY package.json /app/
COPY . /app/

# ADD - 额外支持解压和远程 URL(不推荐,建议用 COPY)
ADD archive.tar.gz /app/
ADD https://example.com/file.txt /app/

建议: 优先使用 COPY,更明确、更可预测。

WORKDIR - 工作目录

1
2
WORKDIR /app
# 后续命令都在 /app 目录下执行

ENV - 环境变量

1
2
3
4
5
6
ENV NODE_ENV=production
ENV APP_PORT=3000

# 多个环境变量
ENV NODE_ENV=production \
APP_PORT=3000

EXPOSE - 声明端口

1
2
3
# 声明容器监听的端口(仅作文档用途)
EXPOSE 3000
EXPOSE 80 443

注意:EXPOSE 不会自动发布端口,运行时仍需 -p 参数。

CMD vs ENTRYPOINT

这是最容易混淆的两个指令:

1
2
3
4
5
6
# CMD - 容器启动时执行的默认命令(可被覆盖)
CMD ["node", "app.js"]
CMD ["npm", "start"]

# ENTRYPOINT - 容器启动时执行的固定命令(不易被覆盖)
ENTRYPOINT ["python", "app.py"]

区别对比:

场景 CMD ENTRYPOINT
被 docker run 参数覆盖 完全覆盖 参数追加
适用场景 默认命令,可灵活覆盖 固定入口,参数化执行

最佳实践 - 组合使用:

1
2
3
4
5
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8080"]

# docker run myapp -> python app.py --port 8080
# docker run myapp --port 3000 -> python app.py --port 3000

多阶段构建

减小最终镜像体积的利器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 第一阶段:构建
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 第二阶段:运行
FROM node:18-alpine
WORKDIR /app
# 只复制构建产物
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm install --production
EXPOSE 3000
CMD ["node", "dist/index.js"]

3. Dockerfile 最佳实践

减小镜像体积

1
2
3
4
5
6
7
8
9
10
11
# 1. 使用 alpine 基础镜像
FROM node:18-alpine # 而不是 node:18

# 2. 多阶段构建(见上文)

# 3. 合并 RUN 命令,清理缓存
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*

# 4. 使用 .dockerignore 排除不需要的文件

.dockerignore 示例:

1
2
3
4
5
6
7
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
*.md

利用构建缓存

把不常变化的指令放前面:

1
2
3
4
5
6
7
8
9
10
11
FROM node:18-alpine
WORKDIR /app

# 先复制 package.json(不常变化)
COPY package*.json ./
RUN npm install

# 再复制源代码(经常变化)
COPY . .

RUN npm run build

安全性考虑

1
2
3
4
5
6
7
8
9
10
# 1. 不使用 root 用户运行
FROM node:18-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# 2. 不在镜像中存储敏感信息
# 使用环境变量或密钥管理工具

# 3. 使用特定版本标签,而不是 latest
FROM node:18.19.0-alpine # 而不是 node:latest