哈喽,大家好!今天我们来聊一个能极大提升开发效率的工具——Docker Compose。
如果你已经在使用 Docker,肯定感受过它带来的便利。但是,当你的应用变得复杂,需要同时运行后端、数据库、缓存、Web 服务器等多个容器时,你会发现,手动管理这些容器简直是一场噩梦。你需要敲一长串的 docker run 命令,还要手动处理容器间的网络连接、数据存储,想想都头大。
别担心,Docker Compose 就是来拯救我们的!它是一个用来定义和运行多容器 Docker 应用的工具。你只需要一个 YAML 配置文件,就能把整个应用的所有服务、网络、数据卷都安排得明明白白,然后用一条命令,就能让它们全部启动或停止。
听起来是不是很酷?这篇文章会带你从头到尾,通过一个实战案例,彻底搞懂 Docker Compose。
为什么你需要 Docker Compose?
在开始之前,我们先明确一下,费心学这个东西到底值不值。答案是:绝对值!
- 一键启动,一键关闭:告别繁琐的
docker run命令。用docker compose up就能启动所有服务,docker compose down则能干净利落地关闭并移除所有相关资源。 - 配置集中化,清晰明了:所有的服务配置都写在一个
docker-compose.yml文件里,整个应用的架构一目了然,维护起来超级方便。 - 环境一致性:无论是你的电脑,还是同事的电脑,或是测试服务器,只要有 Docker 和 Docker Compose,就能保证应用运行环境的高度一致,再也不会出现“在我电脑上明明是好的”这种尴尬情况了。
- 服务间通信简单:Compose 会自动创建一个默认的网络,让你的所有服务都在这个网络里,它们之间可以用服务名直接通信,非常方便。
核心概念:docker-compose.yml 文件
docker-compose.yml 是 Compose 的灵魂。它是一个 YAML 格式的文件,你在这里描述你的应用由哪些部分组成。 它主要包含几个顶级配置项:
-
version:指定 Compose 文件的版本。虽然新版本已经不强制要求写这个了,但写上总是个好习惯。 -
services:定义应用中的各个服务(也就是容器)。这是最核心的部分。 -
volumes:定义数据卷,用于数据的持久化。 -
networks:定义网络,用于容器间的通信。
实战案例:搭建一个带 Redis 计数器的 Web 应用
理论说再多,不如上手练一练。我们来搭建一个简单的项目,它包含三个服务:
- **
webapp**:一个用 Python Flask 编写的后端服务,它会连接 Redis 并显示一个页面访问计数器。 - **
redis**:一个 Redis 缓存服务,用来存储计数值。 - **
nginx**:一个 Nginx 反向代理,作为 Web 流量的入口。
第一步:项目结构
首先,我们先创建好项目的文件结构:
docker-compose-demo/
├── docker-compose.yml
├── webapp/
│ ├── Dockerfile
│ ├── app.py
│ └── requirements.txt
└── nginx/
└── nginx.conf
第二步:编写应用代码
1. Python 应用 (webapp/)
requirements.txt
flask
redis
app.py
这是一个非常简单的 Flask 应用,它会连接到名为 redis 的服务,每次访问页面时,计数器加一。
import time
import redis
from flask import Flask
app = Flask(__name__)
# 注意这里的主机名是 'redis',也就是我们在 docker-compose.yml 中定义的服务名
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
# cache.incr('hits') 会返回增加后的值
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return f'Hello from Docker! I have been seen {count} times.\n'
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
Dockerfile
这个 Dockerfile 负责将我们的 Flask 应用打包成一个镜像。
# 使用官方的 Python 镜像
FROM python:3.9-slim
# 设置工作目录
WORKDIR /app
# 复制依赖文件并安装
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 暴露端口并启动应用
CMD ["python", "app.py"]
2. Nginx 配置 (nginx/)
nginx.conf
这个配置文件让 Nginx 监听 80 端口,并将所有请求转发给我们刚才写的 webapp 服务的 5000 端口。
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://webapp:5000; # 同样,这里使用服务名 'webapp'
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
第三步:编写 docker-compose.yml
好了,所有准备工作都已就绪,现在开始编写最关键的 docker-compose.yml 文件。
version: '3.8'
services:
# Python 后端服务
webapp:
build: ./webapp # 指定 Dockerfile 的位置
container_name: webapp_service
restart: always # 容器退出时总是重启
environment:
- FLASK_ENV=development
expose:
- "5000" # 只在内部网络暴露端口,不映射到主机
networks:
- app-network
# Redis 服务
redis:
image: "redis:alpine" # 直接使用官方镜像
container_name: redis_cache
restart: always
volumes:
- redis-data:/data # 挂载一个命名卷,用于数据持久化
networks:
- app-network
# Nginx 服务
nginx:
image: "nginx:stable-alpine"
container_name: nginx_proxy
restart: always
ports:
- "8080:80" # 将主机的 8080 端口映射到容器的 80 端口
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf # 挂载 Nginx 配置文件
depends_on:
- webapp # 确保 webapp 服务先于 Nginx 启动
networks:
- app-network
# 统一管理数据卷
volumes:
redis-data:
# 统一管理网络
networks:
app-network:
代码解析
让我们来逐段解析一下这个文件:
services:webapp:-
build: ./webapp: 指示 Compose 去webapp目录下寻找Dockerfile来构建镜像。 -
restart: always: 这是一个很好的生产实践,确保服务在意外崩溃后能自动重启。 -
expose:expose用于在内部网络中暴露端口,这样 Nginx 就可以通过http://webapp:5000访问它,但我们不能从主机直接访问。
-
redis:-
image: "redis:alpine": 直接从 Docker Hub 拉取官方的 Redis 镜像。 -
volumes: - redis-data:/data: 这是数据持久化的关键。我们将一个名为redis-data的命名卷挂载到 Redis 容器存放数据的/data目录。这样即使容器被删除,我们的访问计数也不会丢失。
-
nginx:-
ports: - "8080:80": 将我们电脑的8080端口映射到 Nginx 容器的80端口。这样我们就可以通过访问http://localhost:8080来看到网页了。 -
volumes: - ./nginx/nginx.conf:/...: 将本地的 Nginx 配置文件挂载到容器内,覆盖默认配置。 -
depends_on: - webapp: 定义服务间的启动顺序。这确保了webapp服务会比nginx服务先启动。
-
volumes:-
redis-data:: 在这里声明我们上面用到的命名卷。这是一种最佳实践。
-
networks:-
app-network:: 定义一个自定义的桥接网络。让所有服务都连接到同一个网络可以提供更好的隔离和组织。
-
运行与管理你的应用
现在,激动人心的时刻到了!在项目根目录(也就是 docker-compose.yml 所在的目录)下,打开你的终端。
启动应用
docker compose up -d
-
up: 这个命令会构建镜像、创建并启动容器、网络和数据卷。 -
-d:detach模式,让容器在后台运行。
命令执行后,你可以看到 Compose 会依次拉取镜像、构建 webapp 镜像,然后启动所有容器。
现在,打开浏览器访问 http://localhost:8080,你应该能看到 “Hello from Docker! I have been seen 1 times.”。刷新页面,数字会不断增加!
查看服务状态
想看看你的服务们现在怎么样了?
docker compose ps
这个命令会列出当前 Compose 项目中的所有容器及其状态。
查看日志
某个服务出问题了?查看日志是排查问题的第一步。
# 查看所有服务的日志
docker compose logs
# 只看特定服务的日志,并持续跟踪
docker compose logs -f webapp
停止并移除应用
当你不想玩了,可以用下面的命令来停止并移除所有相关的容器、网络。
docker compose down
如果你也想把 Redis 的数据卷一并删除(注意:数据会丢失!),可以加上 -v 参数:
docker compose down -v
其他常用命令
-
docker compose stop: 仅停止容器,但不移除。 -
docker compose start: 启动已停止的容器。 -
docker compose restart: 重启所有服务。 -
docker compose exec webapp /bin/sh: 进入webapp容器的命令行,方便进行调试。
总结
通过这个简单的例子,你应该已经感受到了 Docker Compose 的强大之处。它将复杂的多容器部署流程简化为一份配置文件和几个简单的命令,让你能更专注于代码本身,而不是繁琐的环境配置。
这只是一个入门教程,Docker Compose 还有很多高级功能,比如使用 .env 文件管理环境变量、多阶段构建、服务伸缩等,等待你去探索。
希望这篇文章能帮你敲开 Docker Compose 的大门,快去用它来改造你的项目吧!