Shell脚本如何从源码构建基础镜像

wen 实用脚本 2

本文目录导读:

Shell脚本如何从源码构建基础镜像

  1. 基础构建脚本
  2. 高级构建脚本
  3. 使用示例
  4. 最佳实践

我来详细说明如何从源码构建基础镜像的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

最佳实践

  1. 安全考虑

    • 使用非root用户运行应用
    • 定期扫描安全漏洞
    • 使用最小基础镜像
  2. 性能优化

    • 利用多阶段构建
    • 使用构建缓存
    • 合理分层Dockerfile
  3. 可靠性

    • 版本控制构建脚本
    • 添加错误处理
    • 保存构建日志

这个脚本提供了完整的从源码构建基础镜像的解决方案,可根据实际需求调整。

抱歉,评论功能暂时关闭!