From f846da230cc0e06e4ab18c05911bb9b2cbd85ff1 Mon Sep 17 00:00:00 2001 From: FenjuFu <92919259+FenjuFu@users.noreply.github.com> Date: Mon, 24 Aug 2026 16:37:34 +0800 Subject: [PATCH] feat: add initial RISC-V image support (#725) * feat: add initial RISC-V image support Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com> * chore(ci): tighten riscv64 image guardrails Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --------- Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com> Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Co-authored-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- .github/workflows/publish-images.yml | 8 ++-- .github/workflows/riscv64-images.yml | 65 ++++++++++++++++++++++++++++ README.md | 4 +- docs/01-system-architecture.md | 6 ++- docs/09-deployment.md | 6 ++- docs/RISCV64.md | 51 ++++++++++++++++++++++ server/Dockerfile | 15 +++++-- web/Dockerfile | 5 ++- 8 files changed, 146 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/riscv64-images.yml create mode 100644 docs/RISCV64.md diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 1f04d8bf..1b58fe6f 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -13,9 +13,6 @@ permissions: contents: read packages: write -env: - DOCKER_PLATFORMS: linux/amd64,linux/arm64 - jobs: publish: runs-on: ubuntu-latest @@ -31,16 +28,19 @@ jobs: - name: server context: ./server dockerfile: ./server/Dockerfile + platforms: linux/amd64,linux/arm64,linux/riscv64 image: ghcr.io/${{ github.repository_owner }}/skillhub-server mirror_image: skillhub-server - name: web context: ./web dockerfile: ./web/Dockerfile + platforms: linux/amd64,linux/arm64,linux/riscv64 image: ghcr.io/${{ github.repository_owner }}/skillhub-web mirror_image: skillhub-web - name: scanner context: ./scanner dockerfile: ./scanner/Dockerfile + platforms: linux/amd64,linux/arm64 image: ghcr.io/${{ github.repository_owner }}/skillhub-scanner mirror_image: skillhub-scanner @@ -109,7 +109,7 @@ jobs: with: context: ${{ matrix.context }} file: ${{ matrix.dockerfile }} - platforms: ${{ env.DOCKER_PLATFORMS }} + platforms: ${{ matrix.platforms }} push: true provenance: false sbom: false diff --git a/.github/workflows/riscv64-images.yml b/.github/workflows/riscv64-images.yml new file mode 100644 index 00000000..6fb9d6bc --- /dev/null +++ b/.github/workflows/riscv64-images.yml @@ -0,0 +1,65 @@ +name: RISC-V Images + +on: + pull_request: + paths: + - '.github/workflows/riscv64-images.yml' + - '.github/workflows/publish-images.yml' + - 'server/**' + - 'web/**' + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: Build ${{ matrix.name }} (linux/riscv64) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: server + context: ./server + dockerfile: ./server/Dockerfile + - name: web + context: ./web + dockerfile: ./web/Dockerfile + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: riscv64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build RISC-V image + uses: docker/build-push-action@v6 + with: + context: ${{ matrix.context }} + file: ${{ matrix.dockerfile }} + platforms: linux/riscv64 + load: true + tags: skillhub-${{ matrix.name }}:riscv64-ci + cache-from: type=gha,scope=riscv64-${{ matrix.name }} + cache-to: type=gha,mode=max,scope=riscv64-${{ matrix.name }} + + - name: Verify image architecture and runtime + shell: bash + run: | + image="skillhub-${{ matrix.name }}:riscv64-ci" + test "$(docker image inspect "$image" --format '{{.Architecture}}')" = riscv64 + case "${{ matrix.name }}" in + server) + docker run --rm --platform linux/riscv64 --entrypoint java "$image" -version + ;; + web) + docker run --rm --platform linux/riscv64 --entrypoint nginx "$image" -v + ;; + esac diff --git a/README.md b/README.md index 277e775e..a5c0fb36 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,9 @@ frontend schema, and fails if the checked-in SDK is stale. Published runtime images are built by GitHub Actions and pushed to GHCR. This is the supported path for anyone who wants a ready-to-use local environment without building the backend or frontend on their machine. -Published images target both `linux/amd64` and `linux/arm64`. +Published server and web images target `linux/amd64`, `linux/arm64`, and +`linux/riscv64`; the scanner image currently targets `linux/amd64` and +`linux/arm64`. **Quick deployment with curl:** diff --git a/docs/01-system-architecture.md b/docs/01-system-architecture.md index 54b15cbb..fc587602 100644 --- a/docs/01-system-architecture.md +++ b/docs/01-system-architecture.md @@ -136,7 +136,8 @@ skillhub/ - 开发路径:`make dev-all`。前后端在宿主机运行,`docker-compose.yml` 只负责 PostgreSQL、Redis、MinIO。 - 交付路径:GitHub Actions 构建并发布 `server` / `web` 镜像;用户通过 `compose.release.yml` 在本地一键拉起前后端容器和基础服务。 -- 发布镜像为多架构 manifest,至少覆盖 `linux/amd64` 与 `linux/arm64`。 +- 发布镜像为多架构 manifest:`server` / `web` 覆盖 `linux/amd64`、`linux/arm64` 与 + `linux/riscv64`;`scanner` 暂保持 `linux/amd64` 与 `linux/arm64`。 单机运行时统一入口: - `http://localhost/` → Web 容器(Nginx) @@ -169,7 +170,8 @@ skillhub/ - 数据库迁移:Flyway - 认证:Spring Security OAuth2 Client(一期 GitHub) - 镜像发布:GitHub Actions 推送至 GHCR,默认维护 `edge` 与语义化版本标签 -- 运行时兼容:发布镜像默认输出 `linux/amd64` + `linux/arm64` 多架构 manifest +- 运行时兼容:`server` / `web` 发布镜像默认输出 `linux/amd64` + `linux/arm64` + + `linux/riscv64` 多架构 manifest,`scanner` 暂保持 `linux/amd64` + `linux/arm64` ## 11. Repository / Query Boundary 约定 diff --git a/docs/09-deployment.md b/docs/09-deployment.md index 3b67a771..332a79bc 100644 --- a/docs/09-deployment.md +++ b/docs/09-deployment.md @@ -10,7 +10,8 @@ - 单机交付环境:`docker compose --env-file .env.release -f compose.release.yml up -d` - 前端和后端都运行在容器内 - 使用 GitHub Actions 发布到 GHCR 的镜像 -- 默认发布 `linux/amd64` 与 `linux/arm64` 多架构镜像 +- 默认发布多架构镜像:`server` / `web` 覆盖 `linux/amd64`、`linux/arm64` 与 + `linux/riscv64`,`scanner` 暂保持 `linux/amd64` 与 `linux/arm64` - PostgreSQL、Redis 与应用容器一起通过 Compose 启动 不再维护本地构建整套 demo 容器的中间模式,也不再保留 `docker-compose.prod.yml`。 @@ -205,7 +206,8 @@ Sentinel 配置优先于 Cluster 和单机 `host`/`port`。在 Kubernetes 等 Se - `ghcr.io/iflytek/skillhub-server` - `ghcr.io/iflytek/skillhub-web` 5. 写入 `edge` / `vX.Y.Z` / `latest` / `sha-*` 标签 -6. 同时发布 `linux/amd64` 与 `linux/arm64` manifest,避免 Apple Silicon / ARM 主机依赖模拟层 +6. 同时发布多架构 manifest:`server` / `web` 覆盖 `linux/amd64`、`linux/arm64` 与 + `linux/riscv64`,`scanner` 暂保持 `linux/amd64` 与 `linux/arm64` ## 7 配置管理 diff --git a/docs/RISCV64.md b/docs/RISCV64.md new file mode 100644 index 00000000..162d34c6 --- /dev/null +++ b/docs/RISCV64.md @@ -0,0 +1,51 @@ +# RISC-V (`linux/riscv64`) support + +## Current scope + +RISC-V support is incremental. The SkillHub server and web images have +`linux/riscv64` build and runtime paths. The security scanner and the complete +Docker Compose deployment are not yet supported on RISC-V. + +| Component | `linux/riscv64` status | Notes | +| --- | --- | --- | +| `skillhub-server` | Supported | The architecture-neutral Java 21 JAR is built on the Buildx host and copied into the target-architecture Eclipse Temurin runtime. | +| `skillhub-web` | Supported | Static assets are built on the Buildx host and served by a target-architecture Nginx runtime. | +| `skillhub-scanner` | Not yet verified | Its Python dependency tree still needs a native-extension and runtime audit. | +| PostgreSQL 16 and Redis 7 | Upstream images available | Keep these images explicitly pinned and verify them on the target board before production use. | +| Complete Compose stack | Unsupported | `compose.release.yml` starts the unverified scanner, so do not deploy it unchanged on RISC-V. | + +## Build the supported images + +Buildx can create both images from an AMD64 or ARM64 host. Register a RISC-V +QEMU handler before running these commands when the host is not RISC-V: + +```bash +docker run --privileged --rm tonistiigi/binfmt --install riscv64 +docker buildx create --use --name skillhub-riscv64 + +docker buildx build \ + --platform linux/riscv64 \ + --file server/Dockerfile \ + --tag skillhub-server:riscv64 \ + --load \ + server + +docker buildx build \ + --platform linux/riscv64 \ + --file web/Dockerfile \ + --tag skillhub-web:riscv64 \ + --load \ + web +``` + +The release workflow publishes `linux/amd64`, `linux/arm64`, and +`linux/riscv64` variants for `skillhub-server` and `skillhub-web`. The scanner +remains limited to its existing AMD64/ARM64 platform list. + +## Verification boundary + +The pull-request workflow builds both supported target images, checks their OCI +architecture metadata, and executes the Java and Nginx runtimes under RISC-V +emulation. This is a component-image guardrail, not a full-stack integration +test. A native RISC-V smoke test with PostgreSQL, Redis, object storage, and a +verified scanner remains required before claiming complete deployment support. diff --git a/server/Dockerfile b/server/Dockerfile index 84537b56..bfd02cd1 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,5 +1,6 @@ -# ---- Build Stage ---- -FROM eclipse-temurin:21-jdk-alpine AS build +# Build the architecture-neutral JAR on the Buildx host. This avoids emulating +# the complete Maven build when the target image is linux/riscv64. +FROM --platform=$BUILDPLATFORM eclipse-temurin:21-jdk-alpine AS build WORKDIR /app # Cache dependencies @@ -19,8 +20,14 @@ COPY . . RUN ./mvnw package -DskipTests -B # ---- Runtime Stage ---- -FROM eclipse-temurin:21-jre-alpine -RUN addgroup -S app && adduser -S app -G app +# The Noble variant publishes a linux/riscv64 image; the Alpine JRE currently +# used by this project is limited to amd64 and arm64. +FROM eclipse-temurin:21-jre-noble +RUN apt-get update && \ + apt-get install -y --no-install-recommends wget && \ + rm -rf /var/lib/apt/lists/* && \ + groupadd --system app && \ + useradd --system --gid app --create-home app WORKDIR /app COPY --from=build /app/skillhub-app/target/*.jar app.jar diff --git a/web/Dockerfile b/web/Dockerfile index 241d6996..bf8f1fb0 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,4 +1,7 @@ -FROM node:22-alpine AS build +# The frontend output is static and architecture-neutral. Build it on the +# Buildx host so linux/riscv64 does not depend on a target-architecture Node.js +# image; only the multi-architecture Nginx runtime is target-specific. +FROM --platform=$BUILDPLATFORM node:22-alpine AS build RUN corepack enable WORKDIR /app COPY package.json pnpm-lock.yaml ./