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

从零上线一个项目:Docker 部署流程、Compose 编排与完整源码示例

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

Docker 部署完整教程|附源码

在现代软件开发中,Docker 已经成为部署应用的事实标准之一。无论是个人项目、企业级微服务,还是前后端分离系统,Docker 都能帮助我们快速构建一致、可迁移、易维护的运行环境。

本文将从零开始,带你完整了解 Docker 的部署流程,包括 Docker 基础概念、环境安装、项目准备、Dockerfile 编写、镜像构建、容器运行、Docker Compose 编排、常见部署优化以及完整示例源码。即使你之前没有 Docker 经验,也可以按照本文步骤完成一个可运行项目的容器化部署。


一、为什么要使用 Docker?

在没有 Docker 之前,我们部署项目通常需要手动安装各种环境,例如:

  • JDK
  • Node.js
  • Python
  • MySQL
  • Redis
  • Nginx
  • 各种系统依赖库

不同服务器之间的环境可能存在差异,比如本地开发使用的是 Node.js 18,而服务器上是 Node.js 16;本地 MySQL 是 8.0,服务器上却是 5.7。这类环境不一致问题经常导致“我本地能跑,服务器不能跑”。

Docker 的核心价值就是解决环境一致性问题。

使用 Docker 后,我们可以把应用程序、运行环境、依赖配置统一打包成镜像,然后在任意支持 Docker 的机器上运行。这样部署就变成了:

docker run your-image

相比传统部署方式,Docker 具有以下优势:

  1. 环境一致
    开发、测试、生产环境可以保持高度一致,减少环境差异导致的问题。

  2. 部署简单
    通过镜像即可快速启动应用,不需要在服务器上反复安装依赖。

  3. 易于迁移
    镜像可以推送到镜像仓库,在不同服务器之间快速拉取运行。

  4. 隔离性强
    每个容器都有独立的运行环境,多个应用之间互不影响。

  5. 便于扩展
    配合 Docker Compose、Kubernetes 等工具,可以方便地管理多容器应用。


二、Docker 核心概念

在正式部署之前,需要先理解几个常见概念。

1. 镜像 Image

镜像可以理解为应用运行环境的模板。它包含了应用程序代码、运行时环境、依赖库、配置文件等内容。

例如:

nginx:latest
mysql:8.0
redis:7
node:18-alpine

这些都是常见的 Docker 镜像。

2. 容器 Container

容器是镜像运行后的实例。镜像类似于类,容器类似于对象。

一个镜像可以创建多个容器,例如同一个 Nginx 镜像可以运行多个 Nginx 服务实例。

3. Dockerfile

Dockerfile 是用来构建镜像的文本文件。它描述了镜像从基础环境到最终应用的构建过程。

例如:

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "start"]

4. Docker Compose

Docker Compose 用于管理多个容器。比如一个项目需要同时运行:

  • 后端服务
  • MySQL
  • Redis
  • Nginx

如果逐个使用 docker run 启动会比较麻烦,而 Docker Compose 可以通过一个 docker-compose.yml 文件统一管理。


三、安装 Docker

以下以 Linux 服务器为例,推荐使用 Ubuntu 或 CentOS。

1. Ubuntu 安装 Docker

更新软件源:

sudo apt update

安装依赖:

sudo apt install -y ca-certificates curl gnupg lsb-release

安装 Docker:

curl -fsSL https://get.docker.com | bash

启动 Docker:

sudo systemctl start docker

设置开机自启:

sudo systemctl enable docker

查看 Docker 版本:

docker -v

如果输出类似内容,说明安装成功:

Docker version 26.1.0, build xxxxx

2. CentOS 安装 Docker

安装依赖:

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

安装 Docker:

curl -fsSL https://get.docker.com | bash

启动 Docker:

sudo systemctl start docker

设置开机自启:

sudo systemctl enable docker

查看状态:

sudo systemctl status docker

四、准备示例项目源码

为了让教程更完整,下面我们使用一个简单的 Node.js Web 项目作为示例。该项目提供一个 HTTP 接口,访问后返回一段 JSON 数据。

项目结构如下:

docker-demo
├── app.js
├── package.json
├── Dockerfile
├── .dockerignore
└── docker-compose.yml

五、编写 Node.js 示例源码

1. app.js

const express = require("express");

const app = express();

const PORT = process.env.PORT || 3000;

