返回首页

Docker 入门:把一个 Node 服务容器化并跑起来

从写第一个 Dockerfile 到跑起来,只讲必要的几步。

1 约 2 分钟 · 2174 字 后端

第一次接触 Docker 时,文档很厚,概念也多。其实把一个 Node 应用跑进容器,只需要理解三个东西:镜像、容器、Compose。这篇就用一个最小例子串一遍。

准备:一个最小的 Node 服务

// server.js
import http from 'node:http'

const port = process.env.PORT || 3000
http
  .createServer((_, res) => {
    res.end('hello from docker')
  })
  .listen(port, () => console.log(`server on :${port}`))
// package.json
{
  "name": "demo",
  "type": "module",
  "scripts": { "start": "node server.js" }
}

1. Dockerfile:把代码打成镜像

FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev

FROM node:20-alpine
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "server.js"]

几点值得注意:

  • 多阶段构建:先装依赖,再复制源码,可以利用层缓存,代码改动不会触发重装依赖
  • alpine 镜像体积更小,但要注意有些原生模块需要换成 slimbullseye
  • EXPOSE 只是元信息,真正暴露端口靠 -p 参数

2. 构建并运行

docker build -t demo:latest .
docker run -d --name demo -p 3000:3000 demo:latest
curl http://localhost:3000

常用命令速查:

docker ps              # 查看运行中的容器
docker logs -f demo    # 跟踪日志
docker exec -it demo sh # 进入容器排查
docker stop demo && docker rm demo

3. docker-compose:多容器一把梭

当你需要 Node + MySQL + Redis 一起起来时,纯命令行会很啰嗦。docker-compose.yml 把它们写在一起:

services:
  app:
    build: .
    ports: ['3000:3000']
    environment:
      DB_HOST: db
      DB_USER: root
      DB_PASSWORD: example
    depends_on: [db]

  db:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: example
      MYSQL_DATABASE: demo
    volumes: ['db-data:/var/lib/mysql']

volumes:
  db-data:

一条命令拉起整个栈:

docker compose up -d
docker compose logs -f
docker compose down       # 停止并清理(volumes 会保留)
docker compose down -v    # 连数据卷一起清掉(慎用)

几个新手常踩的坑

  • .dockerignore 一定要写,不然 node_modules / .git 都会被复制进镜像
  • 不要把 package-lock.json 排除掉,否则 CI 与本地装出来的依赖版本不一致
  • 生产环境不要用 latest tag,锁版本可以避免某天突然破坏构建
  • 容器内的端口 ≠ 主机端口,-p 80:3000 才是真正对外的映射

小结

容器化最大的价值不是“更快”,而是**“在哪都一样”**。一旦你的服务能用 docker compose up 跑起来,迁移服务器、配 CI、本地复现 bug 都会轻松很多。