融合模式启动

This commit is contained in:
ryan
2026-06-08 21:12:55 +08:00
parent db0d503bb4
commit 356b3df81e
3 changed files with 134 additions and 121 deletions
+61 -120
View File
@@ -1,4 +1,4 @@
name: Docker Image name: Docker image build (Server)
on: on:
workflow_dispatch: workflow_dispatch:
@@ -7,23 +7,9 @@ on:
description: "Image version/tag to publish, for example v1.0.0-beta" description: "Image version/tag to publish, for example v1.0.0-beta"
required: false required: false
type: string type: string
image_name:
description: "Image name without registry. Defaults to owner/repo."
required: false
type: string
push: push:
tags: ["v*"] tags: ["v*"]
env:
REGISTRY: ghcr.io
DEFAULT_IMAGE_NAME: ${{ github.repository }}
DOCKERFILE: ./docker/Dockerfile
BUILD_CONTEXT: .
CACHE_SCOPE: docker-image
STABLE_FLOATING_TAG: latest
PRERELEASE_FLOATING_TAG: beta
PRERELEASE_PATTERN: (alpha|beta|rc)
permissions: permissions:
contents: read contents: read
packages: write packages: write
@@ -33,116 +19,88 @@ permissions:
jobs: jobs:
build: build:
name: Build (${{ matrix.arch }}) name: Build (${{ matrix.arch }})
runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- arch: amd64 - arch: amd64
platform: linux/amd64 platform: linux/amd64
runner: ubuntu-24.04
- arch: arm64 - arch: arm64
platform: linux/arm64 platform: linux/arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
steps: steps:
- name: Checkout - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0
fetch-tags: true fetch-tags: true
fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: Set build metadata - name: Set image metadata
shell: bash shell: bash
env: env:
INPUT_VERSION: ${{ github.event.inputs.version }} INPUT_VERSION: ${{ github.event.inputs.version }}
INPUT_IMAGE_NAME: ${{ github.event.inputs.image_name }}
run: | run: |
set -euo pipefail POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
INPUT_VERSION="${INPUT_VERSION//[[:space:]]/}"
pointed_tag="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)" echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
input_version="${INPUT_VERSION//[[:space:]]/}" if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
image_name="${INPUT_IMAGE_NAME:-$DEFAULT_IMAGE_NAME}" VERSION="${GITHUB_REF_NAME}"
elif [[ -n "$INPUT_VERSION" ]]; then
if [[ "$GITHUB_REF" == refs/tags/* ]]; then VERSION="$INPUT_VERSION"
version="$GITHUB_REF_NAME" elif [[ -n "$POINTED_TAG" ]]; then
elif [[ -n "$input_version" ]]; then VERSION="$POINTED_TAG"
version="$input_version"
elif [[ -n "$pointed_tag" ]]; then
version="$pointed_tag"
else else
echo "workflow_dispatch requires a version input when HEAD is not tagged" >&2 echo "workflow_dispatch requires an explicit version input when HEAD is not tagged" >&2
exit 1 exit 1
fi fi
if [[ ! -f "$DOCKERFILE" ]]; then echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "Dockerfile not found: $DOCKERFILE" >&2
exit 1
fi
if [[ ! -d "$BUILD_CONTEXT" ]]; then
echo "Docker context not found: $BUILD_CONTEXT" >&2
exit 1
fi
{
echo "IMAGE=${REGISTRY}/${image_name,,}"
echo "VERSION=$version"
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
echo "VCS_REF=$GITHUB_SHA"
} >> "$GITHUB_ENV"
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Log in to registry - name: Log into registry
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
registry: ${{ env.REGISTRY }} registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push digest - name: Build and push
id: build id: build
uses: docker/build-push-action@v6 uses: docker/build-push-action@v7
with: with:
context: ${{ env.BUILD_CONTEXT }} context: .
file: ${{ env.DOCKERFILE }} file: ./docker/Dockerfile
platforms: ${{ matrix.platform }} platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
build-args: | build-args: |
VERSION=${{ env.VERSION }} VERSION=${{ env.VERSION }}
BUILD_DATE=${{ env.BUILD_DATE }} cache-from: type=gha,scope=docker-server-${{ matrix.arch }}
VCS_REF=${{ env.VCS_REF }} cache-to: type=gha,mode=max,ignore-error=true,timeout=20m,scope=docker-server-${{ matrix.arch }}
labels: |
org.opencontainers.image.title=${{ github.event.repository.name }}
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.version=${{ env.VERSION }}
org.opencontainers.image.created=${{ env.BUILD_DATE }}
cache-from: type=gha,scope=${{ env.CACHE_SCOPE }}-${{ matrix.arch }}
cache-to: type=gha,mode=max,ignore-error=true,timeout=20m,scope=${{ env.CACHE_SCOPE }}-${{ matrix.arch }}
- name: Export digest - name: Export digest
shell: bash shell: bash
run: |
mkdir -p /tmp/server-digests
touch "/tmp/server-digests/${DIGEST#sha256:}"
env: env:
DIGEST: ${{ steps.build.outputs.digest }} DIGEST: ${{ steps.build.outputs.digest }}
run: |
set -euo pipefail
mkdir -p /tmp/image-digests
touch "/tmp/image-digests/${DIGEST#sha256:}"
- name: Upload digest - name: Upload digest
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: image-digests-${{ matrix.arch }} name: server-digests-${{ matrix.arch }}
path: /tmp/image-digests/* path: /tmp/server-digests/*
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
- name: Generate artifact attestation - name: Generate artifact attestation
uses: actions/attest-build-provenance@v2 uses: actions/attest-build-provenance@v3
with: with:
subject-name: ${{ env.IMAGE }} subject-name: ${{ env.IMAGE }}
subject-digest: ${{ steps.build.outputs.digest }} subject-digest: ${{ steps.build.outputs.digest }}
@@ -150,96 +108,79 @@ jobs:
merge: merge:
name: Merge multi-arch manifest name: Merge multi-arch manifest
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: build needs: build
steps: steps:
- name: Checkout - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0
fetch-tags: true fetch-tags: true
fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: Set build metadata - name: Set image metadata
shell: bash shell: bash
env: env:
INPUT_VERSION: ${{ github.event.inputs.version }} INPUT_VERSION: ${{ github.event.inputs.version }}
INPUT_IMAGE_NAME: ${{ github.event.inputs.image_name }}
run: | run: |
set -euo pipefail POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
INPUT_VERSION="${INPUT_VERSION//[[:space:]]/}"
pointed_tag="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)" echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
input_version="${INPUT_VERSION//[[:space:]]/}" if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
image_name="${INPUT_IMAGE_NAME:-$DEFAULT_IMAGE_NAME}" VERSION="${GITHUB_REF_NAME}"
elif [[ -n "$INPUT_VERSION" ]]; then
if [[ "$GITHUB_REF" == refs/tags/* ]]; then VERSION="$INPUT_VERSION"
version="$GITHUB_REF_NAME" elif [[ -n "$POINTED_TAG" ]]; then
elif [[ -n "$input_version" ]]; then VERSION="$POINTED_TAG"
version="$input_version"
elif [[ -n "$pointed_tag" ]]; then
version="$pointed_tag"
else else
echo "workflow_dispatch requires a version input when HEAD is not tagged" >&2 echo "workflow_dispatch requires an explicit version input when HEAD is not tagged" >&2
exit 1 exit 1
fi fi
if [[ ! -f "$DOCKERFILE" ]]; then echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "Dockerfile not found: $DOCKERFILE" >&2
exit 1
fi
if [[ ! -d "$BUILD_CONTEXT" ]]; then
echo "Docker context not found: $BUILD_CONTEXT" >&2
exit 1
fi
{
echo "IMAGE=${REGISTRY}/${image_name,,}"
echo "VERSION=$version"
} >> "$GITHUB_ENV"
- name: Download digests - name: Download digests
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
path: /tmp/image-digests path: /tmp/server-digests
pattern: image-digests-* pattern: server-digests-*
merge-multiple: true merge-multiple: true
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Log in to registry - name: Log into registry
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
registry: ${{ env.REGISTRY }} registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifest list - name: Create and push manifest list
working-directory: /tmp/image-digests working-directory: /tmp/server-digests
shell: bash shell: bash
run: | run: |
set -euo pipefail
shopt -s nullglob shopt -s nullglob
references=() references=()
for digest in *; do for digest in *; do
references+=("${IMAGE}@sha256:${digest}") references+=("${IMAGE}@sha256:${digest}")
done done
if [[ ${#references[@]} -eq 0 ]]; then if [ ${#references[@]} -eq 0 ]; then
echo "No digests found in /tmp/image-digests" >&2 echo "No digests found in /tmp/server-digests" >&2
exit 1 exit 1
fi fi
floating_tag="$STABLE_FLOATING_TAG" if [[ "${VERSION}" =~ (alpha|beta|rc) ]]; then
if [[ "$VERSION" =~ $PRERELEASE_PATTERN ]]; then FLOATING_TAG="beta"
floating_tag="$PRERELEASE_FLOATING_TAG" else
FLOATING_TAG="latest"
fi fi
docker buildx imagetools create \ docker buildx imagetools create \
-t "${IMAGE}:${VERSION}" \ -t "${IMAGE}:${VERSION}" \
-t "${IMAGE}:${floating_tag}" \ -t "${IMAGE}:${FLOATING_TAG}" \
"${references[@]}" "${references[@]}"
- name: Inspect image - name: Inspect image
+68
View File
@@ -0,0 +1,68 @@
/*
Copyright 2025 linux.do
Modified by Arctel.net, 2026
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"log"
"sync"
"github.com/Rain-kl/Wavelet/internal/router"
"github.com/Rain-kl/Wavelet/internal/task/scheduler"
"github.com/Rain-kl/Wavelet/internal/task/worker"
"github.com/spf13/cobra"
)
var allCmd = &cobra.Command{
Use: "all",
Short: "以融合模式同时启动 API、Worker 和 Scheduler",
Run: func(cmd *cobra.Command, args []string) {
log.Println("[All] 融合模式启动")
var wg sync.WaitGroup
// 启动 API HTTP 服务
wg.Add(1)
go func() {
defer wg.Done()
log.Println("[All] 启动 API 服务")
router.Serve()
}()
// 启动 Asynq Worker 任务处理服务
wg.Add(1)
go func() {
defer wg.Done()
log.Println("[All] 启动 Worker 服务")
if err := worker.StartWorker(); err != nil {
log.Printf("[All] Worker 启动失败: %v\n", err)
}
}()
// 启动 Asynq 定时任务调度器
wg.Add(1)
go func() {
defer wg.Done()
log.Println("[All] 启动 Scheduler 服务")
if err := scheduler.StartScheduler(); err != nil {
log.Printf("[All] Scheduler 启动失败: %v\n", err)
}
}()
wg.Wait()
},
}
+5 -1
View File
@@ -30,8 +30,10 @@ var rootCmd = &cobra.Command{
migrator.Migrate() migrator.Migrate()
}, },
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
// 无参数时默认以融合模式启动所有服务
if len(args) == 0 { if len(args) == 0 {
log.Fatalf("[CMD] please provide a command\n") allCmd.Run(allCmd, args)
return
} }
appMode := args[0] appMode := args[0]
switch appMode { switch appMode {
@@ -41,6 +43,8 @@ var rootCmd = &cobra.Command{
schedulerCmd.Run(schedulerCmd, args) schedulerCmd.Run(schedulerCmd, args)
case "worker": case "worker":
workerCmd.Run(workerCmd, args) workerCmd.Run(workerCmd, args)
case "all":
allCmd.Run(allCmd, args)
default: default:
log.Fatal("[CMD] unknown app mode\n") log.Fatal("[CMD] unknown app mode\n")
} }