app.get("/", (req, res) => {
  res.json({
    code: 200,
    message: "Docker 部署成功!",
    time: new Date().toISOString()
  });
});

app.get("/health", (req, res) => {
  res.json({
    status: "UP",
    service: "docker-demo"
  });
});

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

这个文件创建了一个 Express 服务,提供两个接口:

  • /:返回部署成功信息
  • /health:健康检查接口

2. package.json

{
  "name": "docker-demo",
  "version": "1.0.0",
  "description": "Docker deployment demo project",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.18.3"
  }
}

六、编写 Dockerfile

在项目根目录下创建 Dockerfile 文件:

FROM node:18-alpine

WORKDIR /app

COPY package.json ./

RUN npm install --production

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

下面逐行解释:

1. 指定基础镜像

FROM node:18-alpine

表示使用 Node.js 18 的 Alpine 版本作为基础镜像。Alpine 是一个轻量级 Linux 发行版,镜像体积较小,适合生产部署。

2. 设置工作目录

WORKDIR /app

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

3. 复制依赖声明文件

COPY package.json ./

先复制 package.json,这样可以利用 Docker 构建缓存。只要依赖文件不变,后续构建时 npm install 可以直接复用缓存。

4. 安装生产依赖

RUN npm install --production

只安装生产环境依赖,避免安装开发依赖,减少镜像体积。

5. 复制项目源码

COPY . .

将当前目录下的项目代码复制到容器中的 /app 目录。

6. 暴露端口

EXPOSE 3000

声明容器内部服务监听的是 3000 端口。

7. 启动命令

CMD ["npm", "start"]

容器启动时执行 npm start


七、编写 .dockerignore

.dockerignore 用于排除不需要复制到镜像中的文件,类似 .gitignore

node_modules
npm-debug.log
Dockerfile
docker-compose.yml
.git
.gitignore
README.md

这样可以避免将无用文件打包进镜像,提高构建速度,减小镜像体积。


八、构建 Docker 镜像

进入项目目录:

cd docker-demo

执行构建命令:

docker build -t docker-demo:1.0 .

参数说明:

  • docker build:构建镜像
  • -t docker-demo:1.0:指定镜像名称和版本标签
  • .:表示 Dockerfile 位于当前目录

构建完成后,查看本地镜像:

docker images

你应该可以看到类似内容:

REPOSITORY      TAG       IMAGE ID       CREATED          SIZE
docker-demo     1.0       xxxxxxxx       1 minute ago     180MB

九、运行 Docker 容器

执行以下命令启动容器:

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

参数说明:

  • -d:后台运行容器
  • --name docker-demo-app:指定容器名称
  • -p 3000:3000:端口映射,宿主机 3000 端口映射到容器 3000 端口
  • docker-demo:1.0:要运行的镜像

查看正在运行的容器:

docker ps

访问接口:

curl http://localhost:3000

返回结果类似:

{
  "code": 200,
  "message": "Docker 部署成功!",
  "time": "2026-01-01T12:00:00.000Z"
}

如果你是在云服务器上部署,需要确认安全组已经放行 3000 端口,然后通过浏览器访问:

http://服务器公网IP:3000

十、常用容器管理命令

1. 查看运行中的容器

docker ps

2. 查看所有容器

docker ps -a

3. 查看容器日志

docker logs docker-demo-app

实时查看日志:

docker logs -f docker-demo-app

4. 停止容器

docker stop docker-demo-app

5. 启动已停止的容器

docker start docker-demo-app

6. 重启容器

docker restart docker-demo-app

7. 删除容器

docker rm docker-demo-app

如果容器正在运行,需要先停止,或者强制删除:

docker rm -f docker-demo-app

8. 删除镜像

docker rmi docker-demo:1.0

十一、使用 Docker Compose 部署

单个容器可以用 docker run,但实际项目中通常不止一个服务。为了更方便管理,我们可以使用 Docker Compose。

在项目根目录创建 docker-compose.yml

version: "3.8"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: docker-demo:1.0
    container_name: docker-demo-app
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
    restart: always

启动服务:

docker compose up -d

如果你的 Docker 版本较旧,可能需要使用:

docker-compose up -d

查看服务状态:

docker compose ps

查看日志:

docker compose logs -f

停止服务:

docker compose down

重新构建并启动:

docker compose up -d --build

十二、添加 Nginx 反向代理

在生产环境中,我们一般不会直接暴露 Node.js 应用端口,而是通过 Nginx 做反向代理。

