Docker容器跑生产环境稳不稳,问题出在哪
Docker容器跑生产环境稳不稳,问题通常不在Docker本身
Docker跑生产环境稳不稳,这个问题在机房、云主机、Kubernetes集群里都被问过很多次。实际使用中发现,容器本身不是最容易出问题的地方,真正出问题的往往是资源隔离、网络、存储、镜像管理、日志、监控、宿主机内核这些环节。
同一个业务,裸机跑得稳,搬到Docker里抖;也有反过来的,原来部署混乱,容器化之后版本固定、回滚清晰,稳定性反而上去了。所以不能简单说Docker稳或者不稳,得看它被怎么用。
生产环境里Docker最怕的不是“容器挂了”,容器挂了还能重启。更麻烦的是容器没挂,但业务慢、网络抖、磁盘IO打满、日志把盘写爆、宿主机conntrack满了、某个limit没配导致整个节点被拖死。
容器不是虚拟机,别按虚拟机习惯去用
很多生产事故一开始就埋在认知里:把Docker容器当成一台轻量虚拟机用。进去改配置、手工装包、容器里跑一堆进程、日志写本地、数据也放容器层,跑一段时间以后,问题就开始堆。
Docker的优势在于进程隔离、环境一致、镜像可复现、启动快。它不适合被当成“可长期手工维护的小服务器”。生产里应该尽量做到容器无状态,配置外置,数据挂载到volume或独立存储,日志输出到stdout/stderr再统一采集。
这里补充一点,容器不是不允许跑多进程,但如果一个容器里同时塞Nginx、PHP-FPM、crond、日志切割、sidecar脚本,后面排障会很难。一个进程异常退出,容器还活着;主业务卡死,健康检查还误判正常。这种问题在线上很常见。
CPU和内存限制没配好,稳定性会很难看
Docker默认不限制资源时,容器会尽量吃宿主机资源。测试环境可能看不出来,到了生产环境,某个Java服务突发Full GC、某个PHP任务跑批、某个Node进程内存泄漏,很容易把宿主机拖到swap甚至OOM。
常见情况是:一台8C 16G的宿主机上跑了十几个容器,平时CPU 20%,内存50%,看起来很健康。活动一来,某个容器CPU冲到700%,其他容器延迟跟着升高。业务层看到的是接口超时,运维层看到的是宿主机load飙到30以上。
CPU限制要看业务类型。Web API可以用cpu quota限制峰值,避免单容器抢占太猛;批处理任务要分开节点,不要和在线服务混跑。内存更要明确设置,尤其是JVM应用,容器内存限制和JVM参数必须匹配。比如容器限制2G,JVM还按宿主机16G识别去分配堆,后面就是OOMKilled。
生产里经常看到的资源配置问题
场景A:容器未设置memory limit,服务内存泄漏,宿主机触发OOM,内核随机杀进程,有时杀到的不是问题容器,而是关键服务。
场景B:容器设置了memory limit,但应用没有适配。Java服务限制2G,Xmx设置到2G,堆外内存、线程栈、Direct Buffer再占一部分,容器很快被OOMKilled。
场景C:CPU limit设得太低。原来服务峰值需要2C,容器只给0.5C,低峰没问题,高峰大量请求排队,P99延迟从80ms升到2s。
场景D:只看平均CPU。宿主机平均CPU 40%,但单核已经打满。像Redis、Nginx部分场景对单核性能很敏感,平均值会掩盖问题。
网络问题比想象中更容易踩坑
Docker网络默认bridge模式够用,但生产里一旦流量大、连接数高、服务多,问题会明显增加。最常见的是NAT、conntrack、端口映射、DNS解析、MTU这些点。
比如高并发短连接业务,容器通过bridge出网,宿主机conntrack表压力会很大。conntrack满了以后,表现不是机器宕机,而是随机连接失败、偶发超时、服务之间调用时好时坏。开发侧看到的是“接口偶发失败”,但根因在宿主机网络栈。
还有一种是Docker内置DNS解析慢。容器里访问外部域名,偶发解析超时,应用层重试之后延迟放大。生产中如果服务依赖域名访问,比如对象存储、支付接口、API网关,这类问题会造成很隐蔽的抖动。
MTU也别忽略。跨云、VPN、隧道、容器网络叠加以后,MTU不一致可能导致大包传输异常。小请求正常,大文件上传慢,TLS握手偶发失败,这些都可能和MTU有关。
网络吞吐和线路质量不能只看Docker
容器网络再怎么调,底层云服务器线路不稳也没用。游戏、跨境业务、海外API、视频分发这类场景,网络质量比容器参数更关键。实际选机器时,除了CPU和内存,还要看带宽峰值、回国线路、丢包率、DDoS防护、晚高峰表现。
如果业务面向海外或国内访问海外节点,可以关注三网精品、软银直连这类线路。比如需要美国节点承载较大下载、转发、Web业务,可以看129云的美国精品大宽带-C型,8C、8G DDR4 ECC、120G SSD、1Gbps峰值、2.5TB流量,适合有带宽峰值需求的Docker生产部署。预算小、业务轻量的Web服务,可以看美国精品网-A型,2C 2G、40Mbps峰值,适合跑反代、轻量API、管理后台这类负载。
日本方向如果更关注亚洲访问延迟,软银直连线路会比普通绕路线路体验稳定一些。129云日本活动机型是4C 4G、30GB SSD、8Mbps、软银直连,适合轻量业务、节点探测、区域服务部署。选型时可以直接问客服线路、测试IP和防护情况,热线400-9177118。
存储是容器生产环境里的高风险区
Docker容器层不适合放生产数据。这个话说起来简单,但线上事故里还是经常见到数据库、上传文件、缓存数据直接写在容器内部。一旦容器被删除、镜像更新、节点迁移,数据恢复会很被动。
生产环境里,MySQL、PostgreSQL、Elasticsearch这类有状态服务放进Docker不是不行,但存储必须认真规划。数据目录挂载到独立磁盘或可靠volume,备份要能恢复,权限要固定,磁盘IO要监控。
OverlayFS在大量小文件、频繁写入、日志密集场景下性能并不理想。比如一个日志量很大的服务,每秒写几千行到容器内文件,跑几天以后磁盘占用、inode、IO wait都可能出问题。更好的做法是日志打到stdout,由采集器处理,或者挂载专门日志目录,并设置清理策略。
数据库跑容器要特别看IO延迟
有些人测试MySQL容器时只看QPS,不看fsync延迟。生产里磁盘抖一下,数据库响应就会抖。SSD不等于稳定低延迟,云盘、宿主机本地盘、RAID策略、快照任务都会影响IO。
一个常见现象:业务低峰时MySQL查询1ms到5ms,高峰或备份时变成50ms到200ms。应用连接池被占满,接口开始排队,最后表现为整站慢。容器只是承载方式,真正的瓶颈在磁盘和连接池。
如果数据库规模不大,Docker部署可以接受;如果是核心交易库、订单库、账务库,更建议用专门的数据库实例或独立节点,容器化要配合主从、备份、监控、恢复演练,而不是只写一个docker run命令。
日志不管,Docker节点迟早被写爆
生产里Docker日志写爆磁盘是非常常见的故障。默认json-file日志驱动如果没有设置max-size和max-file,容器输出越多,宿主机磁盘就越危险。某个异常循环刷日志,一晚上写几十GB并不夸张。
磁盘满以后,影响范围会扩大。容器无法写日志、应用无法写临时文件、数据库无法写binlog、Docker自身也可能异常。业务侧看到的可能是登录失败、上传失败、任务卡住,而不是明确提示“磁盘满了”。
生产建议至少设置日志轮转,比如单文件100MB、保留3到5个。高日志量服务要接入ELK、Loki、ClickHouse或云日志服务。还有一点,日志级别别长期放debug,尤其是网关、支付回调、游戏服连接日志这种高频路径。
镜像管理混乱,会让回滚变成猜谜
Docker生产环境最舒服的地方之一是镜像版本固定,但前提是团队真的在用版本固定。实际使用中发现,有些部署脚本一直拉latest,或者镜像tag叫prod、online、new,出了问题根本不知道线上跑的是哪次构建。
镜像tag应该和Git commit、构建号、发布时间能对应起来。比如 api:2026-05-18-1432-a8c91f 这种虽然长,但排障时很清楚。回滚也不是重新构建,而是拉回上一个已验证镜像。
镜像还要注意体积和安全。一个简单API镜像做成2GB,里面带了编译工具、测试文件、密钥残留,启动慢,漏洞多,传输也慢。生产镜像尽量用多阶段构建,运行时只保留必要依赖。
不要在运行中的容器里改东西
线上临时进容器改配置,看似救急,实际后患很大。容器重启后改动没了,扩容的新容器也没有这个改动。更麻烦的是,排障时没人知道这台容器和镜像已经不一致。
配置应该放在环境变量、配置中心、挂载文件或Secret里。改动通过发布流程进入,而不是靠ssh进去手敲。短期救火可以理解,但救完要补回镜像和配置,否则下一次故障会更难查。
健康检查不是写个端口探测就完事
很多容器编排里都会配置healthcheck,但只检查端口是否打开意义有限。进程活着不代表服务可用,端口监听不代表依赖正常。
比如一个API服务端口还在,但数据库连接池已经耗尽;Nginx还在监听80端口,但后端 upstream 全挂;Java进程还在,但线程池队列堆满,接口响应需要10秒。只用TCP探测,这些都可能被判定健康。
生产环境的健康检查至少要分清存活检查和就绪检查。存活检查判断进程是否需要重启,就绪检查判断是否能接流量。服务启动时,如果依赖初始化要30秒,就绪检查应该等它真的准备好,不要容器刚启动就被流量打进来。
宿主机才是很多容器故障的根
Docker跑在宿主机上,宿主机内核、文件系统、网卡、磁盘、时间同步、系统参数都会影响容器。容器化之后,有些人只盯容器监控,不看宿主机,问题就会漏掉。
常见宿主机指标要看:load average、CPU steal、iowait、内存可用量、swap、磁盘使用率、inode、网卡丢包、TCP重传、conntrack使用率、文件句柄数、系统日志。
CPU steal在云服务器上尤其要关注。如果业务CPU使用率不高,但延迟上升,steal偏高,说明虚拟化层资源被抢占。容器里看应用没问题,宿主机看也未必明显,必须结合云厂商监控和压测结果判断。
时间同步也会引发很奇怪的问题。容器共享宿主机时间,宿主机NTP异常时,Token过期判断、日志时间、分布式锁、证书校验都可能出错。这个问题不高频,但一旦发生,排查很绕。
容器重启策略救不了所有故障
restart=always看起来很安心,但它只能处理进程退出,处理不了服务假死、依赖异常、数据损坏、配置错误。还有些服务崩溃后立即重启,进入循环,日志疯狂刷,反而把节点拖垮。
生产里要关注容器重启次数。一个容器一天重启一两次可能被忽略,但如果每小时重启十几次,只是因为自动拉起来了,业务并不是真的稳定。Kubernetes里CrashLoopBackOff也是类似道理,它不是问题本身,是问题暴露出来的状态。
应用要能优雅退出。收到SIGTERM后停止接新请求,处理完已有请求,再关闭连接。否则发布或扩缩容时,请求会被硬切,用户看到的就是偶发502、连接中断、订单状态不一致。
安全边界别高估Docker
Docker有namespace、cgroups等隔离能力,但它不是强隔离虚拟机。生产里不要随便给容器privileged权限,不要把宿主机/docker.sock挂进业务容器,不要让容器用root权限跑所有东西。
挂载docker.sock风险很高。容器拿到它,基本就能控制宿主机Docker,进一步影响其他容器。很多CI/CD工具、管理面板为了方便会这么做,生产环境要非常谨慎。
镜像来源也要管。公网随便拉的镜像可能带漏洞、后门、挖矿脚本。生产镜像最好经过内部仓库、漏洞扫描、签名或至少固定digest。基础镜像要定期更新,不然OpenSSL、glibc、curl这些漏洞会长期留在线上。
单机Docker和集群编排不是一回事
单机Docker适合小业务、轻量服务、反代、任务服务、开发测试环境。生产规模一大,服务发现、滚动发布、自动扩缩容、故障迁移、配置管理、Secret管理都需要更完整的编排能力。
Kubernetes能解决一部分问题,但也会带来新的复杂度。etcd、CNI、Ingress、Service、StorageClass、Pod调度、资源限制,每一层都有坑。小团队如果业务不复杂,直接用Docker Compose加监控和备份,反而更可控。服务数量多、发布频繁、多节点高可用,再考虑Kubernetes更合适。
多说一句,不要因为“生产环境都上Kubernetes”就硬上。Kubernetes集群本身也要维护,证书过期、节点NotReady、CoreDNS异常、CNI故障、镜像拉取失败,这些都是真实会发生的事情。
压测不能只压应用,还要压容器链路
很多团队压测只测接口QPS,不测容器重启、镜像拉取、日志写入、网络连接数、磁盘写入、发布过程。结果接口压测看起来没问题,上线后发布一滚动,流量一切换,问题就来了。
生产前建议模拟这些场景:容器被kill后能否恢复;节点重启后服务是否自动拉起;日志暴增时磁盘是否会满;数据库慢时接口是否会拖垮线程池;外部DNS异常时服务是否有超时控制;镜像仓库不可用时能否回滚;高并发短连接下conntrack是否接近上限。
数据上可以有个参考:普通Web API单容器如果稳定在500到2000 RPS,要看语言栈、SQL复杂度、缓存命中率和CPU型号;Nginx反代单容器上万并发并不稀奇,但连接数、文件句柄、网卡队列、内核参数必须跟上;Java服务P99延迟如果从100ms涨到1s,通常不是单一指标能解释,要同时看GC、线程池、数据库、网络重传。
生产部署时更关注这些现象
容器频繁OOMKilled
看内存limit、应用堆内存、堆外内存、线程数、缓存大小。不要只把limit调大,先确认是不是泄漏。Java、Go、Node都有各自的内存观察方式,容器层只能看到结果。
接口偶发502或504
看Nginx upstream、应用重启、健康检查、发布过程、连接池、DNS解析、TCP重传。502不一定是网关问题,很多时候是后端容器重启或响应超时。
节点load很高但CPU不高
重点看iowait、磁盘延迟、NFS或云盘状态、日志写入、swap。load高不等于CPU忙,很多阻塞在IO上的进程也会把load顶上去。
容器之间调用偶发慢
看Docker bridge、CNI、conntrack、DNS、服务发现、MTU。应用层重试会放大慢调用,一个请求重试三次,链路压力就不是原来的压力了。
发布后部分实例正常,部分实例异常
看镜像tag是否一致、配置是否一致、环境变量是否缺失、挂载目录权限、节点差异。生产里最怕“同一个服务,不同节点行为不一样”,这种通常不是代码问题,而是环境漂移。
Docker跑生产环境,机器选型别只看价格
容器密度越高,对宿主机稳定性要求越高。一台机器上跑二十个容器,宿主机抖一下就是二十个服务一起抖。CPU型号、ECC内存、SSD质量、带宽峰值、防御能力、线路质量,这些都会影响容器生产体验。
轻量业务可以用2C 2G跑几个服务,但要注意别把数据库、缓存、Web、任务队列全塞进去还不设限制。中等业务建议从4C 8G或8C 8G起步,看业务是吃CPU、吃内存还是吃带宽。大带宽业务要单独看流量包和峰值限制,1Gbps峰值和40Mbps峰值完全不是一个使用场景。
如果需要海外Docker节点,尤其是游戏、企业出海、反向代理、高防业务,可以看看129云的美国精品线路和日本软银直连产品。容器部署本身不挑云厂商,但生产环境挑网络和宿主机质量,晚高峰丢包、绕路、带宽缩水,比Docker参数更影响用户体验。
生产里更推荐的部署习惯
镜像固定版本,不用latest跑线上。
容器设置CPU和内存限制,应用参数跟着限制调整。
日志设置轮转,高频日志接入集中采集。
数据不写容器层,数据库和上传文件使用独立volume或外部存储。
健康检查区分存活和就绪,不让没准备好的容器接流量。
发布支持回滚,回滚使用已验证镜像,不现场重新构建。
宿主机监控和容器监控一起看,别只盯容器面板。
网络连接数、conntrack、文件句柄、磁盘inode这些指标要纳入告警。
安全权限收紧,少用privileged,别随便挂docker.sock。
定期做恢复演练,备份文件能恢复出来才算真的有备份。
有些业务不适合硬塞进Docker
不是所有东西都适合容器化。对内核参数高度依赖、需要特殊硬件驱动、极端低延迟、强状态、高IO数据库、老旧授权软件,这些业务容器化成本可能比收益高。
比如高频交易、特定GPU驱动绑定、复杂存储集群、强依赖本机IP授权的软件,放进Docker之前要认真评估。能跑和适合跑是两回事。生产环境里,稳定比形式重要。
也有一些业务很适合Docker:Web API、前端SSR、Nginx反代、定时任务、异步Worker、CI构建环境、测试环境、微服务实例。这类服务容器化后,发布、扩容、回滚都会清晰很多。
真正的问题通常出在边界处
Docker容器内部看起来很干净,但生产故障经常发生在边界处:容器和宿主机的边界、容器和网络的边界、容器和存储的边界、应用和编排系统的边界。
应用开发只看代码,运维只看机器,网络只看链路,数据库只看实例,各看各的,很容易漏掉容器化带来的中间层问题。排障时要把链路拉直:用户请求进来,经过DNS、线路、负载均衡、宿主机网卡、Docker网络、容器端口、应用线程、数据库连接、磁盘写入,每一段都可能是瓶颈。
线上容器稳定运行一年不重启的情况有,三天两头OOM的情况也有。差别通常不在Docker版本,而在资源限制、镜像规范、日志策略、宿主机质量、网络线路、监控告警、发布流程这些细节。
排障时可以从最朴素的命令看起
docker ps看状态,docker inspect看配置,docker logs看输出,docker stats看资源,dmesg看OOM和内核错误,journalctl看Docker服务状态,ss看连接,df和du看磁盘,top和pidstat看CPU,iostat看磁盘延迟,conntrack看连接跟踪。
不要一上来就怀疑框架、怀疑Docker、怀疑云厂商。先把现象落到数据上:哪个时间点开始,影响哪些容器,是否集中在某个节点,是否伴随发布,是否伴随流量上涨,宿主机有没有重启,磁盘和网络有没有异常。
很多生产问题查到最后都很朴素:日志没限大小,磁盘满了;内存limit太小,被OOMKilled;镜像tag混乱,发布了旧版本;DNS超时,依赖服务访问慢;宿主机conntrack满了,连接随机失败;云线路晚高峰丢包,用户访问抖动。
Docker本身可以跑生产,而且大量生产系统已经这么跑了。它带来的不是天然稳定,而是更标准的交付方式。稳定性要靠资源、网络、存储、监控、发布和宿主机一起撑住。