如何使用Docker部署一个完整的Web应用栈


嘿,朋友!想知道如何用 Docker 部署一整套 Web 应用吗?别担心,这篇教程就是为你准备的。咱们会用大白话,一步步带你从零开始,把一个完整的 Web 应用栈(前端、后端、数据库)装进 Docker 容器里,并让它们愉快地协同工作。

为什么选择 Docker?

想象一下,你精心开发了一个网站,在你的电脑上跑得飞快。但一部署到服务器,就各种报错,什么环境不一致、依赖缺失……是不是很头大?

Docker 就是解决这个问题的神器。它能把你的应用以及它需要的所有东西(比如代码、运行时、库、环境变量)都打包到一个叫做“容器”的小盒子里。这个小盒子可以随处运行,无论是在你的笔记本、测试服务器还是云端,都能保证环境的一致性。 这样一来,“在我电脑上明明是好的”这种话,就可以彻底成为历史了。

我们要搭建什么样的 Web 应用栈?

为了让教程更具代表性,我们选择一个非常流行的技术栈:PERN

  • PostgreSQL:一个功能强大、稳定可靠的开源关系型数据库。
  • Express.js:一个简洁、灵活的 Node.js Web 应用框架,用来搭建我们的后端 API。
  • React:一个用于构建用户界面的 JavaScript 库,是目前最火的前端框架之一。
  • Node.js:一个让 JavaScript 可以在服务器端运行的环境。

这个组合非常经典,能够应对各种复杂的 Web 应用场景。

实战开始:一步步 Docker 化你的 PERN 应用

好了,理论说得差不多了,咱们卷起袖子开始干活吧!

准备工作

在开始之前,请确保你的电脑上已经安装了 Docker 和 Docker Compose。你可以从 Docker 官网轻松获取它们。

项目结构

为了让项目清晰明了,我们先规划一下目录结构:

pern-docker-app/
├── backend/
│   ├── Dockerfile
│   ├── package.json
│   └── server.js
├── frontend/
│   ├── Dockerfile
│   ├── package.json
│   └── src/
│       └── App.js
└── docker-compose.yml
  • backend 目录存放我们的 Express 后端代码。
  • frontend 目录存放我们的 React 前端代码。
  • docker-compose.yml 是一个非常重要的文件,它用来定义和编排我们的多个 Docker 容器。

第一步:后端服务 Docker 化

我们的后端服务是一个简单的 Express 应用,它会提供一个 API 接口。

backend/server.js

const express = require('express');
const { Pool } = require('pg');

const app = express();
const port = 3001;

const pool = new Pool({
  user: 'myuser',
  host: 'db', // 这里我们直接使用 service 的名字
  database: 'mydatabase',
  password: 'mypassword',
  port: 5432,
});

app.get('/api/users', async (req, res) => {
  try {
    const result = await pool.query('SELECT * FROM users');
    res.json(result.rows);
  } catch (err) {
    console.error(err);
    res.status(500).send("Error connecting to database");
  }
});

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

backend/package.json

{
  "name": "backend",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "pg": "^8.7.1"
  }
}

接下来,我们为后端服务编写一个 DockerfileDockerfile 就像一个菜谱,告诉 Docker 如何一步步构建我们的应用镜像。

backend/Dockerfile

# 选择一个官方的 Node.js 镜像作为基础
FROM node:16

# 设置工作目录
WORKDIR /app

# 拷贝 package.json 和 package-lock.json
COPY package*.json ./

# 安装依赖
RUN npm install

# 拷贝所有应用代码
COPY . .

# 暴露端口
EXPOSE 3001

# 启动应用的命令
CMD ["npm", "start"]

第二步:前端服务 Docker 化

前端是一个简单的 React 应用,它会从后端获取数据显示出来。

frontend/src/App.js

import React, { useState, useEffect } from 'react';

function App() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(data => setUsers(data));
  }, []);

  return (
    <div>
      <h1>Users</h1>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

frontend/package.json
(这是一个简化的 package.json,你可以使用 create-react-app 来生成一个完整的项目)

{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build"
  }
}

现在,为前端应用编写 Dockerfile。这里我们使用一个“多阶段构建”的技巧,这样可以大大减小最终镜像的体积。

frontend/Dockerfile

# --- 构建阶段 ---
FROM node:16 as build

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

# --- 生产阶段 ---
FROM nginx:alpine

# 从构建阶段拷贝构建好的静态文件
COPY --from=build /app/build /usr/share/nginx/html

# 暴露端口
EXPOSE 80

# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

第三步:使用 Docker Compose 编排服务

单个的容器是孤立的,我们需要一种方式让它们互相通信。Docker Compose 就是为此而生的。 它可以让我们在一个 YAML 文件中定义和管理多个容器的应用。

docker-compose.yml

version: '3.8'

services:
  # 后端服务
  backend:
    build: ./backend
    ports:
      - "3001:3001"
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgres://myuser:mypassword@db:5432/mydatabase

  # 前端服务
  frontend:
    build: ./frontend
    ports:
      - "3000:80"
    depends_on:
      - backend

  # 数据库服务
  db:
    image: postgres:14-alpine
    restart: always
    environment:
      - POSTGRES_USER=myuser
      - POSTG-RES_PASSWORD=mypassword
      - POSTGRES_DB=mydatabase
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

在这个 docker-compose.yml 文件中,我们定义了三个服务:backendfrontenddb

  • build 指令告诉 Docker Compose 去哪里找 Dockerfile 来构建镜像。
  • ports 指令将容器的端口映射到主机的端口。
  • depends_on 确保服务按正确的顺序启动(比如,后端服务依赖于数据库服务)。
  • environment 用来设置环境变量。
  • volumes 用来持久化数据库的数据,这样即使容器被删除了,数据也不会丢失。

第四步:启动整个应用栈

现在,万事俱备,只欠东风!在项目的根目录下,打开终端,运行以下命令:

docker-compose up --build

--build 参数会强制 Docker Compose 重新构建镜像。第一次启动时,Docker 会下载所有需要的基础镜像并构建你的应用镜像,所以可能会需要一些时间。

当所有的服务都成功启动后,你就可以在浏览器中访问 http://localhost:3000,看到你的 React 应用了!它会向后端发起请求,后端再从 PostgreSQL 数据库中查询数据,最后将数据返回给前端展示。

总结

恭喜你!你已经成功地使用 Docker 部署了一个完整的 Web 应用栈。通过这个过程,你应该已经体会到了 Docker 的强大之处:

  • 环境一致性:告别“在我电脑上明明是好的”的烦恼。
  • 简化部署:一条命令就能启动整个应用栈。
  • 服务隔离:每个服务都在自己的容器中运行,互不干扰。
  • 易于扩展:可以轻松地为每个服务增加更多的容器实例。

希望这篇教程能帮助你打开 Docker 的大门。Docker 的世界还有很多有趣的东西等着你去探索,比如 Docker Swarm 和 Kubernetes,它们可以帮助你管理更大规模的容器集群。 祝你在容器化的世界里玩得开心!


  目录