ToolkitX
知识库工具箱

Docker 安全

镜像安全、容器隔离、资源限制

25min·高级

01. Docker 安全的基本原则

容器不是虚拟机——它们共享同一个宿主机内核。一个能突破容器隔离的漏洞,理论上可能影响到宿主机和其他容器。 基本原则:最小权限——容器只拿它干活必需的权限。不要用 root 跑容器;限制容器的系统调用(seccomp);只读文件系统;限制资源使用。 威胁面:恶意镜像(从 Docker Hub 随便 pull 可能有后门);容器逃逸(利用内核漏洞突破隔离);资源耗尽(一个容器吃光 CPU/内存);敏感信息泄露(密码写 Dockerfile 里)。 好消息是:Docker 默认的安全配置已经比较合理——AppArmor、seccomp、cgroups 都是默认开启的。关键是你不要主动关掉它们。
bash
# 查看 Docker 安全相关的系统信息
docker info | grep -A5 Security

# 检查镜像的漏洞(需安装 docker scan 或 trivy)
docker scan nginx:latest

# 不安全的做法
# docker run --privileged ...  (给了全部权限,非常危险)
# docker run -v /:/host ...    (挂载宿主机根目录)
docker run --privileged 等于把容器的安全机制全关了。除非你确定知道在做什么,否则永远不要用。

02. 用户与权限——不要用 root 跑容器

默认情况下 Docker 容器里的进程是以 root 运行的(容器内的 root,不是宿主机 root)。虽然 Docker 做了一些隔离,但容器 root 在某些条件下还是能触及宿主机资源。 解决办法:在 Dockerfile 里用 USER 指令切换到一个非 root 用户。或者 docker run --user 1000:1000 指定 UID/GID。 还有一个技巧:user namespace remapping——让容器内的 root 映射到宿主机上一个普通用户。即使容器逃逸了,攻击者也只是个普通用户的权限。在 /etc/docker/daemon.json 里配置 userns-remap。 但要注意:user namespace 开启后,容器内的 root 写不了宿主机的 root 文件,有些需要改系统文件的容器(如 MySQL 初始化)会出问题。
bash
# 以非 root 用户运行
# 在你的 Dockerfile 里:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# 或者启动时指定
docker run -u 1000:1000 --name app node:18

# 开启 user namespace remapping
# /etc/docker/daemon.json
{
  "userns-remap": "default"
}
基础镜像的选择也很重要。alpine 默认就有非 root 用户,而有些镜像默认就是 root,需要注意确认。

03. 资源限制——别让一个容器吃光整台机器

不加限制的容器可以吃掉所有 CPU 和内存——如果某个容器内存泄漏了,整台机器都可能 OOM。用 cgroups 限制资源: 内存限制:--memory(硬限制,超了就杀进程或 OOM)、--memory-reservation(软限制,只在内存紧张时生效)。 CPU 限制:--cpus(限制最多用几核)、--cpu-shares(相对权重,多个容器竞争 CPU 时按权重分配)、--cpuset-cpus(绑定到指定 CPU 核)。 磁盘 IO 限制:--blkio-weight(IO 权重)、--device-read-bps(读速率)、--device-write-bps(写速率)。 还有 pids limit:--pids-limit 限制容器里最多能跑多少个进程,防止 fork 炸弹。
bash
# 内存限制
docker run -d --memory 512m --memory-swap 1g --name app node:18

# CPU 限制
docker run -d --cpus 2 --cpu-shares 512 --name app node:18

# PIDs 限制
docker run -d --pids-limit 100 --name app node:18

# docker-compose 里
deploy:
  resources:
    limits:
      cpus: '2'
      memory: 512M
    reservations:
      cpus: '1'
      memory: 256M
生产环境每个容器都要设资源限制,不然一个容器出问题可能拖垮整台机器的所有服务。

04. 只读文件系统与安全扫描

把容器的根文件系统设为只读——容器运行时不能修改文件。修改数据的路径必须通过 Volume 挂载出去。这样即使容器被入侵,攻击者也没法写恶意程序进容器。 只读文件系统用 --read-only 参数,配合 tmpfs 给需要临时写入的路径(如 /tmp、/var/run)。 镜像安全扫描:用 docker scan(基于 Snyk)或 trivy 扫描镜像里的已知漏洞。CI/CD 流程里集成扫描,发现高危漏洞就拦截部署。 还有内容信任(Docker Content Trust)——用 docker trust 签名镜像,部署时只拉签名过的镜像,防篡改。
bash
# 只读文件系统
docker run -d --read-only --tmpfs /tmp --tmpfs /run nginx

# 安全扫描
docker scan nginx:latest
trivy image nginx:latest

# 启用内容信任
export DOCKER_CONTENT_TRUST=1
docker pull nginx:latest  # 只拉签名的
--read-only 可能会导致一些容器启动失败(因为它们需要在某些路径写临时文件)。用 --tmpfs 给这些路径提供临时可写空间。

05. 敏感信息与 Secrets 管理

永远不要把密码、API Key、证书写在 Dockerfile 里——这些会留在镜像的层历史里,谁拿到镜像都能翻出来。 敏感信息的管理方式: 1. 环境变量(不适合太敏感的信息,docker inspect 能看到) 2. Docker Secrets(Swarm 模式,加密存储,只有指定容器能读) 3. 挂载配置文件(把 secrets 放在宿主机文件里挂载进去) 4. 外部密钥管理服务(Vault、AWS Secrets Manager) 对于 docker-compose,用 env_file 或 secrets 配置管理敏感数据。至少不要把秘密写进 docker-compose.yml 然后提交 git。
bash
# Docker Swarm Secrets
# 创建 secret
printf "MySecretPassword" | docker secret create db_password -

# 使用 secret
# docker-compose.yml (Swarm)
services:
  db:
    image: mysql
    secrets:
      - db_password
secrets:
  db_password:
    external: true

# 不用 secrets 时至少用 env 文件
# .env 文件(加进 .gitignore)
# DB_PASSWORD=MySecretPassword
# docker run --env-file .env mysql
docker inspect 容器名 能看到所有环境变量。不要把敏感信息放到 --env 或 ENV 里,用 secrets 或文件挂载。

知识测验

1/5正确 0

docker run --privileged 是什么意思?

下一节

Docker 镜像优化

下一节