Docker 实战:给前端开发者的一份指南

两年前我第一次接触 Docker,完全是一脸懵。镜像、容器、仓库……这些词听着就很运维。

后来被迫用了一段时间,发现确实好用。现在我的所有项目都 Docker 化了,开发环境再也没出过问题。

Docker 是什么

一句话:Docker 让应用在任何机器上都能以相同的方式运行。

传统部署:

开发环境能跑 → 测试环境报错 → 生产环境炸了

Docker 部署:

哪里都能跑

原理是把应用和它的运行环境(操作系统、依赖库、配置)打包成一个”镜像”,然后在这台机器上跑这个镜像。

容器化概念图

核心概念

镜像(Image)

只读模板,包含应用和运行环境。类似虚拟机快照,但更轻量。

容器(Container)

镜像的运行实例。一个镜像可以启动多个容器,互不干扰。

Dockerfile

构建镜像的脚本,告诉 Docker 怎么打包你的应用。

仓库(Registry)

存放镜像的地方。Docker Hub 是官方仓库,国内可以用阿里云、腾讯云的镜像服务。

从零开始:容器化一个 Node.js 应用

假设有一个简单的 Express 应用:

// app.js
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello Docker!');
});

app.listen(3000);

第一步:写 Dockerfile

# 基础镜像
FROM node:20-alpine

# 工作目录
WORKDIR /app

# 复制 package.json(利用缓存)
COPY package*.json ./

# 安装依赖
RUN npm install --production

# 复制代码
COPY . .

# 暴露端口
EXPOSE 3000

# 启动命令
CMD ["node", "app.js"]

几个要点:

  1. alpine 版本体积小,适合生产环境
  2. WORKDIR 创建并进入工作目录
  3. 先复制 package.json,利用 Docker 缓存层,依赖不变就不用重装
  4. CMD 是容器启动时执行的命令

第二步:构建镜像

docker build -t my-app:1.0 .

-t 给镜像打标签,格式是 name:tag

第三步:运行容器

docker run -d -p 3000:3000 my-app:1.0
  • -d:后台运行
  • -p 3000:3000:端口映射,主机端口:容器端口

访问 http://localhost:3000 就能看到结果。

Docker 运行示意图

进阶:多阶段构建

上面的 Dockerfile 有个问题:镜像里包含了 node_modules,体积很大。

多阶段构建可以解决这个问题:

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

# 阶段2:运行
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]

最终镜像只包含构建产物,不包含源码和构建工具,体积小很多。

Docker Compose:多容器编排

实际项目往往不止一个容器。比如一个典型的 Web 应用:

  • Node.js 应用
  • MySQL 数据库
  • Redis 缓存
  • Nginx 反向代理

Docker Compose 可以一键启动所有容器:

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis

  db:
    image: mysql:8
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=myapp
    volumes:
      - db_data:/var/lib/mysql

  redis:
    image: redis:7-alpine

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app

volumes:
  db_data:

启动:

docker-compose up -d

停止:

docker-compose down

常用命令速查

# 查看运行中的容器
docker ps

# 查看所有容器(包括停止的)
docker ps -a

# 查看镜像列表
docker images

# 进入容器
docker exec -it <container_id> sh

# 查看容器日志
docker logs -f <container_id>

# 停止容器
docker stop <container_id>

# 删除容器
docker rm <container_id>

# 删除镜像
docker rmi <image_id>

# 清理无用资源
docker system prune

实战经验

1. 不要用 latest 标签

latest 会指向最新版本,每次拉取可能不一样。生产环境要用具体版本:

FROM node:20.11.0-alpine  # 好
FROM node:latest          # 坏

2. 利用 .dockerignore

类似 .gitignore,排除不需要的文件:

node_modules
.git
.env
*.log

3. 健康检查

HEALTHCHECK --interval=30s CMD curl -f http://localhost:3000/health || exit 1

Docker 会定期检查,如果失败就自动重启容器。

4. 日志处理

容器日志默认存到 /var/lib/docker/containers/,长期运行会占满磁盘。

推荐用日志驱动输出到文件或日志服务:

docker run --log-driver json-file --log-opt max-size=10m --log-opt max-file=3 my-app

国内镜像加速

Docker Hub 在国内访问较慢,可以配置镜像加速:

// /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com"
  ]
}

重启 Docker 生效:

sudo systemctl restart docker

总结

Docker 的学习曲线有点陡,但投入是值得的。一旦习惯了这个工作方式,你会发现:

  • 新同事入职,docker-compose up 一条命令就能跑起来
  • 换电脑不需要重新配置环境
  • 部署到任何服务器都是一样的流程

建议从小项目开始尝试,遇到问题再查文档。实践是最好的老师。