Dockerfile编写 #

什么是Dockerfile? #

Dockerfile是一个文本文件,包含构建Docker镜像所需的所有指令。Docker通过读取Dockerfile中的指令自动构建镜像。

基本结构 #

dockerfile
# 基础镜像
FROM ubuntu:22.04

# 维护者信息
LABEL maintainer="user@example.com"

# 安装依赖
RUN apt-get update && apt-get install -y \
    curl \
    vim

# 设置工作目录
WORKDIR /app

# 复制文件
COPY . /app

# 暴露端口
EXPOSE 8080

# 启动命令
CMD ["nginx", "-g", "daemon off;"]

常用指令详解 #

FROM - 基础镜像 #

指定构建镜像的基础镜像。

dockerfile
# 使用官方镜像
FROM ubuntu:22.04

# 使用Alpine版本
FROM alpine:3.18

# 使用多阶段构建
FROM node:18 AS builder
FROM nginx:alpine AS production

# 使用特定平台
FROM --platform=linux/arm64 alpine:3.18

LABEL - 元数据标签 #

添加镜像的元数据信息。

dockerfile
# 添加多个标签
LABEL maintainer="user@example.com"
LABEL version="1.0.0"
LABEL description="This is a web application"

# 一次性添加多个标签
LABEL maintainer="user@example.com" \
      version="1.0.0" \
      description="Web application"

RUN - 执行命令 #

在构建过程中执行命令。

dockerfile
# 单行命令
RUN apt-get update

