docker build 命令:从入门到实战 – wiki基地


Docker Build 命令详解:从入门到实战

在现代软件开发和部署流程中,Docker 已经成为不可或缺的工具。它通过容器化技术,极大地简化了应用的打包、分发和运行。而构建 Docker 镜像,是将你的应用及其所有依赖打包成可移植单元的第一步,也是最核心的一步。docker build 命令正是负责执行这一关键任务的工具。

本文将带您深入了解 docker build 命令,从基础概念到高级用法,再结合实战示例,助您彻底掌握 Docker 镜像的构建过程。

1. Docker 镜像与 Dockerfile 基础概念

在深入 docker build 命令之前,我们先来理解几个核心概念:

  • Docker 镜像 (Image): Docker 镜像是一个轻量级、独立、可执行的软件包,包含运行一个特定应用所需的一切:代码、运行时环境、库、环境变量和配置文件等。镜像可以看作是一个只读的模板。
  • 容器 (Container): 容器是 Docker 镜像的一个运行实例。一个镜像可以创建多个容器。容器是可读写的,它们在镜像层之上添加了一个薄的读写层。
  • Dockerfile: Dockerfile 是一个文本文件,其中包含了一系列指令,这些指令描述了如何一步步构建一个 Docker 镜像。docker build 命令读取 Dockerfile 中的指令并执行,从而自动化地创建镜像。

简单来说,Dockerfile 是构建镜像的“菜谱”,docker build 命令就是根据这个“菜谱”来“烹饪”镜像。

2. docker build 命令入门

docker build 命令的基本语法如下:

bash
docker build [OPTIONS] PATH | URL | -

这个命令有三个主要部分:

  • docker build: 命令本身。
  • [OPTIONS]: 可选参数,用于控制构建过程,比如指定标签、使用缓存、构建参数等。
  • PATH | URL | -: 构建上下文 (Build Context)。这是构建命令的必需参数,它指定了 Dockerfile 所在的目录(路径)、一个 Git 仓库的 URL 或一个 Tarball 的 URL。或者使用 - 从标准输入读取 Dockerfile。

2.1 构建上下文 (Build Context)

理解构建上下文是使用 docker build 的关键。构建上下文是指本地文件系统中的一个目录(通常是当前目录 .),或者一个可以通过 URL 访问到的资源。在构建过程中,Docker Daemon(Docker守护进程)会将整个构建上下文的内容发送给它。Dockerfile 中的 COPYADD 等指令就是从这个上下文中复制文件到镜像中。

重要提示: 将整个构建上下文发送给 Docker Daemon 可能包含大量不需要的文件,这会减慢构建速度并占用不必要的资源。因此,建议将构建上下文设置为仅包含Dockerfile和构建所需文件的最小目录。使用 .dockerignore 文件可以进一步排除不需要的文件(类似于 Git 的 .gitignore)。

示例:

假设你的项目结构如下:

my-app/
├── Dockerfile
├── app.py
├── requirements.txt
└── static/
└── index.html

你在 my-app 目录下执行构建命令:

bash
docker build .

这里的 . 就代表当前目录 my-app 作为构建上下文。Docker Daemon 会将 my-app 目录下的所有文件(除了 .dockerignore 中指定的)都发送过去。Dockerfile 中的 COPY app.py /app/ 才能找到并复制 app.py 文件。

2.2 第一个 Dockerfile 和构建命令

让我们创建一个最简单的 Dockerfile 来构建一个基于 Alpine Linux 的镜像:

“`dockerfile

Dockerfile

FROM alpine:latest
RUN echo “Hello, Docker Build!”
“`

将上面的内容保存为名为 Dockerfile 的文件,放在一个空目录中。然后在这个目录下打开终端,执行构建命令:

bash
docker build .

