本文目录导读:

我来详细说明如何从源码构建基础镜像的Shell脚本。
基础构建脚本
#!/bin/bash
# build_base_image.sh - 从源码构建基础镜像
set -euo pipefail
# 配置变量
SOURCE_DIR="/tmp/source"
IMAGE_NAME="my-base-image:latest"
BUILD_DIR="/tmp/build"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# 清理函数
cleanup() {
log_info "清理构建环境..."
rm -rf "$BUILD_DIR"
}
# 错误处理
trap 'cleanup' EXIT
# 创建构建目录
prepare_build_dir() {
log_info "准备构建目录..."
mkdir -p "$BUILD_DIR"
}
# 构建Dockerfile
create_dockerfile() {
local dockerfile="$BUILD_DIR/Dockerfile"
cat > "$dockerfile" << 'EOF'
# 使用最小的基础镜像
FROM alpine:latest AS builder
# 安装构建依赖
RUN apk add --no-cache \
build-base \
gcc \
make \
cmake \
git \
curl \
openssl-dev \
zlib-dev \
readline-dev
# 设置工作目录
WORKDIR /app
# 复制源码
COPY . .
# 编译配置
RUN mkdir -p build && \
cd build && \
cmake .. && \
make -j$(nproc)
# 第二阶段:创建最小镜像
FROM alpine:latest
# 安装运行时依赖
RUN apk add --no-cache \
libstdc++ \
libgcc \
ca-certificates \
tzdata
# 创建运行用户
RUN addgroup -g 1000 appuser && \
adduser -u 1000 -G appuser -s /bin/sh -D appuser
# 复制编译产出的二进制文件
COPY --from=builder /app/build/bin/ /usr/local/bin/
# 设置时区
ENV TZ=Asia/Shanghai
# 切换用户
USER appuser
# 暴露端口
EXPOSE 8080
# 设置入口点
ENTRYPOINT ["app"]
CMD ["--help"]
EOF
}
# 克隆源码
clone_source() {
local repo_url="$1"
local branch="${2:-main}"
log_info "克隆源码: $repo_url (branch: $branch)"
if [ -d "$BUILD_DIR/source" ]; then
log_warn "源码目录已存在,更新中..."
cd "$BUILD_DIR/source"
git checkout "$branch"
git pull origin "$branch"
else
git clone --depth 1 --branch "$branch" "$repo_url" "$BUILD_DIR/source"
fi
}
# 自定义构建参数
configure_build() {
local config_file="$BUILD_DIR/build.config"
cat > "$config_file" << 'CONFIG'
# 构建配置
BUILD_TYPE="Release"
INSTALL_PREFIX="/usr/local"
EXTRA_CFLAGS="-O2 -march=x86-64-v2"
ENABLE_DEBUG=0
ENABLE_TESTS=0
CONFIG
}
# 构建镜像
build_image() {
log_info "开始构建Docker镜像..."
cd "$BUILD_DIR/source"
# 复制Dockerfile到源码目录
cp "$BUILD_DIR/Dockerfile" .
# 构建命令
docker build \
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
--build-arg VERSION=$(git describe --tags --always 2>/dev/null || echo "unknown") \
--build-arg COMMIT_HASH=$(git rev-parse HEAD 2>/dev/null || echo "unknown") \
-t "$IMAGE_NAME" \
-f Dockerfile \
.
log_info "镜像构建完成: $IMAGE_NAME"
}
# 多阶段构建(大型项目)
multi_stage_build() {
local dockerfile="$BUILD_DIR/Dockerfile.multistage"
cat > "$dockerfile" << 'EOF'
# 第一阶段:编译依赖
FROM alpine:latest AS deps
RUN apk add --no-cache \
autoconf \
automake \
libtool \
pkgconfig
# 第二阶段:构建应用
FROM alpine:latest AS build
COPY --from=deps /usr/bin/ /usr/bin/
COPY --from=deps /usr/lib/ /usr/lib/
# 第三阶段:运行环境
FROM alpine:latest AS runtime
RUN apk add --no-cache runtime-deps
COPY --from=build /app/bin/ /app/
EOF
}
# 验证镜像
verify_image() {
log_info "验证构建的镜像..."
# 检查镜像是否存在
if ! docker image inspect "$IMAGE_NAME" >/dev/null 2>&1; then
log_error "镜像 $IMAGE_NAME 不存在"
return 1
fi
# 检查镜像大小
local size=$(docker images --format "{{.Size}}" "$IMAGE_NAME")
log_info "镜像大小: $size"
# 扫描安全漏洞
if command -v trivy &>/dev/null; then
log_info "执行安全扫描..."
trivy image --severity HIGH,CRITICAL "$IMAGE_NAME"
fi
}
# 推送镜像到仓库
push_image() {
local registry="${1:-docker.io}"
local username="${2:-}"
local password="${3:-}"
if [ -n "$username" ] && [ -n "$password" ]; then
log_info "登录到镜像仓库..."
echo "$password" | docker login "$registry" -u "$username" --password-stdin
fi
# 打标签并推送
local full_image_name="${registry}/${IMAGE_NAME}"
docker tag "$IMAGE_NAME" "$full_image_name"
docker push "$full_image_name"
log_info "镜像已推送: $full_image_name"
}
# 清理旧版本
cleanup_old_images() {
local keep_versions="${1:-3}"
log_info "清理旧版本(保留最近 $keep_versions 个版本)..."
# 获取所有版本(按时间排序)
docker images --format "{{.Repository}}:{{.Tag}}" | \
grep "^${IMAGE_NAME%:*}" | \
tail -n +$((keep_versions + 1)) | \
xargs -r docker rmi
}
# 主函数
main() {
local repo_url="${1:-}"
local branch="${2:-main}"
if [ -z "$repo_url" ]; then
echo "用法: $0 <git仓库URL> [分支名]"
echo "示例: $0 https://github.com/user/app.git main"
exit 1
fi
log_info "开始从源码构建基础镜像..."
# 准备环境
prepare_build_dir
configure_build
# 获取源码
clone_source "$repo_url" "$branch"
# 创建并构建
create_dockerfile
build_image
# 验证
verify_image
log_info "构建完成!"
log_info "镜像名称: $IMAGE_NAME"
# 可选:推送镜像
if [ "${PUSH_IMAGE:-false}" = "true" ]; then
push_image "${REGISTRY:-}" "${REGISTRY_USER:-}" "${REGISTRY_PASS:-}"
fi
# 可选:清理旧版本
if [ "${CLEANUP_OLD:-false}" = "true" ]; then
cleanup_old_images "${KEEP_VERSIONS:-3}"
fi
}
# 执行主函数
main "$@"
高级构建脚本
#!/bin/bash
# advanced_build.sh - 高级构建脚本
# 缓存优化
setup_build_cache() {
log_info "设置构建缓存..."
# Docker BuildKit 缓存
export DOCKER_BUILDKIT=1
export BUILDKIT_PROGRESS=plain
# 创建缓存目录
mkdir -p /tmp/docker-cache
}
# 并行构建
parallel_build() {
local components=("$@")
for component in "${components[@]}"; do
{
log_info "构建组件: $component"
docker build -t "component-${component}:latest" -f "Dockerfile.${component}" .
} &
done
wait
log_info "所有组件构建完成"
}
# 条件构建
conditional_build() {
local arch=$(uname -m)
case "$arch" in
x86_64)
BUILD_FLAGS="-march=x86-64-v3"
;;
aarch64)
BUILD_FLAGS="-march=armv8-a"
;;
*)
BUILD_FLAGS=""
;;
esac
docker build \
--build-arg ARCH="$arch" \
--build-arg BUILD_FLAGS="$BUILD_FLAGS" \
-t "$IMAGE_NAME" \
.
}
# 增量构建
incremental_build() {
local cache_from="${1:-}"
if [ -n "$cache_from" ]; then
docker build \
--cache-from "$cache_from" \
--build-arg BUILDKIT_INLINE_CACHE=1 \
-t "$IMAGE_NAME" \
.
else
docker build \
--no-cache \
-t "$IMAGE_NAME" \
.
fi
}
# 交叉编译
cross_compile_build() {
local target_arch="${1:-arm64}"
# 设置QEMU模拟器
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# 交叉编译构建
docker buildx build \
--platform "linux/${target_arch}" \
--tag "${IMAGE_NAME}-${target_arch}" \
--load \
.
}
# 构建优化
optimize_build() {
# 使用BuildKit
export DOCKER_BUILDKIT=1
# 启用缓存挂载
docker build \
--mount type=cache,target=/root/.cache/pip \
--mount type=cache,target=/root/.cache/go-build \
-t "$IMAGE_NAME" \
.
}
# 环境检测
check_environment() {
log_info "检查构建环境..."
# 检查Docker
if ! command -v docker &>/dev/null; then
log_error "Docker未安装"
exit 1
fi
# 检查磁盘空间
local free_space=$(df /var/lib/docker | awk 'NR==2 {print $4}')
if [ "$free_space" -lt 10485760 ]; then # 小于10GB
log_warn "磁盘空间不足: $((free_space/1024/1024))GB"
fi
# 检查内存
local total_mem=$(free -m | awk 'NR==2 {print $2}')
if [ "$total_mem" -lt 2048 ]; then
log_warn "内存不足: ${total_mem}MB"
fi
}
# 使用示例
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# 检查环境
check_environment
# 设置缓存
setup_build_cache
# 执行构建
main "$@"
fi
使用示例
# 基本用法 ./build_base_image.sh https://github.com/example/myapp.git main # 高级用法 PUSH_IMAGE=true \ REGISTRY="registry.example.com" \ REGISTRY_USER="admin" \ REGISTRY_PASS="password" \ CLEANUP_OLD=true \ KEEP_VERSIONS=5 \ ./build_base_image.sh https://github.com/example/myapp.git develop # 交叉编译 ./cross_compile_build.sh arm64
最佳实践
-
安全考虑
- 使用非root用户运行应用
- 定期扫描安全漏洞
- 使用最小基础镜像
-
性能优化
- 利用多阶段构建
- 使用构建缓存
- 合理分层Dockerfile
-
可靠性
- 版本控制构建脚本
- 添加错误处理
- 保存构建日志
这个脚本提供了完整的从源码构建基础镜像的解决方案,可根据实际需求调整。