上一篇 下一篇 分享链接 返回 返回顶部

从零跑通 Docker:镜像、容器到 Node.js 项目部署实战源码

发布人:慈云数据-客服中心 发布时间:18小时前 阅读量:2

Docker 新手入门指南|附源码

在现代软件开发中,Docker 已经成为后端开发、运维部署、微服务架构、持续集成与持续交付(CI/CD)中非常重要的基础工具。对于新手来说,Docker 可能一开始看起来有些抽象:什么是镜像?什么是容器?为什么项目跑得好好的,还要用 Docker?本文将从零开始,带你理解 Docker 的核心概念,并通过一个完整示例,演示如何使用 Docker 构建、运行和部署一个简单项目。

本文适合以下人群阅读:

  • 刚接触 Docker 的开发者;
  • 想把本地项目容器化的后端工程师;
  • 需要了解基础部署流程的前端或测试人员;
  • 准备学习微服务、Kubernetes、DevOps 的初学者。

一、什么是 Docker?

Docker 是一个开源的容器化平台,它可以把应用程序以及应用所依赖的运行环境、配置文件、系统库等内容打包在一起,形成一个独立的运行单元,这个运行单元通常被称为 容器

简单来说,Docker 可以解决这样一个经典问题:

“为什么这个程序在我电脑上能运行,在你电脑上就跑不起来?”

在传统开发模式下,一个项目可能依赖不同版本的语言环境、数据库、系统库和配置。例如:

  • Node.js 项目依赖 Node.js 18;
  • Java 项目依赖 JDK 17;
  • Python 项目依赖 Python 3.11;
  • 项目还可能依赖 MySQL、Redis、Nginx 等服务。

如果每个开发者都在自己的电脑上手动安装这些环境,很容易出现版本不一致、配置不统一的问题。而 Docker 的优势就在于:将应用和环境一起打包,保证在不同机器上运行结果尽可能一致。


二、Docker 的核心概念

学习 Docker 之前,需要先理解几个核心概念。

1. 镜像 Image

镜像可以理解为一个“模板”或“安装包”。它包含运行某个应用所需的文件系统、依赖环境、启动命令等内容。

例如:

  • nginx 镜像可以用来启动 Nginx 服务;
  • mysql 镜像可以用来启动 MySQL 数据库;
  • node 镜像可以用来运行 Node.js 应用;
  • openjdkeclipse-temurin 镜像可以用来运行 Java 应用。

镜像本身是静态的,不能直接提供服务,必须基于镜像创建容器。

2. 容器 Container

容器是镜像运行起来之后的实例。

如果镜像是“类”,那么容器就像是“对象”;如果镜像是“安装包”,那么容器就是“已经安装并运行的软件”。

一个镜像可以创建多个容器,每个容器之间默认相互隔离,拥有自己的文件系统、网络环境和进程空间。

3. Dockerfile

Dockerfile 是一个文本文件,用来描述如何构建镜像。它里面会写明:

  • 基于哪个基础镜像;
  • 拷贝哪些代码;
  • 安装哪些依赖;
  • 暴露哪个端口;
  • 容器启动时执行什么命令。

通过 Dockerfile,我们可以把项目构建成自定义镜像。

4. 仓库 Repository

Docker 镜像可以存放在镜像仓库中。常见仓库包括:

  • Docker Hub;
  • 阿里云容器镜像服务;
  • 腾讯云容器镜像服务;
  • GitHub Container Registry;
  • 企业内部私有镜像仓库。

平时执行的 docker pull nginx,就是从远程镜像仓库拉取 nginx 镜像。


三、为什么要使用 Docker?

Docker 并不是为了“炫技”,它解决的是实际工程问题。

1. 环境一致

开发环境、测试环境、生产环境可以使用同一套镜像,减少“环境不同导致的 bug”。

2. 部署简单

传统部署可能需要手动安装运行时、配置环境变量、安装依赖。而 Docker 只需要:

docker run ...

或者:

docker compose up -d

即可启动服务。

3. 隔离性好

不同项目可以运行在不同容器中,互不影响。例如一个项目使用 MySQL 5.7,另一个项目使用 MySQL 8.0,它们可以同时运行在同一台机器上。

4. 方便扩展

Docker 是 Kubernetes、Docker Compose、CI/CD 流水线的重要基础。学会 Docker 后,再学习微服务部署和容器编排会更加容易。


四、Docker 的安装

1. Windows 安装 Docker

Windows 用户推荐安装 Docker Desktop

下载地址:

https://www.docker.com/products/docker-desktop/

安装完成后,在命令行中执行:

docker version

