新闻详情

新闻详情

首页 / 资讯中心 / 详情

Docker Compose实战:Flask+Redis容器化部署访问计数服务

发布时间:2026/9/30 11:59:40来源:尧图网络
Docker Compose实战:Flask+Redis容器化部署访问计数服务
前段时间在练容器化部署的时候正好用一个小实验把 docker-compose、Flask、Redis 这几个东西串了一遍做一个网页访问计数功能每次请求前端页面或者某个接口Redis 里的计数自动加一页面把当前次数显示出来。就这么一个看似简单的小实验背后涉及 Flask 怎么连 Redis、Redis 数据怎么持久化、容器之间怎么通信、docker-compose 怎么编排服务、并发访问时计数会不会丢把这些点一个个捋清楚确实比单纯跑一个 hello world 收获大得多。这篇博文把整个实验从背景、原理到完整代码和调优过程都写下来如果你正在学 docker-compose 或者想把一个 Flask 小服务容器化可以直接照着做不需要额外复杂的架构一台装有 Docker 的机器就够。1. 项目背景与需求分析1.1 访问计数功能的应用场景访问计数是很多 Web 服务里最基础的功能之一比如官网首页展示“自从上线以来被访问了多少次”、API 网关统计调用量、活动页统计点击次数。实现方式很多可以用文件、用数据库、用缓存。但实际生产环境里访问量一旦上来文件存储扛不住并发每次请求都开文件锁写磁盘IO 会成为瓶颈数据库也能做但访问计数这种高频写、低价值的数据专门去写一条 MySQL 记录再更新成本和响应时间都不划算。用 Redis 做计数正好发挥它的内存存储和原子递增特性。我这个实验的目标很直接启动一个 Flask Web 服务提供一个/count页面用户每次访问这个页面Redis 里一个 key 的数值就加 1页面显示“这是第 N 次访问”。要求整个过程全部通过容器来跑Flask 和 Redis 各自一个容器用 docker-compose 统一管理。1.2 为什么选 Flask RedisFlask 是 Python 生态里最轻量的 Web 框架之一写一个接口几行代码就搞定适合快速做实验。它不像 Django 自带一堆东西但正是因为“轻”放到容器里镜像体积小启动快特别适合演示容器化部署的思路。Redis 选在这里而不是别的存储核心原因有三个第一计数操作必须是原子的。Redis 的INCR命令是原子操作多个并发请求同时执行INCR不会出现覆盖丢失的问题。如果用 Python 先读取计数再加一再写回高并发下会有竞态条件计数会偏小。第二Redis 是基于内存的读写速度是纳秒级计数这种高频操作用 Redis 响应极其快。虽然是访问计数但 Redis 在这里的逻辑就是“快”。第三Redis 本身是独立的服务可以很自然地和 Flask 做容器间通信的演示。docker-compose 里 Flask 容器通过服务名访问 Redis 容器的 6379 端口这种网络模式和端口映射机制是这个实验里最值得琢磨的部分。1.3 docker-compose 在这个实验里解决什么问题如果只用 docker run 手动启动两个容器也不是不行但会出现几个麻烦容器启动顺序要自己控制需要先启动 Redis 再启动 Flask每次启动要带一串-p、--name、--network参数如果后来加了 MySQL、Nginx参数会越来越长。而 docker-compose 把服务定义写成 YAML 文件一条docker-compose up -d就把所有服务按依赖关系启动起来网络自动创建日志、配置、环境变量都集中管理。这个实验虽然只有两个服务但把 docker-compose 的依赖控制、网络配置、持久化挂载、重启策略这些基础能力全部用上了算是一个麻雀虽小但五脏俱全的入门案例。2. 技术选型与核心原理2.1 Flask 服务端如何处理请求和计数Flask 应用里只需要一个路由函数响应/countURL。每来一次请求执行一次 Redis 的INCR把结果拼进返回的 HTML 里。本质上这涉及两次网络交互浏览器到 Flask 容器的 HTTP 请求以及 Flask 容器内到 Redis 容器的 TCP 请求。这也是容器化部署需要理解的一个关键点容器之间不是通过 localhost 通信而是通过虚拟网络上的 IP 或服务名通信。在 docker-compose 默认创建的 bridge 网络里Flask 容器可以通过服务名redis解析到 Redis 容器的 IP。Flask 侧的代码设计要考虑到和 Redis 的连接方式。最简单的是在函数内部每次调用redis.Redis(hostredis, port6379)但这样每次请求都新建一个连接性能比较差。调优时会用连接池。这里先按基础的写后面优化。2.2 Redis 的 INCR 命令为什么适合计数Redis 的INCR命令会将 key 中存储的数字值加一。如果 key 不存在会先设为 0 再执行加一操作最终值为 1。它是原子操作Redis 是单线程事件循环模型命令执行不会被其他命令穿插所以多个客户端同时执行INCR也不会出现“读-改-写”的竞态问题。这就是为什么计数这类高频写用 Redis 特别省心。如果用 Python 这样写count r.get(visit_count) r.set(visit_count, int(count) 1)在单线程没问题一旦放两个 gunicorn worker两个请求同时读到同一个旧值分别加一写回就会丢失一次增量。而INCR是服务端原子完成的不存在这个问题。2.3 容器化部署和编排的几个关键理念Docker 容器把应用和运行环境打包在一起解决了“在我机器上明明是好的”这个经典问题。Flask 容器只负责跑 Python 代码Redis 容器负责跑 Redis 服务两者各司其职。容器是轻量级的不需要像虚拟机那样装完整系统镜像分层也方便缓存。docker-compose 是编排工具用 YAML 描述服务之间的关系。它的核心概念是 service而不是 container。每个 service 可以指定镜像、构建文件、端口映射、环境变量、依赖关系等。运行docker-compose up时Compose 会自动创建一个项目专属网络所有服务默认加入这个网络服务名就是主机名。这个实验里如果让 Flask 依赖 Redis就需要在 docker-compose.yml 中写depends_on。它保证 Redis 服务先启动但这里有个坑depends_on只保证容器启动顺序不保证 Redis 已经准备好接受连接。所以 Flask 代码里要对 Redis 连接做重试这个后面会详细说。3. 实验环境准备与目录结构3.1 环境需求整个实验需要你的电脑上装好 Docker。Windows 上可以用 Docker DesktopmacOS 同样Linux 发行版直接装 docker-engine。建议用 20.10 以上版本docker-compose 的话新版 Docker Desktop 自带docker composev2老版本需要单独安装docker-composev1。我这个实验里用的是 Docker 24 和 compose 2.x命令上写的是docker-compose如果你的环境支持docker compose插件也可以把横杠换成空格。查看版本docker --version docker-compose --version如果没有安装 compose可以去 GitHub 上下载对应平台的二进制文件放到/usr/local/bin/docker-compose并赋予可执行权限。当然也要注意系统是否开启了虚拟化支持Windows 下如果启动 Docker Desktop 报错“Virtualization support not detected”需要在 BIOS 里开启虚拟化技术并在系统设置里把 Hyper-V 和 Windows 虚拟机监控程序平台打开。3.2 项目目录结构我习惯把实验项目放在一个干净的目录里比如flask-redis-counter目录结构如下flask-redis-counter ├── app.py ├── requirements.txt ├── Dockerfile ├── docker-compose.yml └── README.md不需要额外复杂的目录结构这样就够用了。后面如果项目变大可以把 app.py 拆成包但现在保持简单最好。3.3 关键文件初始化先创建一个文本文件requirements.txt里面只需要两行flask2.3.3 redis5.0.1版本号可以根据自己实际环境调整但最好固定下来。Flask 2.x 和 Redis 5.x 都是目前比较稳定的版本。固定版本有一个好处Docker 构建镜像时不会因为基础镜像里 Python 环境不同而拉到不兼容的最新版构建结果可复现。Dockerfile 用来构建 Flask 应用的镜像。Redis 镜像不需要我们自己写 Dockerfile直接用官方镜像redis:7-alpine就可以。4. 实现步骤与核心代码解析4.1 编写 Flask 应用 app.pyapp.py 是核心业务代码。先是导入模块然后创建 Flask 应用连接 Redis。基础版本代码from flask import Flask import redis app Flask(__name__) r redis.Redis(hostredis, port6379, db0) app.route(/count) def count(): cnt r.incr(visit_count) return f这是第 {cnt} 次访问 if __name__ __main__: app.run(host0.0.0.0, port5000)这里要解释几个细节第一redis.Redis(hostredis, port6379)里 host 写的是redis这是 docker-compose 服务名不是 IP。如果不用容器直接跑本地 Pythonhost 就得写localhost或者127.0.0.1。容器里用服务名是 docker-compose 自动提供的 DNS 解析。第二r.incr(visit_count)对应 Redis 的INCR命令返回值就是递增后的值直接作为访问次数展示。如果 key 不存在第一次返回 1。第三app.run(host0.0.0.0)必须监听所有网络接口因为 Flask 跑在容器里外部要映射端口访问如果只监听 127.0.0.1容器外就访问不到了。为了演示调优我会在后面把连接池和重试机制加进去。这里先保持最简单能跑通逻辑。4.2 编写 DockerfileDockerfile 用来打包 Flask 应用官方 Python 基础镜像有很多选择。我用的是python:3.11-alpine因为 Alpine 版本体积小整个镜像会小很多。FROM python:3.11-alpine WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app.py . EXPOSE 5000 CMD [python, app.py]逐行解释一下FROM python:3.11-alpine选择基础镜像Alpine 是轻量级 Linux 发行版镜像体积小。如果怕 Alpine 装某些依赖编译麻烦也可以用python:3.11-slim体积略大一点但兼容性更好。这里我们的项目只有纯 Python 依赖Alpine 没问题。WORKDIR /app设置容器内的工作目录后续复制文件和执行命令都在这个目录。COPY requirements.txt .把宿主机上的 requirements.txt 复制到镜像里。这里有个 Docker 层缓存知识先复制 requirements 再执行 pip install这样以后只改业务代码、不改依赖时Docker 构建可以复用缓存层不用重新下载安装所有依赖。RUN pip install --no-cache-dir--no-cache-dir避免把 pip 缓存写进镜像减小体积。COPY app.py .复制业务代码。EXPOSE 5000声明容器内服务监听 5000 端口但这只是声明真正让外部访问还需要在 docker-compose.yml 里做端口映射。CMD [python, app.py]容器启动时执行这个命令。这里直接用 python 内置的开发服务器只适合实验。生产环境应该用 gunicorn后面调优部分会说明。4.3 编写 docker-compose.ymldocker-compose.yml 是这个实验的编排核心完整内容如下version: 3.8 services: web: build: . ports: - 5000:5000 depends_on: - redis restart: unless-stopped redis: image: redis:7-alpine ports: - 6379:6379 volumes: - redis_data:/data restart: unless-stopped volumes: redis_data:先说明几个关键配置version: 3.8是老 compose 的写法新版 compose v2 其实可以忽略这个字段但写上没坏处兼容性更好。services.web.build: .表示当前目录下的 Dockerfile 构建镜像。如果我们不想构建也可以直接写image:指向一个已经构建好的镜像但这里为了演示用构建方式。ports: 5000:5000把宿主机的 5000 端口映射到容器的 5000 端口。访问 http://localhost:5000/count 就会进入 Flask 容器。depends_on让 web 服务在 redis 服务启动后再启动。注意这只是启动顺序不是等待 redis 就绪。redis服务用官方redis:7-alpine镜像6379:6379端口映射。其实这里如果把 Redis 端口映射到宿主机外部也能直接连到 Redis但生产环境一般不建议暴露 Redis 端口只让 Flask 通过内部网络访问即可。我这个实验为了观察 Redis 数据临时映射一下也没问题但你要记得在真实项目中谨慎暴露。volumes把 Redis 容器的/data目录挂载到 docker-compose 管理的命名卷redis_data。这样 Redis 即使容器重建数据也不会丢。我的实验里如果不挂载容器删掉后计数就回到 0调优时测试持久化就很不方便。4.4 启动与验证在项目目录下执行docker-compose up -d第一次启动会先拉取 redis 镜像然后构建 Flask 镜像。构建过程能看到 pip install 的输出完成后两个容器都会启动。查看容器状态docker-compose ps应该能看到web和redis两个服务都在 Up 状态。然后访问浏览器http://localhost:5000/count每次刷新数字都会加 1。如果没有浏览器可以用 curl 测试curl http://localhost:5000/count我实际执行时第一次访问默认返回 1连续访问几次后计数依次递增。再执行docker-compose exec redis redis-cli get visit_count能看到 Redis 里存的值和页面显示一致说明 Flask 确实连上了 Redis。到这里基础功能已经完成。接下来进入调优环节这也是整个实验最有价值的部分。5. 调优与常见问题排查5.1 性能调优方向基础版本能用但有几个明显的性能隐患第一每次请求都新建 Redis 连接。Redis 连接虽然快但毕竟是两条 TCP 往返连接建立、命令执行、断开高并发下频繁创建连接会浪费大量资源。正确做法是使用连接池。连接池可以这样改pool redis.ConnectionPool(hostredis, port6379, db0, max_connections50) r redis.Redis(connection_poolpool)在 Flask 进程启动时就创建好连接池后续请求复用连接避免反复握手。第二Flask 自带开发服务器是单进程的app.run()实际上不适合生产。容器化部署一个典型的应用应该用 gunicorn 这种支持多 worker 的 WSGI 服务器。如果不用 gunicorn默认 Flask 一次只能处理一个请求访问一多就排队。改为 gunicorn 需要改 Dockerfile 的 CMDRUN pip install gunicorn CMD [gunicorn, -w, 2, -b, 0.0.0.0:5000, app:app]-w 2表示两个 worker 进程搭配 Redis 连接池并发能力会有一个明显提升。注意如果 worker 数量变了连接池的 max_connections 也要相应调整避免每个 worker 各建 50 个连接导致连接数超标。第三Redis 的持久化策略。默认 redis 容器如果带/data挂载使用的是 Redis 的 RDB 快照机制默认在满足一定条件时自动保存。访问计数这种场景如果 Redis 突然挂了最多丢失最后几秒的计数一般影响不大。但为了不丢数据可以在 docker-compose.yml 里给 redis 服务加 command 参数调整保存频率command: redis-server --appendonly yes开启 AOF 持久化后每次写操作都会追加到文件里并且容器挂载卷会把/data目录存下来。缺点是有性能开销但实验规模完全没影响。我就是踩过一次坑没挂载卷的时候容器一重建计数清零这才认真研究了一下 redis 数据持久化。第四网络超时和重试。depends_on不能保证 Redis 容器内服务真的就绪Flask 启动后可能因为 Redis 没准备好报ConnectionError。在 Flask 代码里可以给 redis 连接加一个启动时等待逻辑def get_redis_with_retry(): for attempt in range(5): try: r redis.Redis(hostredis, port6379, db0, socket_connect_timeout2) r.ping() return r except redis.ConnectionError: time.sleep(2) raise RuntimeError(Redis 连接失败)我在实验里加了这个逻辑每次docker-compose up都稳定启动不会因为 Redis 没就绪导致 Flask 崩溃重启循环。5.2 常见问题与排查思路实验过程中我整理了几个高频问题做成表格方便对照现象可能原因排查方法docker-compose up报端口占用宿主机 5000 或 6379 端口被占用netstat -anoWindows或lsof -i :5000Linux/macOS查看占用进程Flask 容器启动后一直重启Redis 未就绪导致连接异常看日志docker-compose logs web如果是连接报错给 Flask 加重试逻辑页面能打开但计数不增加Redis 连错地址或 key 写到了别处进入容器docker-compose exec web python测试 Redis 能否连接也可以docker-compose exec redis redis-cli get visit_count查看当前值容器重建后计数归零没有挂载 Redis 数据卷在 docker-compose.yml 里给 redis 服务加volumes重新 up 后数据保留Windows 下 Docker Desktop 启动不了虚拟化未开启或 Hyper-V 未启用按提示检查 BIOS 虚拟化开关以及 Windows 功能中的虚拟机平台访问外部页面很慢容器网络配置问题跨网段访问有延迟确保 Flask 和 Redis 在同一个 docker-compose 网络里不要手动指定不同的外部网络curl http://localhost:5000/count连接拒绝Flask 容器没有监听 0.0.0.0检查 app.run 中 host 是否为0.0.0.0端口映射是否正确排查有个基本套路先看容器运行状态再看容器日志再接进去手动测试。docker-compose logs -f能看到实时日志遇到问题先看这里大部分线索都在日志里。5.3 调优效果验证我调优后重新构建镜像docker-compose down docker-compose up -d --build用abApache Bench做一个小压测模拟 200 次请求、并发 20ab -n 200 -c 20 http://localhost:5000/count调优前单进程 Flask 每次新建连接平均响应时间接近 30ms失败请求数随着并发升高而增加。调优后gunicorn 两个 worker Redis 连接池平均响应时间降到 8ms 左右200 个请求全部成功。这个结果对比很明显也能直观感受到资源复用和并发模型带来的差异。再验证持久化执行docker-compose down后重新up再访问/count计数数字从原来的值继续增加而不是从 1 开始。因为 Redis 的 AOF 文件在卷里容器重建后恢复数据成功。5.4 几个我踩过的小坑第一个是depends_on的误解。一开始我以为写了depends_onRedis 服务就完全可用了结果 web 容器启动后抛redis.exceptions.ConnectionError: Error 111 connecting to redis:6379。后来查资料才知道depends_on只控制启动顺序不等待服务就绪。这也让我理解了为什么很多 compose 配置里会配合 healthcheck 使用。第二个是 Windows 上访问localhost:5000被浏览器当作不安全的网站这是因为 Firefox 默认对非 HTTPS 的 localhost 访问有自己的提示换 Chrome 或 Edge 就好不是程序问题。第三个是 Dockerfile 里COPY的上下文问题。如果不在项目根目录执行docker-compose build而跑到别的目录会报找不到app.py。解决办法是始终保证 docker-compose.yml 所在目录就是构建上下文。第四个是镜像拉取缓慢的问题。国内拉镜像经常超时我给 redis 镜像加标签时最好提前把镜像拉下来或者配置镜像加速器。这个要根据实际情况不是必须。6. 日志与监控视角下的扩展思考实验做到这里基础功能已经很完整了。但我后来又在想访问计数这种服务上线后总不能全靠人肉刷新页面看数字最好留点日志和监控指标。最简单的是在 Flask 里加一个访问日志记录每次请求时间、IP、计数结果。用 Python 的 logging 模块把日志打到 stdout容器启动后就能用docker-compose logs看到。更进一步可以把 Redis 的visit_count定时推到指标系统比如 Prometheus不过这就是另一个项目了。如果想要更完整的生产可用方案可以把 docker-compose.yml 里的 web 服务加上healthcheck让 Compose 定期检查 Flask 是否健康healthcheck: test: [CMD, curl, -f, http://localhost:5000/count] interval: 30s timeout: 5s retries: 3这个镜像里得先装上 curl不然健康检查会失败。加上之后docker 能看到容器是否是 healthy 状态出问题也能更快定位。Redis 侧也可以定期执行INFO命令看内存占用但实验阶段只要确认数据不丢、命令执行正常就够了。7. 一些经验总结现在回过头来看这个 FlaskRedis 访问计数的 docker-compose 实验最大的价值不在于计数本身而是把几个原本孤立的知识点打通了容器和容器之间怎么通信、编排工具怎么管理依赖顺序、端口映射和网络模型是怎么回事、Redis 的命令为什么适合这种场景、高并发下连接复用有多重要。如果让我再重做一遍我会从一开始就把连接池、gunicorn 和挂载卷都写上省得后面再踩一遍重置计数的坑。但反过来想正是先跑一个“能用的笨版本”后面逐步调优才更清楚每一个优化点解决的是什么问题。建议你也可以按照这个顺序来先跑通最简版本确认链路没问题再一个个调优观察变化最后把容错和持久化补上形成一个可以拿去演示或者后续扩展的基线版本。以后想扩展也很容易比如加一个用户 ID 参数让每个用户单独计数或者把计数结果存进数据库做历史分析或者换成 Celery 后面异步处理。只要把 docker-compose 服务列表往下加就好。这套实验所依赖的思路是一致的这才是比计数功能本身值钱的地方。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

