返回首页

VPS 上的 Docker:生产环境 24 个容器

这篇文章剖析了在 6 GB RAM 的单台 VPS 上生产部署 24 个 Docker 容器的过程。它涵盖了隔离的架构原则、通过 healthcheck 的依赖管理、Elasticsearch 和 Chrome Headless 的内存使用优化、通过 SSH 隧道与远程 DB 的安全交互,以及无 Prometheus 的轻量级监控。

一台服务器上 24 个容器:技术剖析
Advertisement 728x90

单台 VPS 运行 24 个容器:中高级 DevOps 的高密度技术架构

一台服务器,6 GB 内存,24 个容器——稳定运行超过一年。这并非实验,而是支撑七个活跃项目的生产基础设施:一个教育科技机器人、反垃圾邮件服务、AI 后端、元搜索引擎、本地大模型推理以及两个实用工具服务。它们虽未直接产生收入,但对运营活动至关重要。目标并非单纯为了削减成本,而是一种有意识的权衡:以手动管理替代托管服务,以可预测的负载替代云弹性,以深度集成替代抽象层。

架构原则:隔离、可观测性、韧性

核心方法依赖于严格的网络隔离。每个项目都位于独立的 Docker 网络(bridge)中,消除了跨依赖并最小化了攻击面。仅使用共享 shared 网络用于系统服务(如监控或通用 Redis)。内部端口(9200, 8088, 9222)仅通过 127.0.0.1 访问;外部访问在 iptables 和 Nginx 配置层面被阻断。

韧性通过 docker-compose.yml 中的健康检查依赖来确保。例如,EdTech 项目中的 PHP 服务不会启动,直到远程数据库的 SSH 隧道通过 nc -z 127.0.0.1 3306 检查。这保证了应用程序不会尝试连接不可用的数据库,从而避免队列因错误而崩溃。

Google AdInline article slot

由于内存限制,可观测性构建时未使用 Prometheus/Grafana。取而代之的是一个每 15 分钟运行的 Bash 脚本:

  • 检查公共端点的 HTTP 状态码(/health, /webhook/advert
  • 通过 docker ps --filter "health=unhealthy" 识别不健康的容器
  • 监控磁盘空间和交换空间使用情况
  • 通过 curl 调用 Bot API 向 Telegram 聊天发送警报

这为单个管理员提供了最小但足够的可见性。

关键组件:Elasticsearch、无头 Chrome 和 SSH 隧道

三个组件定义了 VPS 的性能边界:

Google AdInline article slot
  • Elasticsearch 8.12.2 消耗 1.47 GB 内存。如果在 deploy.resources.limits 中没有显式的 memory: 2g 限制,JVM 开始交换邻近进程,导致不稳定。对于约 5 万文档的索引和日志记录,这在功能和负载之间提供了最佳平衡。像 Meilisearch 这样的替代方案需要迁移现有索引并修改客户端代码,在此阶段被评为高投入低回报的任务。
  • Chrome Headless (zenika/alpine-chrome) 占用 636 MB 内存,尽管它每小时仅使用一次进行 JS 渲染抓取。通过 docker run --rm 按需运行可以节省内存,但每次调用会增加 5–10 秒延迟。对于具有严格 SLA 的服务来说这是不可接受的;在这里,这是一个可接受的妥协。
  • SSH 隧道 在带有 autossh 的独立 Alpine 容器中实现。它们提供对共享主机上 MySQL 的安全访问,而无需在外部开放端口。配置包括 ServerAliveInterval=30AUTOSSH_GATETIME=0 以及通过 nc 的健康检查。此解决方案适用于所有支持 SSH 的主机,并完全取代了付费托管数据库。

Nginx 和 PHP 优化:将延迟降低至毫秒级

Nginx 仅配置用于 Webhook 和 API——除 /health 外不处理任何静态文件。关键优化是将 Telegram Webhook 处理卸载到单独的 PHP 文件(webhook-advert.php),该文件绕过 Laravel 引导。这将响应时间从 80–120 毫秒减少到 15–25 毫秒,这对于来自 Telegram API 的大规模 Webhook 调用(最大超时 — 60 秒)至关重要。

配置还包括:

  • 强制 HTTP → HTTPS 重定向
  • 拒绝处理除 /health/webhook//api/telegram/ 以外的任何路径
  • 增加 fastcgi_read_timeout 至 120 秒以处理长时间运行的任务
  • 使用 realpath_root 正确处理 index.php

SSL 与安全:主机上的三种策略

SSL 终止采用混合方式组织:

Google AdInline article slot
  • getssl + Let's Encrypt —— 针对需要端到端加密且不使用 Cloudflare 的域名
  • Cloudflare Proxy —— 针对大多数域名:SSL 在边缘终止,流量以 HTTP 传输至 VPS,但受 IP 过滤保护(仅限 Cloudflare ASN)
  • 手动更新 —— AI 后端的临时措施,目前正通过 Caddy 进行自动化

Cloudflare 设置包括强制 Always Use HTTPSWAF速率限制(每个 IP 每分钟 100 次请求),弥补了 VPS 侧缺乏 WAF 的不足。

核心要点

  • 通过独立的 Docker 网络实施严格的网络隔离,防止级联故障并简化安全审计。
  • docker-compose 中的健康检查依赖确保服务仅在依赖项就绪后才启动(例如,到数据库的 SSH 隧道)。
  • Elasticsearch 需要硬性内存限制(2 GB);否则,JVM 会交换邻近容器,触发 OOM Killer。
  • 如果启动延迟对业务逻辑至关重要,则在低频使用时保持 Chrome Headless 常驻模式是合理的。
  • 在内存限制下,配合明确定义的 SLO,Bash 监控与 Telegram 警报是 Prometheus/Grafana 的可行替代方案。

— Editorial Team

Advertisement 728x90

继续阅读