执行后,你会看到类似以下的输出:

Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM alpine:latest
---> a24bb4013296
Step 2/2 : RUN echo "Hello, Docker Build!"
---> Running in a0a1c2d3e4f5
Removing intermediate container a0a1c2d3e4f5
---> b1c2d3e4f5a6
Successfully built b1c2d3e4f5a6

  • Sending build context...: Docker Daemon 正在接收构建上下文。
  • Step 1/2 : FROM alpine:latest: 执行 Dockerfile 中的第一条指令 FROM。它从 Docker Hub 下载 alpine:latest 镜像(如果本地没有的话)。
  • ---> a24bb4013296: 这一步构建完成,生成了一个中间镜像层,ID 是 a24bb4013296
  • Step 2/2 : RUN echo "Hello, Docker Build!": 在上一步生成的镜像层之上,执行 RUN 指令。Running in ... 显示了 Docker 为执行 RUN 命令创建了一个临时容器。
  • Removing intermediate container ...: RUN 命令执行完成后,临时容器被删除。
  • ---> b1c2d3e4f5a6: 这一步也构建完成,生成了新的镜像层,ID 是 b1c2d3e4f5a6。这个 ID 就是最终构建好的镜像的 ID。
  • Successfully built b1c2d3e4f5a6: 构建成功,并显示了最终镜像的 ID。

此时,你可以使用 docker images 命令查看本地镜像列表,会看到刚刚构建的镜像(它没有仓库名和标签,只有 Image ID)。

2.3 给镜像打标签 (-t)

直接使用 Image ID 来引用镜像很不方便。通常我们会给镜像打上一个人类可读的标签。使用 -t--tag 选项可以实现这一点。标签的格式通常是 [仓库名/][镜像名][:标签],其中仓库名和标签都是可选的。

bash
docker build -t my-alpine-app:v1.0 .

现在,再次运行 docker images,你会看到一个名为 my-alpine-app、标签为 v1.0 的镜像。

你也可以在一次构建中打多个标签:

bash
docker build -t my-alpine-app:v1.0 -t my-alpine-app:latest .

这样,同一个镜像会同时拥有 v1.0latest 两个标签。

3. 深入 Dockerfile 指令

