Appearance
Dockerfile 基础
Dockerfile 是一个文本文件,包含一系列指令,Docker 按顺序执行这些指令来构建镜像。
第一个 Dockerfile
以一个简单的 Node.js 应用为例:
项目结构:
myapp/
├── Dockerfile
├── package.json
└── app.jsapp.js:
javascript
const http = require('http');
const server = http.createServer((req, res) => {
res.end('Hello from Docker!\n');
});
server.listen(3000, () => console.log('Server running on port 3000'));package.json:
json
{
"name": "myapp",
"version": "1.0.0",
"main": "app.js"
}Dockerfile:
dockerfile
# 基础镜像
FROM node:20-alpine
# 设置工作目录
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
# 安装依赖
RUN npm install
# 复制源码
COPY . .
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "app.js"]构建和运行
bash
# 在 Dockerfile 所在目录执行构建
docker build -t myapp:1.0 .
# 查看构建结果
docker images myapp
# 运行容器
docker run -d -p 3000:3000 --name myapp myapp:1.0
# 测试
curl localhost:3000基础指令详解
FROM — 指定基础镜像
每个 Dockerfile 必须以 FROM 指令开始(ARG 除外):
dockerfile
# 使用官方镜像
FROM ubuntu:22.04
FROM node:20-alpine
FROM python:3.12-slim
# 使用 scratch(空白镜像,用于静态编译的程序)
FROM scratch
# 多阶段构建中的 FROM
FROM golang:1.21 AS builder
FROM alpine:3.19 AS runtimeWORKDIR — 设置工作目录
dockerfile
# 设置后续指令的工作目录(不存在会自动创建)
WORKDIR /app
# 相对路径(相对于上一个 WORKDIR)
WORKDIR /app
WORKDIR src # 实际路径为 /app/srcCOPY — 复制文件
dockerfile
# 复制单个文件
COPY app.js /app/app.js
# 复制到工作目录(WORKDIR)
COPY package.json .
# 复制多个文件
COPY package.json package-lock.json ./
# 使用通配符
COPY *.json ./
COPY src/ ./src/
# 保留文件权限(--chmod)
COPY --chmod=755 entrypoint.sh /entrypoint.shADD — 添加文件(扩展版 COPY)
dockerfile
# 与 COPY 类似,但支持:
# 1. URL 下载
ADD https://example.com/file.tar.gz /app/
# 2. 自动解压 tar 文件
ADD source.tar.gz /app/推荐:大多数情况下使用
COPY,它更透明。只有需要解压 tar 或下载 URL 时才用ADD。
RUN — 执行命令
dockerfile
# 执行 shell 命令(/bin/sh -c)
RUN apt-get update && apt-get install -y curl
# exec 格式(推荐,避免 shell 解析问题)
RUN ["apt-get", "install", "-y", "curl"]
# 多行命令(推荐合并到一条 RUN,减少层数)
RUN apt-get update \
&& apt-get install -y \
curl \
vim \
git \
&& rm -rf /var/lib/apt/lists/*CMD — 容器启动命令
dockerfile
# exec 格式(推荐)
CMD ["node", "app.js"]
CMD ["nginx", "-g", "daemon off;"]
# shell 格式
CMD node app.js
# CMD 提供默认命令,可以被 docker run 的命令参数覆盖
# docker run myapp node other.js # 会覆盖 CMDENTRYPOINT — 入口点
dockerfile
# exec 格式(推荐)
ENTRYPOINT ["node"]
CMD ["app.js"] # 作为 ENTRYPOINT 的默认参数
# ENTRYPOINT 不会被 docker run 的命令参数覆盖
# docker run myapp other.js # 实际执行:node other.jsCMD vs ENTRYPOINT 的区别:
| CMD | ENTRYPOINT | |
|---|---|---|
| 能否被覆盖 | 可以(docker run 后跟命令) | 不能(需要 --entrypoint 才能覆盖) |
| 组合使用 | 作为 ENTRYPOINT 的默认参数 | 定义容器的主命令 |
| 推荐场景 | 提供默认参数 | 容器作为命令工具时 |
EXPOSE — 声明端口
dockerfile
# 声明容器监听的端口(仅文档作用,不会自动映射)
EXPOSE 3000
EXPOSE 80 443
EXPOSE 8080/tcp 8080/udpENV — 环境变量
dockerfile
# 设置环境变量(构建时和运行时都可用)
ENV NODE_ENV=production
ENV PORT=3000
# 多个变量(推荐单条 ENV 声明多个)
ENV NODE_ENV=production \
PORT=3000 \
APP_NAME=myappARG — 构建参数
dockerfile
# 只在构建时可用,不会保留到最终镜像
ARG VERSION=1.0
ARG BUILD_DATE
# 使用构建参数
FROM node:${VERSION}-alpine
# 构建时传入参数
# docker build --build-arg VERSION=20 .完整示例:Python Flask 应用
dockerfile
FROM python:3.12-slim
# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PORT=5000
WORKDIR /app
# 先复制依赖文件(利用缓存)
COPY requirements.txt .
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 暴露端口
EXPOSE 5000
# 使用非 root 用户运行
RUN addgroup --system appgroup && adduser --system --group appuser
USER appuser
CMD ["python", "app.py"].dockerignore 文件
类似 .gitignore,排除不需要复制到镜像的文件:
# .dockerignore
node_modules/
.git/
.gitignore
*.log
.env
dist/
coverage/
__pycache__/
*.pyc
.DS_Store
README.md查看构建过程
bash
# 构建镜像(显示详细输出)
docker build -t myapp:1.0 .
# 构建时显示所有中间层
docker build --no-cache -t myapp:1.0 .
# 查看镜像分层
docker history myapp:1.0总结
Dockerfile 的核心指令:
| 指令 | 作用 |
|---|---|
FROM | 指定基础镜像 |
WORKDIR | 设置工作目录 |
COPY | 复制文件 |
RUN | 执行命令(构建阶段) |
CMD | 容器启动的默认命令 |
ENTRYPOINT | 容器入口点 |
EXPOSE | 声明端口 |
ENV | 设置环境变量 |
ARG | 构建参数 |
下一节介绍所有 Dockerfile 指令的详细用法。