【docker基础】第六课:Web应用与数据库容器部署

📚前言

👀回顾,系统学习docker系列已发布内容:

【docker基础】0、系统学习docker之总计划

【docker基础】第一课、从零开始理解容器技术

【docker基础】第二课:安装、配置与基础命令

【docker基础】第三课:镜像管理与Dockerfile基础

【docker基础】第四课:容器操作与数据管理

【docker基础】第五课:Docker网络详解-CSDN博客

🔗相关文档:

windows下安装docker

【docker基础】Ubuntu 安装 Docker 超详细小白教程

📒本课学习目标:

  1. Nginx 容器部署:运行 Nginx、端口映射、静态网站托管

  2. 配置文件挂载:自定义 Nginx 配置实现灵活部署

  3. MySQL 容器:运行 MySQL、环境变量配置、数据库操作

  4. 数据持久化:使用卷挂载确保数据不丢失

  5. PostgreSQL 容器:另一种主流数据库的部署方式

  6. 实战练习:部署完整的 Web 应用(前端 + 后端 + 数据库)

  7. Docker Compose:使用 YAML 文件管理多容器应用


🌍Docker 第六周学习教程:Web应用与数据库容器部署

欢迎来到 Docker 第六周的学习!本周我们将深入学习如何使用 Docker 部署 Web 应用和数据库服务,这是 Docker 在实际生产环境中最常用的场景。


第一章:Web应用部署 - Nginx容器部署

1.1 什么是 Nginx

Nginx 是一个高性能的 HTTP 和反向代理服务器,常用于:

  • 静态网站托管

  • 负载均衡

  • 反向代理

  • 缓存服务

📌反向代理 是一种服务器配置,它接收来自客户端的请求,然后将这些请求转发给后端的多个服务器,并将响应返回给客户端。

1.2 运行第一个 Nginx 容器

打开终端,执行以下命令:

复制代码
docker run -d -p 8080:80 --name mynginx nginx

命令解释:

  • docker run: 创建并运行容器

  • -d: 后台运行(detached 模式)

  • -p 8080:80: 将宿主机的 8080 端口映射到容器的 80 端口

  • --name mynginx: 给容器命名为 mynginx

  • nginx: 使用的镜像名称

命令执行后会输出类似这样的内容:

复制代码
abc123def4567890abc123def4567890abc123def4567890abc123def

这串字符是容器的 ID。

1.3 验证 Nginx 是否运行成功

复制代码
docker ps

预期输出:

复制代码
CONTAINER ID   IMAGE   COMMAND                  CREATED         STATUS         PORTS                  NAMES
abc123def456   nginx   "/docker-entrypoint...."   2 minutes ago   Up 2 minutes   0.0.0.0:8080->80/tcp   mynginx

现在打开浏览器,访问 http://localhost:8080,您会看到 Nginx 的欢迎页面!

1.4 停止和删除容器

复制代码
# 停止容器
docker stop mynginx

# 删除容器
docker rm mynginx

第二章:静态网站托管

2.1 准备静态网站文件

首先,创建一个本地目录来存放网站文件:

复制代码
mkdir -p ~/mywebsite
cd ~/mywebsite

创建一个简单的 HTML 文件 index.html