Dockerfile 是构建镜像的核心,理解并正确使用 Dockerfile 中的各种指令至关重要。以下是一些最常用和重要的指令:

  • FROM <image> [AS <name>]:

    • 用途: 指定构建过程所基于的父镜像(Base Image)。Dockerfile 中的第一条非注释指令必须是 FROM
    • 说明: 父镜像可以是任何有效的 Docker 镜像,通常从 Docker Hub 获取。AS <name> 用于给当前阶段命名,这在多阶段构建中非常有用。
    • 示例:
      dockerfile
      FROM ubuntu:20.04
      FROM python:3.9-slim AS builder
  • RUN <command>:

    • 用途: 在当前镜像层之上执行任何命令,并在执行后提交结果生成新的镜像层。常用于安装软件包、创建目录、编译应用等。
    • 语法:
      • Shell 形式: RUN command param1 param2 (命令在 /bin/sh -c 或等效的 shell 中运行)
      • Exec 形式: RUN ["executable", "param1", "param2"] (直接执行可执行文件)
    • 最佳实践: 为了减少镜像层数和镜像大小,尽量将多个 RUN 命令合并成一个,并使用 && 连接,同时注意清理不需要的临时文件(如包管理器的缓存)。
    • 示例:
      dockerfile
      RUN apt-get update && apt-get install -y some-package && rm -rf /var/lib/apt/lists/*
      RUN ["/bin/bash", "-c", "echo hello"]
  • COPY <src>... <dest> / COPY ["<src>",... "<dest>"]:

    • 用途: 从构建上下文目录中复制文件或目录到镜像的文件系统中指定路径。
    • 说明: src 是构建上下文中的路径,dest 是镜像中的绝对路径或相对于 WORKDIR 的路径。如果 dest 不存在,会自动创建。
    • ADD 的区别: COPY 仅支持本地文件复制;ADD 除了复制本地文件,还支持解压 Tarball 和从 URL 下载文件。通常推荐使用 COPY,因为它更明确,不易产生意外行为。
    • 示例:
      dockerfile
      COPY app.py /app/
      COPY ./src /app/src
      COPY requirements.txt . # 复制到当前 WORKDIR
  • ADD <src>... <dest> / ADD ["<src>",... "<dest>"]:

    • 用途: 功能类似于 COPY,但额外支持以下功能:
      • src 可以是一个 URL。
      • 如果 src 是一个本地的 Tar 压缩文件,Docker 会自动将其解压到 dest
    • 最佳实践: 尽量使用 COPY,只有在需要自动解压本地 Tar 文件或从 URL 下载时才考虑使用 ADD
    • 示例:
      dockerfile
      ADD https://example.com/somefile.tar.gz /tmp/ # 下载并自动解压
      ADD myarchive.tar.gz /app/ # 解压本地 Tar 到 /app
  • WORKDIR /path/to/workdir:

    • 用途: 设置后续 RUN, CMD, ENTRYPOINT, COPY, ADD 等指令执行时的当前工作目录。如果目录不存在,会自动创建。
    • 说明: 建议在 Dockerfile 中明确设置工作目录,而不是依赖默认值。可以使用多个 WORKDIR 指令,后续指令会在前一个 WORKDIR 设置的基础上进行相对路径操作。
    • 示例:
      dockerfile
      WORKDIR /app
      COPY . . # 复制构建上下文内容到 /app
      RUN echo "Current dir: $(pwd)" # 会输出 /app
  • CMD <command> / CMD ["executable","param1","param2"] / CMD ["param1","param2"] (作为 ENTRYPOINT 的参数):

    • 用途: 设置容器启动时默认执行的命令。一个 Dockerfile 只能有一个 CMD,如果有多个,只有最后一个生效。
    • 语法:
      • Shell 形式: CMD command param1 param2 (在 shell 中运行,适用于执行带有变量或需要 shell 特性的命令)
      • Exec 形式: CMD ["executable", "param1", "param2"] (首选,直接执行可执行文件)
      • Default Parameters 形式: CMD ["param1", "param2"] (用于为 ENTRYPOINT 指令提供默认参数)
    • ENTRYPOINT 的区别: CMD 可以在 docker run 命令中被覆盖(docker run <image> <new_command>);ENTRYPOINT 通常不易被覆盖,它的作用是使容器像一个可执行程序一样运行。如果同时定义了 ENTRYPOINTCMDCMD 的内容会作为参数传递给 ENTRYPOINT
    • 示例:
      dockerfile
      CMD ["python", "app.py"] # Exec 形式,推荐
      CMD python app.py # Shell 形式
      CMD ["--help"] # 作为 ENTRYPOINT ["my-app"] 的默认参数
  • ENTRYPOINT <command> / ENTRYPOINT ["executable", "param1", "param2"]:

    • 用途: 配置一个作为容器主命令执行的命令。与 CMD 类似,但通常用作容器的可执行入口。一个 Dockerfile 只能有一个 ENTRYPOINT
    • 语法:CMD
    • 用途: 常用于创建一个可执行的容器。当与 CMD 结合使用时,CMD 提供默认参数,docker run 命令行提供的参数会覆盖 CMD 的默认参数,然后这些参数一起传递给 ENTRYPOINT
    • 示例:
      dockerfile
      ENTRYPOINT ["/app/run.sh"]
      # 如果 CMD 为 ["--help"],运行容器时默认执行 /app/run.sh --help
      # 如果运行 docker run my-image --version,则执行 /app/run.sh --version
  • ENV <key>=<value> ...:

    • 用途: 设置环境变量。这些环境变量在构建过程中(在 RUN 指令中可以使用)和容器运行时都有效。
    • 说明: 可以一次设置多个环境变量。
    • 示例:
      dockerfile
      ENV APP_HOME=/app
      ENV PATH=$APP_HOME/bin:$PATH VERSION=1.0
  • EXPOSE <port> [<port>/<protocol>...]:

    • 用途: 声明容器运行时监听的端口。这仅是一个文档说明,告诉用户容器暴露了哪些端口。要实际映射端口,需要在 docker run 命令中使用 -p 选项。
    • 示例:
      dockerfile
      EXPOSE 80
      EXPOSE 8000/tcp 5432/udp
  • VOLUME <mountpoint> / VOLUME ["<mountpoint>", ...]:

    • 用途: 创建一个挂载点,可以将主机目录或其它容器的数据卷挂载到容器中。这用于持久化数据或共享数据。
    • 说明: 仅声明容器内部的挂载点,实际的卷创建或绑定挂载需要在 docker run 命令中使用 -v--mount 选项。
    • 示例:
      dockerfile
      VOLUME /data
      VOLUME ["/app/logs", "/app/config"]
  • USER <user>[:<group>]:

    • 用途: 设置后续 RUN, CMD, ENTRYPOINT 指令执行时的用户和用户组。默认是 root
    • 最佳实践: 为了安全起见,除非必要,否则不要使用 root 用户运行容器内的进程。可以创建一个非特权用户并在 USER 指令中指定。
    • 示例:
      dockerfile
      RUN groupadd -r myuser && useradd -r -g myuser myuser
      USER myuser
      CMD ["/app/run_as_user.sh"]
  • ARG <name>[=<default value>]:

    • 用途: 定义一个构建参数 (Build Argument),可以在 docker build 命令中使用 --build-arg <name>=<value> 选项来传递值。这些值在构建过程中可用(例如在 RUN 指令中),但在容器运行时不可用(除非使用 ENV 指令将 ARG 的值显式设置为环境变量)。
    • ENV 的区别: ARG 仅在构建时有效,ENV 在构建时和运行时都有效。
    • 示例:
      dockerfile
      ARG VERSION=latest
      ARG BUILD_DATE
      ENV APP_VERSION=$VERSION # 将构建参数转换为环境变量
      RUN echo "Building version $VERSION built on $BUILD_DATE"
    • 构建命令: docker build --build-arg VERSION=v1.1 --build-arg BUILD_DATE=$(date +%F) -t my-app .
  • LABEL <key>="<value>" [<key>="<value>" ...]:

    • 用途: 为镜像添加元数据(metadata)。可以用于组织镜像、过滤镜像、提供信息等。
    • 示例:
      dockerfile
      LABEL maintainer="Your Name <[email protected]>"
      LABEL version="1.0"
      LABEL description="This is a sample web application image."
  • HEALTHCHECK [OPTIONS] CMD command / HEALTHCHECK NONE:

    • 用途: 配置容器的健康检查。Docker 可以定期执行一个命令来检查容器是否健康运行。
    • 选项: --interval, --timeout, --start-period, --retries
    • 示例:
      dockerfile
      HEALTHCHECK --interval=5m --timeout=3s \
      CMD curl -f http://localhost/ || exit 1
  • SHELL ["executable", "parameters"]:

    • 用途: 覆盖用于 RUN, CMD, ENTRYPOINT 的 shell 形式指令的默认 shell。默认是 ["/bin/sh", "-c"] (Linux) 或 ["cmd", "/S", "/C"] (Windows)。
    • 示例:
      dockerfile
      SHELL ["/bin/bash", "-c"]
      RUN echo "Hello, world!" # 会在 /bin/bash -c "echo \"Hello, world!\"" 中运行
  • STOPSIGNAL signal:

    • 用途: 设置发送给容器以退出的系统调用信号。默认为 SIGTERM
    • 示例:
      dockerfile
      STOPSIGNAL SIGKILL
  • ONBUILD <instruction>:

    • 用途: 定义一个触发器指令。当当前镜像被用作另一个镜像的 FROM 父镜像时,ONBUILD 后面的指令将在子镜像构建时执行。
    • 示例:
      dockerfile
      ONBUILD COPY . /app/src
      ONBUILD RUN echo "This runs in the derived image"

      当另一个 Dockerfile FROM your-image-with-onbuild 时,COPY . /app/srcRUN echo ... 会在子镜像构建的第一步执行。

4. Docker Build 的高级特性与最佳实践

4.1 利用构建缓存

Docker Build 具有强大的构建缓存机制。Docker Daemon 会将每个成功执行的 Dockerfile 指令的结果作为一个镜像层缓存起来。下次构建时,如果遇到完全相同的指令,并且该指令所依赖的文件(对于 COPY/ADD)没有变化,Docker 会直接使用缓存的镜像层,而不是重新执行该指令。这极大地加速了构建过程。

缓存失效规则:

  • FROM 指令:如果父镜像发生变化(例如 alpine:latest 更新了),缓存就会失效。
  • RUN 指令:如果指令字符串完全一样,则命中缓存。
  • COPY / ADD 指令:Docker 会检查源文件和目标文件的内容(校验和)和元数据(修改时间等)。如果源文件或目标文件发生变化,缓存就会失效。
  • 指令顺序:如果 Dockerfile 中的指令顺序发生变化,缓存也会失效。一旦某个指令没有命中缓存,其后的所有指令的缓存都会失效,需要重新执行。

利用缓存的技巧:

  • 将 Dockerfile 中不经常变化的指令放在前面(如 FROM, RUN apt-get update)。
  • 将经常变化的指令放在后面(如 COPY app.py)。
  • 对于 Node.js, Python 等项目,先复制依赖文件 (package.json, requirements.txt),然后安装依赖,最后复制应用代码。这样,如果只有应用代码变动而依赖不变,安装依赖的步骤可以利用缓存:

    “`dockerfile

    Example: Python app

    FROM python:3.9-slim
    WORKDIR /app
    COPY requirements.txt . # Step 1: Copy dependency file
    RUN pip install -r requirements.txt # Step 2: Install dependencies (cache friendly if requirements.txt doesn’t change)
    COPY . . # Step 3: Copy application code (this step invalidates cache if code changes)
    CMD [“python”, “app.py”]
    “`

强制禁用缓存:

使用 --no-cache 选项可以完全禁用构建缓存,强制重新执行所有指令:

bash
docker build --no-cache -t my-app:latest .

这在排除构建问题时可能有用,但会显著增加构建时间。

4.2 .dockerignore 文件

为了优化构建上下文,减少发送到 Docker Daemon 的文件数量,可以使用 .dockerignore 文件。该文件的语法类似于 .gitignore,用于指定在构建上下文中要忽略的文件和目录。

示例 .dockerignore 文件:

.git
.gitignore
node_modules/
tmp/
*.log

这样,在构建时,Docker Daemon 不会将 .git 目录、node_modules 目录等发送到构建上下文。

4.3 多阶段构建 (Multi-stage Builds)

多阶段构建是构建高效、小型镜像的强大技术。它允许你在一个 Dockerfile 中使用多个 FROM 指令,每个 FROM 指令定义一个独立的构建阶段。你可以在前一个阶段编译、测试或构建应用,然后只将最终生成的、可执行的 artifact 复制到下一个(通常是最终的)轻量级运行时镜像中。中间阶段及其包含的所有构建依赖(编译器、SDK、临时文件等)都不会包含在最终镜像中,从而大大减小镜像体积和攻击面。

语法:

使用 FROM <image> AS <stage-name> 给阶段命名。然后使用 COPY --from=<stage-name>COPY --from=<stage-index> 从前一个阶段复制文件。

示例 (Go 应用):

“`dockerfile

Stage 1: Builder

FROM golang:1.18 AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY *.go .
RUN go build -o /app/my-app

Stage 2: Runner

FROM alpine:latest

WORKDIR /app

COPY –from=builder /app/my-app . # Copy the built binary from the builder stage

CMD [“./my-app”]
“`

在这个例子中,第一阶段 (builder) 包含了 Go SDK 和所有构建依赖。第二阶段 (alpine) 是一个非常小的基础镜像。最终的镜像只包含从 builder 阶段复制过来的编译好的 my-app 可执行文件,而不包含 Go SDK 和源码,体积会非常小。

使用 docker build . 构建时,Docker 会执行所有阶段,但最终只生成最后一个阶段的镜像。你也可以使用 --target <stage-name> 选项来构建到指定阶段,这在调试构建过程时很有用。

bash
docker build --target builder -t my-app-builder . # 只构建 builder 阶段

4.4 BuildKit (下一代构建引擎)

BuildKit 是 Docker 提供的一个更现代、更强大、更安全的镜像构建引擎。它带来了许多改进,包括:

  • 并行构建: 可以并行执行 Dockerfile 中的独立指令,显著提升构建速度。
  • 跳过未使用的阶段: 对于多阶段构建,如果未使用 --target 指定,BuildKit 会自动跳过最终镜像不需要的阶段,进一步加快构建。
  • 新的构建特性: 支持 Secrets(安全地将敏感信息传递给构建过程)、SSH mounts(在构建过程中通过 SSH 访问私有仓库)、Cache mounts(在不同构建之间共享缓存目录)等。
  • 更好的缓存管理。
  • 更详细的构建输出。

启用 BuildKit:

  • 环境变量: 在执行 docker build 命令前设置 DOCKER_BUILDKIT=1
    bash
    DOCKER_BUILDKIT=1 docker build -t my-app .
  • Daemon 配置:/etc/docker/daemon.json 中添加 "features": {"buildkit": true} 并重启 Docker Daemon(生产环境推荐)。

示例: 使用 BuildKit Secrets (假设有一个 secret 文件 my-token):

“`dockerfile

syntax=docker/dockerfile:1.2 # Specify BuildKit frontend

FROM alpine:latest
RUN –mount=type=secret,id=mysecret cat /run/secrets/mysecret
“`

构建命令 (假设你的 secret 文件在 /path/to/my-token):

bash
DOCKER_BUILDKIT=1 docker build --secret id=mysecret,src=/path/to/my-token -t secret-builder .

这样,my-token 文件的内容就会安全地传递到构建过程中的 /run/secrets/mysecret,不会残留在镜像层中。

4.5 使用构建参数 --build-arg

前面提到 ARG 指令定义构建参数。使用 --build-arg <name>=<value> 选项可以在构建时为这些参数赋值。这使得构建过程更加灵活,可以根据需要构建不同版本或配置的镜像。

示例:

Dockerfile:
dockerfile
FROM ubuntu:latest
ARG VERSION=1.0
RUN echo "Building version: $VERSION"
ENV APP_VERSION=$VERSION

构建命令:
bash
docker build --build-arg VERSION=2.5 -t my-app:2.5 .
docker build -t my-app:latest . # 使用默认值 1.0

5. docker build 实战示例

5.1 构建一个简单的 Node.js Web 应用

假设你的 Node.js 应用结构如下:

nodejs-app/
├── Dockerfile
├── package.json
├── package-lock.json
└── server.js

package.json:
json
{
"name": "simple-node-app",
"version": "1.0.0",
"description": "A simple Node.js web app",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.17.1"
}
}

server.js:
“`javascript
const express = require(‘express’);
const app = express();
const port = 3000;

app.get(‘/’, (req, res) => {
res.send(‘Hello from Dockerized Node.js App!’);
});

app.listen(port, () => {
console.log(App listening at http://localhost:${port});
});
“`

Dockerfile (nodejs-app/Dockerfile):
“`dockerfile

Use a multi-stage build for smaller image size

Stage 1: Builder – Install dependencies

FROM node:18-alpine AS builder

WORKDIR /app

Copy package.json and package-lock.json first to leverage cache

COPY package*.json ./

Install dependencies

RUN npm ci

Copy the rest of the application code

COPY . .

Stage 2: Runner – Minimal runtime image

FROM node:18-alpine

WORKDIR /app

Copy only node_modules and the app code from the builder stage

COPY –from=builder /app/node_modules ./node_modules
COPY –from=builder /app/server.js .

Expose the port the app runs on

EXPOSE 3000

Define the command to run the application

CMD [“node”, “server.js”]
“`

构建命令 (在 nodejs-app 目录下):

bash
docker build -t simple-node-app:latest .

解释:

  1. Stage 1 (builder):
    • 使用 node:18-alpine 作为基础镜像,它包含了 Node.js 运行时和 npm。
    • 设置工作目录为 /app
    • COPY package*.json ./:这是为了利用缓存。如果 package.jsonpackage-lock.json 没有变化,即使其他应用代码变化了,npm ci 这一步也可以直接使用缓存。
    • RUN npm ci: 安装项目依赖。使用 npm cinpm install 更适合 CI/CD 环境,它会基于 package-lock.json 精确安装依赖,并且速度更快。
    • COPY . .: 复制剩余的所有应用代码到 /app
  2. Stage 2 (runner):
    • 再次使用 node:18-alpine 作为基础镜像。注意这里没有安装任何构建工具或开发依赖。
    • 设置工作目录为 /app
    • COPY --from=builder /app/node_modules ./node_modules: 从 builder 阶段的 /app/node_modules 复制已经安装好的依赖到当前阶段。
    • COPY --from=builder /app/server.js .: 从 builder 阶段复制主应用文件。
    • EXPOSE 3000: 声明容器将监听 3000 端口。
    • CMD ["node", "server.js"]: 设置容器启动时执行的命令。

这样构建出的 simple-node-app:latest 镜像将只包含运行应用所需的 Node.js 运行时、node_modules 目录和 server.js 文件,而不会包含构建阶段产生的其他临时文件和工具,体积会小得多。

构建完成后,你可以运行容器:

bash
docker run -d -p 3000:3000 simple-node-app:latest

然后访问 http://localhost:3000 即可看到 “Hello from Dockerized Node.js App!”。

5.2 使用 .dockerignore 优化构建

在上面的 Node.js 示例中,如果你有 node_modules 目录在本地,并且没有使用 .dockerignore,Docker 会尝试将其也复制到构建上下文中。这会浪费带宽和时间,因为你已经在 Dockerfile 中通过 npm ci 安装了依赖。

nodejs-app 目录下创建 .dockerignore 文件:

node_modules/
npm-debug.log
Dockerfile
.git
.gitignore
*.swp

现在再次构建,你会发现 “Sending build context to Docker daemon” 这一步发送的数据量变小了。

6. 进一步探索的选项

除了上述内容,docker build 还有一些其他有用的选项:

  • -f /path/to/Dockerfile: 指定非默认名称或位置的 Dockerfile 文件。
    bash
    docker build -f dockerfiles/prod.Dockerfile -t my-app:prod .
  • --iidfile /path/to/imageid.file: 将构建完成后镜像的 ID 写入指定文件。
  • --squash: (注意:此选项有一些限制和潜在问题,且在某些场景下不推荐,多阶段构建通常是更好的选择)尝试将新的文件系统层压缩为单个新层。
  • --platform linux/amd64 等:指定构建目标平台(当使用 Buildx 或 BuildKit 时)。

7. 总结

docker build 命令是 Docker 工作流程的核心。通过学习如何编写高效的 Dockerfile,并结合构建上下文、缓存机制、.dockerignore 文件、多阶段构建以及 BuildKit 等高级特性,您可以创建出体积更小、构建更快、更安全可靠的 Docker 镜像。

从基础的 FROMRUN 到高级的多阶段构建和 BuildKit 特性,本文为您提供了 docker build 的全面指南。掌握了这些知识,您就能更好地将您的应用打包成可移植的 Docker 镜像,为后续的容器化部署打下坚实的基础。

持续实践和探索,结合您的具体应用场景,您将能更有效地利用 Docker 的强大功能。


滚动至顶部