1. 先别急着敲命令:前端为什么需要 Docker
作为一个每天都跟浏览器、Node、npm 打交道的前端,我第一次接触 Docker 的真实感受是:这东西好像是后端运维的事,跟我有什么关系?直到后来公司让我负责把前端项目部署到测试服务器,我才发现,如果不搞清楚 Docker,前端上线的路会走得特别憋屈——要么在一台刚装好的服务器上手动装 Nginx、配 Node、传文件、改配置,整套流程重复三五遍,要么就只能在本地跑通,换台机器就各种踩坑。
这篇文章的定位很明确:用前端能理解的思路,把 Docker 从零讲清楚,并且带你完整走一遍“本地容器化构建 → 镜像打包 → 服务器部署 → 线上访问”的闭环。你不需要是运维专家,只要会基本的命令行操作,跟着做就能把项目真正跑在服务器上。
我默认你的项目是前端常见的形态:用 Vite / Webpack 构建的静态站点,或者带 Node 后端接口的全栈应用,再或者搭配 MySQL 这类中间件。我会把这些场景全部串进实操里。读完你会明白,Docker 解决的不是“怎么跑一个命令”,而是“怎么让一个项目在任何机器上都能用一模一样的方式跑起来”这件事。
2. 本地环境搭建:Docker Desktop 安装与高频踩坑
2.1 安装前先确认两件事:虚拟化和 WSL2
前端开发者的主力机大多是 Windows 或 macOS。macOS 装 Docker Desktop 相对省心,Windows 上容易出幺蛾子,很多报错都出在“装之前没检查环境”。
Windows 上安装 Docker Desktop 有两个底层依赖需要提前确认。
第一,CPU 虚拟化必须在 BIOS 里开启。任务管理器 → 性能 → CPU,右下角能看到“虚拟化: 已启用”。如果是“已禁用”,需要重启进 BIOS 找 Intel Virtualization Technology 或 AMD SVM Mode 打开。
第二,Docker Desktop 在 Windows 上需要 WSL2 作为后端支撑。以管理员身份打开 PowerShell,执行:
wsl --status如果提示没有安装,先执行:
wsl --install装完后重启。这一步完成了再装 Docker Desktop,启动失败的概率会低很多。顺带说明一个容易被忽略的点:WSL2 和虚拟机平台是两套机制,Docker Desktop 会自己管理底层虚拟机,你不需要手动装 Hyper-V,除非你的机器是 Windows 专业版而且公司强制要求用 Hyper-V 隔离。
macOS 用户相对省事,但如果你用的是 Intel 芯片的老款 Mac,装 Docker Desktop 后发热和内存占用会比较明显,后面我会说怎么限制资源。
2.2 Docker Desktop 安装与启动检查
Docker Desktop 的安装包直接去官网下载就行,Windows 用户下载 exe,macOS 用户下载 dmg。安装过程中保持默认选项即可,但有一个勾选框值得留意:Windows 上会询问是否使用 WSL2 而不是 Hyper-V,建议保持勾选 WSL2,这样和终端环境的集成更自然,性能损耗也更小。
安装完后不用急着打开界面,先确认命令行工具是否可用:
docker version正常情况会输出 Client 和 Server 两段信息。注意一个关键细节:如果你只看到 Client 段,看不到 Server 段,说明 Docker 引擎没起来。这时候去打开 Docker Desktop 图标,等它状态变成绿色的 Running 再执行一次。
还有一个前端同学经常搞混的概念:docker 命令是客户端,真正干活的是 Docker 引擎,也就是 Docker Desktop 里那个跑在 WSL2 里的虚拟机。你执行 docker ps 时,客户端通过管道和引擎通信。所以遇到“无法连接”类报错,第一反应不是重装命令,而是去检查引擎状态。
2.3 高频启动错误排查:从 virtualization support 到 npipe
我把搜索热度最高的几个报错信息整理出来,每个都是我实际遇到过或者被身边同事问过的:
| 报错信息 | 原因 | 解决办法 |
|---|---|---|
| virtualisation support wasn't detected | BIOS 虚拟化未开启或未生效 | 进 BIOS 开启 VT-x/AMD-V,重启后确认任务管理器中“虚拟化”为已启用 |
| failed to connect to the docker api at npipe:////./pipe/dockerDesktopLinuxEngine | Docker 引擎没启动或处于启动中状态 | 打开 Docker Desktop 等待引擎 Running;如果一直失败,退出重开,或检查 WSL2 状态 |
| docker: permission denied while trying to connect | 当前用户不在 docker 用户组 | 执行 sudo usermod -aG docker $USER,然后重新登录终端 |
| Docker Desktop failed to start because virtualisation support... | 同上虚拟化问题,也可能是旧版 Docker 残留配置冲突 | 先开虚拟化,再卸载重装 Docker Desktop,删除 %AppData%\Docker 配置目录 |
第二种情况在 Windows 上出现频率最高。我自己排查过一台电脑,WSL 版本是 1,docker 一直连不上,把 WSL 升级到 2 之后问题直接消失。升级命令:
wsl --set-version Ubuntu-22.04 2如果提示没有安装发行版,默认的 docker-desktop 发行版会自动创建,不用手动管。
2.4 配置镜像加速与资源限制
Docker Desktop 装好后,有两处配置建议前端同学提前设置。
镜像加速。国内网络环境下,直接拉取 Docker Hub 镜像经常卡到怀疑人生。打开 Docker Desktop → Settings → Docker Engine,在 JSON 配置里加上 registry-mirrors:
{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com" ] }写完点击 Apply & Restart。注意镜像加速只影响 docker pull 的速度,不影响容器内部网络,这个后面部署时还会再提。
资源限制。Settings → Resources 里可以设置 CPU、内存和 Swap。前端项目构建时 Node 比较吃内存,建议至少给 Docker 分配 4GB 内存,否则构建大项目时可能直接 OOM。如果你用的是 16GB 内存的 Mac,给 6GB 是合理值,留够宿主机的余量。
3. 前端第一课:用 Docker 跑起来一个 Nginx 静态站点
3.1 镜像、容器、仓库:三个最容易混淆的概念
很多教程上来就列命令,但前端同学最容易卡在概念上。我用一个类比帮助理解。
镜像(Image)就像一个安装包,它包含了运行某个应用需要的所有东西:操作系统基础层、软件、配置、环境变量。容器(Container)是镜像运行起来后的实例,你可以同时用同一个镜像启动多个容器,就像用同一个安装包装了多台机器一样。
仓库(Registry)是存放镜像的地方,最知名的是 Docker Hub。docker pull 就是从仓库下载镜像到本地,docker push 就是把本地镜像上传到仓库。
这三个概念对应到前端的工作流里:镜像就是你的"构建产物"加运行环境的封装,容器就是这个产物在某个端口上的实际运行实例,仓库就是你的"版本发布平台"。
3.2 最简命令:让 Nginx 容器转起来
我们在本地先跑一个最简 Nginx,用这个例子理解容器命令。
docker run -d --name my-nginx -p 8080:80 nginx:latest拆解一下这条命令:
- docker run:创建并启动容器。
- -d:后台运行,不占用当前终端。
- --name my-nginx:给容器起名字,方便后续管理。
- -p 8080:80:把宿主机的 8080 端口映射到容器内的 80 端口。
- nginx:latest:镜像名和标签。
执行完浏览器访问 http://localhost:8080,能看到 Nginx 默认欢迎页。
这时候你要理解端口映射的意义。容器内部是一个独立的网络空间,Nginx 监听的是容器里的 80 端口,宿主机访问不到。必须通过 -p 把宿主机的端口映射进去,才能真正访问到服务。8080 是宿主机的入口,80 是容器内部的端口,这个映射关系在部署到服务器时同样关键。
再看几个最常用的容器管理命令:
docker ps # 列出运行中的容器 docker ps -a # 列出所有容器,包括已停止的 docker stop my-nginx # 停止容器 docker start my-nginx # 启动已停止的容器 docker rm my-nginx # 删除容器 docker logs my-nginx # 查看容器日志注意 docker rm 删除的是容器,不是镜像。想删除镜像要 docker rmi。如果你停止容器后重新 docker start,之前的端口映射和配置都还在,但如果你 docker rm 后再 docker run,就得重新带全所有参数。这个细节在写自动化部署脚本时容易踩坑。
3.3 把前端项目放进去:数据卷与文件复制
跑起来默认页不算本事,把我们的前端构建产物放进去才算事。做法有两种。
第一种,直接用 docker cp 把本地的 dist 目录复制进容器:
docker cp ./dist my-nginx:/usr/share/nginx/html这个命令适合临时验证,但不推荐作为正式方案,因为容器一旦被删除,里面的文件就没了。容器是临时性的,任何在容器内部产生的修改都不应该被视为持久数据。
第二种是数据卷(Volume)挂载,用 -v 参数把宿主机目录映射进容器:
docker run -d --name my-nginx -p 8080:80 -v /path/to/dist:/usr/share/nginx/html:ro nginx:latest这样宿主机的 dist 目录直接出现在容器的 nginx html 目录下,本地构建完刷新就能看到效果,开发调试非常方便。我只在本地调试时用这种方式,正式部署我不用它,原因后面部署篇会详细说。
3.4 进入容器内部排查问题
前端调试时最常用到的两个命令是 docker logs 和 docker exec。
docker logs 查看的是容器内进程的输出。Nginx 的访问日志和错误日志都会输出到终端上,如果页面 502、504,多半能从日志里发现端倪。
docker exec 用于进入运行中的容器执行命令:
docker exec -it my-nginx bash进去之后你可以看文件、改配置、测试网络,相当于远程登录到这台"微型服务器"里。用 Ctrl+D 退出。
这里补充一个我自己常用的排查套路:页面访问异常时,先 docker logs 看 Nginx 日志,再 docker exec 进容器看 /etc/nginx/conf.d 下的配置,最后用 curl 测试容器内部能否访问后端服务。一层层排查,比在宿主机上瞎猜高效得多。
4. 前端项目容器化:Dockerfile 多阶段构建实战
4.1 多阶段构建:一个 Dockerfile 里装下构建与运行
前端项目容器化的核心不是把 Node 装进容器然后跑 npm run dev,而是用多阶段构建把"编译"和"运行"拆开,最终镜像只保留 Nginx 和静态文件,不含 Node 和源码。
一个标准 Vue/React 项目的 Dockerfile 长这样:
# 第一阶段:构建 FROM node:20-alpine AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build # 第二阶段:运行 FROM nginx:stable-alpine COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]这个写法最大的好处是镜像体积小。第一阶段用的 node:20-alpine 可能有两三百兆,但第二阶段只拷贝了编译产物进去,最终镜像可能只有几十兆。部署时拉取更快,服务器磁盘占用也更少。
npm ci 和 npm install 的区别补充一句:npm ci 严格按照 package-lock.json 安装,速度更快,也不会因为版本漂移产生和本地不一致的问题。CI 环境里必须用它。
4.2 .dockerignore:别把 node_modules 一起烤进去
前端项目里最容易犯的错误是忘了写 .dockerignore。如果你没写,那么执行 docker build 时,会把当前目录所有文件都发送给 Docker 构建上下文,包括 node_modules、dist、.git。这不仅会导致构建极慢,还可能因为把本地的二进制依赖带进去而构建失败。
一个精简的 .dockerignore 长这样:
node_modules dist .git *.log .DS_Store .vscode .idea记住一个原则:构建阶段需要什么就 COPY 什么,不需要的必须挡在上下文外。node_modules 尤其重要,因为整个构建的核心目的就是在容器内重新安装依赖,而不是复用本地的。
4.3 前端路由刷新 404:Nginx 配置必须在镜像里
前端项目用 history 路由模式时,比如 Vue Router 的 createWebHistory 或 React Router 的 BrowserRouter,直接部署 Nginx 会出现一个经典问题:首页能打开,但刷新 /about 页面时 404。
原因很简单:Nginx 接收到 /about 请求后,去 html 目录下找 about 文件,找不到,就返回了 404。而前端路由实际是通过 JS 控制的,所有路径都应该回到 index.html,由前端路由自己判断。
所以 nginx.conf 需要配置 try_files:
server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://backend:3000; } }location /api/ 里的 proxy_pass 解决了前后端联调的问题:前端请求 /api 开头的接口时,Nginx 把请求转发给容器里运行的后端服务。这个配置在本地开发时用不到,但上服务器跑全栈项目时几乎必用。
4.4 构建镜像与 Compose 编排:把前端后端数据库串起来
写好了 Dockerfile,执行构建:
docker build -t my-app:latest .-t 指定镜像名和标签。构建过程会分步执行 Dockerfile 里的指令,每一步都会生成一个缓存层。改一次代码后重新构建,只有变化的部分会重新执行,所以构建速度会很快。
如果项目依赖的前端构建耗时较长,比如几十秒甚至几分钟,构建镜像时会感觉卡在那。这属于正常现象,不用慌。
实际线上项目通常不止前端一个服务。假设还有一个 Node 后端服务和 MySQL 数据库,手动用 docker run 逐个启动再配网络会非常痛苦。此时用 Docker Compose。
在项目根目录创建 docker-compose.yml:
version: "3.8" services: frontend: build: ./frontend ports: - "80:80" depends_on: - backend backend: build: ./backend environment: - DB_HOST=mysql - DB_PORT=3306 - DB_NAME=my_app - DB_USER=root - DB_PASSWORD=change_me ports: - "3000:3000" depends_on: mysql: condition: service_healthy mysql: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD=change_me - MYSQL_DATABASE=my_app volumes: - mysql-data:/var/lib/mysql ports: - "3306:3306" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 5s timeout: 3s retries: 10 volumes: mysql-data:一个关键知识点:Compose 里的服务名(frontend、backend、mysql)就是容器之间互访的域名。后端连接数据库时,host 填 mysql,而不是 localhost,因为数据库跑在另一个容器里。同理,Nginx 配置里 proxy_pass http://backend:3000,也是走 Compose 服务名。
MySQL 数据持久化通过 volume 实现。没有这一行,容器删除后数据库数据就没了。有这一行,数据会保存在 Docker 管理的卷里,哪怕容器重建,数据还在。
启动命令:
docker compose up -d --build第一次执行会拉取基础镜像并构建所有服务,之后再次执行,如果镜像没变化,直接就启动容器,秒级完成。
5. 从本地到服务器:部署闭环实战
5.1 部署方案选型:宝塔还是纯命令行
本地搞定的下一步是上服务器。前端同学第一次部署时最容易纠结的是:到底用宝塔面板还是手动敲命令?我的看法是,如果目的是搞懂 Docker 本身,必须走一遍纯命令行的流程。宝塔是一个优秀的图形化管理工具,但它的便捷性会掩盖 Docker 的工作原理。
我推荐的最小部署路径是:
- 买一台 Linux 服务器,系统选 Ubuntu Server 22.04 LTS,内存 2GB 起步。
- 用 SSH 登录服务器。
- 安装 Docker Engine。
- 把本地构建好的镜像传到服务器。
- 用 docker compose 启动正式服务。
这条路径跑通后,你以后再接触任何自称"一键部署"的工具,都能立刻判断它背地里干了什么。
5.2 服务器安装 Docker Engine
Ubuntu 服务器上安装 Docker Engine 的标准流程:
sudo apt update sudo apt install -y apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin装完后验证:
sudo docker run hello-world这条命令会拉取一个测试镜像并在容器里输出一段欢迎信息,说明引擎工作正常。
有个细节说一下:默认情况下,docker 命令需要 root 权限,每次敲 docker 都要加 sudo。为了方便,可以把自己加入 docker 用户组:
sudo usermod -aG docker $USER执行完退出 SSH 重新登录生效。这个操作等同于给当前用户开放了管理 Docker 的权限,有安全风险,但在个人服务器和团队内网环境里足够常见,前提是你清楚后果。
5.3 镜像传输:本地构建好还是服务器构建?
把镜像弄到服务器上有几种方案,我按推荐程度排序:
方案一,本地构建镜像,导出 tar 包,上传服务器再导入。
# 本地 docker save my-app:latest | gzip > my-app.tar.gz scp my-app.tar.gz user@服务器IP:/opt/my-app/ # 服务器 cd /opt/my-app docker load < my-app.tar.gz这个方案的好处是服务器上不需要 Node 环境,也不用拉 npm 依赖,拉到的镜像体积小,传输快。坏处是要是服务器是 ARM 架构的,比如某些云服务器是 ARM 芯片,本地构建的 x86 镜像就跑不了。所以构建前先确认服务器架构,用 uname -m 查看。
方案二,把源码 git clone 到服务器,在服务器上 docker compose build。这个适合服务器性能不错,而且你希望整个构建流程在服务器端闭环的场景。缺点是要在服务器上装 Dockerfile 里用到的基础镜像和依赖,第一次构建会比较慢。
方案三,推送到镜像仓库,服务器从仓库拉取。如果是个人项目或者公司有私有仓库,这是最正规的姿势。公共 Docker Hub 上可以建免费私有仓库,但因为网络原因,在国内服务器上拉取 Docker Hub 镜像速度不稳定,建议配置镜像加速。
综合来看,前端个人项目最省心的还是方案一。我实际部署时经常是把镜像导出后 scp 上传,简单直接,不受服务器网络影响。
5.4 服务器上的 docker-compose.yml 与生产配置
在服务器上创建部署目录,比如 /opt/my-app,把 docker-compose.yml 传上去。生产环境下的 Compose 文件和本地调试版有几点不同:
- 镜像直接指定已导入的镜像名,而不是用 build:设置。
- 环境变量、数据库密码等敏感信息,用 .env 文件管理,不要把密码硬编码在 compose 文件里。
- 前端 Nginx 容器映射 80 端口,对外直接提供服务。
- 后端服务可以不映射到宿主机端口,只让 Nginx 内部访问,减少暴露面。
一个生产环境的 compose 示例:
version: "3.8" services: frontend: image: my-app-frontend:latest ports: - "80:80" restart: always depends_on: - backend backend: image: my-app-backend:latest restart: always environment: - DB_HOST=mysql - DB_PASSWORD=${DB_PASSWORD} volumes: - uploads:/app/uploads mysql: image: mysql:8.0 restart: always environment: - MYSQL_ROOT_PASSWORD=${DB_PASSWORD} - MYSQL_DATABASE=my_app volumes: - mysql-data:/var/lib/mysql volumes: mysql-data: uploads:.env 文件:
DB_PASSWORD=your_strong_password启动:
docker compose up -d-restart: always 的作用是:服务器重启或容器异常退出时,Docker 会自动拉起容器。这个参数在本地开发时不重要,但线上必须加,否则服务器一重启服务就全挂了。
5.5 上线后的日常管理:更新、日志与备份
部署完只是开始。前端项目迭代快,改一次代码就得更新一次发布。
更新流程我总结成固定套路:
# 1. 本地构建新镜像并导出 docker build -t my-app-frontend:latest . docker save my-app-frontend:latest | gzip > my-app-frontend.tar.gz # 2. 上传服务器 scp my-app-frontend.tar.gz user@服务器IP:/opt/my-app/ # 3. 服务器上加载新镜像并重启容器 cd /opt/my-app docker load < my-app-frontend.tar.gz docker compose up -ddocker compose up -d 检测到镜像发生了变化,会用新镜像重建容器,期间服务会有短暂中断。对于个人项目和多数企业内部系统,这个中断时间完全可接受。如果追求零停机,需要引入负载均衡和多实例滚动更新,那是另一个话题。
日志管理用 Docker 自带的 json-file 日志驱动就够了。默认情况下,容器日志会一直累加,时间长了占满磁盘。稳妥做法是在 compose 文件里限制日志大小:
services: frontend: image: my-app-frontend:latest ports: - "80:80" logging: driver: json-file options: max-size: "10m" max-file: "3"数据库备份最实用的方式是定时用 mysqldump 导出 SQL 文件,再定期把备份文件下载到本地。容器里执行:
docker exec mysql mysqldump -u root -p"$DB_PASSWORD" my_app > backup_$(date +%F).sql配合 crontab 定时执行,能做到每天自动备份。个人服务器环境里把这个跑通了,数据安全性就有基本保障。
6. 前端部署高频问题与排查技巧实录
6.1 端口被占用导致容器启动失败
启动 Nginx 容器时报错 "port is already allocated",说明宿主机上已经有进程占用了 80 或 8080 端口。排查命令:
sudo lsof -i :80 sudo netstat -tulpn | grep :80找出占用进程后,要么停掉它,要么把 Docker 端口映射改成其他端口。在云服务器上还有一个容易忽略的点:云平台的安全组规则必须放行对应端口,否则即使容器启动正常、端口映射正确,外部依然无法访问。前端同学第一次部署最容易卡在这一步,先在服务器本地 curl localhost:80 测试,能通说明服务正常,再检查安全组。
6.2 容器能启动但页面 502 Bad Gateway
出现 502,说明 Nginx 已经成功接收请求,但转发给后端时失败了。按顺序排查:
- 后端容器是否正常运行:docker ps 看状态。
- 后端服务端口是否正确:docker logs backend 看启动日志。
- Nginx 配置里的 proxy_pass 地址是否正确:Compose 环境里服务名是否拼对,宿主机部署则要用宿主机 IP。
- 前后端容器是否在同一个 Docker 网络里:不在同一个网络时,服务名解析不了,就访问不到。
解决这个问题时,我习惯先在 Compose 网络里测试服务互通。进入前端容器执行:
docker exec -it frontend bash curl http://backend:3000/health能返回数据,说明容器间网络没问题,继续排查 Nginx 配置和路径。
6.3 前端刷新 404,部署后只能在根路径访问
前面提过,history 路由必须配合 try_files。这里补充一个更隐蔽的情况:Nginx 生效的是 /etc/nginx/conf.d/ 下的配置文件,而你用 docker cp 或卷挂载替换了它,但没有重启容器或重新加载配置。
修改 Nginx 配置后执行:
docker exec frontend nginx -s reload让 Nginx 重新加载配置,不用重启整个容器。如果修改是通过重新构建镜像完成的,那 docker compose up -d 会重建容器,不需要额外操作。
6.4 MySQL 容器数据丢失或时区不对
MySQL 容器重建后数据没了,先看 docker-compose.yml 里有没有挂载 volume。没挂载的话,数据都在容器可写层,容器删除就清零。这是用容器跑数据库最容易踩的坑,没有之一。
时区不对是另一个高频问题。MySQL 8.0 默认使用 UTC 时间,而前端展示通常需要北京时间。启动容器时加参数:
environment: - TZ=Asia/Shanghai command: --default-time-zone=+08:00前端代码里对时间字段的处理也要统一,建议后端所有时间存 UTC 时间戳,前端展示时用 dayjs 或 date-fns 转本地时区,彻底避开时区混乱。
6.5 构建镜像时 npm install 特别慢
构建阶段执行 npm install 时,如果长时间卡住,大概率是 npm 镜像源的问题。在 Dockerfile 里切换源:
RUN npm config set registry https://registry.npmmirror.com && npm ci如果你用 pnpm,对应设置 registry 的方式也类似。另一个办法是把 npm install 放在单独的一层,利用 Docker 层缓存。只要 package.json 和 lock 文件不变,这层缓存就不会失效,后面迭代构建时会快很多。
6.6 容器时间与宿主机不一致
容器默认使用 UTC 时区,日志时间比北京时间晚 8 小时。排查问题时经常对不上。解决方案是在 Compose 环境变量里统一设置 TZ=Asia/Shanghai,并让容器共享宿主机的时区文件。如果你已经写了定时任务,比如数据库备份,时间错乱会导致备份时间不符合预期。这个问题不算严重,但遇到了很恶心,提前配好能省很多事。
写在最后的一些体会
踩过这么多坑之后,我的体会是:Docker 对于前端的价值,不在于让你成为运维专家,而在于把"部署"从一门玄学变成一种可复制的工程能力。以前你交付项目是扔一个压缩包加一篇部署文档,现在你交付的是一个镜像加一条 docker compose up,跑在哪台机器上行为都一样。
最后分享两个我自己使用中沉淀下来的小习惯。
第一,本地开发时尽量用 docker compose 把整套环境拉起来,不要只容器化前端,而让后端和数据库裸跑在宿主机上。前后端容器在同一个网络里通信,才最能模拟生产环境。
第二,给镜像打标签时养成带版本号的习惯,比如 my-app:20250615,不要总是覆盖 latest。这样线上出了问题,可以快速回滚到上一个版本。我有一次就是升级前没保留旧版本镜像,结果新版本有问题,只能重新构建旧代码,白白多花了一个小时。版本号这个习惯,关键时候能救命。