复制代码
<!DOCTYPE html>
<html>
<head>
    <title>我的第一个 Docker 网站</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin-top: 100px;
            background-color: #f0f0f0;
        }
        h1 {
            color: #333;
        }
        .container {
            background: white;
            padding: 40px;
            border-radius: 10px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            width: 500px;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🎉 欢迎来到我的 Docker 网站!</h1>
        <p>这是一个使用 Nginx 容器托管的静态网站。</p>
        <p>通过 Docker 挂载宿主机目录实现文件共享。</p>
    </div>
</body>
</html>

2.2 使用目录挂载部署静态网站

复制代码
docker run -d -p 8080:80 --name mywebsite -v ~/mywebsite:/usr/share/nginx/html nginx

命令解释:

  • -v ~/mywebsite:/usr/share/nginx/html: 将宿主机的 ~/mywebsite 目录挂载到容器内的 /usr/share/nginx/html 目录

  • /usr/share/nginx/html: 是 Nginx 默认的网页根目录

2.3 测试网站

打开浏览器访问 http://localhost:8080,您会看到自己创建的网页!

2.4 热更新测试

无需重启容器,直接修改 index.html 文件:

复制代码
echo "<h1>🔄 网站已更新!</h1>" >> ~/mywebsite/index.html

刷新浏览器,您会看到网页内容已经更新了!


第三章:配置文件挂载

3.1 为什么需要挂载配置文件

容器默认的配置可能不符合我们的需求:

  • 修改端口

  • 添加 SSL 证书

  • 配置反向代理

  • 设置缓存策略

3.2 创建自定义 Nginx 配置

创建配置文件目录:

复制代码
mkdir -p ~/nginx/conf
mkdir -p ~/nginx/html

创建自定义配置文件 ~/nginx/conf/default.conf

复制代码
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    # 根目录和默认页面
    root   /usr/share/nginx/html;
    index  index.html index.htm;

    # 主页面
    location / {
        try_files $uri $uri/ =404;
    }

    # 自定义错误页面
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # 添加访问日志
    access_log /var/log/nginx/host.access.log;
    error_log /var/log/nginx/host.error.log;
}

创建一个简单的错误页面 ~/nginx/html/50x.html

复制代码
<!DOCTYPE html>
<html>
<head>
    <title>服务器错误</title>
</head>
<body>
    <h1>Oops! 服务器出错了 (50x)</h1>
    <p>请稍后再试...</p>
</body>
</html>

3.3 启动带自定义配置的容器

复制代码
docker run -d -p 8080:80 --name customnginx \
  -v ~/nginx/html:/usr/share/nginx/html \
  -v ~/nginx/conf:/etc/nginx/conf.d \
  nginx

命令解释:

  • -v ~/nginx/html:/usr/share/nginx/html: 挂载网页目录

  • -v ~/nginx/conf:/etc/nginx/conf.d: 挂载配置目录

3.4 验证配置是否生效

查看容器日志:

复制代码
docker logs customnginx

预期输出:

复制代码
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
...
nginx: [notice] 1#1: start worker process 32

访问 http://localhost:8080 查看效果。


第四章:数据库容器 - MySQL 部署

4.1 运行 MySQL 容器

复制代码
docker run -d -p 3306:3306 --name mymysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  mysql:5.7

命令解释:

  • -e MYSQL_ROOT_PASSWORD=123456: 设置环境变量,配置 MySQL root 用户密码为 123456

  • mysql:5.7: 使用 MySQL 5.7 版本镜像(指定版本更稳定)

4.2 连接 MySQL 容器

方法一:使用容器内的 mysql 命令

复制代码
docker exec -it mymysql mysql -uroot -p123456

命令解释:

  • docker exec: 在运行的容器中执行命令

  • -it: 交互式终端

  • mysql -uroot -p123456: 使用 root 用户登录,密码为 123456

方法二:使用本地 MySQL 客户端

复制代码
mysql -h localhost -P 3306 -u root -p123456

4.3 创建数据库和表

进入 MySQL 命令行后,执行以下 SQL 命令:

复制代码
-- 创建数据库
CREATE DATABASE myappdb;

-- 使用数据库
USE myappdb;

-- 创建用户表
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 插入测试数据
INSERT INTO users (name, email) VALUES 
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com');

-- 查询数据
SELECT * FROM users;

预期输出:

复制代码
+----+--------+---------------------+---------------------+
| id | name   | email               | created_at          |
+----+--------+---------------------+---------------------+
|  1 | 张三   | zhangsan@example.com| 2024-01-15 10:30:00 |
|  2 | 李四   | lisi@example.com    | 2024-01-15 10:30:00 |
+----+--------+---------------------+---------------------+

4.4 数据持久化问题

问题: 如果删除容器,数据会丢失!

复制代码
# 停止并删除容器
docker stop mymysql
docker rm mymysql

