云服务器Docker容器化部署避坑指南:从Overlay2到cgroup v2的深度调优
0. 问题背景
很多人把Docker当虚拟机用,直接在云服务器上 yum install docker 就跑起来。结果磁盘无故暴涨、容器OOM悄无声息、网络延迟抖成心电图。踩坑两年后总结:默认配置只适合“能跑”,远不是“稳跑”。
本文涉及的真实环境为 轻云互联 的4C8G云服务器,系统 CentOS 7.9 + 内核 5.10(升级过)。所有操作均为一线血泪验证。
1. 存储驱动:干掉Overlay,拥抱Overlay2
先确认当前驱动:
docker info | grep "Storage Driver"
大多数云镜像还在用 overlay(不是overlay2),这会导致容器删除后空间不释放,最终占满云盘。Overlay2才是现代推荐方案,而且需要内核支持 overlay2 + lowerdir 多层级。
切换到Overlay2的方式:
systemctl stop docker
vim /etc/docker/daemon.json
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
systemctl start docker
docker info | grep "Storage Driver" # 确认显示 overlay2
如果内核版本低于4.0,override_kernel_check 会强制启用,但建议升级内核。轻云互联的镜像模板支持一键升级到5.x。
2. cgroup v2:别再让内存计入错误片
Docker依赖cgroup做资源隔离。CentOS7默认cgroup v1,但cgroup v2对内存回收更精准,尤其memory.high 和 memory.low 的配合能避免OOM Killer胡乱杀人。
启用cgroup v2(需内核4.5+):
grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1"
reboot
cat /proc/cmdline | grep cgroup # 应有 unified_cgroup_hierarchy=1
docker info | grep "Cgroup" # 显示 Cgroup Driver: cgroupfs 或 systemd
注意:cgroup v2下Docker必须用 cgroupfs 驱动而非 systemd(除非你装了最新的runc>=1.1),否则容器启动失败。修改daemon.json:
{
"exec-opts": ["native.cgroupdriver=cgroupfs"]
}
重启后检查:docker info | grep "Cgroup Driver"
3. 资源限制:别让 --memory 白设
很多人写 docker run -m 512m,但系统内存压力时容器依然被OOM甚至宿主机严重swap。关键参数:
docker run -d --name nginx_demo \
--memory="256m" \
--memory-reservation="128m" \
--memory-swap="256m" \ # swap总上限,等于memory表示不额外使用swap
--cpu-period="100000" \
--cpu-quota="50000" \ # 限制0.5核
--kernel-memory="64m" \ # 限制内核内存
nginx:alpine
真实排错:之前给PHP容器设 -m 512m --memory-swap -1(无上限),结果云服务器内存被吃光,Swap飙升到IO瓶颈。正确做法:--memory-swap 等于 --memory 彻底禁用swap,或设为2倍(如果业务需要换出)。
另外,云服务器上 /proc/sys/vm/swappiness 最好设成1(仍然使用swap,但极低概率),避免容器突发内存占用时系统频繁换页:
echo 1 > /proc/sys/vm/swappiness
sysctl -w vm.swappiness=1
4. 网络性能:bridge模式下的损耗该省则省
Docker默认bridge走NAT,性能损耗约5%-10% L3转发。对于高IO应用(如Redis、数据库),建议用host网络或macvlan。
host网络示例(直接共享宿主机网络栈,无额外封装):
docker run --network host -d nginx:alpine
缺点:端口冲突、无容器隔离。如果安全要求高,上macvlan:
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 mynet
docker run --network mynet --ip 192.168.1.100 -d nginx
注意:macvlan需要主机开启ip_forward,并且容器IP落在同一子网,云服务商的安全组要放行。
5. 日志管理:慎用json-file,避免磁盘爆满
默认json-file日志不自动轮转,一周跑满20G云盘。推荐改用journald或local:
{
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
local驱动是Docker 20.10+专属,性能优于json-file且自动压缩。
6. 安全加固:用户命名空间映射
容器内root默认对应宿主机root,一旦逃逸直接拿root。打开用户映射:
usermod --add-subuids 100000-165535 --add-subgids 100000-165535 dockremap
systemctl restart docker
docker info | grep "Userns" # 显示 Userns: exclusions
此时启动容器都自动使用remapped用户,容器内root在宿主机上是一个普通用户。
7. 实战:一键部署优化后的Nginx+PHP
在轻云互联云服务器上,用上面所有调优参数写一个完整的docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:1.25-alpine
network_mode: host
mem_limit: 256m
mem_reservation: 128m
cpus: 0.5
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
volumes:
- /opt/www:/usr/share/nginx/html:ro
security_opt:
- no-new-privileges:true
php:
image: php:8.2-fpm-alpine
network_mode: host
mem_limit: 512m
cpus: 0.5
user: "1000:1000"
environment:
- PHP_OPCACHE_ENABLE=1
volumes:
- /opt/www:/var/www/html
启动前确保内核参数已调优:
echo "vm.swappiness=1" >> /etc/sysctl.conf
sysctl -p
运行 docker-compose up -d 后,使用 docker stats 观察资源使用,应稳定在设定范围内。如果遇到OOM,优先检查是否没设置 --memory-swap 导致swap吃掉内存。
8. 终极检查脚本
部署完成后建议跑一遍验证:
#!/bin/bash
echo "=== Storage Driver ==="
docker info 2>/dev/null | grep "Storage Driver"
echo "=== Cgroup Driver ==="
docker info 2>/dev/null | grep "Cgroup Driver"
echo "=== Kernel Memory & cgroup v2 ==="
cat /proc/cmdline | grep -o "unified_cgroup_hierarchy=1" || echo "No cgroup v2"
echo "=== Log Driver ==="
docker info 2>/dev/null | grep "Logging Driver"
echo "=== User Namespaces ==="
docker info 2>/dev/null | grep "Userns"
echo "=== Swappiness ==="
cat /proc/sys/vm/swappiness
全部参数都正常后,这台云服务器的Docker性能可媲美轻量级KVM。