diff --git a/.woodpecker/gitea-release.yml b/.woodpecker/gitea-release.yml index 981bde8..322c7ce 100644 --- a/.woodpecker/gitea-release.yml +++ b/.woodpecker/gitea-release.yml @@ -1,27 +1,43 @@ -labels: - ci: domain-linux-native - platform: linux/amd64 - -clone: - - name: git - image: plugin-git - settings: - lfs: false - -when: - - event: tag - ref: refs/tags/v* - -depends_on: - - linux-release - - windows-release - -steps: - - name: publish-gitea-release - image: bash - environment: +labels: + ci: domain-linux-native + platform: linux/amd64 + +clone: + - name: git + image: plugin-git + settings: + lfs: false + +when: + - event: tag + ref: refs/tags/v* + +depends_on: + - linux-release + - windows-release + +steps: + - name: publish-agent-probe-oci + image: bash + environment: GITEA_BASE: http://127.0.0.1:3100 - GITEA_TOKEN: - from_secret: gitea_token - commands: - - bash ci/publish-gitea-release.sh + GITEA_TOKEN: + from_secret: gitea_token + ZOT_REGISTRY: registry.oeo.one + ZOT_USERNAME: justme + ZOT_SECRET: + from_secret: zot_secret + commands: + - bash ci/release-node-images.sh + depends_on: [] + + - name: publish-gitea-release + image: bash + environment: + GITEA_BASE: http://127.0.0.1:3100 + GITEA_TOKEN: + from_secret: gitea_token + commands: + - bash ci/publish-gitea-release.sh + depends_on: + - publish-agent-probe-oci diff --git a/README.md b/README.md index dfc4fb4..dda569a 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ npm run build --prefix web 前端产物位于 `web/display/dist/` 和 `web/admin/dist/`。NativeAOT 目标平台和 Windows 上使用 AotAnywhere 交叉构建的说明见 [`docs/deployment.md`](docs/deployment.md)。 -Backend 以 OCI 镜像发布,支持 Linux/amd64 与 Linux/arm64;Agent 和 Probe 发布 Linux glibc/musl x64、arm64,macOS x64/arm64,以及 Windows x64/arm64 二进制包。容器部署配置见 [`compose.yaml`](compose.yaml),发布说明见 [`docs/deployment.md`](docs/deployment.md)。 +Backend、Agent 和 Probe 均以 OCI 镜像发布,支持 Linux/amd64 与 Linux/arm64;Agent 和 Probe 另外提供 Linux glibc/musl、macOS 与 Windows 二进制包。容器部署配置见 [`compose.yaml`](compose.yaml),发布说明见 [`docs/deployment.md`](docs/deployment.md)。 ### 本地运行 diff --git a/backend/Directory.Build.props b/backend/Directory.Build.props index bbd8e84..65e3c69 100644 --- a/backend/Directory.Build.props +++ b/backend/Directory.Build.props @@ -13,7 +13,7 @@ true $(MSBuildThisFileDirectory)artifacts true - 0.1.0 + 0.1.1 false diff --git a/backend/agent/README.md b/backend/agent/README.md index 53ae9b4..42266c5 100644 --- a/backend/agent/README.md +++ b/backend/agent/README.md @@ -47,6 +47,8 @@ fleet-enroll --server https:// --join --name --ide monitor-agent --config /etc/monitor/agent.json ``` +也可使用 OCI 镜像 `registry.oeo.one/monitor/monitor-agent:`,自动选择 Linux amd64 或 arm64 镜像。镜像同时包含 `fleet-enroll`;可通过 `--entrypoint /usr/local/bin/fleet-enroll` 执行首次注册。容器默认读取 `/etc/monitor/agent.json`,需要挂载配置与身份目录;建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。镜像以 UID/GID 1654 运行,挂载目录必须允许该用户读取配置和身份文件。 + 不传 `--config` 时默认读取 `/etc/monitor/agent.json`。Agent 默认监听控制端口 `7443/tcp`,数据端口 `7443/udp` 和 `7444/tcp`;防火墙和面板中的节点端口配置需与本地配置一致。`reporting.host_stats` 可关闭主机状态上报;启用时只上报资源使用等汇总数据,不包含主机名、进程或 IP。 ## 设计说明 diff --git a/backend/agent/src/Monitor.Agent/AgentApp.cs b/backend/agent/src/Monitor.Agent/AgentApp.cs index a7136a7..c79fb0a 100644 --- a/backend/agent/src/Monitor.Agent/AgentApp.cs +++ b/backend/agent/src/Monitor.Agent/AgentApp.cs @@ -12,7 +12,7 @@ namespace Monitor.Agent; /// Agent 的组装点:数据面、点对点监听、与 Backend 的链路都在这里接起来。没有全局状态,测试可以在同一进程里启动多个。 public sealed class AgentApp : IDisposable { - public const string Version = "0.1.0"; + public const string Version = "0.1.1"; private readonly AgentConfig _config; private readonly LoadedNodeIdentity _identity; diff --git a/backend/probe/README.md b/backend/probe/README.md index 4b50574..cfeb861 100644 --- a/backend/probe/README.md +++ b/backend/probe/README.md @@ -39,6 +39,8 @@ monitor-probe --config /etc/monitor/probe.json 不传 `--config` 时默认读取 `/etc/monitor/probe.json`。配置中的 `limits` 是本地硬上限,Backend 无法远程放宽;路由器等低性能设备可调低测试码率和包速率,避免 Probe 自身成为测量瓶颈。部署时可使用仓库的 `packaging/` 文件和安装脚本。 +也可使用 OCI 镜像 `registry.oeo.one/monitor/monitor-probe:`,自动选择 Linux amd64 或 arm64 镜像。容器默认读取 `/etc/monitor/probe.json`,并在同一目录维护 `agents.json` 缓存,因此挂载目录必须允许 UID/GID 1654 读写。建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。 + ## 设计说明 - Probe 只执行编译进程序的测量模块。它不认识 Backend,不落盘测量数据,也不能执行命令或自行更新。 diff --git a/backend/probe/src/Monitor.Probe/ProbeApp.cs b/backend/probe/src/Monitor.Probe/ProbeApp.cs index f6a71a9..aeb880c 100644 --- a/backend/probe/src/Monitor.Probe/ProbeApp.cs +++ b/backend/probe/src/Monitor.Probe/ProbeApp.cs @@ -11,7 +11,7 @@ namespace Monitor.Probe; /// public sealed class ProbeApp : IDisposable { - public const string Version = "0.1.0"; + public const string Version = "0.1.1"; private readonly ConnectionEnv _env; private readonly Dictionary _connections = []; diff --git a/ci/publish-gitea-release.sh b/ci/publish-gitea-release.sh index 4f0b182..80dabea 100644 --- a/ci/publish-gitea-release.sh +++ b/ci/publish-gitea-release.sh @@ -26,7 +26,7 @@ printf 'machine %s login justme password %s\n' "$gitea_host" "$GITEA_TOKEN" > "$ chmod 600 "$netrc" trap 'rm -rf "$tmp"' EXIT -release_body="Agent and Probe builds: Linux glibc and musl (x64, arm64), macOS (x64, arm64), and Windows (x64, arm64). macOS binaries are not Developer ID signed or notarized. Backend OCI images: registry.oeo.one/monitor/monitor-backend:$version (linux/amd64 and linux/arm64)." +release_body="Agent and Probe builds: Linux glibc and musl (x64, arm64), macOS (x64, arm64), and Windows (x64, arm64). Linux glibc Agent and Probe OCI images: registry.oeo.one/monitor/monitor-agent:$version and registry.oeo.one/monitor/monitor-probe:$version (linux/amd64 and linux/arm64). Backend OCI image: registry.oeo.one/monitor/monitor-backend:$version (linux/amd64 and linux/arm64). macOS binaries are not Developer ID signed or notarized." payload="$(printf '{"tag_name":"%s","target_commitish":"%s","name":"MyMonitor %s","body":"%s","draft":false,"prerelease":false}' \ "$tag" "${CI_COMMIT_SHA:-}" "$tag" "$release_body")" release_status="$(curl --silent --show-error --retry 3 --netrc-file "$netrc" \ diff --git a/ci/release-node-images.sh b/ci/release-node-images.sh new file mode 100644 index 0000000..8adb51f --- /dev/null +++ b/ci/release-node-images.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +# Publish glibc OCI images for the Linux Agent and Probe on amd64 and arm64. +set -euo pipefail + +tag="${CI_COMMIT_TAG:-}" +[[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] || { + echo "release must run from a vMAJOR.MINOR.PATCH tag" >&2 + exit 2 +} +[[ -n "${GITEA_TOKEN:-}" ]] || { echo "GITEA_TOKEN is required" >&2; exit 2; } +[[ -n "${ZOT_USERNAME:-}" && -n "${ZOT_SECRET:-}" ]] || { + echo "Zot credentials are required" >&2 + exit 2 +} + +version="${tag#v}" +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +project_version="$(sed -n 's#.*\(.*\).*#\1#p' "$root/backend/Directory.Build.props" | head -1)" +if [[ "$version" != "$project_version" ]]; then + echo "tag version $version does not match project version $project_version" >&2 + exit 1 +fi + +gitea_base="${GITEA_BASE:-http://127.0.0.1:3100}" +gitea_base="${gitea_base%/}" +gitea_host="${gitea_base#https://}" +gitea_host="${gitea_host#http://}" +gitea_host="${gitea_host%%/*}" +gitea_host="${gitea_host%%:*}" +registry="${ZOT_REGISTRY:-registry.oeo.one}" +image_root="$registry/monitor" +work="$root/.release/node-images" +netrc="$work/netrc" +buildah_home="/home/justme/woodpecker/buildah-home/node-images" +mkdir -p "$buildah_home" +export HOME="$buildah_home" +mkdir -p "$work" +chmod 700 "$work" +printf 'machine %s login justme password %s\n' "$gitea_host" "$GITEA_TOKEN" > "$netrc" +chmod 600 "$netrc" +export REGISTRY_AUTH_FILE="$work/zot-auth.json" +trap 'rm -rf "$work"' EXIT + +logged_in=false +for attempt in 1 2 3 4; do + if buildah login --tls-verify=true --username "$ZOT_USERNAME" --password-stdin "$registry" <<< "$ZOT_SECRET" >/dev/null; then + logged_in=true + break + fi + if [[ "$attempt" -lt 4 ]]; then + echo "Zot login attempt $attempt failed; retrying in 3 seconds" >&2 + sleep 3 + fi +done +[[ "$logged_in" == true ]] || { echo "could not authenticate to Zot after 4 attempts" >&2; exit 1; } + +build_arch_image() { + local component="$1" arch="$2" suffix="$3" + local filename="monitor-$component-v$version-linux-$suffix.tar.gz" + local context="$work/$component-$arch" + local archive="$work/$filename" + local containerfile="$root/packaging/container/${component^}.Containerfile" + local image="$image_root/monitor-$component" + local local_image="localhost/monitor-$component:$version-$arch" + + curl --fail-with-body --silent --show-error --retry 3 --netrc-file "$netrc" \ + "$gitea_base/api/packages/justme/generic/monitor-node/$version/$filename" -o "$archive" + mkdir -p "$context/app" "$context/config" + if [[ "$component" == agent ]]; then + tar -xzf "$archive" -C "$work" monitor-agent fleet-enroll + install -m 0755 "$work/fleet-enroll" "$context/app/fleet-enroll" + else + tar -xzf "$archive" -C "$work" "monitor-$component" + fi + install -m 0755 "$work/monitor-$component" "$context/app/monitor-$component" + : > "$context/config/.keep" + + buildah bud --tls-verify=true --arch "$arch" \ + --build-arg "APP_VERSION=$version" \ + -f "$containerfile" -t "$local_image" "$context" >/dev/null + + local pushed=false + for attempt in 1 2 3; do + if buildah push --tls-verify=true "$local_image" "docker://$image:$version-$arch" >/dev/null; then + pushed=true + break + fi + if [[ "$attempt" -lt 3 ]]; then + echo "Zot push for $component/$arch attempt $attempt failed; retrying in 5 seconds" >&2 + sleep 5 + fi + done + [[ "$pushed" == true ]] || { echo "could not push $component/$arch image after 3 attempts" >&2; return 1; } + echo "pushed Zot image $image:$version-$arch" +} + +publish_index() { + local component="$1" image="$image_root/monitor-$component" + local manifest="localhost/monitor-$component:$version-multiarch" + buildah manifest rm "$manifest" >/dev/null 2>&1 || true + buildah manifest create "$manifest" >/dev/null + buildah manifest add "$manifest" "docker://$image:$version-amd64" >/dev/null + buildah manifest add "$manifest" "docker://$image:$version-arm64" >/dev/null + buildah manifest push --all --format oci "$manifest" "docker://$image:$version" >/dev/null + if [[ "$version" != *-* ]]; then + buildah manifest push --all --format oci "$manifest" "docker://$image:latest" >/dev/null + fi + buildah manifest rm "$manifest" >/dev/null + echo "published multi-architecture index $image:$version" + if [[ "$version" != *-* ]]; then + echo "published multi-architecture index $image:latest" + fi +} + +for component in agent probe; do + build_arch_image "$component" amd64 amd64 + build_arch_image "$component" arm64 arm64 + publish_index "$component" +done diff --git a/compose.yaml b/compose.yaml index 93b92cc..1b83feb 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,6 +1,6 @@ services: backend: - image: registry.oeo.one/monitor/monitor-backend:${MONITOR_VERSION:-0.1.0} + image: registry.oeo.one/monitor/monitor-backend:${MONITOR_VERSION:-0.1.1} container_name: monitor-backend restart: unless-stopped init: true diff --git a/docs/deployment.md b/docs/deployment.md index 22860d1..6800961 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -36,7 +36,21 @@ Compose 将容器端口只绑定到宿主机 `127.0.0.1:8080`,适合由同机 ## 节点二进制发布 -Woodpecker 在 `vMAJOR.MINOR.PATCH` tag 上,由 Windows runner 使用 AotAnywhere 并行构建 Agent/Probe 的 Linux glibc、Linux musl、macOS 和 Windows 产物。Agent 包还包含一次性注册工具 `fleet-enroll`。Linux 归档带有安装脚本和 systemd/OpenWrt 单元;Windows 产物为 `.zip`,Linux/macOS 产物为 `.tar.gz`。所有平台产物和 Backend OCI 镜像成功后,CI 会创建对应的 Gitea Release,并附上压缩包和 SHA-256 校验文件。macOS 产物目前未签名/公证。安装说明见 [`../backend/agent/README.md`](../backend/agent/README.md) 与 [`../backend/probe/README.md`](../backend/probe/README.md)。 +Woodpecker 在 `vMAJOR.MINOR.PATCH` tag 上,由 Windows runner 使用 AotAnywhere 并行构建 Agent/Probe 的 Linux glibc、Linux musl、macOS 和 Windows 产物。Agent 包还包含一次性注册工具 `fleet-enroll`。Linux 归档带有安装脚本和 systemd/OpenWrt 单元;Windows 产物为 `.zip`,Linux/macOS 产物为 `.tar.gz`。Linux glibc 的 Agent 与 Probe 二进制还会打包为 OCI 镜像,支持 `linux/amd64` 和 `linux/arm64`;musl、macOS、Windows 仍通过对应平台归档分发。所有平台产物和 OCI 镜像发布成功后,CI 会创建对应的 Gitea Release,并附上压缩包和 SHA-256 校验文件。macOS 产物目前未签名/公证。安装说明见 [`../backend/agent/README.md`](../backend/agent/README.md) 与 [`../backend/probe/README.md`](../backend/probe/README.md)。 + +Agent 和 Probe 镜像分别发布为 `registry.oeo.one/monitor/monitor-agent:` 与 `registry.oeo.one/monitor/monitor-probe:`,包含 amd64、arm64 多架构索引;也可指定 `:-amd64` 或 `:-arm64`。稳定版本同时更新 `latest`。镜像以非 root 用户(UID/GID 1654)运行,默认读取 `/etc/monitor/agent.json` 或 `/etc/monitor/probe.json`。运行容器时挂载对应配置目录;Probe 还会在该目录写入 `agents.json`,因此目录需可由 1654 用户写入。Agent、Probe 需使用宿主网络以保持节点端口和测量流量行为,并按需添加 `NET_RAW` 能力以执行 ICMP 测量: + +```sh +docker run -d --name monitor-agent --restart unless-stopped --network host \ + --cap-add NET_RAW -v "$PWD/agent-config:/etc/monitor" \ + registry.oeo.one/monitor/monitor-agent: + +docker run -d --name monitor-probe --restart unless-stopped --network host \ + --cap-add NET_RAW -v "$PWD/probe-config:/etc/monitor" \ + registry.oeo.one/monitor/monitor-probe: +``` + +宿主机配置目录应允许 UID/GID 1654 读写;Probe 需要写权限来更新 `agents.json`。也可通过 Docker Compose 配置相同的网络、卷和能力。 实测体积(`StackTraceSupport=false`、`IlcFoldIdenticalMethodBodies=true`、`OptimizationPreference=Size`): diff --git a/docs/project-layout.md b/docs/project-layout.md index 6dfdf2c..b3a7c83 100644 --- a/docs/project-layout.md +++ b/docs/project-layout.md @@ -253,7 +253,7 @@ Backend 允许的包:ASP.NET Core 共享框架、`Microsoft.Data.Sqlite`、`Mi true true true - 0.1.0 + 0.1.1 diff --git a/packaging/container/Agent.Containerfile b/packaging/container/Agent.Containerfile new file mode 100644 index 0000000..7b0e07b --- /dev/null +++ b/packaging/container/Agent.Containerfile @@ -0,0 +1,17 @@ +FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-noble-chiseled-extra + +ARG APP_VERSION=unknown +LABEL org.opencontainers.image.title="Monitor Agent" \ + org.opencontainers.image.description="MyMonitor node agent for Linux" \ + org.opencontainers.image.licenses="Apache-2.0" \ + org.opencontainers.image.version="$APP_VERSION" + +COPY --chown=1654:1654 app/monitor-agent /usr/local/bin/monitor-agent +COPY --chown=1654:1654 app/fleet-enroll /usr/local/bin/fleet-enroll +COPY --chown=1654:1654 config/ /etc/monitor/ + +USER 1654:1654 +WORKDIR /etc/monitor +VOLUME ["/etc/monitor"] +ENTRYPOINT ["/usr/local/bin/monitor-agent"] +CMD ["--config", "/etc/monitor/agent.json"] diff --git a/packaging/container/Probe.Containerfile b/packaging/container/Probe.Containerfile new file mode 100644 index 0000000..cfeb418 --- /dev/null +++ b/packaging/container/Probe.Containerfile @@ -0,0 +1,16 @@ +FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-noble-chiseled-extra + +ARG APP_VERSION=unknown +LABEL org.opencontainers.image.title="Monitor Probe" \ + org.opencontainers.image.description="MyMonitor network measurement probe for Linux" \ + org.opencontainers.image.licenses="Apache-2.0" \ + org.opencontainers.image.version="$APP_VERSION" + +COPY --chown=1654:1654 app/monitor-probe /usr/local/bin/monitor-probe +COPY --chown=1654:1654 config/ /etc/monitor/ + +USER 1654:1654 +WORKDIR /etc/monitor +VOLUME ["/etc/monitor"] +ENTRYPOINT ["/usr/local/bin/monitor-probe"] +CMD ["--config", "/etc/monitor/probe.json"]