# 重新创建容器
docker run -d -p 3306:3306 --name mymysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

# 连接查看数据(数据已丢失!)
docker exec -it mymysql mysql -uroot -p123456 -e "USE myappdb; SELECT * FROM users;"

错误输出:

复制代码
ERROR 1049 (42000): Unknown database 'myappdb'

第五章:数据库数据持久化

5.1 使用数据卷挂载实现持久化

复制代码
# 创建数据目录
mkdir -p ~/mysql/data

# 启动带数据卷挂载的 MySQL 容器
docker run -d -p 3306:3306 --name mymysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v ~/mysql/data:/var/lib/mysql \
  mysql:5.7

命令解释:

  • -v ~/mysql/data:/var/lib/mysql: 将宿主机目录挂载到 MySQL 容器的数据目录

  • /var/lib/mysql: MySQL 默认的数据存储目录

5.2 验证数据持久化

  1. 创建数据库和表(重复第四章的步骤)

  2. 停止并删除容器

  3. 重新创建容器(使用相同的数据卷挂载)

  4. 验证数据是否存在

    停止并删除容器

    docker stop mymysql
    docker rm mymysql

    重新创建容器(使用相同的挂载目录)

    docker run -d -p 3306:3306 --name mymysql
    -e MYSQL_ROOT_PASSWORD=123456
    -v ~/mysql/data:/var/lib/mysql
    mysql:5.7

    验证数据

    docker exec -it mymysql mysql -uroot -p123456 -e "USE myappdb; SELECT * FROM users;"

预期输出: 数据依然存在!


第六章:PostgreSQL 容器部署

6.1 运行 PostgreSQL 容器

复制代码
docker run -d -p 5432:5432 --name mypostgres \
  -e POSTGRES_PASSWORD=123456 \
  -e POSTGRES_DB=myappdb \
  -v ~/postgres/data:/var/lib/postgresql/data \
  postgres:13

命令解释:

  • POSTGRES_PASSWORD: 设置 PostgreSQL 密码

  • POSTGRES_DB: 设置默认数据库名称

  • -v ~/postgres/data:/var/lib/postgresql/data: 数据持久化

6.2 连接 PostgreSQL

复制代码
docker exec -it mypostgres psql -U postgres

6.3 创建表和插入数据

复制代码
-- 创建用户表
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 插入数据
INSERT INTO users (name, email) VALUES 
('王五', 'wangwu@example.com'),
('赵六', 'zhaoliu@example.com');

-- 查询数据
SELECT * FROM users;

第七章:实战练习 - 部署完整的 Web 应用

7.1 项目架构

我们将部署一个完整的 Web 应用:

  • 前端: Nginx 托管静态页面

  • 后端: Python Flask API

  • 数据库: MySQL

7.2 创建项目结构

复制代码
mkdir -p ~/fullstack-app/{frontend,backend}
cd ~/fullstack-app

📌解释:

mkdir -p ~/fullstack-app/{frontend,backend}

等价于:

mkdir -p ~/fullstack-app/frontend

mkdir -p ~/fullstack-app/backend

7.3 创建后端 API (Flask)

创建 backend/app.py

复制代码
from flask import Flask, jsonify, request
from flask_cors import CORS
import pymysql

app = Flask(__name__)
CORS(app)  # 允许跨域访问

# 数据库连接配置
db = pymysql.connect(
    host='mymysql',  # 容器名称作为主机名
    user='root',
    password='123456',
    database='myappdb'
)

@app.route('/api/users', methods=['GET'])
def get_users():
    cursor = db.cursor()
    cursor.execute("SELECT * FROM users")
    users = cursor.fetchall()
    cursor.close()
    
    # 转换为字典列表
    result = []
    for user in users:
        result.append({
            'id': user[0],
            'name': user[1],
            'email': user[2],
            'created_at': str(user[3])
        })
    return jsonify(result)

