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 的强大功能。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部