docker-compose实验案例之docker容器化部署下 Flask+Redis 访问计数功能的实现与调优
实验背景及其灵感:
随着云计算和微服务架构的普及,应用的部署和运维模式发生了根本性变化。传统的 "单机部署" 模式难以满足微服务多组件、多环境一致性的需求,而 Docker 容器技术通过 "打包应用及其依赖" 实现了环境隔离与快速交付,Docker-Compose 则进一步解决了多容器服务的编排、依赖管理和网络配置问题,成为中小型微服务项目快速落地的核心工具。
在 Web 应用开发中,"访问次数统计" 是典型的轻量级业务场景,既涉及 Web 服务的对外暴露,也涉及缓存服务的数据持久化与原子操作。选择 Flask(轻量级 Python Web 框架)+ Redis(高性能缓存数据库)的组合,能以极简的代码实现核心功能;同时,该场景天然适配容器化部署的需求 ------Web 服务和缓存服务需独立运行、相互依赖,且要求环境一致性(避免 "本地运行正常、部署出错" 的问题),成为容器编排入门的经典实践场景;
接下来我将通过 flask 接合 redis 完成访问次统计的功能的实验:
1.实验环境搭建
1.创建目录,并编写python应用程序
[root@localhost ~]# mkdir myapp
[root@localhost ~]# cd myapp/
[root@localhost myapp]# vim app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
# 连接Redis服务:host为"redis"(对应docker-compose中redis服务名,容器间可通过服务名访问),端口6379
def get_hit_count(): # 设置重试次数最多为5,避免Redis启动延迟导致连接失败
retries = 5
while True:
try:
return cache.incr('hits') # 原子操作:给Redis中"hits"键的值+1,返回最新值(实现计数)
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5) # 重试逻辑:剩余重试次数>0时,等待0.5秒后重试;次数为0时抛出异常
@app.route('/') # 定义根路由(访问/路径时触发)
def hello():
count = get_hit_count() # 获取当前访问次数
return 'Hello World! I have been seen {} times.\n'.format(count) # 返回带计数的响应内容
# 启动Flask服务:监听所有网卡(0.0.0.0),开启调试模式
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
#关键逻辑:Redis 连接的host='redis'是核心:docker-compose会为编排的服务创建专属网络,容器间可通过服务名(如redis)直接访问,无需手动配置 IP;
重试逻辑:解决depends_on仅保证启动顺序、不保证 Redis 服务就绪的问题(Redis 启动可能慢于 Web 服务,首次连接会失败,重试可规避);
cache.incr('hits'):Redis 原子递增操作,避免并发访问时计数错误。
2.安装程序并生成依赖库文件;
[root@localhost myapp]# pip install redis
Collecting redis
Downloading redis-7.0.1-py3-none-any.whl (339 kB)
|████████████████████████████████| 339 kB 400 kB/s
Collecting async-timeout>=4.0.3
Downloading async_timeout-5.0.1-py3-none-any.whl (6.2 kB)
Installing collected packages: async-timeout, redis
Successfully installed async-timeout-5.0.1 redis-7.0.1
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
[root@localhost myapp]# pip freeze > requirements.txt
# 查看所有的依赖库
[root@localhost myapp]# cat requirements.txt
blinker==1.9.0
click==8.3.1
Flask==3.1.2
itsdangerous==2.2.0
Jinja2==3.1.6
MarkupSafe==3.0.3
redis==7.1.0
Werkzeug==3.1.5
2.镜像构建
2.1编写dockerfile
[root@localhost myapp]# vim Dockerfile
文件内容如下:
FROM python:3.14-alpine
WORKDIR /code
COPY . .
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]
#关键逻辑:选择alpine版本基础镜像:大幅降低镜像体积;设置容器内工作目录(后续命令均在该目录执行);COPY . .:将宿主机myapp目录下的app.py、requirements.txt等文件复制到容器内;安装依赖:--no-cache-dir 不缓存安装包,减少镜像体积;-r 读取requirements.txt的依赖清单;声明容器暴露的端口5000;容器启动命令:执行app.py,启动Flask服务。
2.2编写.dockerignore
[root@localhost myapp]# vim .dockerignore
文件内容如下:
Dockerfile
2.3查看项目结构
[root@localhost myapp]# ll
total 12
-rw-r--r--. 1 root root 582 Feb 1 15:57 app.py
-rw-r--r--. 1 root root 140 Feb 1 16:10 Dockerfile
-rw-r--r--. 1 root root 122 Feb 1 16:00 requirements.txt
3.定义服务
3.1编写docker-compose.yml
[root@localhost myapp]# vim docker-compose.yml
文件内容如下:
services:
web:
build: . # 构建规则:基于当前目录的Dockerfile构建镜像
ports:
- 5000:5000 # 端口映射:宿主机5000端口 -> 容器5000端口
depends_on: # 依赖关系:启动web前先启动redis
- redis
redis: # 使用官方的redis alpine镜像
image: redis:alpine
3.2启动并运行容器
[root@localhost myapp]# docker-compose up -d
[+] Building 1.0s (11/11) FINISHED
...
[+] up 4/4
✔ Image myapp-web Built 1.1s
✔ Network myapp_default Created 0.0s
✔ Container myapp-redis-1 Created 0.1s
✔ Container myapp-web-1 Created 0.0s
3.3查看运行容器
[root@localhost myapp]# docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
myapp-redis-1 redis:alpine "docker-entrypoint.s..." redis About a minute ago Up About a minute 6379/tcp
myapp-web-1 myapp-web "python app.py" web About a minute ago Up About a minute 0.0.0.0:5000->5000/tcp, [::]:5000->5000/tcp
3.4访问测试
[root@localhost myapp]# curl 192.168.110.148:5000
Hello World! I have been seen 1 times.
[root@localhost myapp]# curl 192.168.110.148:5000
Hello World! I have been seen 2 times.
[root@localhost myapp]# curl 192.168.110.148:5000
Hello World! I have been seen 3 times.
4.项目优化
4.1添加数据卷
修改docker-compose.yml文件
[root@localhost myapp]# vim docker-compose.yml
文件内容如下:
services:
web:
build: .
ports:
- 5000:5000
depends_on:
- redis
volumes: #核心优化点,将宿主机myapp目录挂载到容器/code目录
- .:/code
redis:
image: redis:alpine
#优化逻辑:将宿主机myapp目录挂载到容器/code目录,宿主机修改app.py后,容器内的代码会实时同步;无需重新构建镜像、重启容器,即可看到代码修改后的效果(Flask 调试模式会自动重载代码)。
4.2重启docker-compose并修改宿主机中的代码
[root@localhost myapp]# docker-compose up -d
[root@localhost myapp]# vim app.py
修改内容如下:
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I am along! I have been seen {} times.\n'.format(count)
#修改访问回复,进行重新测试访问
4.3访问测试
[root@localhost myapp]# curl 192.168.110.148:5000
Hello World! I am along! I have been seen 4 times.
[root@localhost myapp]# curl 192.168.110.148:5000
Hello World! I am along! I have been seen 5 times.
[root@localhost myapp]# curl 192.168.110.148:5000
Hello World! I am along! I have been seen 6 times.
[root@localhost myapp]#
实验总结:
本实验以 "访问次数统计" 为业务载体,完整实现了从 "应用开发→依赖管理→镜像构建→多服务编排→功能验证→优化迭代" 的容器化部署全流程,最终达成以下目标:
- 基于 Docker-Compose 实现 Flask Web 服务与 Redis 缓存服务的协同运行,容器间网络互通、依赖可控;
- 实现访问次数的原子计数,验证 Redis 在轻量级数据存储场景的可用性;
- 通过数据卷挂载优化开发流程,实现宿主机代码与容器内代码的实时同步,无需重复构建镜像。
学习心得:
掌握容器化核心思想,理解 "镜像 - 容器" 的关系,以及 Dockerfile 如何将应用打包为标准化镜像;理解多容器编排逻辑,掌握 Docker-Compose 中services、depends_on、ports、volumes等核心配置的作用,解决多服务的依赖、网络、数据同步问题;同时夯实基础组件使用能力:熟悉 Flask Web 框架的基础用法,以及 Redis 原子操作(incr)在计数场景的应用,理解缓存服务与 Web 服务的协同模式。
工程实践意义:
1.标准化部署流程:通过容器化消除 "开发环境与生产环境不一致" 的问题,降低部署门槛;
2.提升开发效率:数据卷挂载的优化方式,可复用于日常开发(如前端代码热更新、后端代码实时调试),减少 "改代码→构建镜像→重启容器" 的重复操作;
3.轻量化微服务落地:本实验是微服务架构的极简缩影,可扩展至更复杂的场景(如多 Web 节点负载均衡、Redis 集群、数据库服务编排),为中小型项目微服务改造提供参考。