feat: bundle multi-arch nginx for native fnOS stage 2

This commit is contained in:
2026-10-02 15:32:37 +08:00
parent 6725fc131d
commit b05098926c
6 changed files with 108 additions and 7 deletions
+29 -4
View File
@@ -11,22 +11,35 @@
构建过程不再引用 `F:\Fonu-main\havline`。 构建过程不再引用 `F:\Fonu-main\havline`。
阶段一范围: 阶段二范围:
- Go 主程序原生运行 - Go 主程序原生运行
- 统一网关 `/app/havline` - 统一网关 `/app/havline`
- Unix Socket `app.sock` - Unix Socket `app.sock`
- `cmd/main` 启动、停止和状态检查 - `cmd/main` 启动、停止和状态检查
- amd64 与 arm64 双架构二进制 - amd64 与 arm64 双架构二进制
- Nginx amd64 与 arm64 双架构二进制
- Nginx 依赖库与 `mime.types`
- HTTP 18080 与 HTTPS 9443
当前不包含: 当前不包含:
- Nginx 二进制
- frpc - frpc
- cloudflared - cloudflared
这些依赖将在后续阶段加入。 这些依赖将在后续阶段加入。
## 构建前置条件
构建脚本需要本地存在用于提取 Nginx 的 Havline 镜像:
```text
jayhub.top/mrlee/havline:1.9.0 amd64
havline:1.9.0 arm64
```
如果本地没有对应架构镜像,先加载 amd64 与 arm64 镜像归档。
## 构建 ## 构建
```powershell ```powershell
@@ -43,5 +56,17 @@ dist/havline-native-1.9.0.fpk
构建脚本会: 构建脚本会:
1. 使用 `VITE_BASE_PATH=/app/havline/` 构建前端; 1. 使用 `VITE_BASE_PATH=/app/havline/` 构建前端;
2. 交叉编译 amd64 与 arm64 Go 二进制; 2. 从本地镜像提取 amd64 与 arm64 Nginx 及依赖库;
3. 调用 `fnpack build` 打包。 3. 交叉编译 amd64 与 arm64 Go 二进制;
4. 调用 `fnpack build` 打包。
## 真机注意
当前 Nginx 来自 Debian 12,依赖目标系统 glibc。飞牛设备建议满足:
```text
glibc >= 2.36
```
如果目标设备 glibc 版本较低,需要改为静态编译 Nginx 或打包完整
loader 与 libc。
+6
View File
@@ -26,6 +26,12 @@ http {
include ` + absNginxPath(cfg.NginxMimeTypes) + `; include ` + absNginxPath(cfg.NginxMimeTypes) + `;
default_type application/octet-stream; default_type application/octet-stream;
client_body_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "client_body_temp")) + `;
proxy_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "proxy_temp")) + `;
fastcgi_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "fastcgi_temp")) + `;
uwsgi_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "uwsgi_temp")) + `;
scgi_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "scgi_temp")) + `;
log_format havline_access '$time_iso8601 $host $server_port $request_method $request_uri $status $request_time $remote_addr $upstream_addr $request_length $bytes_sent'; log_format havline_access '$time_iso8601 $host $server_port $request_method $request_uri $status $request_time $remote_addr $upstream_addr $request_length $bytes_sent';
access_log ` + absNginxPath(filepath.Join(cfg.LogsDir(), "access.log")) + ` havline_access; access_log ` + absNginxPath(filepath.Join(cfg.LogsDir(), "access.log")) + ` havline_access;
+54
View File
@@ -21,6 +21,55 @@ function Copy-Tree {
} }
} }
function Resolve-DockerCli {
if ($env:DOCKER_BIN) { return $env:DOCKER_BIN }
$command = Get-Command docker -ErrorAction SilentlyContinue
if ($command) { return $command.Source }
$default = "C:\Program Files\Docker\Docker\resources\bin\docker.exe"
if (Test-Path -LiteralPath $default) { return $default }
throw "找不到 docker 命令"
}
function Export-Nginx {
param(
[string]$DockerBin,
[string]$Platform,
[string]$Arch,
[string]$Image,
[string]$OutputDir
)
$archInfo = (& $DockerBin image inspect $Image --format '{{.Architecture}}' 2>$null)
if ($LASTEXITCODE -ne 0) {
throw "找不到 Nginx 来源镜像:$Image"
}
if ($archInfo.Trim() -ne $Arch) {
throw "镜像架构不匹配:$Image 是 $($archInfo.Trim()),需要 $Arch"
}
$archDir = Join-Path $OutputDir $Arch
New-Item -ItemType Directory -Force $archDir | Out-Null
$script = @'
set -e
mkdir -p /out/lib
cp /usr/sbin/nginx /out/nginx
cp /etc/nginx/mime.types /out/mime.types
ldd /usr/sbin/nginx | awk '/=> \// {print $3} /^\// {print $1}' | sort -u | while read lib; do
case "$(basename "$lib")" in
libc.so.6|ld-linux-*.so.*) continue ;;
esac
cp "$lib" /out/lib/
done
'@
& $DockerBin run --rm --platform $Platform -v "${archDir}:/out" --entrypoint sh $Image -c $script
if ($LASTEXITCODE -ne 0) {
throw "提取 $Arch Nginx 失败,退出码:$LASTEXITCODE"
}
Copy-Item -LiteralPath (Join-Path $archDir "mime.types") -Destination (Join-Path $OutputDir "mime.types") -Force
}
if (-not (Test-Path -LiteralPath $project)) { if (-not (Test-Path -LiteralPath $project)) {
throw "找不到 Native 应用工程:$project" throw "找不到 Native 应用工程:$project"
} }
@@ -67,6 +116,11 @@ if ($LASTEXITCODE -ge 8) {
Copy-Tree -Source (Join-Path $mainRepo "migrations") -Destination (Join-Path $stage "app\migrations") Copy-Tree -Source (Join-Path $mainRepo "migrations") -Destination (Join-Path $stage "app\migrations")
Copy-Tree -Source (Join-Path $mainRepo "web\assets\image") -Destination (Join-Path $stage "app\web\assets\image") Copy-Tree -Source (Join-Path $mainRepo "web\assets\image") -Destination (Join-Path $stage "app\web\assets\image")
$serverDir = Join-Path $stage "app\server"
$dockerBin = Resolve-DockerCli
Export-Nginx -DockerBin $dockerBin -Platform "linux/amd64" -Arch "amd64" -Image "jayhub.top/mrlee/havline:$version" -OutputDir $serverDir
Export-Nginx -DockerBin $dockerBin -Platform "linux/arm64" -Arch "arm64" -Image "havline:$version" -OutputDir $serverDir
$env:CGO_ENABLED = "0" $env:CGO_ENABLED = "0"
$env:GOOS = "linux" $env:GOOS = "linux"
+13 -1
View File
@@ -17,6 +17,18 @@ if [ ! -f "$BIN" ]; then
exit 1 exit 1
fi fi
chmod +x "$BIN" NGINX_BIN="$TRIM_APPDEST/server/$ARCH_DIR/nginx"
if [ ! -f "$NGINX_BIN" ]; then
echo "找不到 Nginx 二进制:$NGINX_BIN" > "$TRIM_TEMP_LOGFILE"
exit 1
fi
MIME_TYPES="$TRIM_APPDEST/server/mime.types"
if [ ! -f "$MIME_TYPES" ]; then
echo "找不到 Nginx mime.types:$MIME_TYPES" > "$TRIM_TEMP_LOGFILE"
exit 1
fi
chmod +x "$BIN" "$NGINX_BIN"
echo "Havline 数据目录已准备:$TRIM_PKGVAR" echo "Havline 数据目录已准备:$TRIM_PKGVAR"
exit 0 exit 0
+4
View File
@@ -58,8 +58,12 @@ start_app() {
export HAVLINE_GATEWAY_SOCKET="$SOCKET_PATH" export HAVLINE_GATEWAY_SOCKET="$SOCKET_PATH"
export HAVLINE_GATEWAY_PREFIX="/app/havline" export HAVLINE_GATEWAY_PREFIX="/app/havline"
export HAVLINE_UPDATER_SOCKET="" export HAVLINE_UPDATER_SOCKET=""
export HAVLINE_NGINX_BIN="$TRIM_APPDEST/server/$arch/nginx"
export HAVLINE_NGINX_MIME_TYPES="$TRIM_APPDEST/server/mime.types"
export HAVLINE_NGINX_PID="$TRIM_PKGVAR/nginx/nginx.pid"
export HAVLINE_NGINX_HTTP_PORT="${wizard_nginx_http_port:-18080}" export HAVLINE_NGINX_HTTP_PORT="${wizard_nginx_http_port:-18080}"
export HAVLINE_NGINX_HTTPS_PORT="${wizard_nginx_https_port:-9443}" export HAVLINE_NGINX_HTTPS_PORT="${wizard_nginx_https_port:-9443}"
export LD_LIBRARY_PATH="$TRIM_APPDEST/server/$arch/lib:${LD_LIBRARY_PATH:-}"
export TZ="${wizard_timezone:-Asia/Shanghai}" export TZ="${wizard_timezone:-Asia/Shanghai}"
if [ -n "${wizard_initial_admin_password:-}" ]; then if [ -n "${wizard_initial_admin_password:-}" ]; then
+2 -2
View File
@@ -1,7 +1,7 @@
appname = havline appname = havline
version = 1.9.0 version = 1.9.0
display_name = Havline display_name = Havline
desc = NAS 公网访问管理:原生应用阶段一 desc = NAS 公网访问管理:原生应用阶段二
platform = all platform = all
source = thirdparty source = thirdparty
maintainer = Havline maintainer = Havline
@@ -11,4 +11,4 @@ desktop_uidir = ui
desktop_applaunchname = havline.main desktop_applaunchname = havline.main
ctl_stop = true ctl_stop = true
os_min_version = 1.1.3100 os_min_version = 1.1.3100
changelog = 原生应用阶段一:Go 主程序、统一网关和启停脚本 changelog = 原生应用阶段二:Nginx amd64/arm64 双架构打包