Docker 进阶实战系列(一):容器镜像安全,最小镜像与非 Root 运行
# Docker进阶实战系列(一):容器镜像安全,最小镜像与非Root运行
> 本系列承接上一阶段 Docker Compose 实战,聚焦生产环境容器进阶能力,每篇短小精炼,主打实操落地。
> 前置知识:掌握基础Docker命令、Dockerfile编写、Compose基础使用。
## 前言
很多新手写Dockerfile习惯直接用`ubuntu`、`centos`或者完整`openjdk`大镜像,并且容器内全程用root用户运行。
这种做法有两大隐患:
1. 镜像体积巨大,包含大量无用依赖,漏洞多,被攻击面大
2. 容器内root权限,一旦服务被入侵,攻击者拿到root权限,会横向影响宿主机
本篇我们解决两个核心问题:**制作最小化镜像** + **容器禁止使用root运行**。
## 一、什么是最小镜像?
常见镜像体积对比(参考)
- ubuntu 基础镜像:~70MB
- alpine 基础镜像:~5MB
- 自己打包的带全套工具的Java镜像:几百MB
- 多阶段构建的Java最小镜像:几十MB
核心手段:**多阶段构建(multi-stage build)**
> 构建阶段:下载依赖、编译代码;运行阶段:只拷贝编译产物,不带编译器、源码、包管理器。
## 二、实战1:多阶段构建 SpringBoot 最小镜像
项目目录
demo/
├── Dockerfile
└── target/demo.jar
`Dockerfile`
```dockerfile
# 阶段1:编译构建(只在构建时生效,最终镜像不会保留这一层)
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
# 提前下载依赖,缓存优化
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
# 阶段2:运行镜像,使用轻量alpine jre
FROM openjdk:17-jre-alpine
WORKDIR /app
# 从构建阶段拷贝打好的jar包,其余构建内容全部丢弃
COPY --from=builder /app/target/*.jar app.jar
# 新增普通用户,不使用root
RUN addgroup -g 1001 appgroup && adduser -G appgroup -g "app user" -u 1001 -s /bin/sh -D appuser
# 切换到普通用户
USER appuser
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
构建命令
docker build -t spring-mini:v1 .
# 查看镜像大小
docker images | grep spring-mini
对比:传统单阶段构建镜像体积会大很多,多阶段构建直接剥离编译环境。
三、实战 2:镜像漏洞扫描(trivy)
生产上线前,必须扫描镜像高危漏洞。Trivy 是轻量容器漏洞扫描工具。
安装 & 扫描命令
# 安装trivy(Linux)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# 扫描本地镜像
trivy image spring-mini:v1
扫描结果重点看:CRITICAL(严重)、HIGH(高危)漏洞。
alpine 镜像的漏洞数量远小于 ubuntu/centos 基础镜像。
四、为什么不要容器 root?
默认容器内进程是 root,如果服务存在代码执行漏洞,攻击者拿到容器 root。
在开启特权模式情况下,可以逃逸拿到宿主机权限。
最佳实践:业务容器一律使用普通用户运行。
额外限制:docker-compose 强制非 root
在 compose 文件中指定用户 ID,进一步约束
services:
demo-app:
image: spring-mini:v1
# 指定uid,和Dockerfile创建用户保持一致
user: "1001:1001"
ports:
- "8080:8080"
五、额外避坑清单
- alpine 使用 musl libc,部分二进制程序(如某些 glibc 编译程序)会无法运行,这时改用
debian-slim - 非 root 用户没有权限绑定 1024 以下端口(80、443),业务服务尽量监听 1024 以上端口,前端由 Nginx 代理
- 不要在镜像内保存密钥、密码、token!敏感配置通过环境变量 / Secret 注入
- 不要使用
latest标签,固定版本,防止拉取镜像时意外更新
小结
- 多阶段构建:分离编译环境和运行环境,减小镜像体积,减少漏洞
- 业务容器强制使用普通用户运行,降低容器逃逸风险
- 上线前使用 Trivy 扫描镜像高危漏洞
下一篇预告:Docker 进阶实战系列(二):容器日志驱动与日志轮转,防止日志打满宿主机磁盘