Skip to content

Dockerfile详解

Dockerfile基础

Dockerfile是一个文本文件,包含了一系列指令,用于自动化构建Docker镜像。通过Dockerfile,我们可以定义镜像的构建过程,包括基础镜像、依赖安装、文件复制、环境配置等。

Dockerfile指令

基础指令

FROM

指定基础镜像,必须是Dockerfile的第一个指令。

dockerfile
# 使用官方镜像
FROM nginx:1.21

# 使用Alpine Linux(轻量级)
FROM alpine:3.14

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

RUN

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

dockerfile
# 执行单个命令
RUN apt-get update

# 执行多个命令(推荐)
RUN apt-get update && \
    apt-get install -y curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 使用shell形式
RUN /bin/bash -c 'source $HOME/.bashrc && echo $HOME'

# 使用exec形式(推荐)
RUN ["apt-get", "update"]

COPY

复制文件或目录到镜像中。

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

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

# 复制多个文件
COPY package*.json ./

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

# 保持文件权限
COPY --chown=user:group file.txt /app/

ADD

类似COPY,但功能更强大,支持URL和自动解压。

dockerfile
# 复制文件(与COPY相同)
ADD package.json /app/

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

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

# 注意:优先使用COPY,除非需要ADD的特殊功能

WORKDIR

设置工作目录,后续指令将在此目录下执行。

dockerfile
# 设置工作目录
WORKDIR /app

# 相对路径
WORKDIR src
WORKDIR ../dist

# 绝对路径
WORKDIR /usr/src/app

ENV

设置环境变量。

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

# 设置多个环境变量
ENV NODE_ENV=production \
    PORT=3000 \
    DATABASE_URL=postgresql://localhost:5432/mydb

# 使用变量
ENV PATH=/usr/local/bin:$PATH

ARG

定义构建时参数,只在构建过程中有效。

dockerfile
# 定义构建参数
ARG VERSION=latest
ARG BUILD_DATE

# 使用构建参数
FROM nginx:${VERSION}

# 构建时传递参数
# docker build --build-arg VERSION=1.21 .

运行时指令

CMD

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

dockerfile
# shell形式
CMD echo "Hello World"

# exec形式(推荐)
CMD ["echo", "Hello World"]

# 作为ENTRYPOINT的参数
CMD ["--help"]

ENTRYPOINT

指定容器启动时的入口点。

dockerfile
# shell形式
ENTRYPOINT echo "Hello"

# exec形式(推荐)
ENTRYPOINT ["echo", "Hello"]

# 与CMD结合使用
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["--help"]

EXPOSE

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

dockerfile
# 声明单个端口
EXPOSE 80

# 声明多个端口
EXPOSE 80 443

# 声明协议
EXPOSE 80/tcp
EXPOSE 53/udp

其他指令

USER

指定运行容器时的用户。

dockerfile
# 创建用户
RUN adduser -D -s /bin/sh appuser

# 切换到用户
USER appuser

# 使用UID
USER 1000

VOLUME

创建挂载点。

dockerfile
# 创建单个挂载点
VOLUME ["/data"]

# 创建多个挂载点
VOLUME ["/data", "/logs"]

LABEL

添加元数据标签。

dockerfile
# 添加标签
LABEL version="1.0"
LABEL description="My application"

# 添加多个标签
LABEL maintainer="developer@example.com" \
      version="1.0" \
      description="My application"

HEALTHCHECK

定义健康检查。

dockerfile
# 使用命令检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/ || exit 1

# 禁用健康检查
HEALTHCHECK NONE

最佳实践

1. 多阶段构建

dockerfile
# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# 运行阶段
FROM node:16-alpine AS production
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

2. 层缓存优化

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

# 再复制源代码
COPY . .
RUN npm run build

3. 使用.dockerignore

创建.dockerignore文件:

node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.nyc_output
.coverage

4. 安全最佳实践

dockerfile
# 使用非root用户
RUN adduser -D -s /bin/sh appuser
USER appuser

# 使用具体版本标签
FROM nginx:1.21-alpine

# 定期更新基础镜像
# 扫描漏洞
# 最小化攻击面

实践练习