如果能看到客户端和服务端版本信息,说明安装成功。

2. macOS 安装 Docker

macOS 同样推荐安装 Docker Desktop。安装完成后执行:

docker info

如果能够正常输出 Docker 运行信息,说明 Docker 已经可用。

3. Linux 安装 Docker

以 Ubuntu 为例:

sudo apt update
sudo apt install -y docker.io
sudo systemctl enable docker
sudo systemctl start docker

查看版本:

docker version

如果不想每次都使用 sudo,可以将当前用户加入 docker 用户组:

sudo usermod -aG docker $USER

然后重新登录终端即可。


五、第一个 Docker 命令:运行 Nginx

我们先运行一个最简单的 Nginx 容器。

docker run -d --name my-nginx -p 8080:80 nginx

参数说明:

参数 含义
docker run 创建并运行容器
-d 后台运行
--name my-nginx 指定容器名称
-p 8080:80 将宿主机 8080 端口映射到容器 80 端口
nginx 使用 nginx 镜像

启动成功后,在浏览器访问:

http://localhost:8080

如果看到 Nginx 欢迎页面,就说明容器运行成功。

查看正在运行的容器:

docker ps

停止容器:

docker stop my-nginx

删除容器:

docker rm my-nginx

删除镜像:

docker rmi nginx

六、常用 Docker 命令速查

1. 镜像相关命令

查看本地镜像:

docker images

拉取镜像:

docker pull nginx

删除镜像:

docker rmi nginx

构建镜像:

docker build -t my-app:1.0 .

2. 容器相关命令

查看运行中的容器:

docker ps

查看所有容器:

docker ps -a

启动容器:

docker start 容器名或容器ID

停止容器:

docker stop 容器名或容器ID

重启容器:

docker restart 容器名或容器ID

删除容器:

docker rm 容器名或容器ID

进入容器:

docker exec -it 容器名 /bin/bash

有些轻量级镜像没有 bash,可以使用:

docker exec -it 容器名 /bin/sh

3. 日志相关命令

查看容器日志:

docker logs 容器名

实时查看日志:

docker logs -f 容器名

查看最近 100 行日志:

docker logs --tail=100 容器名

七、实战项目:使用 Docker 部署一个 Node.js Web 服务

下面我们通过一个简单的 Node.js 项目来演示如何将应用容器化。

项目功能很简单:启动一个 HTTP 服务,访问接口后返回一段 JSON 数据。

1. 项目目录结构

docker-node-demo
├── Dockerfile
├── package.json
└── src
    └── app.js

八、源码示例

1. package.json

{
  "name": "docker-node-demo",
  "version": "1.0.0",
  "description": "A simple Node.js demo for Docker beginners",
  "main": "src/app.js",
  "scripts": {
    "start": "node src/app.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  }
}

2. src/app.js

const express = require('express');

const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({
    message: 'Hello Docker!',
    description: '这是一个运行在 Docker 容器中的 Node.js 服务',
    time: new Date().toISOString()
  });
});

app.get('/health', (req, res) => {
  res.json({
    status: 'ok',
    uptime: process.uptime()
  });
});

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

3. Dockerfile

# 使用官方 Node.js 镜像作为基础镜像
FROM node:18-alpine

# 设置容器内的工作目录
WORKDIR /app

# 先复制 package.json,便于利用 Docker 缓存
COPY package.json ./

# 安装项目依赖
RUN npm install

# 复制项目源码
COPY src ./src

# 暴露容器端口
EXPOSE 3000

# 容器启动时执行的命令
CMD ["npm", "start"]

九、构建并运行镜像

进入项目根目录:

cd docker-node-demo

构建镜像:

docker build -t docker-node-demo:1.0 .

参数说明:

  • -t docker-node-demo:1.0:指定镜像名称和标签;
  • .:表示 Dockerfile 所在的构建上下文为当前目录。

构建成功后查看镜像:

docker images

运行容器:

docker run -d --name node-demo -p 3000:3000 docker-node-demo:1.0

访问接口:

curl http://localhost:3000

返回示例:

{
  "message": "Hello Docker!",
  "description": "这是一个运行在 Docker 容器中的 Node.js 服务",
  "time": "2026-01-01T12:00:00.000Z"
}

访问健康检查接口:

curl http://localhost:3000/health

返回示例:

{
  "status": "ok",
  "uptime": 25.362
}

十、理解 Dockerfile 的构建过程

上面的 Dockerfile 虽然不长,但包含了 Docker 镜像构建的核心逻辑。

FROM node:18-alpine

表示使用 Node.js 18 的 Alpine 版本作为基础镜像。Alpine 是一个非常轻量的 Linux 发行版,适合用来构建体积较小的镜像。