可以将应用监听在内部端口,例如 3000,然后由 Nginx 对外提供 80 或 443 端口。

扩展后的项目结构如下:

docker-demo
├── app.js
├── package.json
├── Dockerfile
├── .dockerignore
├── docker-compose.yml
└── nginx
    └── default.conf

1. nginx/default.conf

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://app:3000;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

其中:

proxy_pass http://app:3000;

这里的 app 是 Docker Compose 中的服务名称。Compose 会自动创建网络,服务之间可以通过服务名互相访问。

2. 修改 docker-compose.yml

version: "3.8"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: docker-demo:1.0
    container_name: docker-demo-app
    environment:
      - NODE_ENV=production
      - PORT=3000
    restart: always

  nginx:
    image: nginx:1.25-alpine
    container_name: docker-demo-nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
    restart: always

启动:

docker compose up -d --build

此时访问:

http://服务器公网IP

就会通过 Nginx 转发到 Node.js 应用。


十三、部署带 MySQL 的应用示例

如果你的应用依赖数据库,可以在 Compose 中加入 MySQL 服务。

示例:

version: "3.8"

services:
  app:
    build: .
    image: docker-demo:1.0
    container_name: docker-demo-app
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
      - DB_HOST=mysql
      - DB_PORT=3306
      - DB_USER=root
      - DB_PASSWORD=123456
      - DB_NAME=demo
    depends_on:
      - mysql
    restart: always

  mysql:
    image: mysql:8.0
    container_name: docker-demo-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_DATABASE=demo
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    restart: always

volumes:
  mysql_data:

这里使用了 Docker Volume:

volumes:
  mysql_data:

它的作用是持久化 MySQL 数据。否则容器删除后,数据库数据也可能丢失。

生产环境中不建议直接将 MySQL 暴露到公网。如果必须开放端口,应限制访问来源 IP,并设置复杂密码。


十四、生产环境部署建议

1. 不要使用 latest 标签

很多初学者喜欢使用:

node:latest
mysql:latest
nginx:latest

但生产环境不推荐这样做。因为 latest 会随着官方镜像更新而变化,可能导致重新部署时出现兼容性问题。

推荐指定明确版本:

FROM node:18.20-alpine

或:

image: mysql:8.0

2. 使用 restart 策略

生产环境建议配置:

restart: always

这样容器异常退出后会自动重启。

常见策略包括:

restart: no
restart: always
restart: unless-stopped
restart: on-failure

3. 配置健康检查

可以在 docker-compose.yml 中添加健康检查:

services:
  app:
    build: .
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

健康检查可以帮助我们判断容器内服务是否真正可用。

4. 使用环境变量管理配置

不要将数据库密码、Token、密钥直接写死在代码中。推荐通过环境变量传入:

environment:
  - DB_PASSWORD=${DB_PASSWORD}

然后创建 .env 文件:

DB_PASSWORD=your_strong_password

注意 .env 文件不要提交到 Git 仓库。

5. 控制镜像体积

镜像越小,传输和部署速度越快,也能减少潜在安全风险。

优化方式包括:

  • 使用 alpine 版本基础镜像
  • 使用 .dockerignore
  • 删除不必要文件
  • 多阶段构建
  • 只安装生产依赖

十五、使用多阶段构建优化镜像

如果是前端项目或需要编译的后端项目,可以使用多阶段构建。

以下是一个前端 Vue/React 项目构建示例:

FROM node:18-alpine AS builder

WORKDIR /app

COPY package.json package-lock.json ./

RUN npm install

COPY . .

RUN npm run build

FROM nginx:1.25-alpine

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

EXPOSE 80

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

这个 Dockerfile 分为两个阶段:

  1. builder 阶段
    使用 Node.js 安装依赖并构建前端静态文件。

  2. 运行阶段
    使用 Nginx 作为最终运行环境,只保留构建后的静态文件。

这样最终镜像不包含 Node.js、源码和开发依赖,体积更小,也更安全。


十六、镜像推送到 Docker Hub

如果你希望在其他服务器部署,可以将镜像推送到 Docker Hub 或私有镜像仓库。

1. 登录 Docker Hub

docker login

输入用户名和密码。

2. 给镜像打标签

假设你的 Docker Hub 用户名是 yourname

docker tag docker-demo:1.0 yourname/docker-demo:1.0

3. 推送镜像

