问题场景:
一个 Web 应用通常需要:Nginx(前端)+ Python/Node(后端)+ MySQL/Redis(数据库)
手动启动 3 个容器,还要配置网络、卷、依赖顺序 → 繁琐且容易出错
解决方案:docker-compose.yml
一个 YAML 文件定义所有容器的配置(镜像、端口、卷、网络、依赖)
一条命令
docker compose up -d启动全部一条命令
docker compose down停止全部
核心概念:
| 概念 | 作用 |
|---|---|
services | 定义每个容器(相当于 docker run) |
volumes | 定义持久化卷(独立于容器生命周期) |
networks | 定义自定义网络(容器间通过服务名通信) |
depends_on | 定义启动顺序(例如 db 先于 app) |
第 18 步操作:做一个完整的 Web 应用
我们搭建一个 Python Web + Redis 计数器 的应用:
用户每次访问,计数器 +1
Redis 存储计数
使用 Docker Compose 一键启动
1. 创建项目目录
cd ~
mkdir compose-demo
cd compose-demo
2. 创建 Python 应用(连接 Redis)
root@docker-test:~/compose-demo# cat > app.py << 'EOF'
> from flask import Flask
> from redis import Redis
> import os
>
> app = Flask(__name__)
> redis = Redis(host='redis', port=6379) # 'redis' 是服务名,Compose 自动做 DNS
>
> @app.route('/')
> def hello():
> count = redis.incr('hits')
> return f'<h1>Hello! You are visitor number {count}.</h1>'
>
> if __name__ == '__main__':
> app.run(host='0.0.0.0', port=5000)
> EOF
3. 创建 requirements.txt
root@docker-test:~/compose-demo# cat > requirements.txt << 'EOF'
> flask
> redis
> EOF
4. 创建 Dockerfile
root@docker-test:~/compose-demo# cat > Dockerfile << 'EOF'
> FROM python:3.9-alpine
> WORKDIR /app
> COPY requirements.txt .
> RUN pip install --no-cache-dir -r requirements.txt
> COPY app.py .
> CMD ["python", "app.py"]
> EOF
5. 创建 docker-compose.yml(核心文件)
root@docker-test:~/compose-demo# cat docker-compose.yml
services:
web:
build: . # 使用当前目录的 Dockerfile 构建镜像
ports:
- "5000:5000" # 宿主机:容器
depends_on:
- redis # 确保 redis 先启动
networks:
- my-network
redis:
image: redis:alpine # 使用官方 Redis 镜像
networks:
- my-network
volumes:
- redis-data:/data # 持久化 Redis 数据
networks:
my-network: # 自定义网络,容器间通过服务名通信
volumes:
redis-data: # 持久化卷
root@docker-test:~/compose-demo#
6. 启动整套应用
root@docker-test:~/compose-demo# docker compose up -d
[+] Running 8/8
✔ redis Pulled 8.0s
✔ 6a0ac1617861 Already exists 0.0s
✔ 9fba6c0845a7 Pull complete 1.5s
✔ f668659e960c Pull complete 2.0s
✔ 2156d2090f3d Pull complete 6.7s
✔ df16ec49200a Pull complete 6.7s
✔ 4f4fb700ef54 Pull complete 6.7s
✔ 111abd5c7b8c Pull complete 6.8s
Compose can now delegate builds to bake for better performance.
To do so, set COMPOSE_BAKE=true.
[+] Building 20.5s (11/11) FINISHED docker:default
=> [web internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 189B 0.0s
=> [web internal] load metadata for docker.io/library/python:3.9-alpine 0.9s
=> [web internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [web 1/5] FROM docker.io/library/python:3.9-alpine@sha256:c99b6eb43b3ac4d750db3d6e8b22268d5e 0.0s
=> [web internal] load build context 0.0s
=> => transferring context: 450B 0.0s
=> CACHED [web 2/5] WORKDIR /app 0.0s
=> [web 3/5] COPY requirements.txt . 0.0s
=> [web 4/5] RUN pip install --no-cache-dir -r requirements.txt 19.2s
=> [web 5/5] COPY app.py . 0.0s
=> [web] exporting to image 0.2s
=> => exporting layers 0.2s
=> => writing image sha256:2a1cd8747627ceea90024ca38c4bd4b348b98153e62b3d19e2722e4326b1e2f1 0.0s
=> => naming to docker.io/library/compose-demo-web 0.0s
=> [web] resolving provenance for metadata file 0.0s
[+] Running 5/5
✔ web Built 0.0s
✔ Network compose-demo_my-network Created 0.1s
✔ Volume "compose-demo_redis-data" Created 0.0s
✔ Container compose-demo-redis-1 Started 0.2s
✔ Container compose-demo-web-1 Started 0.4s
root@docker-test:~/compose-demo#
7. 查看运行状态
root@docker-test:~/compose-demo# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
compose-demo-redis-1 redis:alpine "docker-entrypoint.s…" redis 26 seconds ago Up 25 seconds 6379/tcp
compose-demo-web-1 compose-demo-web "python app.py" web 26 seconds ago Up 25 seconds 0.0.0.0:5000->5000/tcp
8. 测试访问
root@docker-test:~/compose-demo# curl http://localhost:5000
<h1>Hello! You are visitor number 1.</h1>root@docker-test:~/compose-demo#
9. 查看日志
root@docker-test:~/compose-demo# docker compose logs web
web-1 | * Serving Flask app 'app'
web-1 | * Debug mode: off
web-1 | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
web-1 | * Running on all addresses (0.0.0.0)
web-1 | * Running on http://127.0.0.1:5000
web-1 | * Running on http://172.19.0.3:5000
web-1 | Press CTRL+C to quit
web-1 | 172.19.0.1 - - [12/May/2026 08:49:37] "GET / HTTP/1.1" 200 -
root@docker-test:~/compose-demo#
10. 停止并清理
docker compose down -v # -v 会删除卷(生产环境慎用)
第 19 步:测试联动与常用 Compose 命令
现在我们来多次访问,验证 Redis 计数器是否持久化。
1. 多次访问测试
root@docker-test:~/compose-demo# curl http://localhost:5000
<h1>Hello! You are visitor number 2.</h1>root@docker-test:~/compose-demo# curl http://localhost:5000
<h1>Hello! You are visitor number 3.</h1>root@docker-test:~/compose-demo#
2. 查看服务状态
docker compose ps
3. 查看 Redis 容器的日志
docker compose logs redis
4. 进入 web 容器查看环境
docker compose exec web sh
# 进入后执行
env | grep REDIS
exit
5. 停止服务(保留容器和卷)
docker compose stop
6. 再次启动(计数应该还在)
docker compose start
curl http://localhost:5000 # 计数会继续增加,不是从 1 开始
7. 完全停止并删除所有资源(包括卷)
docker compose down -v
演示如下
root@docker-test:~/compose-demo# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
compose-demo-redis-1 redis:alpine "docker-entrypoint.s…" redis 7 minutes ago Up 7 minutes 6379/tcp
compose-demo-web-1 compose-demo-web "python app.py" web 7 minutes ago Up 7 minutes 0.0.0.0:5000->5000/tcp
root@docker-test:~/compose-demo# docker compose logs redis
redis-1 | Starting Redis Server
redis-1 | 1:C 12 May 2026 08:49:03.946 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis-1 | 1:C 12 May 2026 08:49:03.946 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis-1 | 1:C 12 May 2026 08:49:03.946 * Redis version=8.6.3, bits=64, commit=00000000, modified=1, pid=1, just started
redis-1 | 1:C 12 May 2026 08:49:03.946 * Configuration loaded
redis-1 | 1:M 12 May 2026 08:49:03.947 * monotonic clock: POSIX clock_gettime
redis-1 | 1:M 12 May 2026 08:49:03.948 * Running mode=standalone, port=6379.
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> RedisBloom version 8.6.2 (Git=unknown)
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> Registering configuration options: [
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> { bf-error-rate : 0.01 }
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> { bf-initial-size : 100 }
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> { bf-expansion-factor : 2 }
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> { cf-bucket-size : 2 }
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> { cf-initial-size : 1024 }
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> { cf-max-iterations : 20 }
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> { cf-expansion-factor : 1 }
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> { cf-max-expansions : 32 }
redis-1 | 1:M 12 May 2026 08:49:03.948 * <bf> ]
redis-1 | 1:M 12 May 2026 08:49:03.948 * Module 'bf' loaded from /usr/local/lib/redis/modules//redisbloom.so
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> Redis version found by RedisSearch : 8.6.3 - oss
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> RediSearch version 8.6.7 (Git=3020486)
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> Low level api version 1 initialized successfully
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> gc: ON, prefix min length: 2, min word length to stem: 4, prefix max expansions: 200, query timeout (ms): 500, timeout policy: return, oom policy: return, cursor read size: 1000, cursor max idle (ms): 300000, max doctable size: 1000000, max number of search results: 1000000, default scorer: BM25STD,
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> Initialized thread pools!
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> Disabled workers threadpool of size 0
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> Subscribe to config changes
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> Subscribe to cluster slot migration events
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> Enabled role change notification
redis-1 | 1:M 12 May 2026 08:49:03.952 * <search> Cluster configuration: AUTO partitions, type: 0, coordinator timeout: 0ms
redis-1 | 1:M 12 May 2026 08:49:03.953 * Module 'search' loaded from /usr/local/lib/redis/modules//redisearch.so
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> RedisTimeSeries version 80602, git_sha=5d7c61c9f861b5cb83989463595c2c9f6b2bfe63
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> Redis version found by RedisTimeSeries : 8.6.3 - oss
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> Registering configuration options: [
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> { ts-compaction-policy : }
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> { ts-num-threads : 3 }
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> { ts-retention-policy : 0 }
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> { ts-duplicate-policy : block }
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> { ts-chunk-size-bytes : 4096 }
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> { ts-encoding : compressed }
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> { ts-ignore-max-time-diff: 0 }
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> { ts-ignore-max-val-diff : 0.000000 }
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> ]
redis-1 | 1:M 12 May 2026 08:49:03.953 * <timeseries> Detected redis oss
redis-1 | 1:M 12 May 2026 08:49:03.954 * <timeseries> Subscribe to ASM events
redis-1 | 1:M 12 May 2026 08:49:03.954 * <timeseries> Enabled diskless replication
redis-1 | 1:M 12 May 2026 08:49:03.954 * Module 'timeseries' loaded from /usr/local/lib/redis/modules//redistimeseries.so
redis-1 | 1:M 12 May 2026 08:49:03.954 * <ReJSON> Created new data type 'ReJSON-RL'
redis-1 | 1:M 12 May 2026 08:49:03.955 * <ReJSON> version: 80600 git sha: unknown branch: unknown
redis-1 | 1:M 12 May 2026 08:49:03.955 * <ReJSON> Exported RedisJSON_V1 API
redis-1 | 1:M 12 May 2026 08:49:03.955 * <ReJSON> Exported RedisJSON_V2 API
redis-1 | 1:M 12 May 2026 08:49:03.955 * <ReJSON> Exported RedisJSON_V3 API
redis-1 | 1:M 12 May 2026 08:49:03.955 * <ReJSON> Exported RedisJSON_V4 API
redis-1 | 1:M 12 May 2026 08:49:03.955 * <ReJSON> Exported RedisJSON_V5 API
redis-1 | 1:M 12 May 2026 08:49:03.955 * <ReJSON> Exported RedisJSON_V6 API
redis-1 | 1:M 12 May 2026 08:49:03.955 * <ReJSON> Enabled diskless replication
redis-1 | 1:M 12 May 2026 08:49:03.955 * <ReJSON> Initialized shared string cache, thread safe: true.
redis-1 | 1:M 12 May 2026 08:49:03.955 * Module 'ReJSON' loaded from /usr/local/lib/redis/modules//rejson.so
redis-1 | 1:M 12 May 2026 08:49:03.955 * <search> Acquired RedisJSON_V6 API
redis-1 | 1:M 12 May 2026 08:49:03.956 * Server initialized
redis-1 | 1:M 12 May 2026 08:49:03.956 * Ready to accept connections tcp
redis-1 | 1:M 12 May 2026 08:49:03.956 # WARNING: Redis does not require authentication and is not protected by network restrictions. Redis will accept connections from any IP address on any network interface.
root@docker-test:~/compose-demo# docker compose exec web sh
/app # env | grep REDIS
/app # exit
root@docker-test:~/compose-demo# docker compose stop
[+] Stopping 2/2
✔ Container compose-demo-web-1 Stopped 10.2s
✔ Container compose-demo-redis-1 Stopped 0.2s
root@docker-test:~/compose-demo# docker compose start
[+] Running 2/2
✔ Container compose-demo-redis-1 Started 0.2s
✔ Container compose-demo-web-1 Started 0.2s
root@docker-test:~/compose-demo# curl http://localhost:5000
<h1>Hello! You are visitor number 4.</h1>root@docker-test:~/compose-demo#