# 多行命令(推荐使用\连接)
RUN apt-get update && apt-get install -y \
    curl \
    vim \
    git \
    && rm -rf /var/lib/apt/lists/*

# exec形式
RUN ["apt-get", "install", "-y", "nginx"]

# 使用管道
RUN wget -O - https://example.com/install.sh | bash

COPY - 复制文件 #

从构建上下文复制文件到镜像。

dockerfile
# 复制单个文件
COPY package.json /app/

# 复制目录
COPY src/ /app/src/

# 复制并重命名
COPY config.prod.json /app/config.json

# 使用通配符
COPY *.json /app/
COPY hom?.txt /app/

# 设置文件权限
COPY --chown=user:group file.txt /app/
COPY --chmod=755 script.sh /app/

ADD - 添加文件 #

类似COPY,但支持URL和解压tar文件。

dockerfile
# 复制文件
ADD file.txt /app/

# 从URL下载
ADD https://example.com/file.tar.gz /app/

# 自动解压tar文件
ADD archive.tar.gz /app/

# 推荐使用COPY(除非需要解压或下载)

WORKDIR - 工作目录 #

设置工作目录。

dockerfile
# 设置工作目录
WORKDIR /app

# 相对路径(基于上一个WORKDIR)
WORKDIR src
# 结果: /app/src

# 绝对路径
WORKDIR /var/www

# 可以使用环境变量
ENV DIR=/app
WORKDIR $DIR

ENV - 环境变量 #

设置环境变量。

dockerfile
# 设置单个环境变量
ENV APP_ENV=production

# 设置多个环境变量
ENV APP_ENV=production \
    NODE_VERSION=18 \
    PATH=/app/bin:$PATH

# 使用环境变量
ENV APP_HOME=/app
WORKDIR $APP_HOME

ARG - 构建参数 #

定义构建时的变量。

dockerfile
# 定义构建参数
ARG VERSION=1.0.0
ARG NODE_VERSION=18

# 使用构建参数
FROM node:${NODE_VERSION}
ARG VERSION
ENV APP_VERSION=$VERSION

# 构建时传递
# docker build --build-arg VERSION=2.0.0 -t myapp .

EXPOSE - 暴露端口 #

声明容器运行时监听的端口。

dockerfile
# 暴露单个端口
EXPOSE 80

# 暴露多个端口
EXPOSE 80 443

# 暴露UDP端口
EXPOSE 53/udp

# 注意: EXPOSE只是声明,不会实际发布端口
# 需要使用 -p 参数映射端口

CMD - 容器启动命令 #

指定容器启动时默认执行的命令。

dockerfile
# exec形式(推荐)
CMD ["nginx", "-g", "daemon off;"]

# shell形式
CMD nginx -g "daemon off;"

# 作为ENTRYPOINT的默认参数
CMD ["--port", "8080"]

ENTRYPOINT - 入口点 #

配置容器为可执行程序。

dockerfile
# exec形式(推荐)
ENTRYPOINT ["docker-entrypoint.sh"]

# 结合CMD使用
ENTRYPOINT ["node"]
CMD ["app.js"]

# 可以被 --entrypoint 覆盖
# docker run --entrypoint /bin/bash myimage

CMD vs ENTRYPOINT #

text
┌─────────────────────────────────────────────────────┐
│                CMD vs ENTRYPOINT                     │
├─────────────────────────────────────────────────────┤
│                                                     │
│  ENTRYPOINT:                                        │
│  - 定义容器的主命令                                  │
│  - 不容易被覆盖                                      │
│  - 适合将容器作为可执行程序                          │
│                                                     │
│  CMD:                                               │
│  - 设置默认参数或命令                                │
│  - 容易被docker run参数覆盖                          │
│  - 适合设置默认启动命令                              │
│                                                     │
│  组合使用:                                          │
│  ENTRYPOINT ["node"]                                │
│  CMD ["app.js"]                                     │
│                                                     │
│  运行: docker run myimage server.js                 │
│  实际执行: node server.js                           │
│                                                     │
└─────────────────────────────────────────────────────┘

VOLUME - 数据卷 #

创建挂载点。

dockerfile
# 创建匿名卷
VOLUME /data

# 创建多个卷
VOLUME ["/data", "/logs"]

# 注意: 不能指定宿主机路径
# 需要在运行时使用 -v 指定

USER - 用户 #

指定运行容器的用户。

dockerfile
# 创建用户
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 切换用户
USER appuser

# 使用UID/GID
USER 1000:1000

HEALTHCHECK - 健康检查 #

定义健康检查命令。

dockerfile
# 基本用法
HEALTHCHECK CMD curl -f http://localhost/ || exit 1

# 完整配置
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

# 禁用健康检查
HEALTHCHECK NONE

ONBUILD - 构建触发器 #

定义触发器,在子镜像构建时执行。

dockerfile
# 定义触发器
ONBUILD COPY . /app
ONBUILD RUN npm install

# 当其他镜像FROM这个镜像时,会执行上述命令

Dockerfile示例 #

Node.js应用 #

dockerfile
FROM node:18-alpine

WORKDIR /app

# 先复制依赖文件
COPY package*.json ./

# 安装依赖
RUN npm ci --only=production

# 复制应用代码
COPY . .

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s CMD curl -f http://localhost:3000/health || exit 1

# 启动应用
USER node
CMD ["node", "server.js"]

Python应用 #

dockerfile
FROM python:3.11-slim

WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 创建非root用户
RUN useradd -m appuser
USER appuser

EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]

Nginx应用 #

dockerfile
FROM nginx:alpine

# 复制配置文件
COPY nginx.conf /etc/nginx/nginx.conf
COPY conf.d/ /etc/nginx/conf.d/

# 复制静态文件
COPY dist/ /usr/share/nginx/html/

# 设置权限
RUN chown -R nginx:nginx /usr/share/nginx/html

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

多阶段构建 #

dockerfile
# 构建阶段
FROM node:18 AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 生产阶段
FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

最佳实践 #

1. 使用.dockerignore #

text
# .dockerignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
*.md
.env

2. 最小化层数 #

dockerfile
# 不推荐
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim

# 推荐
RUN apt-get update && apt-get install -y \
    curl \
    vim \
    && rm -rf /var/lib/apt/lists/*

3. 利用构建缓存 #

dockerfile
# 先复制依赖文件,利用缓存
COPY package*.json ./
RUN npm install

# 再复制源代码
COPY . .

4. 使用特定版本 #

dockerfile
# 不推荐
FROM node:latest

# 推荐
FROM node:18.19.0-alpine

5. 安全实践 #

dockerfile
# 不以root运行
RUN useradd -m appuser
USER appuser

# 不存储敏感信息
# 使用环境变量或secrets

小结 #

本节学习了Dockerfile的编写方法:

  • 常用指令的使用方法
  • CMD和ENTRYPOINT的区别
  • 多阶段构建
  • 最佳实践

下一步 #

接下来,让我们学习 镜像构建与优化,了解如何高效构建和优化Docker镜像。