一、没有 Docker 的日子有多痛?
1.1 "在我机器上能跑啊"
这是程序员最经典的甩锅语录,但很多时候真不是甩锅。丸子做课程设计时,三个人的小组项目,光是统一开发环境就折腾了一周:
- 小A的 Windows 上 node-sass 死活装不上
- 小B的 Mac 需要装一堆 Python 依赖
- 丸子的 Ubuntu 倒是能跑,但跟生产环境不一致
最后想了个"天才"的办法:用 U 盘拷代码,只在一台电脑上开发,效率自然也是非常的低。
1.2 "入职大礼包"
丸子去实习,第一天上班的日程:
- 9:00 报到
- 9:30 开始装环境
- 10:00 Node.js 装好了
- 10:30 npm install 卡在 node-sass
- 11:00 求助同事,发现要装 Python 2.7
- 11:30 Windows 编译工具报错
- 12:00 吃饭,下午继续...
到下午 4 点,还在配环境。mentor 苦笑着说:"正常,我当年花了 8 小时。"
1.3 多版本噩梦
实验室同时维护三个项目:
bash
项目A: Node 12 + Webpack 4
项目B: Node 16 + Vite
项目C: Node 18 尝鲜版
每次切换项目前,丸子都要:
bash
nvm use 12
# 跑项目A
# 改需求
nvm use 16
# 等等,我刚刚在项目A改了啥?
# 切回去...啊又报错了!
1.4 部署是玄学。。。
最恐怖的是部署上线。本地开发、测试环境、预发环境、生产环境,四套环境能跑出四种不同的效果。有一次因为"生产环境的 Node 版本高了一点,有个 API 行为不一致",全组人加班到天亮。
这些问题最终都指向同一个核心矛盾:代码是同一份,但运行环境千差万别。Docker 就是为了解决这个矛盾而生的。
二、Docker 是什么?容器技术的原理
2.1 一句话理解 Docker
Docker 就是把你的应用和它需要的一切(运行环境、系统工具、依赖库)打包成一个"集装箱",这个集装箱可以在任何支持 Docker 的地方运行,而且运行结果完全一致。
想象一下:
- 传统方式:我写了份菜谱(代码)给你,你得自己买锅碗瓢盆、调料、灶具(环境),然后做出来的菜可能跟我的不太一样
- Docker 方式:我把整道菜做好,真空包装(镜像)寄给你,你只需要加热一下(运行容器),味道跟我做的一模一样
2.2 底层原理
Docker 的核心来自 Linux 的三种技术:
1. Namespace(命名空间)------ "分房间"
bash
# 在容器里
$ ps aux
PID USER COMMAND
1 app npm start
2 app node server.js
# 在宿主机上
$ ps aux | grep npm
1001 root npm start
容器以为自己独占系统,其实只是被"隔离"在一个小房间里。每个容器有自己的进程树、网络、用户等。
2. Cgroups(控制组)------ "限流"
dockerfile
# 限制容器最多用 512M 内存
docker run -m 512m my-app
给每个容器分配资源配额,防止一个容器吃光所有内存。
3. Union File System(联合文件系统)------ "分层存储"
这是 Docker 最聪明的地方。镜像像千层蛋糕:
最上层:你的代码(可写)
中间层:项目依赖(node_modules)
基础层:Node.js 运行环境
最底层:Alpine Linux
每一层都是只读的,只有最上层可写。这带来两个好处:
- 多个容器可以共享基础层,节省磁盘
- 构建镜像时,如果某一层没变,直接用缓存,加速构建
2.3 虚拟机 vs Docker
很多人分不清 Docker 和虚拟机,其实很简单:
虚拟机:每个应用自带操作系统
[你的App] + [Node.js] + [CentOS]
[别人的App] + [Python] + [Ubuntu]
├── 硬件
Docker:所有应用共享操作系统内核
[你的App] + [Node.js] [别人的App] + [Python]
├── Docker Engine
├── 宿主机操作系统
├── 硬件
Docker 更轻、更快、更省资源。启动一个虚拟机要几分钟,启动一个容器只要几秒钟。
三、前端什么时候该用 Docker?
场景1:新人快速上岗(最快5分钟)
问题 :新同事小李来了,你要花半天时间帮他配环境
Docker 方案:
bash
# 小李只需要
git clone <项目地址>
docker-compose up -d
# 然后就可以开始写代码了
你的项目里需要这些文件:
Dockerfile
dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
.dockerignore(重要!)
node_modules
npm-debug.log
.DS_Store
.git
docker-compose.yml
yaml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app # 代码实时同步
- /app/node_modules # 重要!避免覆盖容器内的node_modules
environment:
- CHOKIDAR_USEPOLLING=true # 解决热更新问题
场景2:多项目、多 Node 版本
问题 :同时维护三个不同 Node 版本的项目
传统方案 :nvm 切来切去,精神分裂
Docker 方案:每个项目独立环境
项目A的 Dockerfile:
dockerfile
FROM node:12.22-alpine
# Node 12 项目
项目B的 Dockerfile:
dockerfile
FROM node:16.15-alpine
# Node 16 项目
项目C的 Dockerfile:
dockerfile
FROM node:18.7-alpine
# Node 18 项目
要运行项目A:
bash
cd project-a
docker-compose up
完全不用操心版本冲突。
场景3:前后端分离开发
问题 :后端 API 还没写好,前端没法联调
Docker 方案:用容器启动 Mock 服务
yaml
# docker-compose.yml
version: '3.8'
services:
frontend:
build: ./frontend
ports: ["3000:3000"]
volumes:
- ./frontend:/app
depends_on:
- mock-api
mock-api:
image: mockserver/mockserver
ports: ["8080:8080"]
command: [
"-serverPort", "8080",
"-proxyRemotePort", "80",
"-proxyRemoteHost", "jsonplaceholder.typicode.com"
]
后端接口没写好?没事,我自己 Mock 一个。
场景4:团队统一开发环境
问题 :有人用 VS Code,有人用 WebStorm,有人用 vim,环境各不相同
Docker 方案:容器内开发
dockerfile
# Dockerfile.dev
FROM node:18-alpine
RUN apk add --no-cache git curl
WORKDIR /app
# 安装全局依赖
RUN npm install -g nodemon typescript
bash
# 进入容器开发
docker run -it -v $(pwd):/app -p 3000:3000 my-dev-image sh
# 现在所有人都在完全相同的环境里写代码
场景5:CI/CD 自动化
问题 :每次部署都要手动构建,容易出错
Docker 方案:GitHub Actions 自动构建
yaml
# .github/workflows/deploy.yml
name: Build and Deploy
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build Docker Image
run: |
docker build -t my-app .
docker tag my-app username/my-app:latest
- name: Push to Registry
run: docker push username/my-app:latest
- name: Deploy to Server
run: |
ssh user@server "docker pull username/my-app:latest"
ssh user@server "docker stop my-app || true"
ssh user@server "docker run -d -p 80:3000 --name my-app username/my-app:latest"
场景6:演示项目、课程作业
问题 :教授/面试官要运行你的项目,但你没法帮他配环境
Docker 方案:一键运行
在你的项目 README 里写上:
bash
# 运行这个项目
git clone https://github.com/yourname/your-project
cd your-project
docker-compose up
# 打开 http://localhost:3000