别上K8s!中小项目用Docker Compose构建高可用架构实战

两年前,我接手过一个烂摊子。

当时那个创业团队只有5个后端开发,却维护着一套复杂的Kubernetes集群。前任架构师为了追求"技术先进性",把原本简单的电商SaaS拆成了几十个微服务。结果呢?业务代码没写多少,大家每天都在折腾Pod驱逐、网络插件冲突和证书过期。

这其实是很多中小团队的缩影:陷入了"简历驱动开发"的陷阱,用屠龙刀去切土豆。

在没有专职运维(Ops)的情况下,对于大多数日活(DAU)在10万以下、服务器规模在10台以内的项目,Docker Compose 配合合理的架构设计,不仅够用,而且真香

今天咱们不聊那些花里胡哨的概念,就聊聊我是如何用 Docker Compose 帮那个团队把架构"降级",反而让系统稳定性提升了 99.9% 的实战经验。

一、 摆脱"环境玄学",把基础设施代码化

很多资深开发都经历过这种绝望时刻:代码在本地跑得欢,一部署到测试环境就崩,到了生产环境直接报错 Connection Refused。然后大家聚在一起排查半天,发现是测试环境的 Redis 版本低了,或者生产环境的防火墙没开。

Docker Compose 的核心价值,不仅仅是启动容器,而是"基础设施即代码"(IaC)的最简化落地。

在重构那个项目时,我发现他们每个人的本地开发环境都不一样,有人用 Mac 的 Docker Desktop,有人直接在物理机装 MySQL。

落地动作: 我强制要求项目根目录必须包含一份标准化的 docker-compose.yml,并且配合 .env 文件管理差异。

配图

version: '3.8'
services:
  api_server:
    image: myapp/backend:${TAG:-latest}
    restart: always
    environment:
      - DB_HOST=db
      - REDIS_HOST=cache
    # 关键点:用Healthcheck保证依赖就绪
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    depends_on:
      db:
        condition: service_healthy
  
  db:
    image: mysql:5.7
    # 生产环境挂载数据卷,别直接用容器内存储
    volumes:
      - ./data/mysql:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
      timeout: 20s
      retries: 10

效果立竿见影: 新来的实习生,以前配置环境要花一天,现在 git clone 下来,敲一行 docker-compose up -d,5分钟内就能开始写业务代码。环境一致性带来的效率提升,远比你想象的要大。

二、 单机部署也能做"高可用"

有人会反驳我:“Docker Compose 只是单机编排,生产环境单点故障怎么办?”

这是个误区。对于中小项目,真正的可用性瓶颈通常不在于服务器挂了,而在于发布时的服务中断,或者内存泄漏导致的进程崩溃。

我之前带过一个营销活动项目,流量突增。因为代码里有个低级 Bug 导致内存泄漏,服务每隔几小时就 OOM(内存溢出)挂掉。

在没法立即修复代码的情况下,我在 docker-compose.yml 里加了两行配置,硬是扛过了那次活动:

  1. 资源限制(Deploy Resources): 既然会泄露,就限制它最大只能吃 1G 内存,别把宿主机拖死。
  2. 重启策略(Restart Policy): 挂了立刻自动重启。
services:
  campaign_service:
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 1G
    restart: always # 哪怕进程崩了,Docker守护进程也会秒级拉起它

关于"零宕机"发布的实操:

配图

很多团队用 Compose 发布是直接 down 掉再 up,这中间会有几秒甚至几十秒的服务中断。

我常用的一个土办法,非常有效:蓝绿部署的"乞丐版"实现

在同一台机器上,我准备两套配置 docker-compose-blue.ymldocker-compose-green.yml,它们共用数据库,但监听不同的端口(比如 8081 和 8082)。前端挂一个 Nginx 做反向代理。

发布流程变成了:

  1. 启动 Green 容器;
  2. 脚本自动检测 Green 端口是否健康;
  3. 修改 Nginx 配置,把流量切到 Green;
  4. 停掉 Blue 容器。

这个脚本我写完用了两年,至今还在那家公司的 Jenkins 里跑着,稳得一匹。

三、 日志与监控:别等到报警才抓瞎

配图

容器化改造最怕的是"黑盒化"。以前应用跑在宿主机上,我要看日志,直接 tail -f 完事。容器化后,日志散落在各个 Container 里,一旦出问题,排查起来非常痛苦。

踩过几次坑后,我总结了一条铁律:中小团队别碰 ELK(Elasticsearch, Logstash, Kibana),那玩意儿维护成本太高。

我推荐一个轻量级组合:Loki + Promtail + Grafana

这也是我在上个项目中落地的方案。Promtail 极其轻量,通过 Docker Compose 部署成 DaemonSet(或者直接作为服务运行),去采集 /var/lib/docker/containers 下的 JSON 日志,直接推给 Loki。

真实场景复盘: 有次周五下午(往往是事故高发期),客服反馈用户登录慢。我没登服务器,直接打开 Grafana 看板,通过 Loki 聚合的日志,1分钟内定位到是某个 SQL 查询在 Docker 容器内超时了。

如果当时我要去 5 台服务器上分别 grep 日志,估计晚饭就得在公司吃了。

写在最后

技术圈有个怪象,好像不用 K8s、不搞微服务就不够"架构"。但对于大多数中小团队的架构师来说,技术的 ROI(投入产出比)才是最重要的衡量标准。

Docker Compose 也许不够"高大上",但它简单、直观、易于调试。在业务快速迭代、人员变动频繁的早期阶段,“简单"本身就是最大的"稳健”。

关于容器化选型,你更倾向于哪种? A. 一步到位上 K8s,为未来扩容做准备。 B. 先用 Docker Compose 跑通,业务量大了再迁移。 (欢迎在评论区告诉我你的选择)

最后,如果你想在团队落地这套方案,建议从这 3 个动作开始:

  1. 盘点现状: 找出目前最容易因为环境不一致导致 Bug 的服务,作为试点。
  2. 标准化配置: 编写通用的 Dockerfile 基础镜像,和一份包含 Healthcheck 的 docker-compose.yml 模板。
  3. 脚本化发布: 写一个简单的 Shell 脚本,把 docker-compose up -d 和健康检查串联起来,接入 Jenkins 或 GitLab CI。