自己动手写 Dockerfile:构建你的第一个镜像


自己动手写 Dockerfile:构建你的第一个镜像

Dockerfile 长啥样?

Dockerfile 其实就是一个文本文件,里面一条一条指令,告诉 Docker 怎么一步步构建你的镜像。来看一个最简单的例子,我们要做一个能提供静态文件的 HTTP 服务镜像:

# 使用官方 Node 镜像作为基础
FROM node:18-alpine3.14

# 设置容器内的工作目录
WORKDIR /app

# 把当前目录下的所有文件复制到容器的工作目录
COPY . .

# 安装依赖(如果有 package.json 的话)
RUN npm install

# 声明容器运行时监听的端口
EXPOSE 3000

# 容器启动时执行的命令
CMD ["node", "server.js"]

这个文件里每一条指令都会生成一个“镜像层”,一层叠一层,最后变成一个完整的镜像。接下来我们一条条解释。

指令详解

  • FROM:指定基础镜像。几乎所有镜像都基于一个已有的操作系统或环境镜像,比如 nodenginxalpine 等。这里我们选了 node:18-alpine3.14,因为 Alpine Linux 非常小巧,能让最终镜像体积小很多。
  • WORKDIR:设置工作目录。后续的 COPYRUNCMD 等指令都会在这个目录下执行。如果目录不存在,Docker 会自动创建。
  • COPY:把构建上下文(我们马上会讲)中的文件复制到镜像里。这里 COPY . . 表示把当前目录(构建上下文)的所有内容复制到镜像的 /app 下。
  • RUN:在构建过程中执行命令。比如这里运行 npm install 安装依赖。每一条 RUN 都会生成一个新层。
  • EXPOSE:告诉 Docker 容器内的应用会监听哪个端口。这只是一个声明,实际运行时还需要用 -p 做端口映射。
  • CMD:指定容器启动时执行的命令。注意,这个命令可以被 docker run 后面跟着的命令覆盖。比如你 docker run myimage node server.js 就会覆盖掉 CMD。

VOLUME 指令是干嘛的?

你可能注意到文件里没写 VOLUME,但文件内容里提到了它。VOLUME 指令的作用是声明容器内的某个目录应该被持久化。即使你运行容器时没挂载数据卷,Docker 也会自动为这个目录创建一个匿名卷,保证里面的数据不会因为容器删除而丢失。比如:

VOLUME /data

这样即使你忘了 -v/data 下的数据也会被保留在某个卷里。不过实际生产环境一般还是会手动挂载,便于管理。

构建上下文与 .dockerignore

当你执行 docker build 命令时,会指定一个构建上下文,通常是当前目录。比如:

docker build -t my-http-server .

这个点 . 就是构建上下文。Docker 会把该目录下的所有文件(包括子目录)打包发送给 Docker 守护进程(daemon),然后才开始构建。如果你的目录里有 node_modules、日志文件等不必要的东西,发送过去不仅慢,还会让镜像变大(因为 COPY 时会复制它们)。

这时候就需要 .dockerignore 文件,写法类似 .gitignore,在里面列出不需要发送的文件和文件夹,比如:

node_modules
.git
*.log
Dockerfile

这样构建时就忽略它们,加快构建速度,减小镜像体积。

多阶段构建:瘦身利器

有时我们构建应用需要编译工具、依赖包,但运行应用时并不需要它们。比如一个前端项目,构建时需要 Node 和 Webpack,但运行只需要生成的静态文件。多阶段构建就是用来解决这个问题的。

看个例子:

# 第一阶段:构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 第二阶段:运行
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

这里我们用了两个 FROM,第一个阶段叫 builder,专门用来构建,生成了 dist 目录。第二阶段直接用 Nginx 镜像,把第一阶段生成的 dist 目录复制过来。最终镜像只有 Nginx 和静态文件,体积非常小。多阶段构建不仅减小了镜像,还提高了安全性,因为运行环境里没有源代码和构建工具。

Alpine 基础镜像的妙处

前面我们多次提到 alpine,它到底有多小?以 Node 为例,node:18 镜像大概 1GB 左右,而 node:18-alpine 只有 100 多 MB。因为 Alpine 是一个极简的 Linux 发行版,只包含最基本的工具,像 shapk 包管理器等。用它做基础镜像,构建出来的镜像体积小,传输快,部署也快。

总结

通过写一个 Dockerfile,你可以精确控制镜像的每一层。记住几个要点:选择合适的 FROM(优先 Alpine),利用 .dockerignore 减少上下文,用 多阶段构建 瘦身,理解 CMDENTRYPOINT 的区别(我们后面会细讲)。现在你可以试着为自己的 Node 项目写个 Dockerfile 了。

声明:麋鹿与鲸鱼|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 自己动手写 Dockerfile:构建你的第一个镜像


Carpe Diem and Do what I like