第 18 -19步:Docker Compose 原理
第 18 -19步:Docker Compose 原理

问题场景:

  • 一个 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# 









推荐阅读

docker volume

第一步:创建数据卷 docker volume create my-data 部分含义docker volumeDocker 数据卷管理命令create创建新数据卷my-data数据卷名称(可自定义)作用:在 /var/lib/docker/volumes/my-data/_data 创建一个目录,用来持久化数据。第二步:查看数据卷docker volume ls第三步:查看数据卷详情docker volume inspect my

90 0 赞2026-05-20分享技术分享

24步:常用操作

容器日志问题默认情况下,容器将 stdout/stderr 输出到 json-file 日志驱动,日志文件存在宿主机 /var/lib/docker/containers/&lt;container-id&gt;/&lt;container-id&gt;-json.log。如果不加限制,日志会无限增长,占满磁盘。1. 查看日志 docker logs &lt;容器名&gt; # 打印全部 docker logs --tail 100

58 0 赞2026-05-13分享技术专题

23步:Docker 技能验证测试

第 1 题:基础命令(10分)题目: 拉取 ubuntu:22.04 镜像,并运行一个交互式容器,进入 /tmp 目录,创建一个名为 test.txt 的文件,内容为 hello docker,然后退出容器。请写出完整命令: [root@localhost ~]# docker pull ubuntu:22.04 22.04: Pulling from library/ubuntu f63eb04151bc: Pull compl

101 0 赞2026-05-13分享技术专题

第 22 步:端口映射与网络隔离原理

关键理解:默认 bridge 网络:容器 IP 是 172.17.0.x,宿主机无法直接访问容器端口,必须用&nbsp;-p 8080:80&nbsp;做端口映射自定义 bridge 网络:容器间可以通信,但外界访问仍需端口映射为什么刚才的&nbsp;my-net&nbsp;容器不能从外部访问?因为它们没有&nbsp;-p&nbsp;参数,宿主机不知道如何转发流量第 22 步操作:测试端口映射 + 自定义网络1. 运行一个 Nginx

88 0 赞2026-05-12分享技术专题

第 21 步:Docker 网络基础原理

Docker 默认提供三种网络驱动:网络模式特点适用场景bridge默认网络,容器通过 NAT 访问外网,容器间需端口映射单机、简单场景host容器直接使用宿主机网络栈(无隔离)性能敏感、端口不冲突none无网络完全隔离、特殊安全场景overlay跨多台宿主机通信(Swarm/K8s)集群场景你之前用的默认 bridge 网络的问题:容器间不能直接用容器名通信(只能用 IP)需要手动&nbsp;--link(已废弃)自定义 bridge

81 0 赞2026-05-12分享技术专题

第 20 步:修改代码并热更新(使用 Compose)

在生产开发中,你修改代码后希望快速看到效果,有两种方式:重建镜像:docker compose up -d --build(会重新构建 web 镜像)挂载源码:开发时把代码目录挂载进去,改代码自动生效(无需重建)我们演示第二种(更接近真正的开发体验)。

84 0 赞2026-05-12分享技术专题

第 17 步:上传镜像到 Docker Hub(分享给别人)

前提:&nbsp;在&nbsp;https://hub.docker.com&nbsp;注册一个免费账号(如果还没有)原理:Docker Hub 是公共镜像仓库(类似 GitHub for 镜像)上传命令:docker push 用户名/镜像名:标签上传前需要先&nbsp;docker tag&nbsp;打上正确标签第 17 步操作(如果你有 Docker Hub 账号)1. 登录 Docker Hubbash docker logi

92 0 赞2026-05-12分享技术专题

第 16 步:更新镜像 - 修改代码后重新构建

场景:&nbsp;你修改了代码,需要更新容器。核心原则:&nbsp;不要手动进入容器改文件,而是重新构建镜像&nbsp;→ 删除旧容器 → 运行新容器。第 16 步操作1. 修改 app.py,改变返回内容bash cd ~/my-python-app cat &gt; app.py &lt;&lt; 'EOF' from http.server import HTTPServer, BaseHTTPRequestHandler i

90 0 赞2026-05-12分享技术专题

第 15 步:优化 Dockerfile(减小体积 + 利用缓存)

当前 Dockerfile 的问题:任何代码修改都会导致&nbsp;COPY . /app&nbsp;重新运行,但&nbsp;apt&nbsp;之类的不需要重新安装镜像包含 Python 编译工具链(slim&nbsp;已经较小,但还可以更小)优化技巧:问题优化方法体积大使用&nbsp;alpine&nbsp;基础镜像(约 50MB vs 122MB)依赖变化频繁先&nbsp;COPY requirements.txt&nbsp;安装依

87 0 赞2026-05-12分享技术专题

第 14 步:Dockerfile 基础原理

制作自己的镜像 是 Docker 最核心的技能,也是从“使用者”变成“创造者”的关键一步

59 0 赞2026-05-12分享技术专题
admin

发文31·粉丝0

目录

沉浸

点赞

分享

收藏

文章目录
广告