WORKDIR /app

设置容器内部工作目录,后续命令会在 /app 下执行。

COPY package.json ./
RUN npm install

这里先复制 package.json 再安装依赖,是为了利用 Docker 的构建缓存。如果源码发生变化但依赖没有变化,Docker 不需要重新执行 npm install,可以提升构建速度。

COPY src ./src

复制项目源码到镜像中。

EXPOSE 3000

声明容器内部服务监听的端口。需要注意的是,EXPOSE 只是声明,并不会自动把端口映射到宿主机。真正的端口映射需要在 docker run 时使用 -p 参数。

CMD ["npm", "start"]

指定容器启动时默认执行的命令。


十一、使用 .dockerignore 优化构建

在实际项目中,我们通常会添加 .dockerignore 文件,避免把不必要的文件复制进镜像。

例如:

node_modules
npm-debug.log
.git
.gitignore
README.md
.DS_Store
.env

为什么要忽略这些文件?

  • node_modules 应该在容器内安装,避免宿主机依赖和容器环境不一致;
  • .git 会增大构建上下文体积;
  • .env 可能包含敏感信息,不应该随意打进镜像;
  • 日志文件、系统文件没有必要进入镜像。

十二、Docker 数据卷 Volume

容器默认是临时的。如果删除容器,容器内部产生的数据也可能随之丢失。对于数据库、上传文件、日志等需要持久化的数据,通常要使用数据卷。

创建数据卷:

docker volume create my-data

查看数据卷:

docker volume ls

运行容器时挂载数据卷:

docker run -d \
  --name my-nginx-volume \
  -p 8080:80 \
  -v my-data:/usr/share/nginx/html \
  nginx

其中:

-v my-data:/usr/share/nginx/html

表示把 Docker 数据卷 my-data 挂载到容器内的 /usr/share/nginx/html 目录。

除了命名数据卷,也可以使用宿主机目录挂载:

docker run -d \
  --name nginx-local \
  -p 8080:80 \
  -v $(pwd)/html:/usr/share/nginx/html \
  nginx

这样可以很方便地把本地静态页面交给 Nginx 容器运行。


十三、Docker 网络基础

容器之间默认是隔离的,但 Docker 提供了网络机制,让容器之间可以通信。

创建网络:

docker network create app-net

运行 MySQL 容器:

docker run -d \
  --name mysql-demo \
  --network app-net \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e MYSQL_DATABASE=testdb \
  mysql:8.0

运行应用容器:

docker run -d \
  --name app-demo \
  --network app-net \
  -p 3000:3000 \
  docker-node-demo:1.0

在同一个 Docker 网络中,容器可以通过容器名称访问彼此。例如应用连接 MySQL 时,数据库主机名可以写成:

mysql-demo

而不是 localhost

这是新手非常容易踩坑的地方:在容器内部,localhost 指的是容器自己,而不是宿主机,也不是其他容器。


十四、使用 Docker Compose 管理多个服务

当项目只有一个容器时,使用 docker run 还比较方便。但如果项目包含应用、数据库、Redis、Nginx 等多个服务,手写多个 docker run 命令就会变得复杂。此时可以使用 Docker Compose

Docker Compose 使用 docker-compose.ymlcompose.yml 文件描述多个容器服务。

下面是一个示例:

services:
  app:
    build: .
    container_name: node-demo-compose
    ports:
      - "3000:3000"
    environment:
      - PORT=3000
    networks:
      - app-net

  nginx:
    image: nginx:latest
    container_name: nginx-demo-compose
    ports:
      - "8080:80"
    networks:
      - app-net

networks:
  app-net:
    driver: bridge

启动服务:

docker compose up -d

查看服务:

docker compose ps

查看日志:

docker compose logs -f

停止并删除服务:

docker compose down

Docker Compose 的好处是可以把服务配置写进文件中,方便版本管理,也方便团队成员一键启动相同环境。


十五、镜像标签与版本管理

构建镜像时,建议不要总是使用 latest 标签。因为 latest 并不代表最新稳定版本,它只是一个普通标签。

推荐方式:

docker build -t docker-node-demo:1.0.0 .
docker build -t docker-node-demo:2026-01-01 .
docker build -t docker-node-demo:dev .

在生产环境中,最好使用明确版本号,例如:

docker-node-demo:1.0.0

这样在出现问题时,可以快速回滚到旧版本。


十六、Docker 新手常见问题

1. 为什么容器启动后马上退出?

常见原因是容器主进程执行完了。例如你运行:

docker run ubuntu

Ubuntu 容器会立即退出,因为没有持续运行的前台进程。