docker push yourname/docker-demo:1.0

4. 在服务器拉取镜像

docker pull yourname/docker-demo:1.0

5. 运行镜像

docker run -d \
  --name docker-demo-app \
  -p 3000:3000 \
  yourname/docker-demo:1.0

十七、完整源码汇总

下面给出完整可运行源码。

1. app.js

const express = require("express");

const app = express();

const PORT = process.env.PORT || 3000;

app.get("/", (req, res) => {
  res.json({
    code: 200,
    message: "Docker 部署成功!",
    time: new Date().toISOString()
  });
});

app.get("/health", (req, res) => {
  res.json({
    status: "UP",
    service: "docker-demo"
  });
});

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

2. package.json

{
  "name": "docker-demo",
  "version": "1.0.0",
  "description": "Docker deployment demo project",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.18.3"
  }
}

3. Dockerfile

FROM node:18-alpine

WORKDIR /app

COPY package.json ./

RUN npm install --production

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

4. .dockerignore

node_modules
npm-debug.log
Dockerfile
docker-compose.yml
.git
.gitignore
README.md

5. docker-compose.yml

version: "3.8"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: docker-demo:1.0
    container_name: docker-demo-app
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
    restart: always

6. 带 Nginx 的 docker-compose.yml

version: "3.8"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: docker-demo:1.0
    container_name: docker-demo-app
    environment:
      - NODE_ENV=production
      - PORT=3000
    restart: always

  nginx:
    image: nginx:1.25-alpine
    container_name: docker-demo-nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
    restart: always

7. nginx/default.conf

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://app:3000;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

十八、常见问题排查

1. 端口被占用

如果启动容器时报错:

Bind for 0.0.0.0:3000 failed: port is already allocated

说明宿主机 3000 端口已经被占用。

可以查看端口占用:

sudo lsof -i:3000

或修改端口映射:

docker run -d -p 3001:3000 docker-demo:1.0

此时访问:

http://服务器IP:3001

2. 容器启动后马上退出

查看日志:

docker logs docker-demo-app

常见原因包括:

  • 启动命令错误
  • 依赖安装失败
  • 环境变量缺失
  • 端口配置错误
  • 代码运行异常

3. 修改代码后不生效

如果代码已经修改,但容器中没有变化,需要重新构建镜像:

docker compose up -d --build

或者:

docker build -t docker-demo:1.0 .
docker rm -f docker-demo-app
docker run -d --name docker-demo-app -p 3000:3000 docker-demo:1.0

4. 无法从外网访问

请检查以下内容:

  1. 容器是否正常运行:
docker ps
  1. 端口是否映射正确:
docker port docker-demo-app
  1. 服务器防火墙是否放行端口:
sudo ufw allow 3000
  1. 云服务器安全组是否开放对应端口。

十九、推荐部署流程

一个比较规范的 Docker 部署流程如下:

本地开发代码
    ↓
编写 Dockerfile
    ↓
编写 docker-compose.yml
    ↓
本地构建并测试
    ↓
提交代码到 Git 仓库
    ↓
服务器拉取代码
    ↓
执行 docker compose up -d --build
    ↓
配置 Nginx 和域名
    ↓
配置 HTTPS
    ↓
上线运行

如果项目规模较大,可以进一步接入 CI/CD,例如 GitHub Actions、GitLab CI、Jenkins 等,实现代码提交后自动构建镜像、推送镜像、远程部署。


二十、总结

本文完整介绍了 Docker 部署应用的基本流程,从 Docker 的核心概念讲起,依次完成了环境安装、示例项目编写、Dockerfile 编写、镜像构建、容器运行、Docker Compose 编排、Nginx 反向代理、MySQL 扩展、镜像推送以及常见问题排查。

对于大多数中小型项目来说,掌握以下几个文件就已经可以完成 Docker 化部署:

  • Dockerfile
  • .dockerignore
  • docker-compose.yml
  • nginx/default.conf

其中,Dockerfile 负责构建应用镜像,docker-compose.yml 负责管理服务编排,Nginx 用于统一入口和反向代理。通过 Docker,我们可以让部署过程更加标准化、自动化和可复制。

如果你是第一次接触 Docker,建议先从本文的 Node.js 示例项目开始练习,理解镜像构建和容器运行的基本逻辑;随后再尝试加入 MySQL、Redis、Nginx 等组件,逐步过渡到真实生产环境部署。

目录结构
全文