VPS上的Docker生产环境:会出什么问题以及如何解决
Docker在你的笔记本上运行正常。在公网VPS上,它会绕过防火墙、用日志填满磁盘、以root权限运行所有进程,且没有更新策略。以下是你需要解决的8个问题。
你在VPS上安装了Docker,运行了docker compose up,应用上线了。搞定了?
没那么简单。在你的笔记本上,没人扫描你的端口,磁盘空间充足,安全性无关紧要。在面向互联网的VPS上,Docker的默认配置会主动跟你作对。
本页涵盖你将遇到的8个问题,并链接到每个问题的专门指南。浏览一遍,找到适用于你服务器的内容,跟着详细指南操作。
前置条件
本指南假设你已经了解Docker基础知识。如果需要补课:
- Docker命令和概念
- 使用Compose运行多服务应用
本文中的所有命令和输出均在Debian 12和Ubuntu 24.04上使用Docker Engine 29.x和Compose v2(版本5.x)测试通过。
在公网VPS上运行Docker会出什么问题?
将Docker从开发环境迁移到公网VPS时,有八个问题会出现:防火墙失效、容器以root权限运行并拥有完整的主机访问权限、日志在没有轮转的情况下填满磁盘、容器网络与主机网络冲突、服务没有资源限制和健康检查、卷没有备份策略、端口在没有TLS的情况下暴露、容器镜像在没有更新计划的情况下过期。这些都是Docker的默认行为,每一个都会在生产环境中给你造成麻烦。
详细指南前的快速概览:
| # | 问题 | 诊断命令 | 风险等级 |
|---|---|---|---|
| 1 | 防火墙被绕过 | sudo iptables -L DOCKER-USER -n |
严重 |
| 2 | Root容器 | docker info --format '{{.SecurityOptions}}' |
严重 |
| 3 | 日志填满磁盘 | du -sh /var/lib/docker/containers/*/*-json.log |
高 |
| 4 | 网络冲突 | docker network ls |
中 |
| 5 | 没有资源限制 | docker stats --no-stream |
高 |
| 6 | 没有卷备份 | docker volume ls |
高 |
| 7 | 没有反向代理/TLS | ss -tlnp | grep -E ':80|:443' |
严重 |
| 8 | 镜像过期 | docker compose images |
中 |
现在就在你的服务器上运行这些命令。如果有任何输出让你意外,请阅读下面对应的章节。
Docker会绕过你的防火墙吗?
会。Docker直接操作iptables,在nat和filter表中插入规则,这些规则在UFW或firewalld看到流量之前就已被评估。当你用-p 8080:80发布端口时,该端口对整个互联网开放,即使你的UFW规则设置了deny incoming。
检查这是否影响到你:
sudo iptables -L DOCKER-USER -n -v
如果输出显示一条空链或只有一条无条件的RETURN规则而没有过滤,那么每个发布的端口都是完全开放的。
根本原因:发往Docker容器的数据包经过FORWARD链和DOCKER链。UFW在INPUT链上运行。两者永远不会交汇。
最简单的即时修复方法是将发布的端口只绑定到127.0.0.1:
ports:
- "127.0.0.1:8080:80"
这使端口只能从主机本身访问。配合反向代理来处理外部流量。
关于DOCKER-USER链修复、UFW集成和nftables配置的完整指南。
在VPS上以root运行Docker危险吗?
默认情况下,Docker守护进程以root运行,容器在其命名空间内也以root运行。如果攻击者逃出容器,就会以root身份登陆主机。在公网VPS上,这直接导致服务器被完全入侵。
检查你当前的配置:
docker info --format '{{.SecurityOptions}}'
在输出中查找name=rootless。如果不存在,你的守护进程以root运行。
同时检查你的容器是否在内部以root运行:
docker ps -q | xargs -I {} docker exec {} id
如果大多数容器显示uid=0(root),它们在容器内以root运行。在Dockerfile中设置了USER appuser的镜像会显示非root的uid。
你有三层防御:
- Rootless模式在非root用户下运行整个Docker守护进程。即使容器逃逸也只会以非特权用户身份登陆主机。
- Seccomp配置文件限制容器可以使用的系统调用。Docker自带的默认配置文件阻止约44个危险的系统调用,但你可以进一步收紧。
- AppArmor / SELinux在所有其他措施之上添加强制访问控制。
Rootless Docker的完整配置、自定义seccomp配置文件和no-new-privileges。
为什么Docker容器会填满你的磁盘?
Docker的默认日志驱动是json-file,没有大小限制也没有轮转。你的应用写到stdout的每一行都存储在/var/lib/docker/containers/<id>/<id>-json.log中。一个繁忙的Web应用可以在几天内生成几个GB的日志。
检查日志目前占用了多少空间:
sudo du -sh /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -rh | head -5
在一个运行了几周没有日志轮转的VPS上,你可能会看到这样的输出:
4.2G /var/lib/docker/containers/a1b2c3.../a1b2c3...-json.log
1.8G /var/lib/docker/containers/d4e5f6.../d4e5f6...-json.log
256M /var/lib/docker/containers/g7h8i9.../g7h8i9...-json.log
日志不是唯一的磁盘消耗者。检查Docker的整体磁盘使用情况:
docker system df
运行5个服务的服务器的典型输出:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 12 5 4.2GB 2.8GB (66%)
Containers 8 5 52MB 12MB (23%)
Local Volumes 6 4 1.1GB 245MB (22%)
Build Cache 18 0 890MB 890MB (100%)
构建缓存100%可回收。那7个未使用的镜像占了2.8 GB。在40 GB的VPS磁盘上,这些很快就会累积起来。
解决方案分两部分:在/etc/docker/daemon.json中配置日志轮转,以及定期清理未使用的镜像和构建缓存。
完整的日志轮转配置、磁盘监控和自动清理:
Docker网络在单台VPS上如何工作?
Docker创建自己的桥接网络(默认172.17.0.0/16)并在内部管理容器IP。在VPS上,这可能与你的主机网络、VPN子网或其他服务冲突。
查看Docker创建了哪些网络:
docker network ls
NETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 bridge bridge local
f6e5d4c3b2a1 host host local
9i8h7g6f5e4d none null local
每次docker compose up都会创建一个额外的网络。几个项目之后,你可能有十几个子网重叠的网络。
VPS网络的关键决策:
- 桥接模式(默认):容器获得内部IP,端口通过iptables发布。适用于大多数配置。增加了一层NAT。
- Host模式:容器直接共享主机的网络栈。没有NAT开销,但没有端口隔离。适用于对性能敏感的服务。
- 自定义桥接网络:同一自定义网络上的容器可以通过容器名互相访问。这是Compose自动创建的。
在VPS上的要点:在docker-compose.yml中显式定义你的子网,而不是让Docker选择。这可以避免与你的托管商内部网络发生冲突。
桥接模式与host模式对比、自定义子网和容器间DNS的详细指南:
没有资源限制和健康检查会发生什么?
没有内存限制时,一个存在内存泄漏的容器会消耗所有可用RAM,触发Linux OOM killer,并导致无关的容器甚至SSH守护进程崩溃。没有健康检查时,Docker无法知道容器已经出故障了。它保持"running"状态,同时返回错误。
检查你的容器是否设置了限制:
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.CPUPerc}}"
如果MEM %列显示了值但你从未设置过限制,这些百分比是相对于主机总RAM计算的。显示45%的容器正在使用你整个VPS内存的45%,而且没有任何东西阻止它占用更多。
docker-compose.yml中的最小生产配置:
services:
app:
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
restart: unless-stopped策略自动重启崩溃的容器但尊重手动停止。healthcheck让Docker标记不健康的容器并根据重试次数重启它们。
Compose资源限制、健康检查模式和重启策略的完整指南:
如何备份Docker卷?
Docker卷在容器外持久化数据。如果卷被删除或损坏,你的数据库、上传文件或配置就会丢失。Docker没有内置的卷备份机制。
列出你的卷及其大小:
docker system df -v | grep -A 100 "Local Volumes space usage"
或检查各个卷的挂载点:
docker volume ls -q | xargs -I {} docker volume inspect {} --format '{{.Name}}: {{.Mountpoint}}'
挂载点默认在/var/lib/docker/volumes/下。你可以用标准Linux工具备份它们,但需要先停止或暂停容器以避免不一致的快照。对于数据库,运行中数据库的文件系统副本不是可靠的备份。你需要先做一个dump。
备份策略、恢复流程和自动化调度:
如何用TLS暴露Docker服务?
在每个容器上直接用-p 443:443发布端口无法扩展到一个以上的服务。你需要一个反向代理来终止TLS、将流量路由到正确的容器并处理证书续期。
检查你的VPS上当前监听的内容:
ss -tlnp | grep -E ':80|:443'
如果你看到应用容器直接绑定到80或443端口,它们在没有代理层的情况下暴露。这意味着每个服务都需要自己的证书管理,而且你无法在一个VPS上托管多个域名。
三个反向代理选项主导Docker生态系统:
| 代理 | 自动发现 | 自动TLS | 配置方式 |
|---|---|---|---|
| Traefik | 是(Docker标签) | 内置Let's Encrypt | 标签 + YAML |
| Caddy | 通过插件 | 默认自动 | Caddyfile |
| Nginx Proxy Manager | Docker socket | Let's Encrypt界面 | Web界面 |
Traefik是Docker原生部署最常见的选择,因为它读取容器标签并自动生成路由规则。添加服务时无需更新配置文件。
各选项的对比、安装指南和TLS配置:
VPS上Docker容器的更新策略是什么?
Docker镜像不会自行更新。如果你运行postgres:16而安全补丁在postgres:16.4中发布,你的容器会继续运行旧镜像,直到你显式拉取并重建容器。
检查你的服务正在使用哪些镜像:
docker compose images
将镜像摘要与最新可用版本进行比较:
docker compose pull --dry-run 2>&1
如果dry-run输出中有镜像显示为"pulled",你的运行版本已经过期。
关键决策:
- 固定镜像标签:在生产环境中永远不要使用
:latest。使用特定版本标签如postgres:16.4或SHA摘要。 - 定时拉取:按计划运行
docker compose pull && docker compose up -d,但先在staging环境测试更新。 - 零停机重启:使用健康检查和
docker compose up -d --no-deps <service>逐个更新服务。 - 镜像扫描:Trivy等工具在部署前扫描你的镜像是否存在已知CVE。
更新策略、镜像固定模式和零停机部署:
生产环境运行Docker需要Kubernetes吗?
不需要。Docker Compose是单服务器工作负载的生产就绪部署工具。Kubernetes解决的是多节点编排、自动扩缩容和跨集群自愈。如果你的应用在一台VPS上运行,Compose提供了你所需的一切,没有Kubernetes的运维开销。
Compose适用的场景:
- 运行3-15个容器的单台VPS
- 静态或可预测的流量模式
- 没有专职平台工程师的小团队
- 更新期间可以容忍几秒停机的服务
超出Compose能力的场景:
- 需要多台服务器实现高可用
- 基于流量峰值的自动扩缩容
- 包含数百个微服务的复杂服务网格
- 需要跨节点蓝绿部署的零停机要求
轻量级的中间方案是K3s,一个精简版Kubernetes,可以在单台VPS上运行,大约占用512 MB的RAM开销。但对于大多数个人项目、SaaS MVP和小型生产应用,Compose是正确的工具。
生产环境的Docker VPS需要多少RAM?
运行Docker生产环境的VPS至少需要4 GB RAM来支持3-5个容器。Docker守护进程本身大约使用100-200 MB。每个容器在此基础上增加自己的内存占用。如果没有设置内存限制,一个行为异常的容器就能耗尽所有资源。
| VPS规格 | 容器数量 | 适用场景 |
|---|---|---|
| 4 GB RAM | 3-5个轻量级 | 博客、API、数据库、Redis |
| 8 GB RAM | 5-10个混合 | SaaS MVP、多服务、监控栈 |
| 16 GB RAM | 10-20个 | 多项目、CI runner、较重的数据库 |
| 32 GB RAM | 20+个 | AI推理、大型数据库、构建服务器 |
存储和RAM同样重要。Docker镜像、卷、日志和构建缓存会不断累积。计划至少40 GB的NVMe存储,并从第一天起就配置中描述的监控。
对于容器化Web服务来说,CPU很少是瓶颈。4个vCPU可以处理大多数工作负载。例外情况:视频转码、AI推理和构建服务器。
需要适配Docker生产工作负载的NVMe存储VPS,请查看Virtua Cloud VPS方案。
生产环境检查清单
在VPS上将Docker投入生产之前,逐项检查:
- 防火墙规则阻止所有Docker发布的端口(通过反向代理的除外)
- 容器尽可能以非root用户运行
- Docker守护进程以rootless模式运行,或容器使用seccomp + no-new-privileges
- 日志轮转已在
/etc/docker/daemon.json中配置 -
docker system prune按计划运行(每周cron) - 每个服务在
docker-compose.yml中都有内存和CPU限制 - 每个服务都有健康检查
- 重启策略已设置(
unless-stopped或on-failure) - 包含重要数据的卷有自动备份
- 反向代理处理TLS终止和证书续期
- 镜像标签固定到特定版本,不使用
:latest - 你有经过测试的更新流程来拉取新镜像
-
journalctl -u docker和docker logs <container>可以工作,你知道在哪里查看
如果你无法勾选每一项,请阅读上面各章节中链接的文章。
版权所有 2026 Virtua.Cloud。保留所有权利。 本内容为 Virtua.Cloud 团队原创作品。 未经书面许可,禁止复制、转载或再分发。