使用 cgroups 冻结器(freezer)实现空闲停机
解读
国内云原生面试常把“资源利用率”与“成本优化”挂钩。
空闲停机指在业务低峰期把容器内进程全部挂起,CPU 瞬间降为 0,内存保留但不被调度,从而节省宿主机 CPU 配额与云厂商计费周期。
cgroups freezer 子系统是 Linux 内核提供的“时间片冻结”机制,可将指定 cgroup 内所有任务状态置为 FREEZING→FROZEN,实现秒级挂起与恢复,无需修改应用代码,也不依赖虚拟机暂停。
Docker 自 20.10 起已把 freezer cgroup 暴露给 --cgroup-parent,但默认不启用;国内主流内核(CentOS 7.9 3.10.0-1160+、TencentOS 4、Alibaba Cloud Linux 3)均已合入 freezer 补丁,满足生产条件。
面试官想考察:
- 是否知道 freezer 与
SIGSTOP的本质区别(内核调度器旁路 vs 信号投递) - 能否在 Docker 生态里闭环:镜像、运行时、编排、监控、自动唤醒
- 对国内云厂商计费模型的理解(按量付费 CPU 秒级账单、包年包月闲置退费策略)
知识点
- cgroups v1 freezer 子系统接口:
cgroup.freeze写 1 冻结、写 0 解冻;状态文件cgroup.events可 poll 监听 - cgroups v2 unified hierarchy:freezer 功能合并进
cgroup.freeze,路径统一在/sys/fs/cgroup/docker/xxx.scope/ - Docker 挂载规则:
--cgroup-parent指定自定义 slice,需宿主机/sys/fs/cgroup/freezer已挂载;runc1.0+ 自动识别 freezer 控制器 - runc 补丁:2018 年社区提交
freezer-state.json,保证runc pause使用 freezer 而非SIGSTOP;containerd 1.4+ 默认开启 - 信号差异:
SIGSTOP无法冻结 PID namespace 内 init 进程,且不能冻结内核线程;freezer 对整个 cgroup 原子操作,无惧孤儿进程 - 内存行为:冻结后内存驻留,未被回收;若需进一步降本,可再触发
memory.force_empty或配合swapoff - Kubernetes 集成:kubelet 1.22+ 内置
SuspendContainerCRI API,底层即调用 freezer;国内 ACK、TKE、CCE 均已灰度 - 自动唤醒策略:cron +
cgroup.freeze=0、Prometheus 指标container_cpu_usage_seconds_total < 0.01持续 300s、或基于 QPS 阈值 - 计费收益:阿里云容器实例按 CPU 秒计费,冻结后 CPU 用量归零,账单下降 30%~70%;腾讯云 EKS 对
BestEffortPod 冻结不计费 - 风险:冻结时间超过 Docker 健康检查周期会被判定
unhealthy,需同步调整HEALTHCHECK --interval或编排层livenessProbe;Java 应用长时间冻结可能触发GC lockerStall,需添加-XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+G1SuspendOnTTE
答案
1. 前置检查
# 确认宿主机已挂载 freezer
mount | grep freezer
# 若为空,执行
sudo mkdir -p /sys/fs/cgroup/freezer
sudo mount -t cgroup -ofreezer freezer /sys/fs/cgroup/freezer
2. 运行容器并指定 cgroup 父节点
docker run -d --name demo \
--cgroup-parent /docker/freezer.slice \
-p 8080:8080 \
myapp:alpine
此时 Docker 会在 /sys/fs/cgroup/freezer/docker/freezer.slice/docker-xxx.scope/ 创建对应目录
3. 手动冻结与解冻(验证阶段)
# 冻结
echo 1 | sudo tee /sys/fs/cgroup/freezer/docker/freezer.slice/docker-xxx.scope/cgroup.freeze
# 查看状态
cat /sys/fs/cgroup/freezer/docker/freezer.slice/docker-xxx.scope/cgroup.events
# 输出 frozen 1 表示成功
# 解冻
echo 0 | sudo tee /sys/fs/cgroup/freezer/docker/freezer.slice/docker-xxx.scope/cgroup.freeze
4. 自动化闭环脚本(生产示例)
#!/bin/bash
CGROUP_ROOT="/sys/fs/cgroup/freezer/docker/freezer.slice"
THRESHOLD_CPU="0.01"
DURATION="300"
while true; do
for scope in "$CGROUP_ROOT"/docker-*.scope; do
CONTAINER_ID=$(basename "$scope" | sed 's/docker-\(.*\)\.scope/\1/')
CPU_USAGE=$(docker stats --no-stream --format "{{.CPUPerc}}" "$CONTAINER_ID" | sed 's/%//')
# 低于阈值累计 5 分钟则冻结
if (( $(echo "$CPU_USAGE < $THRESHOLD_CPU" | bc -l) )); then
touch /tmp/${CONTAINER_ID}.idle
if [[ $(find /tmp/${CONTAINER_ID}.idle -mmin +5) ]]; then
echo 1 > "$scope/cgroup.freeze"
logger "Frozen $CONTAINER_ID"
fi
else
rm -f /tmp/${CONTAINER_ID}.idle
echo 0 > "$scope/cgroup.freeze" 2>/dev/null
fi
done
sleep 30
done
脚本放 systemd timer,每 30s 巡检一次,实现“空闲 5 分钟即冻结,有流量立即解冻”
5. 与 CI/CD 集成
在 GitLab CI 中新增 freeze 阶段,调用上述脚本;解冻由 Ingress gateway 钩子触发:首次请求到达时执行 echo 0 > cgroup.freeze,实现“冷启动”秒级恢复
6. 监控与可观测
- Prometheus 暴露
cgroup_freezer_frozen_state指标,通过 node-exporter textfile 收集 - Grafana 大盘叠加 CPU 账单曲线,直观展示冻结带来的成本下降
- 告警规则:冻结超过 24h 的容器自动发出提示,避免遗忘导致业务雪崩
拓展思考
- 双策略降本:freezer 只省 CPU,内存仍计费;可再引入 Kubernetes Vertical Pod Autoscaler (VPA) 在冻结前把内存 request 调到最小,解冻后动态扩容,实现“CPU+内存”双降
- Serverless 冷启动对比:freezer 恢复时间 50~100ms,优于函数计算 800ms,但需常驻内存;对于 <1min 一次调用的场景,直接改用 Knative 按需缩零更划算
- 安全隔离:freezer 需要宿主机
/sys/fs/cgroup/freezer可写,多租户场景需开启cgroup namespaces与rdma隔离,防止恶意容器冻结他人 slice;可配合 AppArmor 规则deny mount /sys/fs/cgroup/**限制越权 - cgroups v3 展望:社区已讨论把 freezer 功能整合进 core scheduling,未来可做到“冻结+迁移”到低成本离线核,进一步降低整机功耗;国内大厂可提前参与 龙蜥社区 cgroups v3 SIG,贡献场景案例
- 合规风险:部分金融监管要求交易流水实时落盘,冻结可能导致缓冲区延迟写入;需在应用层实现
fsyncon freeze hook,或把 freezer 排除核心支付容器,仅对只读查询服务生效