@app.route('/api/users', methods=['POST'])
def add_user():
    data = request.json
    name = data.get('name')
    email = data.get('email')
    
    cursor = db.cursor()
    sql = "INSERT INTO users (name, email) VALUES (%s, %s)"
    cursor.execute(sql, (name, email))
    db.commit()
    cursor.close()
    
    return jsonify({'message': '用户添加成功', 'id': cursor.lastrowid}), 201

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

创建 backend/requirements.txt

复制代码
Flask==2.0.1
Flask-CORS==3.0.10
PyMySQL==1.0.2

7.4 创建 Dockerfile 用于构建后端镜像

创建 backend/Dockerfile

复制代码
# 使用 Python 基础镜像
FROM python:3.9-slim

# 设置工作目录
WORKDIR /app

# 复制依赖文件
COPY requirements.txt .

# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY app.py .

# 暴露端口
EXPOSE 5000

# 启动应用
CMD ["python", "app.py"]

7.5 构建后端镜像

复制代码
cd ~/fullstack-app/backend
docker build -t myflaskapp .

命令解释:

  • docker build: 构建镜像

  • -t myflaskapp: 给镜像打标签为 myflaskapp

  • .: 使用当前目录的 Dockerfile

7.6 创建前端页面

创建 frontend/index.html

复制代码
<!DOCTYPE html>
<html>
<head>
    <title>用户管理系统</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        h1 {
            color: #2c3e50;
            text-align: center;
        }
        .user-list {
            margin-top: 30px;
        }
        .user-card {
            background: #f8f9fa;
            padding: 15px;
            margin: 10px 0;
            border-radius: 5px;
            border-left: 4px solid #3498db;
        }
        .form-container {
            background: #fff;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }
        input {
            display: block;
            width: 100%;
            padding: 10px;
            margin: 10px 0;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
        }
        button {
            background: #3498db;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background: #2980b9;
        }
    </style>
</head>
<body>
    <h1>👥 用户管理系统</h1>
    
    <div class="form-container">
        <h3>添加新用户</h3>
        <input type="text" id="name" placeholder="姓名">
        <input type="email" id="email" placeholder="邮箱">
        <button onclick="addUser()">添加用户</button>
    </div>
    
    <div class="user-list" id="userList">
        <!-- 用户列表将在这里显示 -->
    </div>

    <script>
        // 获取用户列表
        async function loadUsers() {
            const response = await fetch('http://localhost:5000/api/users');
            const users = await response.json();
            displayUsers(users);
        }

        // 显示用户列表
        function displayUsers(users) {
            const userList = document.getElementById('userList');
            userList.innerHTML = '';
            
            users.forEach(user => {
                const card = document.createElement('div');
                card.className = 'user-card';
                card.innerHTML = `
                    <h4>${user.name}</h4>
                    <p>邮箱: ${user.email}</p>
                    <p>创建时间: ${user.created_at}</p>
                `;
                userList.appendChild(card);
            });
        }

        // 添加用户
        async function addUser() {
            const name = document.getElementById('name').value;
            const email = document.getElementById('email').value;
            
            await fetch('http://localhost:5000/api/users', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ name, email })
            });
            
            // 清空表单
            document.getElementById('name').value = '';
            document.getElementById('email').value = '';
            
            // 重新加载用户列表
            loadUsers();
        }

        // 页面加载时获取用户列表
        window.onload = loadUsers;
    </script>
</body>
</html>

7.7 启动完整的应用栈

步骤 1:启动 MySQL 容器

复制代码
docker run -d -p 3306:3306 --name mymysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v ~/mysql/data:/var/lib/mysql \
  mysql:5.7

步骤 2:创建数据库表

复制代码
docker exec -it mymysql mysql -uroot -p123456 -e "
CREATE DATABASE IF NOT EXISTS myappdb;
USE myappdb;
CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
"

步骤 3:启动 Flask 后端

复制代码
docker run -d -p 5000:5000 --name myflaskapp \
  --link mymysql:mymysql \
  myflaskapp

命令解释:

  • --link mymysql:mymysql: 链接到 mymysql 容器,使其可以通过容器名访问

步骤 4:启动 Nginx 前端

