diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7761d2d0..c4ba3b74 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -120,16 +120,15 @@ jobs: path: target/${{ matrix.target }}/release/rustfs retention-days: 1 - # Build and push Docker images + # Build and push multi-arch Docker images build-images: needs: [skip-check, build-binary] if: needs.skip-check.outputs.should_skip != 'true' runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 60 strategy: matrix: image-type: [production, ubuntu, rockylinux, devenv] - platform: [linux/amd64, linux/arm64] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -211,86 +210,22 @@ jobs: flavor: | latest=false - - name: Build and push Docker image + - name: Build and push multi-arch Docker image uses: docker/build-push-action@v5 with: context: ${{ steps.dockerfile.outputs.context }} file: ${{ steps.dockerfile.outputs.dockerfile }} - platforms: ${{ matrix.platform }} - push: ${{ github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) }} + platforms: linux/amd64,linux/arm64 + push: ${{ (github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/'))) || github.event.inputs.push_to_registry == 'true' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha,scope=${{ matrix.image-type }}-${{ matrix.platform }} - cache-to: type=gha,mode=max,scope=${{ matrix.image-type }}-${{ matrix.platform }} + cache-from: type=gha,scope=${{ matrix.image-type }} + cache-to: type=gha,mode=max,scope=${{ matrix.image-type }} build-args: | BUILDTIME=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }} VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }} REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }} - # Create multi-arch manifests - create-manifest: - needs: [skip-check, build-images] - if: needs.skip-check.outputs.should_skip != 'true' && github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) - runs-on: ubuntu-latest - strategy: - matrix: - image-type: [production, ubuntu, rockylinux, devenv] - steps: - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set image suffix - id: suffix - run: | - case "${{ matrix.image-type }}" in - production) echo "suffix=" >> $GITHUB_OUTPUT ;; - ubuntu) echo "suffix=-ubuntu22.04" >> $GITHUB_OUTPUT ;; - rockylinux) echo "suffix=-rockylinux9.3" >> $GITHUB_OUTPUT ;; - devenv) echo "suffix=-devenv" >> $GITHUB_OUTPUT ;; - esac - - - name: Create and push manifest - run: | - # Set tag based on ref - if [[ $GITHUB_REF == refs/tags/* ]]; then - TAG=${GITHUB_REF#refs/tags/} - else - TAG="main" - fi - - SUFFIX="${{ steps.suffix.outputs.suffix }}" - - # Docker Hub manifest - docker buildx imagetools create -t ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX} \ - ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX}-linux-amd64 \ - ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX}-linux-arm64 - - # GitHub Container Registry manifest - docker buildx imagetools create -t ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX} \ - ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX}-linux-amd64 \ - ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX}-linux-arm64 - - # Create latest tag for main branch - if [[ $GITHUB_REF == refs/heads/main ]]; then - docker buildx imagetools create -t ${REGISTRY_IMAGE_DOCKERHUB}:latest${SUFFIX} \ - ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX}-linux-amd64 \ - ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX}-linux-arm64 - - docker buildx imagetools create -t ${REGISTRY_IMAGE_GHCR}:latest${SUFFIX} \ - ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX}-linux-amd64 \ - ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX}-linux-arm64 - fi - # Security scanning security-scan: needs: [skip-check, build-images] diff --git a/Makefile b/Makefile index 8284cd7f..1fe3ab0a 100644 --- a/Makefile +++ b/Makefile @@ -94,3 +94,106 @@ build-gnu: deploy-dev: build-musl @echo "🚀 Deploying to dev server: $${IP}" ./scripts/dev_deploy.sh $${IP} + +# Multi-architecture Docker build targets +.PHONY: docker-build-multiarch +docker-build-multiarch: + @echo "🏗️ Building multi-architecture Docker images..." + ./scripts/build-docker-multiarch.sh + +.PHONY: docker-build-multiarch-push +docker-build-multiarch-push: + @echo "🚀 Building and pushing multi-architecture Docker images..." + ./scripts/build-docker-multiarch.sh --push + +.PHONY: docker-build-multiarch-version +docker-build-multiarch-version: + @if [ -z "$(VERSION)" ]; then \ + echo "❌ 错误: 请指定版本, 例如: make docker-build-multiarch-version VERSION=v1.0.0"; \ + exit 1; \ + fi + @echo "🏗️ Building multi-architecture Docker images (version: $(VERSION))..." + ./scripts/build-docker-multiarch.sh --version $(VERSION) + +.PHONY: docker-push-multiarch-version +docker-push-multiarch-version: + @if [ -z "$(VERSION)" ]; then \ + echo "❌ 错误: 请指定版本, 例如: make docker-push-multiarch-version VERSION=v1.0.0"; \ + exit 1; \ + fi + @echo "🚀 Building and pushing multi-architecture Docker images (version: $(VERSION))..." + ./scripts/build-docker-multiarch.sh --version $(VERSION) --push + +.PHONY: docker-build-ubuntu +docker-build-ubuntu: + @echo "🏗️ Building multi-architecture Ubuntu Docker images..." + ./scripts/build-docker-multiarch.sh --type ubuntu + +.PHONY: docker-build-rockylinux +docker-build-rockylinux: + @echo "🏗️ Building multi-architecture RockyLinux Docker images..." + ./scripts/build-docker-multiarch.sh --type rockylinux + +.PHONY: docker-build-devenv +docker-build-devenv: + @echo "🏗️ Building multi-architecture development environment Docker images..." + ./scripts/build-docker-multiarch.sh --type devenv + +.PHONY: docker-build-all-types +docker-build-all-types: + @echo "🏗️ Building all multi-architecture Docker image types..." + ./scripts/build-docker-multiarch.sh --type production + ./scripts/build-docker-multiarch.sh --type ubuntu + ./scripts/build-docker-multiarch.sh --type rockylinux + ./scripts/build-docker-multiarch.sh --type devenv + +.PHONY: docker-inspect-multiarch +docker-inspect-multiarch: + @if [ -z "$(IMAGE)" ]; then \ + echo "❌ 错误: 请指定镜像, 例如: make docker-inspect-multiarch IMAGE=rustfs/rustfs:latest"; \ + exit 1; \ + fi + @echo "🔍 Inspecting multi-architecture image: $(IMAGE)" + docker buildx imagetools inspect $(IMAGE) + +.PHONY: build-cross-all +build-cross-all: + @echo "🔧 Building all target architectures..." + @if ! command -v cross &> /dev/null; then \ + echo "📦 Installing cross..."; \ + cargo install cross; \ + fi + @echo "🔨 Generating protobuf code..." + cargo run --bin gproto || true + @echo "🔨 Building x86_64-unknown-linux-musl..." + cargo build --release --target x86_64-unknown-linux-musl --bin rustfs + @echo "🔨 Building aarch64-unknown-linux-gnu..." + cross build --release --target aarch64-unknown-linux-gnu --bin rustfs + @echo "✅ All architectures built successfully!" + +.PHONY: help-docker +help-docker: + @echo "🐳 Docker 多架构构建帮助:" + @echo "" + @echo "基本构建:" + @echo " make docker-build-multiarch # 构建多架构镜像(不推送)" + @echo " make docker-build-multiarch-push # 构建并推送多架构镜像" + @echo "" + @echo "版本构建:" + @echo " make docker-build-multiarch-version VERSION=v1.0.0 # 构建指定版本" + @echo " make docker-push-multiarch-version VERSION=v1.0.0 # 构建并推送指定版本" + @echo "" + @echo "镜像类型:" + @echo " make docker-build-ubuntu # 构建 Ubuntu 镜像" + @echo " make docker-build-rockylinux # 构建 RockyLinux 镜像" + @echo " make docker-build-devenv # 构建开发环境镜像" + @echo " make docker-build-all-types # 构建所有类型镜像" + @echo "" + @echo "辅助工具:" + @echo " make build-cross-all # 构建所有架构的二进制文件" + @echo " make docker-inspect-multiarch IMAGE=xxx # 检查镜像的架构支持" + @echo "" + @echo "环境变量 (在推送时需要设置):" + @echo " DOCKERHUB_USERNAME Docker Hub 用户名" + @echo " DOCKERHUB_TOKEN Docker Hub 访问令牌" + @echo " GITHUB_TOKEN GitHub 访问令牌" diff --git a/scripts/build-docker-multiarch.sh b/scripts/build-docker-multiarch.sh new file mode 100755 index 00000000..789202da --- /dev/null +++ b/scripts/build-docker-multiarch.sh @@ -0,0 +1,242 @@ +#!/bin/bash +set -euo pipefail + +# 多架构 Docker 构建脚本 +# 支持构建并推送 x86_64 和 ARM64 架构的镜像 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +cd "$PROJECT_ROOT" + +# 默认配置 +REGISTRY_IMAGE_DOCKERHUB="rustfs/rustfs" +REGISTRY_IMAGE_GHCR="ghcr.io/rustfs/s3-rustfs" +VERSION="${VERSION:-latest}" +PUSH="${PUSH:-false}" +IMAGE_TYPE="${IMAGE_TYPE:-production}" + +# 帮助信息 +show_help() { + cat << EOF +用法: $0 [选项] + +选项: + -h, --help 显示此帮助信息 + -v, --version TAG 设置镜像版本标签 (默认: latest) + -p, --push 推送镜像到仓库 + -t, --type TYPE 镜像类型 (production|ubuntu|rockylinux|devenv, 默认: production) + +环境变量: + DOCKERHUB_USERNAME Docker Hub 用户名 + DOCKERHUB_TOKEN Docker Hub 访问令牌 + GITHUB_TOKEN GitHub 访问令牌 + +示例: + # 仅构建不推送 + $0 --version v1.0.0 + + # 构建并推送到仓库 + $0 --version v1.0.0 --push + + # 构建 Ubuntu 版本 + $0 --type ubuntu --version v1.0.0 +EOF +} + +# 解析命令行参数 +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + exit 0 + ;; + -v|--version) + VERSION="$2" + shift 2 + ;; + -p|--push) + PUSH=true + shift + ;; + -t|--type) + IMAGE_TYPE="$2" + shift 2 + ;; + *) + echo "未知参数: $1" + show_help + exit 1 + ;; + esac +done + +# 设置 Dockerfile 和后缀 +case "$IMAGE_TYPE" in + production) + DOCKERFILE="Dockerfile" + SUFFIX="" + ;; + ubuntu) + DOCKERFILE=".docker/Dockerfile.ubuntu22.04" + SUFFIX="-ubuntu22.04" + ;; + rockylinux) + DOCKERFILE=".docker/Dockerfile.rockylinux9.3" + SUFFIX="-rockylinux9.3" + ;; + devenv) + DOCKERFILE=".docker/Dockerfile.devenv" + SUFFIX="-devenv" + ;; + *) + echo "错误: 不支持的镜像类型: $IMAGE_TYPE" + echo "支持的类型: production, ubuntu, rockylinux, devenv" + exit 1 + ;; +esac + +echo "🚀 开始多架构 Docker 构建" +echo "📋 构建信息:" +echo " - 镜像类型: $IMAGE_TYPE" +echo " - Dockerfile: $DOCKERFILE" +echo " - 版本标签: $VERSION$SUFFIX" +echo " - 推送: $PUSH" +echo " - 架构: linux/amd64, linux/arm64" + +# 检查必要的工具 +if ! command -v docker &> /dev/null; then + echo "❌ 错误: 未找到 docker 命令" + exit 1 +fi + +# 检查 Docker Buildx +if ! docker buildx version &> /dev/null; then + echo "❌ 错误: Docker Buildx 不可用" + echo "请运行: docker buildx install" + exit 1 +fi + +# 创建并使用 buildx 构建器 +BUILDER_NAME="rustfs-multiarch-builder" +if ! docker buildx inspect "$BUILDER_NAME" &> /dev/null; then + echo "🔨 创建多架构构建器..." + docker buildx create --name "$BUILDER_NAME" --use --bootstrap +else + echo "🔨 使用现有构建器..." + docker buildx use "$BUILDER_NAME" +fi + +# 构建多架构二进制文件 +echo "🔧 构建多架构二进制文件..." + +# 检查是否存在预构建的二进制文件 +if [[ ! -f "target/x86_64-unknown-linux-musl/release/rustfs" ]] || [[ ! -f "target/aarch64-unknown-linux-gnu/release/rustfs" ]]; then + echo "⚠️ 未找到预构建的二进制文件,正在构建..." + + # 安装构建依赖 + if ! command -v cross &> /dev/null; then + echo "📦 安装 cross 工具..." + cargo install cross + fi + + # 生成 protobuf 代码 + echo "📝 生成 protobuf 代码..." + cargo run --bin gproto || true + + # 构建 x86_64 + echo "🔨 构建 x86_64 二进制文件..." + cargo build --release --target x86_64-unknown-linux-musl --bin rustfs + + # 构建 ARM64 + echo "🔨 构建 ARM64 二进制文件..." + cross build --release --target aarch64-unknown-linux-gnu --bin rustfs +fi + +# 准备构建参数 +BUILD_ARGS="" +TAGS="" + +# Docker Hub 标签 +if [[ -n "${DOCKERHUB_USERNAME:-}" ]]; then + TAGS="$TAGS -t $REGISTRY_IMAGE_DOCKERHUB:$VERSION$SUFFIX" +fi + +# GitHub Container Registry 标签 +if [[ -n "${GITHUB_TOKEN:-}" ]]; then + TAGS="$TAGS -t $REGISTRY_IMAGE_GHCR:$VERSION$SUFFIX" +fi + +# 如果没有设置标签,使用本地标签 +if [[ -z "$TAGS" ]]; then + TAGS="-t rustfs:$VERSION$SUFFIX" +fi + +# 构建镜像 +echo "🏗️ 构建多架构 Docker 镜像..." +BUILD_CMD="docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --file $DOCKERFILE \ + $TAGS \ + --build-arg BUILDTIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) \ + --build-arg VERSION=$VERSION \ + --build-arg REVISION=$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')" + +if [[ "$PUSH" == "true" ]]; then + # 登录到仓库 + if [[ -n "${DOCKERHUB_USERNAME:-}" ]] && [[ -n "${DOCKERHUB_TOKEN:-}" ]]; then + echo "🔐 登录到 Docker Hub..." + echo "$DOCKERHUB_TOKEN" | docker login --username "$DOCKERHUB_USERNAME" --password-stdin + fi + + if [[ -n "${GITHUB_TOKEN:-}" ]]; then + echo "🔐 登录到 GitHub Container Registry..." + echo "$GITHUB_TOKEN" | docker login ghcr.io --username "$(whoami)" --password-stdin + fi + + BUILD_CMD="$BUILD_CMD --push" +else + BUILD_CMD="$BUILD_CMD --load" +fi + +BUILD_CMD="$BUILD_CMD ." + +echo "📋 执行构建命令:" +echo "$BUILD_CMD" +echo "" + +# 执行构建 +eval "$BUILD_CMD" + +echo "" +echo "✅ 多架构 Docker 镜像构建完成!" + +if [[ "$PUSH" == "true" ]]; then + echo "🚀 镜像已推送到仓库" + + # 显示推送的镜像信息 + echo "" + echo "📦 推送的镜像:" + if [[ -n "${DOCKERHUB_USERNAME:-}" ]]; then + echo " - $REGISTRY_IMAGE_DOCKERHUB:$VERSION$SUFFIX" + fi + if [[ -n "${GITHUB_TOKEN:-}" ]]; then + echo " - $REGISTRY_IMAGE_GHCR:$VERSION$SUFFIX" + fi + + echo "" + echo "🔍 验证多架构支持:" + if [[ -n "${DOCKERHUB_USERNAME:-}" ]]; then + echo " docker buildx imagetools inspect $REGISTRY_IMAGE_DOCKERHUB:$VERSION$SUFFIX" + fi + if [[ -n "${GITHUB_TOKEN:-}" ]]; then + echo " docker buildx imagetools inspect $REGISTRY_IMAGE_GHCR:$VERSION$SUFFIX" + fi +else + echo "💾 镜像已构建到本地" + echo "" + echo "🔍 查看镜像:" + echo " docker images rustfs:$VERSION$SUFFIX" +fi + +echo "" +echo "🎉 构建任务完成!"