LeetCode《程序员面试金典》01.01 Is Unique 判定字符是否唯一:位掩码 O(1) 空间的 7 语言题解 2026/9/30 12:52:41

LeetCode《程序员面试金典》01.01 Is Unique 判定字符是否唯一:位掩码 O(1) 空间的 7 语言题解

示例工程教程 【免费下载链接】leetcode 🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解 项目地址: https:/…

阅读更多 →
双管齐下:乳房修复与提升术式的革新与拓展 2026/9/30 12:52:35

双管齐下:乳房修复与提升术式的革新与拓展

乳房整形手术在满足女性美学需求的同时,也面临术后并发症及二次修复的挑战。近期,南京医科大学友谊整形外科医院吴国平教授团队一组聚焦于乳房假体移位修复与乳房下垂采用自体组织上提的系列研究,分别从不同角度提出了创新性的术式&#xff0…

阅读更多 →
devops-exercises 实战:在 AWS VPC 中跨可用区创建 Subnet(Console / Terraform / Pulumi 三种方案) 2026/9/30 12:52:27

devops-exercises 实战:在 AWS VPC 中跨可用区创建 Subnet(Console / Terraform / Pulumi 三种方案)

文档教程DevOps运维 【免费下载链接】devops-exercises Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions 项目地址&…

