📚前言
👀回顾,系统学习docker系列已发布内容:
【docker基础】第三课:镜像管理与Dockerfile基础
【docker基础】第五课:Docker网络详解-CSDN博客
【docker基础】第六课:Web应用与数据库容器部署-CSDN博客
🔗相关文档:
【docker基础】Ubuntu 安装 Docker 超详细小白教程
📒本课学习目标:
-
Docker Compose 基础:安装和基本概念
-
YAML 配置文件:语法基础和 docker-compose.yml 结构
-
服务定义与依赖:环境变量、数据卷、网络配置
-
多服务应用部署:前端 + 后端 + 数据库的完整配置
-
服务间通信:容器名称解析和网络连接
-
常用命令:up/down/ps/logs/exec 等
🌍Docker 第七周学习教程:Docker Compose 实战
欢迎来到 Docker 第七周的学习!本周我们将深入学习 Docker Compose,这是 Docker 官方提供的用于定义和运行多容器应用的工具。通过 Docker Compose,您可以用一个 YAML 文件来配置应用的所有服务,然后使用一个命令即可启动所有服务。
第一章:Docker Compose 基础
1.1 什么是 Docker Compose
Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。它允许您通过一个 docker-compose.yml 文件来配置应用所需的所有服务,然后使用单个命令即可启动、停止和管理整个应用栈。
为什么需要 Docker Compose?
在之前的学习中,我们部署一个完整的应用需要:
-
启动 MySQL 容器
-
启动后端 API 容器
-
启动前端 Nginx 容器
-
配置容器之间的网络连接
-
配置数据持久化
这个过程非常繁琐,需要执行多个命令。而 Docker Compose 可以让这一切变得简单!
1.2 Docker Compose 的安装
检查是否已安装
首先检查系统中是否已经安装了 Docker Compose:
docker-compose --version
预期输出:
docker-compose version 1.29.2, build 5becea4c
如果显示版本信息,说明已经安装。如果提示命令不存在,请继续下面的安装步骤。
在 Linux 系统上安装
# 下载 Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 添加执行权限
sudo chmod +x /usr/local/bin/docker-compose
# 创建软链接
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
在 Windows 和 macOS 上安装
Docker Desktop for Windows 和 macOS 已经包含了 Docker Compose,安装 Docker Desktop 后即可使用。
1.3 Docker Compose 的基本工作流程
使用 Docker Compose 的典型流程:
-
创建
docker-compose.yml文件,定义所有服务 -
运行
docker-compose up启动所有服务 -
运行
docker-compose down停止并移除所有服务
第二章:YAML 配置文件基础
2.1 什么是 YAML
YAML 是一种人类可读的数据序列化格式,常用于配置文件。它使用缩进(通常是 2 个空格)来表示层级结构。
YAML 的基本语法规则:
-
使用缩进表示层级(不要使用 Tab 键)
-
键值对使用冒号
:分隔,冒号后必须有空格 -
列表使用短横线
-开头 -
字符串不需要引号(除非包含特殊字符)
2.2 YAML 示例
# 这是注释
# 键值对
name: myapp
version: "1.0"
debug: false
# 列表
services:
- web
- db
- redis
# 嵌套结构
database:
host: localhost
port: 3306
credentials:
username: admin
password: secret
2.3 docker-compose.yml 文件结构
docker-compose.yml 文件的基本结构如下:
# 指定 Compose 文件格式版本
version: '3.8'
# 定义所有服务
services:
# 服务名称(自定义)
service_name:
# 使用的镜像
image: image_name:tag
# 或者构建本地 Dockerfile
build: ./path/to/dockerfile
# 端口映射
ports:
- "host_port:container_port"
# 环境变量
environment:
- ENV_VAR=value
# 数据卷挂载
volumes:
- host_path:container_path
# 依赖关系
depends_on:
- another_service
第三章:服务定义与依赖
3.1 服务配置详解
让我们通过一个完整的示例来理解服务配置:
version: '3.8'
services:
# 定义 MySQL 服务
db:
# 使用 MySQL 5.7 镜像
image: mysql:5.7
# 容器名称
container_name: mymysql
# 环境变量
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: myappdb
# 数据卷挂载(持久化)
volumes:
- ./mysql/data:/var/lib/mysql
# 端口映射
ports:
- "3306:3306"
# 重启策略
restart: always
配置项说明:
| 配置项 | 说明 | 示例 |
|---|---|---|
image |
使用的 Docker 镜像 | mysql:5.7 |
container_name |
容器名称 | mymysql |
environment |
环境变量 | MYSQL_ROOT_PASSWORD: 123456 |
volumes |
数据卷挂载 | ./mysql/data:/var/lib/mysql |
ports |
端口映射 | "3306:3306" |
restart |
重启策略 | always(总是重启) |
3.2 环境变量配置
环境变量有三种配置方式:
方式一:直接键值对
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: myappdb
方式二:列表形式
environment:
- MYSQL_ROOT_PASSWORD=123456
- MYSQL_DATABASE=myappdb
方式三:从文件加载
env_file:
- .env
.env 文件内容:
MYSQL_ROOT_PASSWORD=123456
MYSQL_DATABASE=myappdb
3.3 数据卷挂载
数据卷挂载用于实现数据持久化,有两种方式:
方式一:绑定挂载(主机目录)
volumes:
- ./mysql/data:/var/lib/mysql
- ./frontend:/usr/share/nginx/html
方式二:命名卷
volumes:
db_data:
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
区别:
-
绑定挂载:将主机的具体目录挂载到容器
-
命名卷:由 Docker 管理,存储在 Docker 指定的目录
3.4 依赖关系配置
使用 depends_on 可以指定服务的启动顺序:
services:
backend:
build: ./backend
depends_on:
- db # 等待 db 服务启动后再启动
注意: depends_on 只会等待容器启动,不会等待服务完全就绪(如数据库启动完成)。
第四章:多服务应用部署实战
4.1 项目架构
我们将部署一个完整的 Web 应用:
-
前端: Nginx 托管静态页面
-
后端: Python Flask API
-
数据库: MySQL
4.2 创建项目结构
mkdir -p ~/compose-demo/{backend,frontend}
cd ~/compose-demo
4.3 创建后端 API (Flask)
创建 backend/app.py:
from flask import Flask, jsonify, request
from flask_cors import CORS
import pymysql
import os
app = Flask(__name__)
CORS(app)
# 从环境变量获取数据库配置
db_host = os.environ.get('DB_HOST', 'localhost')
db_user = os.environ.get('DB_USER', 'root')
db_password = os.environ.get('DB_PASSWORD', '123456')
db_name = os.environ.get('DB_NAME', 'myappdb')
# 数据库连接
db = pymysql.connect(
host=db_host,
user=db_user,
password=db_password,
database=db_name
)
@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
创建 backend/Dockerfile:
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"]
4.4 创建前端页面
创建 frontend/index.html:
<!DOCTYPE html>
<html>
<head>
<title>用户管理系统 - Compose 版</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
h1 {
color: #2c3e50;
text-align: center;
}
.form-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
input {
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;
}
.user-card {
background: white;
padding: 15px;
margin: 10px 0;
border-radius: 8px;
box-shadow: 0 1px 5px rgba(0,0,0,0.05);
border-left: 4px solid #3498db;
}
</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 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>
4.5 创建 docker-compose.yml
创建 docker-compose.yml 文件:
# 指定 Compose 文件版本
version: '3.8'
# 定义服务
services:
# MySQL 数据库服务
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"
restart: always
# Flask 后端服务
backend:
build: ./backend
container_name: mybackend
ports:
- "5000:5000"
depends_on:
- db
environment:
- DB_HOST=db
- DB_USER=root
- DB_PASSWORD=123456
- DB_NAME=myappdb
restart: always
# Nginx 前端服务
frontend:
image: nginx:latest
container_name: myfrontend
ports:
- "80:80"
volumes:
- ./frontend:/usr/share/nginx/html
restart: always
# 定义命名卷
volumes:
mysql_data:
4.6 启动应用
在项目目录下执行:
docker-compose up -d
命令解释:
-
docker-compose up: 启动所有服务 -
-d: 后台运行(detached 模式)
预期输出:
Creating network "compose-demo_default" with the default driver
Creating volume "compose-demo_mysql_data" with default driver
Building backend
Step 1/6 : FROM python:3.9-slim
...
Successfully built abc123def456
Successfully tagged compose-demo_backend:latest
Creating mymysql ... done
Creating mybackend ... done
Creating myfrontend ... done
4.7 验证服务
查看正在运行的容器:
docker-compose ps
预期输出:
Name Command State Ports
-----------------------------------------------------------------------------------
mybackend python app.py Up 0.0.0.0:5000->5000/tcp
myfrontend /docker-entrypoint.sh ngin ... Up 0.0.0.0:80->80/tcp
mymysql docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp
打开浏览器访问 http://localhost,您会看到用户管理系统页面!
第五章:服务间通信
5.1 容器名称解析
Docker Compose 会自动创建一个专用网络,所有服务都连接到这个网络上。在这个网络中,容器可以通过服务名称进行通信。
例如,在我们的应用中:
-
backend服务可以通过db访问 MySQL 数据库 -
frontend服务可以通过backend或localhost:5000访问后端 API
5.2 网络配置
Docker Compose 默认会创建一个名为 项目名_default 的网络。您也可以自定义网络:
version: '3.8'
networks:
mynetwork:
driver: bridge
services:
db:
image: mysql:5.7
networks:
- mynetwork
backend:
build: ./backend
networks:
- mynetwork
5.3 服务间通信示例
在后端代码中,我们使用 db 作为数据库主机名:
db = pymysql.connect(
host='db', # 服务名称作为主机名
user='root',
password='123456',
database='myappdb'
)
这是因为 Docker Compose 会在内部 DNS 中注册服务名称,使得容器之间可以通过服务名互相访问。
第六章:Docker Compose 常用命令
6.1 启动服务
# 前台启动(会显示所有日志)
docker-compose up
# 后台启动
docker-compose up -d
# 启动指定服务
docker-compose up -d backend
6.2 查看服务状态
docker-compose ps
输出示例:
Name Command State Ports
-----------------------------------------------------------------------------------
mybackend python app.py Up 0.0.0.0:5000->5000/tcp
myfrontend /docker-entrypoint.sh ngin ... Up 0.0.0.0:80->80/tcp
mymysql docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp
6.3 查看日志
# 查看所有服务日志
docker-compose logs
# 查看指定服务日志
docker-compose logs backend
# 实时查看日志(类似 tail -f)
docker-compose logs -f backend
6.4 进入容器
docker-compose exec <服务名> <命令>
# 进入 MySQL 容器
docker-compose exec db mysql -uroot -p123456
# 进入后端容器
docker-compose exec backend /bin/bash
6.5 停止服务
# 停止服务但保留容器
docker-compose stop
# 停止并移除容器(但保留数据卷)
docker-compose down
# 停止并移除容器和数据卷
docker-compose down -v
6.6 构建镜像
# 构建所有服务的镜像
docker-compose build
# 构建指定服务的镜像
docker-compose build backend
# 强制重新构建
docker-compose build --no-cache backend
6.7 重启服务
# 重启所有服务
docker-compose restart
# 重启指定服务
docker-compose restart backend
6.8 查看服务配置
# 查看服务的运行配置
docker-compose config
# 验证配置文件是否正确
docker-compose config -q
第七章:实战练习 - 部署带 Redis 的应用
📌Redis (Remote Dictionary Server) 是一个开源的 内存数据结构存储系统 ,可以用作数据库、缓存、消息代理和队列:
数据主要存储在 内存 中,读写速度极快
支持数据持久化到磁盘(可选)
7.1 添加 Redis 服务
修改 docker-compose.yml,添加 Redis 服务:
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"
restart: always
redis:
image: redis:6
container_name: myredis
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: always
backend:
build: ./backend
container_name: mybackend
ports:
- "5000:5000"
depends_on:
- db
- redis
environment:
- DB_HOST=db
- DB_USER=root
- DB_PASSWORD=123456
- DB_NAME=myappdb
- REDIS_HOST=redis
restart: always
frontend:
image: nginx:latest
container_name: myfrontend
ports:
- "80:80"
volumes:
- ./frontend:/usr/share/nginx/html
restart: always
volumes:
mysql_data:
redis_data:
7.2 更新后端代码使用 Redis
更新 backend/app.py:
from flask import Flask, jsonify, request
from flask_cors import CORS
import pymysql
import redis
import os
app = Flask(__name__)
CORS(app)
# 数据库配置
db_host = os.environ.get('DB_HOST', 'localhost')
db_user = os.environ.get('DB_USER', 'root')
db_password = os.environ.get('DB_PASSWORD', '123456')
db_name = os.environ.get('DB_NAME', 'myappdb')
# Redis 配置
redis_host = os.environ.get('REDIS_HOST', 'localhost')
# 数据库连接
db = pymysql.connect(host=db_host, user=db_user, password=db_password, database=db_name)
# Redis 连接
r = redis.Redis(host=redis_host, port=6379, decode_responses=True)
@app.route('/api/users', methods=['GET'])
def get_users():
# 先从 Redis 缓存获取
cache_key = 'users_cache'
cached_users = r.get(cache_key)
if cached_users:
import json
return jsonify(json.loads(cached_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])
})
# 存入 Redis 缓存,有效期 60 秒
r.setex(cache_key, 60, str(result))
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()
# 清除缓存
r.delete('users_cache')
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
redis==4.3.4
7.3 重新启动服务
# 停止旧服务
docker-compose down
# 重新构建并启动
docker-compose up -d --build
7.4 验证 Redis 缓存
# 进入 Redis 容器
docker-compose exec redis redis-cli
# 查看缓存键
KEYS *
# 获取缓存内容
GET users_cache
第八章:常见问题与解决方案
8.1 服务启动顺序问题
问题: 后端服务启动时,数据库可能还未就绪,导致连接失败。
解决方案: 使用 depends_on 配合健康检查:
services:
db:
image: mysql:5.7
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p123456"]
interval: 5s
timeout: 5s
retries: 5
backend:
build: ./backend
depends_on:
db:
condition: service_healthy
8.2 端口冲突问题
问题: 端口已被占用。
解决方案: 修改 docker-compose.yml 中的端口映射:
services:
frontend:
image: nginx
ports:
- "8080:80" # 将 8080 改为其他未被占用的端口
8.3 数据卷权限问题
问题: 挂载的目录权限不足。
解决方案: 在 Dockerfile 中设置正确的用户权限,或使用命名卷。
8.4 配置文件热更新
问题: 修改配置后需要重启容器。
解决方案: 对于开发环境,可以使用绑定挂载实现热更新:
services:
backend:
build: ./backend
volumes:
- ./backend:/app # 挂载代码目录
本周总结
本周我们学习了:
-
Docker Compose 基础:安装和基本概念
-
YAML 配置文件:语法基础和 docker-compose.yml 结构
-
服务定义与依赖:环境变量、数据卷、网络配置
-
多服务应用部署:前端 + 后端 + 数据库的完整配置
-
服务间通信:容器名称解析和网络连接
-
常用命令:up/down/ps/logs/exec 等
练习作业:
-
使用 Docker Compose 部署一个 WordPress 博客(提示:需要 WordPress 镜像和 MySQL)
-
尝试添加 Redis 缓存到您的应用中
-
学习使用 Docker Compose 的健康检查功能
命令速查表
| 操作 | 命令 |
|---|---|
| 启动服务 | docker-compose up -d |
| 停止服务 | docker-compose down |
| 查看状态 | docker-compose ps |
| 查看日志 | docker-compose logs -f <服务名> |
| 进入容器 | docker-compose exec <服务名> /bin/bash |
| 构建镜像 | docker-compose build |
| 重启服务 | docker-compose restart |
| 验证配置 | docker-compose config -q |