Shell脚本如何定制化基础镜像(高效自动化指南)
目录导读
为什么需要定制化基础镜像?
在日常开发或运维中,官方镜像(如 ubuntu:latest、python:3.9)虽然方便,但往往包含大量冗余软件包,导致镜像体积臃肿、安全漏洞增加。通过Shell脚本定制化基础镜像,你可以:

- 精简体积:仅保留运行所需的最小依赖,使镜像从1.2GB压缩至300MB以下。
- 固化安全基线:自动执行补丁更新、禁用root登录、加固SSH配置。
- 统一团队标准:一次编写,多项目复用,避免每个Dockerfile重复写相同命令。
真实案例:某金融公司通过Shell脚本定制化 alpine:3.18,将基础镜像从50MB降至8MB,同时集成了内部CA证书和监控探针,部署效率提升了40%。
Shell脚本定制镜像的核心思路
定制化不是“硬编码”,而是遵循 “分层构建 + 环境感知 + 幂等性” 原则:
- 分层构建:将基础系统层(如apt更新)、运行时层(如Python)、应用层(如配置文件)分开,利用Docker缓存加速。
- 环境感知:通过传入变量(如
$BASE_IMAGE、$TIMEZONE)实现不同环境(开发/生产)的差异化配置。 - 幂等性:脚本多次执行结果一致,
apt-get install -y --no-install-recommends确保不安装建议包。
关键语法点:在Shell脚本中,
set -euxo pipefail能提前捕获错误,防止镜像构建中途失败而产生脏缓存。
实战:三步编写定制化Shell脚本
Step 1:编写基础镜像构建脚本 (build_base.sh)
#!/bin/bash
set -euxo pipefail
BASE_IMAGE="${1:-ubuntu:22.04}"
OUTPUT_TAG="${2:-mybase:latest}"
# 清理旧镜像减少磁盘占用
docker rmi $OUTPUT_TAG 2>/dev/null || true
# 使用临时Dockerfile内联方式(避免额外文件)
cat << EOF | docker build -t $OUTPUT_TAG -f - .
FROM $BASE_IMAGE
ENV DEBIAN_FRONTEND=noninteractive \
TZ=Asia/Shanghai
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
vim-tiny \
&& rm -rf /var/lib/apt/lists/*
# 添加自定义用户和权限
RUN groupadd -r myapp && useradd -r -g myapp -m -s /bin/bash myapp
EOF
执行方式:./build_base.sh alpine:3.18 custom-alpine:v1
Step 2:在Dockerfile中调用脚本
不需要重复写所有命令,直接引用脚本:
FROM scratch AS base COPY --from=build /base-scripts/ ./scripts/ RUN ./scripts/build_base.sh
Step 3:集成健康检查与日志
在脚本末尾追加:
# 添加S6-overlay作为init系统(适合服务化运行) S6_VERSION="v3.1.5.1" curl -L https://github.com/just-containers/s6-overlay/releases/download/$S6_VERSION/s6-overlay-amd64.tar.xz | tar Jx -C /
最佳实践与常见错误规避
| 错误做法 | 正确做法 |
|---|---|
| 在脚本中写死版本号 | 用变量 $PYTHON_VERSION=${PYTHON_VERSION:-3.11.2} 动态传递 |
| 忽略层缓存 | 将变动频繁的步骤(如应用代码)放在Dockerfile末尾 |
直接使用rm -rf删除文件 |
使用 && 串联命令,确保在同一RUN层中清理 |
| 不处理apt/dnf缓存 | 安装后立即执行 rm -rf /var/lib/apt/lists/* |
安全提示:如果需要在镜像中传递敏感信息(如Token),请使用Docker构建的 --secret 选项,而非环境变量。
问答环节:你关心的问题
Q1:Shell脚本和Dockerfile应该分开维护吗?
A:建议将Shell脚本放在独立仓库(如 base-image-templates/),通过 git submodule 引入各项目Dockerfile目录,这样修改一次脚本,所有项目自动受益。
Q2:怎么给不同架构(ARM/x86)定制镜像?
A:在脚本中检测 uname -m 或利用Docker Buildx工具:
arch=$(uname -m)
if [ "$arch" = "aarch64" ]; then
echo "arm64" > /tmp/arch.txt
fi
Q3:定制后的镜像如何做安全扫描?
A:在脚本末尾集成 trivy image 扫描命令,如果不通过则直接退出构建:
trivy image --severity HIGH,CRITICAL --exit-code 1 "$OUTPUT_TAG"
Q4:有没有更简洁的方式复用脚本?
A:推荐将常用函数(如 install_deps()、add_user())封装在 functions.sh 里,然后通过 source functions.sh 在主脚本中引用。
延伸阅读:如果你希望系统化掌握镜像定制,可以进一步学习 distroless 镜像(默认不含shell)与多阶段构建的结合使用,在镜像过程中,使用 docker history 命令分析每一层增加的体积,能帮你精准找到优化点。