生产安全与资源限制
1. 概念
容器默认配置不安全。容器共享宿主内核,默认 root + 无资源限制 = 被入侵后直接提权 + 耗尽宿主资源。生产必须加固。
2. 非 root 运行(最重要)
FROM node:20-alpine
WORKDIR /app
COPY --chown=node:node . .
USER node
CMD ["node", "server.js"]
K8s 强制:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
为什么:容器内 root = 宿主内核 root uid 0。容器逃逸漏洞(如 runc CVE-2024-21626)利用容器 root 提权到宿主 root。
3. 只读文件系统
docker run --read-only --tmpfs /tmp --tmpfs /var/cache myimage
# K8s
securityContext:
readOnlyRootFilesystem: true
应用需要写的目录用 tmpfs / volume。大多数前端应用只需 /tmp。
4. capabilities 最小化
Linux capabilities 把 root 权限切成小块。容器默认保留一些 cap(如 NET_BIND_SERVICE、SETUID)。
# 看容器默认 cap
docker run --rm alpine grep Cap /proc/1/status
# 全部去掉
docker run --cap-drop ALL myimage
# 只加需要的
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myimage
K8s:
securityContext:
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
80% 的应用 cap-drop ALL 就够。
5. seccomp profile
系统调用白名单。Docker 默认的 seccomp profile 已禁了约 50 个危险 syscall(如 ptrace、mount、keyctl)。
生产不要 --security-opt seccomp=unconfined。
6. AppArmor / SELinux
强制访问控制。限制容器能读写什么文件、连什么网络。Docker 默认自带 AppArmor profile。
加固不需要自己写,确认没关掉就行:
docker info | grep -i apparmor
# Security Options: apparmor
7. 资源限制
7.1 内存
docker run -m 2g --memory-swap 2g myimage
# -m:最大内存
# --memory-swap:含 swap(设成和 -m 一样 = 禁 swap)
超 memory limit → OOM kill 容器。Node 应用对应设 --max-old-space-size < 容器 limit 的 75%:
docker run -m 2g -e NODE_OPTIONS="--max-old-space-size=1536" myimage
7.2 CPU
docker run --cpus 2.0 myimage # 最多用 2 核
docker run --cpu-shares 512 myimage # 相对权重(默认 1024)
docker run --cpuset-cpus 0,1 myimage # 绑定 CPU 0 和 1
--cpus 是硬限制,--cpu-shares 是竞争时的分配比例。
7.3 PID 限制
docker run --pids-limit 200 myimage
防 fork 炸弹。
7.4 磁盘
docker run --storage-opt size=5G myimage # 需要 overlay2 + xfs + pquota
更常用:不设容器磁盘限制,但监控 volume 使用率。
8. 网络隔离
docker run --network none myimage # 完全无网络
多容器场景:用不同 network 隔离。
services:
web:
networks: [frontend, backend]
db:
networks: [backend] # 对外不可见
nginx:
networks: [frontend]
networks:
frontend:
backend:
internal: true # 无外网
9. 镜像安全
9.1 锁定版本
FROM node:20.11.1-alpine3.19@sha256:abc123
9.2 不装不必要的包
# ✗
RUN apk add bash curl wget vim git openssh
# ✓ 生产镜像什么都不装
FROM gcr.io/distroless/nodejs20
9.3 CI 扫描
每次构建都跑 Trivy / Scout,高危漏洞阻断部署。
9.4 签名验证
Cosign 签名 + K8s admission policy 强制验签。
10. Docker daemon 安全
// /etc/docker/daemon.json
{
"userns-remap": "default", // 开 user namespace(容器 root → 宿主非 root)
"live-restore": true, // daemon 重启不杀容器
"no-new-privileges": true, // 禁止进程获取额外权限
"icc": false, // 禁止容器间通信(除非显式 link)
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Docker socket(/var/run/docker.sock)不要挂进容器,等于给容器宿主 root 权限。
11. Rootless Docker
完全不以 root 跑 Docker daemon:
dockerd-rootless-setuptool.sh install
更安全(daemon 没 root),但兼容性差(不能绑 1024 以下端口等)。
12. K8s 安全策略
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
spec:
privileged: false
runAsUser:
rule: MustRunAsNonRoot
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
allowedCapabilities: []
requiredDropCapabilities:
- ALL
readOnlyRootFilesystem: true
PodSecurityPolicy 已被 Pod Security Admission 替代。
13. 安全检查清单
生产容器上线前:
- 非 root 运行
- 镜像扫描无高危漏洞
- 资源限制(memory + CPU)
- 只读文件系统 + tmpfs
- capabilities drop ALL
- 不挂 Docker socket
- 镜像签名
- 日志限制
- 网络隔离
- 无硬编码 secret
14. 常见反模式
- 容器跑 root:一条容器逃逸 = 宿主 root
--privileged:等于关掉所有安全机制- 挂
/var/run/docker.sock:给容器完全宿主控制权 - 不限内存:内存泄漏拖垮宿主所有容器
- 用
latest基础镜像不扫描:含 CVE 上线 - 关 seccomp / AppArmor "解决兼容问题":等于关安全门
- 容器内装 SSH:应用
docker exec或 kubectl exec 调试 - 没设日志上限:stdout 日志写满磁盘