当前位置: 首页 > news >正文

Docker 学习笔记(四):Dockerfile,把项目打成自己的镜像

Docker 学习笔记(四):Dockerfile,把项目打成自己的镜像

前几篇讲的是:

  • 怎么拉别人做好的镜像;
  • 怎么用docker run启动容器;
  • 怎么理解 Docker 网络。

但是学 Docker 最关键的一步是:

如何把自己的项目做成镜像。

这个过程靠的就是 Dockerfile。


1. Dockerfile 是什么?

Dockerfile 是一个文本文件,里面写着构建镜像的步骤。

你可以把它理解成:

Dockerfile = 镜像制作说明书

比如一个最小 Node 项目的 Dockerfile:

FROM node:22-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD ["npm", "start"]

然后执行:

dockerbuild-tmy-node-app:1.0.

就能构建出一个镜像:

dockerimages

运行:

dockerrun-d--namemy-node-app-p3000:3000 my-node-app:1.0

2. Dockerfile 的核心指令

2.1FROM:基于哪个镜像

FROM node:22-alpine

任何 Dockerfile 基本都从FROM开始。

它表示:

我的镜像不是从零开始,而是基于一个已有镜像继续加工。

比如:

FROM nginx:alpine FROM node:22-alpine FROM mongo:7

实际项目中,不建议随便写latest,因为它会变化。

更推荐写明确版本:

FROM node:22-alpine

2.2WORKDIR:设置工作目录

WORKDIR /app

后面的命令默认都在/app目录下执行。

相当于:

cd/app

如果目录不存在,Docker 会自动创建。


2.3COPY:复制文件到镜像里

COPY package*.json ./ COPY . .

第一句:把本地的package.jsonpackage-lock.json复制到镜像的/app

第二句:把当前目录其他文件复制进去。

为什么不直接先COPY . .

因为 Docker 构建有缓存机制。

更推荐这样写:

COPY package*.json ./ RUN npm ci COPY . .

这样只要依赖文件没变,npm ci这一层就可以复用缓存,加快构建。


2.4RUN:构建阶段执行命令

RUN npm ci RUN npm run build

RUN是在构建镜像时执行。

它和CMD的区别非常重要:

指令执行时机
RUNdocker build 构建镜像时
CMDdocker run 启动容器时

2.5ENV:设置默认环境变量

ENV NODE_ENV=production

这样容器运行时默认有这个环境变量。

但更敏感的配置,比如数据库密码,不建议写死在 Dockerfile 里。

更推荐运行时传入:

dockerrun-eMONGO_URL=xxx my-api:1.0

或者在 Compose 里配置。


2.6EXPOSE:声明容器使用哪个端口

EXPOSE 3000

注意:

EXPOSE 只是声明,不等于自动映射端口。

真正让宿主机访问容器,还要靠:

-p3000:3000

EXPOSE更像是告诉别人:这个容器内部服务监听的是 3000 端口。


2.7CMD:容器启动命令

CMD ["npm", "start"]

它表示容器启动后默认执行什么命令。

推荐使用 JSON 数组形式:

CMD ["node", "dist/main.js"]

而不是:

CMD node dist/main.js

数组形式更清晰,也更适合信号处理。


3. 为 NestJS 后端写 Dockerfile

假设项目是 NestJS 后端:

nest-server/ src/ package.json package-lock.json tsconfig.json nest-cli.json

可以写:

FROM node:22-alpine AS deps WORKDIR /app COPY package*.json ./ RUN npm ci FROM node:22-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . RUN npm run build FROM node:22-alpine AS runner WORKDIR /app ENV NODE_ENV=production COPY package*.json ./ RUN npm ci --omit=dev COPY --from=builder /app/dist ./dist EXPOSE 3000 CMD ["node", "dist/main.js"]

这是一个多阶段构建。

它的思路是:

第一阶段 deps:安装完整依赖 第二阶段 builder:编译 TypeScript 第三阶段 runner:只保留生产运行需要的文件

好处是最终镜像更干净。


4. 为 React 前端写 Dockerfile

假设前端是 React/Vite:

web/ src/ package.json index.html vite.config.ts

Dockerfile:

FROM node:22-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:alpine AS runner COPY --from=builder /app/dist /usr/share/nginx/html EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]

思路:

Node 阶段:负责安装依赖、打包前端 Nginx 阶段:只负责托管 dist 静态文件

前端最终不需要 Node.js 运行环境,只需要 Nginx 托管静态资源。


5..dockerignore很重要

很多人写 Dockerfile,会忘记.dockerignore

它类似.gitignore,用于告诉 Docker 构建时不要把某些文件复制进去。

建议:

node_modules dist build .git .gitignore Dockerfile .dockerignore npm-debug.log .env .env.*