阅读更多 →
C++实现时间片轮转与SJF进程调度模拟:原理、代码与课程设计实战 2026/9/30 12:52:17

C++实现时间片轮转与SJF进程调度模拟:原理、代码与课程设计实战

项目标题: "基于时间片轮转和SJF的进程调度系统的模拟设计2操作系统C(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码" 掐指一算,又到了操作系统课程设计的高峰期。每年这个时候,总有不少同学…

阅读更多 →
React 19 + TypeScript 升级实战:从编译报错到类型收敛 2026/9/30 12:52:16

React 19 + TypeScript 升级实战:从编译报错到类型收敛

React 19 正式发布之后,我做的第一件事就是把手上一个中后台项目从 React 18 升到 React 19。升级本身不算难,但 TypeScript 这边的新规则让我在编译错误里泡了整整两天:useRef 必须传初始值了、forwardRef 突然显得多余、函数组件返回类型变…

阅读更多 →
给LLM Agent装上后视镜:hindsight记忆机制从原理到Docker落地 2026/9/30 12:52:16

给LLM Agent装上后视镜:hindsight记忆机制从原理到Docker落地

1. 从“hindsight”说起:为什么我们需要给 Agent 装上“后视镜”第一次看到“hindsight”这个词,我脑子里蹦出来的不是词典释义,而是自己踩过的一个坑。去年我搭了一个基于 LLM 的运维助手,能连 MCP 工具、能查 Docker 容器状态、…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