feat: one-line install for agents and probes from one shared script (curl | bash, zsh, PowerShell) [CI]
ci/woodpecker/push/main-amd64-build Pipeline was successful

This commit is contained in:
justme committed 2026-10-07 20:02:38 +08:00
1 parent 5528753d45
commit af2962fc58
29 files changed
+558 -124

No files matched your search

+3 -1
View File
@@ -9,9 +9,11 @@ LABEL org.opencontainers.image.title="MoShiro Agent" \
COPY --chown=1654:1654 app/moshiro-agent /usr/local/bin/moshiro-agent
COPY --chown=1654:1654 app/fleet-enroll /usr/local/bin/fleet-enroll
COPY --chown=1654:1654 config/ /etc/moshiro/
COPY --chown=1654:1654 --chmod=0755 entrypoint.sh /usr/local/bin/moshiro-entrypoint
ENV MOSHIRO_ROLE=agent
USER 1654:1654
WORKDIR /etc/moshiro
VOLUME ["/etc/moshiro"]
ENTRYPOINT ["/usr/local/bin/moshiro-agent"]
ENTRYPOINT ["/usr/local/bin/moshiro-entrypoint"]
CMD ["--config", "/etc/moshiro/agent.json"]
@@ -35,9 +35,11 @@ LABEL org.opencontainers.image.title="MoShiro Agent" \
COPY --chown=1654:1654 --from=build /out/agent/moshiro-agent /usr/local/bin/moshiro-agent
COPY --chown=1654:1654 --from=build /out/enroll/fleet-enroll /usr/local/bin/fleet-enroll
COPY --chown=1654:1654 config/ /etc/moshiro/
COPY --chown=1654:1654 --chmod=0755 entrypoint.sh /usr/local/bin/moshiro-entrypoint
ENV MOSHIRO_ROLE=agent
USER 1654:1654
WORKDIR /etc/moshiro
VOLUME ["/etc/moshiro"]
ENTRYPOINT ["/usr/local/bin/moshiro-agent"]
ENTRYPOINT ["/usr/local/bin/moshiro-entrypoint"]
CMD ["--config", "/etc/moshiro/agent.json"]
+3 -1
View File
@@ -9,9 +9,11 @@ LABEL org.opencontainers.image.title="MoShiro Probe" \
COPY --chown=1654:1654 app/moshiro-probe /usr/local/bin/moshiro-probe
COPY --chown=1654:1654 app/fleet-enroll /usr/local/bin/fleet-enroll
COPY --chown=1654:1654 config/ /etc/moshiro/
COPY --chown=1654:1654 --chmod=0755 entrypoint.sh /usr/local/bin/moshiro-entrypoint
ENV MOSHIRO_ROLE=probe
USER 1654:1654
WORKDIR /etc/moshiro
VOLUME ["/etc/moshiro"]
ENTRYPOINT ["/usr/local/bin/moshiro-probe"]
ENTRYPOINT ["/usr/local/bin/moshiro-entrypoint"]
CMD ["--config", "/etc/moshiro/probe.json"]
@@ -35,9 +35,11 @@ LABEL org.opencontainers.image.title="MoShiro Probe" \
COPY --chown=1654:1654 --from=build /out/probe/moshiro-probe /usr/local/bin/moshiro-probe
COPY --chown=1654:1654 --from=build /out/enroll/fleet-enroll /usr/local/bin/fleet-enroll
COPY --chown=1654:1654 config/ /etc/moshiro/
COPY --chown=1654:1654 --chmod=0755 entrypoint.sh /usr/local/bin/moshiro-entrypoint
ENV MOSHIRO_ROLE=probe
USER 1654:1654
WORKDIR /etc/moshiro
VOLUME ["/etc/moshiro"]
ENTRYPOINT ["/usr/local/bin/moshiro-probe"]
ENTRYPOINT ["/usr/local/bin/moshiro-entrypoint"]
CMD ["--config", "/etc/moshiro/probe.json"]
+31
View File
@@ -0,0 +1,31 @@
#!/bin/sh
# 容器入口(Agent 和 Probe 共用):第一次启动用 MOSHIRO_SERVER / MOSHIRO_JOIN / MOSHIRO_NAME 自己注册并写配置,
# 身份和配置都在命名卷 /etc/moshiro 里,之后重启(或换新镜像重建容器)直接启动,不需要再给参数。
set -eu
role="${MOSHIRO_ROLE:?MOSHIRO_ROLE is set by the image}"
dir=/etc/moshiro
identity="$dir/$role-id"
config="$dir/$role.json"
if [ ! -f "$identity/node.json" ]; then
if [ -z "${MOSHIRO_SERVER:-}" ] || [ -z "${MOSHIRO_JOIN:-}" ] || [ -z "${MOSHIRO_NAME:-}" ]; then
echo "first start: set MOSHIRO_SERVER, MOSHIRO_JOIN and MOSHIRO_NAME (the panel shows the complete command)" >&2
exit 1
fi
/usr/local/bin/fleet-enroll --server "$MOSHIRO_SERVER" --join "$MOSHIRO_JOIN" --name "$MOSHIRO_NAME" --identity-dir "$identity"
fi
if [ ! -f "$config" ]; then
if [ -z "${MOSHIRO_SERVER:-}" ]; then
echo "$config is missing: set MOSHIRO_SERVER to create it" >&2
exit 1
fi
if [ "$role" = agent ]; then
printf '{"fleet":{"server":"%s","identity_dir":"%s"},"listen":{"bind":"::"}}\n' "$MOSHIRO_SERVER" "$identity" > "$config"
else
printf '{"fleet":{"server":"%s","identity_dir":"%s"}}\n' "$MOSHIRO_SERVER" "$identity" > "$config"
fi
fi
exec "/usr/local/bin/moshiro-$role" "$@"
+77
View File
@@ -0,0 +1,77 @@
# MoShiro 节点一键安装(Windows,Agent 和 Probe 共用同一份)。面板在 /install.ps1?code=… 生成这份脚本,把下面四个值填好:
# irm 'https://panel/install.ps1?code=jt_…' | iex
# 做的事:用 Docker Desktop 起一个容器,身份和配置存在命名卷里(重启、换新镜像重建都不用再给参数),节点名字默认取计算机名。
# 可选环境变量:MOSHIRO_NAME=节点名字(名字被占用时用)。
function Install-MoShiro {
$ErrorActionPreference = 'Stop'
$role = '@@ROLE@@'
$server = '@@SERVER@@'
$code = '@@CODE@@'
$image = '@@IMAGE@@'
$name = if ($env:MOSHIRO_NAME) { $env:MOSHIRO_NAME } else { $env:COMPUTERNAME }
$name = $name -replace '[^A-Za-z0-9._-]', '-'
if ($name.Length -gt 64) { $name = $name.Substring(0, 64) }
if (-not $name) { $name = 'node' }
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
Write-Error 'Docker Desktop is required: https://docs.docker.com/desktop/setup/install/windows-install/'
return
}
docker info *> $null
if ($LASTEXITCODE -ne 0) {
Write-Error 'Docker is installed but not running: start Docker Desktop and run this again'
return
}
$container = "moshiro-$role"
$volume = "moshiro-$role-config"
docker inspect $container *> $null
if ($LASTEXITCODE -eq 0) {
docker start $container | Out-Null
Write-Host "$container is already installed and running."
Write-Host "reinstall: docker rm -f $container; docker volume rm $volume (then run the install command again)"
return
}
Write-Host "pulling $image"
docker pull $image | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Error "could not pull $image (private registry? run: docker login $($image.Split('/')[0]))"
return
}
$arguments = @('run', '-d', '--name', $container, '--restart', 'unless-stopped',
'--read-only', '--tmpfs', '/tmp:rw,noexec,nosuid,size=64m', '--cap-drop', 'all', '--security-opt', 'no-new-privileges',
'-v', "${volume}:/etc/moshiro")
# Docker Desktop 没有宿主网络:Agent 把端口发布出来(面板里的地址需要指向这台机器的公网地址)
if ($role -eq 'agent') { $arguments += @('-p', '7443:7443/tcp', '-p', '7443:7443/udp', '-p', '7444:7444/tcp') }
$arguments += @('-e', "MOSHIRO_SERVER=$server", '-e', "MOSHIRO_JOIN=$code", '-e', "MOSHIRO_NAME=$name", $image)
Write-Host "starting $container as '$name'"
docker @arguments | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Error 'docker run failed'
return
}
# 等注册完成:第一次启动会用上面的令牌注册,成功之后才算装好
for ($i = 0; $i -lt 30; $i++) {
$running = docker inspect -f '{{.State.Running}}' $container 2>$null
$logs = (docker logs $container 2>&1) -join "`n"
if ($logs -match 'link up') {
Write-Host "done: $role '$name' is online. It shows up in the panel now."
Write-Host "logs: docker logs -f $container"
return
}
if ($running -ne 'true') {
($logs -split "`n" | Select-Object -Last 8) -join "`n" | Write-Host
Write-Error "$container stopped. If the name '$name' is taken, run again with `$env:MOSHIRO_NAME='<other-name>'. To start over: docker rm -f $container; docker volume rm $volume"
return
}
Start-Sleep -Seconds 1
}
Write-Host "$container is running, but has not reported online yet. Check: docker logs $container"
}
Install-MoShiro
+103
View File
@@ -0,0 +1,103 @@
#!/bin/sh
# MoShiro 节点一键安装(Agent 和 Probe 共用同一份)。面板在 /install?code=… 生成这份脚本,把下面四个值填好:
# curl -fsSL 'https://panel/install?code=jt_…' | bash (macOS 用 zsh)
# 做的事:用 Docker 或 Podman 起一个容器,身份和配置存在命名卷里(重启、换新镜像重建都不用再给参数),节点名字默认取主机名。
# 可选环境变量:MOSHIRO_NAME=节点名字(名字被占用时用)、MOSHIRO_ENGINE=docker|podman、MOSHIRO_DRY_RUN=1(只打印命令)。
set -eu
ROLE='@@ROLE@@'
SERVER='@@SERVER@@'
CODE='@@CODE@@'
IMAGE='@@IMAGE@@'
say() { printf '%s\n' "$*"; }
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
name="${MOSHIRO_NAME:-$(hostname -s 2>/dev/null || hostname 2>/dev/null || echo node)}"
name="$(printf '%s' "$name" | tr -c 'A-Za-z0-9._-' '-' | cut -c1-64)"
[ -n "$name" ] || name=node
engine="${MOSHIRO_ENGINE:-}"
if [ -z "$engine" ]; then
if command -v docker >/dev/null 2>&1; then engine=docker
elif command -v podman >/dev/null 2>&1; then engine=podman
else die "Docker or Podman is required (https://docs.docker.com/engine/install/)"; fi
fi
command -v "$engine" >/dev/null 2>&1 || die "$engine is not installed"
# 当前用户连不上引擎(不在 docker 组、rootful podman)就用 sudo
use_sudo=0
if ! "$engine" info >/dev/null 2>&1 </dev/null; then
[ "$(id -u)" -ne 0 ] || die "$engine is installed but not running"
command -v sudo >/dev/null 2>&1 || die "$engine needs root: run this as root or install sudo"
use_sudo=1
fi
eng() { if [ "$use_sudo" -eq 1 ]; then sudo "$engine" "$@"; else "$engine" "$@"; fi; }
container="moshiro-$ROLE"
volume="moshiro-$ROLE-config"
linux=0
[ "$(uname -s)" = Linux ] && linux=1
restart=unless-stopped
[ "$engine" = podman ] && restart=always
set -- run -d --name "$container" --restart "$restart" \
--read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m --cap-drop all --security-opt no-new-privileges \
-v "$volume:/etc/moshiro"
[ "$engine" = podman ] && set -- "$@" --security-opt label=disable
if [ "$linux" -eq 1 ]; then
# 宿主网络:Agent 对外暴露端口,也靠它识别本机的公网 IPv4 和 IPv6 地址
set -- "$@" --network host
if [ "$ROLE" = agent ]; then
# 主机资源占用(只读),管理端的 Agent 详情页显示 CPU、内存、磁盘、网络
set -- "$@" -v /etc/os-release:/etc/os-release:ro -v /proc:/host/proc:ro -v /sys:/host/sys:ro -v /:/host/root:ro \
-e MOSHIRO_NETWORK_SCOPE=host -e HOST_PROC=/host/proc -e HOST_SYS=/host/sys -e HOST_ROOT=/host/root
fi
elif [ "$ROLE" = agent ]; then
# macOS 上的 Docker 没有宿主网络:把 Agent 的端口发布出来(面板里的地址需要指向这台机器的公网地址)
set -- "$@" -p 7443:7443/tcp -p 7443:7443/udp -p 7444:7444/tcp
fi
set -- "$@" -e "MOSHIRO_SERVER=$SERVER" -e "MOSHIRO_JOIN=$CODE" -e "MOSHIRO_NAME=$name" "$IMAGE"
if [ "${MOSHIRO_DRY_RUN:-}" = 1 ]; then
say "$engine $*"
exit 0
fi
if eng inspect "$container" >/dev/null 2>&1 </dev/null; then
eng start "$container" >/dev/null </dev/null
say "$container is already installed and running."
say "reinstall: $engine rm -f $container && $engine volume rm $volume (then run the install command again)"
exit 0
fi
say "pulling $IMAGE"
if ! eng pull "$IMAGE" >/dev/null </dev/null; then
die "could not pull $IMAGE (private registry? run: $engine login ${IMAGE%%/*})"
fi
say "starting $container as '$name'"
eng "$@" >/dev/null </dev/null
# Podman 的容器开机自启要靠 podman-restart.service
if [ "$engine" = podman ] && command -v systemctl >/dev/null 2>&1; then
if [ "$(id -u)" -eq 0 ]; then systemctl enable --now podman-restart >/dev/null 2>&1 || true
elif [ "$use_sudo" -eq 1 ]; then sudo systemctl enable --now podman-restart >/dev/null 2>&1 || true; fi
fi
# 等注册完成:第一次启动会用上面的令牌注册,成功之后才算装好
i=0
while [ "$i" -lt 30 ]; do
state="$(eng inspect -f '{{.State.Running}}' "$container" 2>/dev/null </dev/null || echo false)"
logs="$(eng logs "$container" 2>&1 </dev/null || true)"
case "$logs" in
*"link up"*) say "done: $ROLE '$name' is online. It shows up in the panel now."; say "logs: $engine logs -f $container"; exit 0 ;;
esac
if [ "$state" != true ]; then
printf '%s\n' "$logs" | tail -n 8 >&2
die "$container stopped. If the name '$name' is taken, run again with MOSHIRO_NAME=<other-name>. To start over: $engine rm -f $container && $engine volume rm $volume"
fi
i=$((i + 1))
sleep 1
done
say "$container is running, but has not reported online yet. Check: $engine logs $container"