如果不写.dockerignore,可能会导致:

  • 构建上下文很大;
  • 本地node_modules被复制进镜像;
  • .env等敏感文件进镜像;
  • 构建速度变慢。

6. 构建镜像

在 Dockerfile 所在目录执行:

dockerbuild-tmy-api:1.0.

解释:

部分含义
docker build构建镜像
-t my-api:1.0镜像名和标签
.构建上下文是当前目录

查看镜像:

dockerimages

运行镜像:

dockerrun-d--namemy-api-p3000:3000 my-api:1.0

查看日志:

dockerlogs-fmy-api

7. 镜像标签怎么理解?

镜像名通常长这样:

my-api:1.0

其中:

my-api 是镜像名 1.0 是 tag

如果不写 tag,默认是latest

dockerbuild-tmy-api.

等价于:

dockerbuild-tmy-api:latest.

但在实际项目里,不建议完全依赖latest

更推荐:

dockerbuild-tmy-api:2026-06-29.dockerbuild-tmy-api:v1.0.0.dockerbuild-tmy-api:commit-abc123.

这样出问题时更容易回滚。


8. 第四篇小结

Dockerfile 的主线是:

选择基础镜像 ↓ 设置工作目录 ↓ 复制依赖声明文件 ↓ 安装依赖 ↓ 复制项目代码 ↓ 构建项目 ↓ 声明端口 ↓ 指定启动命令

常见指令:

指令作用
FROM基础镜像
WORKDIR工作目录
COPY复制文件
RUN构建阶段执行命令
ENV默认环境变量
EXPOSE声明端口
CMD容器启动命令

下一篇讲 Docker Compose:不用手写一堆docker run,用一个 YAML 同时启动前端、后端和 MongoDB。


参考资料

  • Dockerfile reference: https://docs.docker.com/reference/dockerfile/
  • Build, tag, and publish an image: https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/
  • Docker build reference: https://docs.docker.com/reference/cli/docker/buildx/build/
http://www.jsqmd.com/news/1100673/

相关文章:

  • 哑铃图:数据对比的优雅之选合集 - 数据可视化(66)
  • MySQL从零到实战:新手避坑指南与系统化入门路径
  • 鸿蒙跨平台框架2026年中总结:Flutter 发展进化之路
  • Python+Appium自动化测试实战:头条视频自动播放脚本开发指南
  • AI Agent平台架构设计:从核心原理到高可用实现与面试指南
  • 美团1.6万亿模型用国产芯片跑出来的,性能还超了GPT-5.5和Claude
  • 别再只懂向量搜索了!手把手教你用Elasticsearch BM25 + LangChain自查询,给RAG降本增效
  • SQL注入手工检测全流程:从原理到实战的深度解析
  • 实时视频翻译系统架构与性能优化解析
  • 别再傻傻用for循环了!STM32F407ZET6的SysTick延时函数保姆级配置指南(附避坑点)
  • 告别点灯!用ESP8266+Arduino IDE做个能远程控制的智能开关(附完整代码)
  • 告别Transformer卡顿?手把手带你用Vision Mamba跑通ImageNet分类(附代码)
  • 【窗口函数】RANK ()
  • 如何快速获取网盘直链:LinkSwift下载助手完整使用教程
  • 安达发|aps自动排单:为纺织行业数字化生产注入“增效魔法”
  • Node.js性能测试终极指南:Artillery与k6深度对比与实践
  • 从零实现Transformer:自注意力机制、多头注意力与位置编码详解
  • Fan Control深度解析:Windows平台高级风扇控制架构与实战配置
  • 24小时出货?猎板特急订单实战流程揭秘
  • Fuel Core:用 Rust 搭建的模块化区块链执行层
  • 告别路由器!用一根网线让ZYNQ7020开发板共享笔记本WiFi上网(Win10保姆级教程)
  • 从Selenium到指纹浏览器:浏览器自动化与反检测技术演进全解析
  • YonBIP开发实战:手把手教你搞定树形和表型参照(附完整前后端代码)
  • 技术产品路线图规划:从战略意图到可执行交付物的系统化拆解
  • 保姆级教程:用ESP8266-01和AT指令,5分钟搞定阿里云物联网平台设备连接与数据收发
  • 【VMware NAT端口转发终极指南】:20年虚拟化专家亲授5步精准配置法,99%用户忽略的3个致命陷阱!
  • Java的文本块与多行字符串在模板代码生成中的格式化处理
  • 告别纯数据炼丹:用PyTorch手把手教你给神经网络加上物理‘紧箍咒’
  • 告别Transformer卡顿?手把手带你用Vision Mamba跑通高分辨率图像分类(附代码)
  • 保姆级教程:用Python和Pandas手搓一个ETF网格交易回测脚本(附完整代码)