练习1:创建简单的Web应用镜像

创建Dockerfile

dockerfile
FROM nginx:alpine

# 复制自定义配置
COPY nginx.conf /etc/nginx/nginx.conf

# 复制网站文件
COPY html/ /usr/share/nginx/html/

# 暴露端口
EXPOSE 80

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

创建nginx.conf

nginx
events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    server {
        listen 80;
        server_name localhost;
        
        location / {
            root /usr/share/nginx/html;
            index index.html;
        }
    }
}

创建html/index.html

html
<!DOCTYPE html>
<html>
<head>
    <title>Docker练习</title>
</head>
<body>
    <h1>Hello Docker!</h1>
    <p>这是一个Docker练习项目</p>
</body>
</html>

构建和运行:

bash
# 构建镜像
docker build -t my-webapp:latest .

# 运行容器
docker run -d -p 8080:80 --name webapp my-webapp:latest

# 访问 http://localhost:8080

练习2:Node.js应用镜像

创建Dockerfile

dockerfile
FROM node:16-alpine

# 设置工作目录
WORKDIR /app

# 复制package文件
COPY package*.json ./

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

# 复制源代码
COPY . .

# 创建非root用户
RUN adduser -D -s /bin/sh appuser && \
    chown -R appuser:appuser /app
USER appuser

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# 启动应用
CMD ["npm", "start"]

创建package.json

json
{
  "name": "docker-practice-app",
  "version": "1.0.0",
  "description": "Docker练习应用",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.18.0"
  }
}

创建app.js

javascript
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({ message: 'Hello Docker!', timestamp: new Date().toISOString() });
});

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'healthy' });
});

app.listen(port, '0.0.0.0', () => {
  console.log(`应用运行在端口 ${port}`);
});

练习3:多阶段构建

创建Dockerfile

dockerfile
# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 生产阶段
FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

高级技巧

1. 构建参数和条件构建

dockerfile
ARG NODE_ENV=production
ARG BUILD_DATE
ARG VCS_REF

FROM node:16-alpine

LABEL maintainer="developer@example.com" \
      build-date=$BUILD_DATE \
      vcs-ref=$VCS_REF

WORKDIR /app
COPY package*.json ./

# 根据环境安装依赖
RUN if [ "$NODE_ENV" = "development" ]; then \
        npm install; \
    else \
        npm ci --only=production; \
    fi

COPY . .

# 根据环境设置命令
CMD if [ "$NODE_ENV" = "development" ]; then \
        npm run dev; \
    else \
        npm start; \
    fi

2. 健康检查

dockerfile
FROM nginx:alpine

COPY . /usr/share/nginx/html/

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1

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

3. 多架构构建

dockerfile
FROM --platform=$BUILDPLATFORM node:16-alpine 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
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

调试技巧

1. 构建调试

bash
# 查看构建过程
docker build --no-cache -t myapp .

# 构建到特定阶段
docker build --target builder -t myapp:builder .

# 查看镜像层
docker history myapp:latest

2. 运行时调试

bash
# 以调试模式运行
docker run -it --entrypoint /bin/sh myapp:latest

# 挂载源代码进行调试
docker run -v $(pwd):/app -it myapp:latest /bin/sh

常见问题

1. 构建失败

bash
# 查看详细构建日志
docker build --no-cache --progress=plain -t myapp .

# 检查.dockerignore文件
cat .dockerignore

2. 镜像过大

bash
# 分析镜像大小
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

# 使用多阶段构建
# 使用Alpine基础镜像
# 清理不必要的文件

3. 构建缓慢

bash
# 使用构建缓存
# 优化Dockerfile层顺序
# 使用.dockerignore排除不必要文件

下一步

掌握Dockerfile后,您可以:

  1. 继续学习 Docker网络
  2. 学习Docker Compose进行多容器管理
  3. 探索CI/CD中的Docker使用

学习检查点

完成本章学习后,您应该能够:

  • [ ] 理解Dockerfile的基本语法和指令
  • [ ] 编写高效的Dockerfile
  • [ ] 掌握多阶段构建技巧
  • [ ] 了解Dockerfile最佳实践
  • [ ] 解决Dockerfile相关问题