可以这样进入交互模式:

docker run -it ubuntu /bin/bash

2. 为什么访问不到容器服务?

可能原因包括:

  • 容器内部服务没有监听正确端口;
  • 没有使用 -p 映射端口;
  • 应用只监听了 127.0.0.1,没有监听 0.0.0.0
  • 防火墙或安全组未放行端口。

对于 Web 服务,建议监听:

app.listen(3000, '0.0.0.0');

3. 容器里改了文件,为什么删除容器后没了?

容器文件系统默认不是用来长期保存数据的。需要持久化的数据应该使用:

  • Docker Volume;
  • 宿主机目录挂载;
  • 外部数据库或对象存储。

4. Docker 镜像太大怎么办?

可以从几个方面优化:

  • 使用更小的基础镜像,例如 alpine
  • 使用 .dockerignore
  • 减少不必要的软件安装;
  • 合并或优化构建层;
  • 对编译型项目使用多阶段构建。

十七、进阶示例:多阶段构建

对于前端、Go、Java 等项目,可以使用多阶段构建减少最终镜像体积。下面以一个简单前端项目为例:

# 第一阶段:构建阶段
FROM node:18-alpine AS builder

WORKDIR /app

COPY package.json ./
RUN npm install

COPY . .
RUN npm run build

# 第二阶段:运行阶段
FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

多阶段构建的核心思想是:

  • 第一阶段负责编译、打包;
  • 第二阶段只保留运行时真正需要的文件;
  • 最终镜像更小、更安全、更适合生产环境。

十八、推荐的 Docker 学习路线

对于新手来说,可以按照以下顺序学习:

  1. 理解镜像、容器、Dockerfile;
  2. 掌握常用 Docker 命令;
  3. 学会构建自己的应用镜像;
  4. 学会端口映射、数据卷、网络;
  5. 学会 Docker Compose;
  6. 学习镜像优化和多阶段构建;
  7. 了解 CI/CD 中如何构建和推送镜像;
  8. 再进一步学习 Kubernetes。

不要一开始就直接学习 Kubernetes。Kubernetes 是建立在容器基础之上的编排平台,如果 Docker 基础不牢,学习 Kubernetes 会非常吃力。


十九、完整源码汇总

下面再次汇总本文示例项目源码。

package.json

{
  "name": "docker-node-demo",
  "version": "1.0.0",
  "description": "A simple Node.js demo for Docker beginners",
  "main": "src/app.js",
  "scripts": {
    "start": "node src/app.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  }
}

src/app.js

const express = require('express');

const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({
    message: 'Hello Docker!',
    description: '这是一个运行在 Docker 容器中的 Node.js 服务',
    time: new Date().toISOString()
  });
});

app.get('/health', (req, res) => {
  res.json({
    status: 'ok',
    uptime: process.uptime()
  });
});

app.listen(PORT, '0.0.0.0', () => {
  console.log(`Server is running on port ${PORT}`);
});

Dockerfile

FROM node:18-alpine

WORKDIR /app

COPY package.json ./

RUN npm install

COPY src ./src

EXPOSE 3000

CMD ["npm", "start"]

.dockerignore

node_modules
npm-debug.log
.git
.gitignore
README.md
.DS_Store
.env

compose.yml

services:
  app:
    build: .
    container_name: docker-node-demo
    ports:
      - "3000:3000"
    environment:
      - PORT=3000
    restart: unless-stopped

启动命令:

docker compose up -d

停止命令:

docker compose down

二十、总结

Docker 的核心价值在于:让应用和运行环境一起交付。对于新手来说,刚开始不需要掌握所有复杂概念,只要先理解镜像、容器、Dockerfile、端口映射、数据卷和 Docker Compose,就已经可以完成大多数基础开发和部署工作。

本文通过 Nginx 示例和 Node.js 实战项目,演示了 Docker 的基本使用流程:

  1. 拉取并运行镜像;
  2. 编写 Dockerfile;
  3. 构建自定义镜像;
  4. 运行容器并映射端口;
  5. 使用 .dockerignore 优化构建;
  6. 使用 Volume 持久化数据;
  7. 使用 Docker Compose 管理服务。

如果你是 Docker 新手,建议不要只看概念,一定要亲自敲一遍命令,构建一个自己的镜像,并成功运行起来。只有当你真正把项目装进容器,访问到接口,查看到日志,处理过端口和数据卷问题之后,Docker 的知识才会从“看懂”变成“会用”。

Docker 是通往 DevOps、微服务和云原生的重要入口。掌握它,会让你的开发和部署能力迈上一个新的台阶。

目录结构
全文