复制代码
docker run -d -p 80:80 --name myfrontend \
  -v ~/fullstack-app/frontend:/usr/share/nginx/html \
  nginx

7.8 测试完整应用

  1. 打开浏览器访问 http://localhost

  2. 您会看到用户管理系统页面

  3. 可以添加新用户,用户数据会存储在 MySQL 数据库中


第八章:使用 Docker Compose 简化部署(选学)

8.1 什么是 Docker Compose

Docker Compose 是一个工具,可以通过一个 YAML 文件定义和运行多个 Docker 容器。

8.2 安装 Docker Compose

复制代码
# 检查是否已安装
docker-compose --version

8.3 创建 docker-compose.yml

复制代码
version: '3.8'

services:
  db:
    image: mysql:5.7
    container_name: mymysql
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: myappdb
    volumes:
      - ./mysql/data:/var/lib/mysql
    ports:
      - "3306:3306"

  backend:
    build: ./backend
    container_name: myflaskapp
    ports:
      - "5000:5000"
    depends_on:
      - db
    environment:
      - DB_HOST=db
      - DB_USER=root
      - DB_PASSWORD=123456
      - DB_NAME=myappdb

  frontend:
    image: nginx
    container_name: myfrontend
    ports:
      - "80:80"
    volumes:
      - ./frontend:/usr/share/nginx/html

8.4 使用 Compose 启动应用

复制代码
cd ~/fullstack-app
docker-compose up -d

8.5 停止应用

复制代码
docker-compose down

内容回顾

本周我们学习了:

  1. Nginx 容器部署:运行 Nginx、端口映射、静态网站托管

  2. 配置文件挂载:自定义 Nginx 配置实现灵活部署

  3. MySQL 容器:运行 MySQL、环境变量配置、数据库操作

  4. 数据持久化:使用卷挂载确保数据不丢失

  5. PostgreSQL 容器:另一种主流数据库的部署方式

  6. 实战练习:部署完整的 Web 应用(前端 + 后端 + 数据库)

  7. Docker Compose:使用 YAML 文件管理多容器应用

练习作业:

  1. 尝试部署一个 WordPress 博客(提示:需要 WordPress 镜像和 MySQL)

  2. 尝试使用 Docker Compose 部署一个包含 Redis 缓存的应用


命令速查表

操作 命令
运行 Nginx docker run -d -p 8080:80 nginx
挂载目录 docker run -d -p 8080:80 -v /host/path:/container/path nginx
运行 MySQL docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
进入容器 docker exec -it <容器名> /bin/bash
查看日志 docker logs <容器名>
停止容器 docker stop <容器名>
删除容器 docker rm <容器名>
构建镜像 docker build -t <镜像名> .
相关推荐
Full Stack Developme1 小时前
计算机加密与解密的历史
运维·服务器·网络·云计算
Mr -老鬼1 小时前
EasyClick 入门指南:HTTP 网络请求与 API 对接实战
网络·网络协议·http·自动化·#easyclick
Shacoray1 小时前
K8s 中 Ingress 的 HTTPS 证书 如何生成?
容器·https·kubernetes
开发者联盟league1 小时前
使用k8s安装Jenkins
容器·kubernetes·jenkins
IPDEEP全球代理1 小时前
静态住宅ip哪家好?2026年静态住宅ip测评
运维·服务器·网络
liulilittle2 小时前
删除 Inflight Bounds:为什么 KCC 放弃了 BDP 钳位
linux·网络·tcp/ip·计算机网络·信息与通信·tcp·通信
云飞云共享云桌面2 小时前
面向机械研发:双服务器架构搭配云飞云运行 SolidWorks 方案详解
运维·服务器·前端·网络·架构·制造
“码”力全开2 小时前
突破安防黑盒:基于 Docker 与边缘计算的 AI 视频管理平台,实现 GB28181/RTSP 统一接入与全源码交付二次开发架构解析
人工智能·docker·边缘计算
吕工-老船长19982 小时前
20260610----S905Y5(Android14)-----连接WiFi成功后显示网络受限或者开机不会回连
网络