modify: aarch64 设备docker部署

This commit is contained in:
root
2026-04-30 16:26:50 +08:00
parent 072a59a169
commit 074414aeab
6 changed files with 114 additions and 36 deletions
+56 -29
View File
@@ -1,48 +1,75 @@
# FROM debian:bookworm-slim AS builder # 架构参数:可选值 x86_64 或 aarch64
ARG TARGETARCH=x86_64
FROM ubuntu:24.04 AS builder FROM ubuntu:24.04 AS builder
RUN apt-get update && \ ARG TARGETARCH
apt-get install -y --no-install-recommends ca-certificates tzdata && \
# 跳过 GPG 签名验证(兼容 BM1684 等系统时间不准的设备)
RUN apt-get update -o Acquire::AllowInsecureRepositories=yes \
-o Acquire::AllowDowngradeToInsecureRepositories=yes \
--allow-unauthenticated 2>/dev/null || true && \
apt-get install -y --no-install-recommends \
-o Acquire::AllowInsecureRepositories=yes \
--allow-unauthenticated \
ca-certificates tzdata 2>/dev/null || true && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
COPY build/chongming /tmp/chongming COPY build/chongming /tmp/chongming
RUN mkdir -p /tmp/rootfs/app && \ # 构建最小 rootfs
# === 临时目录 === RUN \
mkdir -p /tmp/rootfs/tmp && \ if [ "$TARGETARCH" = "aarch64" ]; then \
chmod 1777 /tmp/rootfs/tmp && \ LIB_PATH="/lib/aarch64-linux-gnu"; \
# === 数据目录 === LD_LINKER="/lib/ld-linux-aarch64.so.1"; \
mkdir -p /tmp/rootfs/app/data && \ LD_LINKER_TARGET="/tmp/rootfs/lib/ld-linux-aarch64.so.1"; \
chmod 777 /tmp/rootfs/app/data && \ else \
# === 解释器目录 === LIB_PATH="/lib/x86_64-linux-gnu"; \
mkdir -p /tmp/rootfs/lib64 && \ LD_LINKER="/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"; \
mkdir -p /tmp/rootfs/lib/x86_64-linux-gnu && \ LD_LINKER_TARGET="/tmp/rootfs/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"; \
cp /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /tmp/rootfs/lib/x86_64-linux-gnu/ && \ fi && \
ln -s /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /tmp/rootfs/lib64/ld-linux-x86-64.so.2 && \ mkdir -p /tmp/rootfs/app && \
mkdir -p /tmp/rootfs/tmp && chmod 1777 /tmp/rootfs/tmp && \
mkdir -p /tmp/rootfs/app/data && chmod 777 /tmp/rootfs/app/data && \
# === aarch64 链接器路径特殊处理 ===
if [ "$TARGETARCH" = "aarch64" ]; then \
mkdir -p /tmp/rootfs/lib && \
cp /lib/ld-linux-aarch64.so.1 /tmp/rootfs/lib/ && \
mkdir -p /tmp/rootfs/lib64 && \
ln -s /lib/ld-linux-aarch64.so.1 /tmp/rootfs/lib64/ld-linux-aarch64.so.1; \
else \
mkdir -p /tmp/rootfs/lib64 && \
mkdir -p /tmp/rootfs${LIB_PATH} && \
cp ${LIB_PATH}/ld-linux-x86-64.so.2 /tmp/rootfs${LIB_PATH}/ && \
ln -s ${LIB_PATH}/ld-linux-x86-64.so.2 /tmp/rootfs/lib64/ld-linux-x86-64.so.2; \
fi && \
# === 依赖库 === # === 依赖库 ===
cp /lib/x86_64-linux-gnu/libdl.so.2 /tmp/rootfs/lib/x86_64-linux-gnu/ && \ mkdir -p /tmp/rootfs${LIB_PATH} && \
cp /lib/x86_64-linux-gnu/libz.so.1 /tmp/rootfs/lib/x86_64-linux-gnu/ && \ cp ${LIB_PATH}/libdl.so.2 /tmp/rootfs${LIB_PATH}/ && \
cp /lib/x86_64-linux-gnu/libpthread.so.0 /tmp/rootfs/lib/x86_64-linux-gnu/ && \ cp ${LIB_PATH}/libz.so.1 /tmp/rootfs${LIB_PATH}/ && \
cp /lib/x86_64-linux-gnu/libc.so.6 /tmp/rootfs/lib/x86_64-linux-gnu/ && \ cp ${LIB_PATH}/libpthread.so.0 /tmp/rootfs${LIB_PATH}/ && \
cp /lib/x86_64-linux-gnu/librt.so.1 /tmp/rootfs/lib/x86_64-linux-gnu/ && \ cp ${LIB_PATH}/libc.so.6 /tmp/rootfs${LIB_PATH}/ && \
cp /lib/x86_64-linux-gnu/libm.so.6 /tmp/rootfs/lib/x86_64-linux-gnu/ && \ cp ${LIB_PATH}/librt.so.1 /tmp/rootfs${LIB_PATH}/ && \
cp /lib/x86_64-linux-gnu/libutil.so.1 /tmp/rootfs/lib/x86_64-linux-gnu/ && \ cp ${LIB_PATH}/libm.so.6 /tmp/rootfs${LIB_PATH}/ && \
cp /lib/x86_64-linux-gnu/libnss_dns.so.2 /tmp/rootfs/lib/x86_64-linux-gnu/ 2>/dev/null || true && \ cp ${LIB_PATH}/libutil.so.1 /tmp/rootfs${LIB_PATH}/ && \
cp /lib/x86_64-linux-gnu/libnss_files.so.2 /tmp/rootfs/lib/x86_64-linux-gnu/ 2>/dev/null || true && \ cp ${LIB_PATH}/libnss_dns.so.2 /tmp/rootfs${LIB_PATH}/ 2>/dev/null || true && \
cp /lib/x86_64-linux-gnu/libresolv.so.2 /tmp/rootfs/lib/x86_64-linux-gnu/ 2>/dev/null || true && \ cp ${LIB_PATH}/libnss_files.so.2 /tmp/rootfs${LIB_PATH}/ 2>/dev/null || true && \
cp ${LIB_PATH}/libresolv.so.2 /tmp/rootfs${LIB_PATH}/ 2>/dev/null || true && \
# === 时区数据 === # === 时区数据 ===
mkdir -p /tmp/rootfs/usr/share/zoneinfo && \ mkdir -p /tmp/rootfs/usr/share/zoneinfo && \
cp -r /usr/share/zoneinfo/Asia /tmp/rootfs/usr/share/zoneinfo/ && \ cp -r /usr/share/zoneinfo/Asia /tmp/rootfs/usr/share/zoneinfo/ 2>/dev/null || true && \
cp /usr/share/zoneinfo/UTC /tmp/rootfs/usr/share/zoneinfo/ && \ cp /usr/share/zoneinfo/UTC /tmp/rootfs/usr/share/zoneinfo/ 2>/dev/null || true && \
# === 证书和 DNS === # === 证书和 DNS ===
mkdir -p /tmp/rootfs/etc/ssl/certs && \ mkdir -p /tmp/rootfs/etc/ssl/certs && \
cp /etc/ssl/certs/ca-certificates.crt /tmp/rootfs/etc/ssl/certs/ && \ cp /etc/ssl/certs/ca-certificates.crt /tmp/rootfs/etc/ssl/certs/ 2>/dev/null || true && \
echo "hosts: files dns" > /tmp/rootfs/etc/nsswitch.conf && \ echo "hosts: files dns" > /tmp/rootfs/etc/nsswitch.conf && \
# === /proc /sys /dev === # === /proc /sys /dev ===
mkdir -p /tmp/rootfs/proc /tmp/rootfs/sys /tmp/rootfs/dev mkdir -p /tmp/rootfs/proc /tmp/rootfs/sys /tmp/rootfs/dev
FROM scratch FROM scratch
ARG TARGETARCH
COPY --from=builder /tmp/rootfs/ / COPY --from=builder /tmp/rootfs/ /
COPY build/chongming /app/chongming COPY build/chongming /app/chongming
@@ -55,4 +82,4 @@ WORKDIR /app
EXPOSE 8000 EXPOSE 8000
CMD ["./chongming"] CMD ["./chongming"]
+13 -2
View File
@@ -77,7 +77,7 @@ chongming/
### 运行原理 ### 运行原理
开发时通过 `uv run serve` 直接启动 uvicon。 开发时通过 `uv run serve` 直接启动 uvicon。
打包后,`chongming` 可执行文件内嵌了经过混淆的引导程序,引导程序从加密的 Module-Bank 文件 (`app.mbank`, `plugins.mbank`) 中动态加载应用模块,同时从 SQLite VFS (`static.svfs`) 提供前端静态文件。所有 Python 业务代码不暴露与明文磁盘。 打包后,`chongming` 可执行文件内嵌了经过混淆的引导程序,引导程序从加密的 Module-Bank 文件 (`app.mbank`, `plugins.mbank`) 中动态加载应用模块,同时从 SQLite VFS (`static.svfs`) 提供前端静态文件。所有 Python 业务代码不暴露于明文磁盘。
--- ---
@@ -197,12 +197,23 @@ build/
## Docker 部署 ## Docker 部署
Docker 镜像采用多角度构建 + `scratch` 基础镜像,真正做到最小化。 Docker 镜像采用多阶段构建 + `scratch` 基础镜像,真正做到最小化。同时支持 **x86_64** 与 **aarch64 (ARM64)** 双架构。
### 构建镜像 ### 构建镜像
```bash ```bash
# 默认构建 x86_64 架构
docker build -t chongming:latest . docker build -t chongming:latest .
# 在 ARM64 设备上构建
docker build --build-arg TARGETARCH=aarch64 -t chongming:arm64 .
# 使用 Docker Buildx 构建多架构镜像(推送到仓库)
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg TARGETARCH \
-t your-registry/chongming:latest \
--push .
``` ```
### 执行部署脚本 ### 执行部署脚本
+3
View File
@@ -67,6 +67,9 @@ host = "0.0.0.0"
port = 8000 port = 8000
reload = false reload = false
workers = 4 workers = 4
# 对于嵌入式设备(如 BM1684),建议启用环境变量覆盖:
# docker run -e APP_WORKERS=2 ...
# 可减少线程数,避免 "can't start new thread" 错误
[production.logging] [production.logging]
backup_count = 5 # 最多保存 10 个日志文件 backup_count = 5 # 最多保存 10 个日志文件
+34 -3
View File
@@ -12,8 +12,25 @@ log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# 配置 # 检测架构
IMAGE="chongming:latest" ARCH=$(uname -m)
case "$ARCH" in
x86_64|amd64)
DEFAULT_IMAGE="chongming:latest"
ARCH_FLAG="x86_64"
;;
aarch64|arm64)
DEFAULT_IMAGE="chongming:arm64"
ARCH_FLAG="aarch64"
;;
*)
log_error "不支持的架构: $ARCH"
exit 1
;;
esac
# 配置:可通过环境变量覆盖镜像标签,例如: IMAGE=chongming:arm64 ./deploy.sh
IMAGE="${IMAGE:-$DEFAULT_IMAGE}"
CONTAINER="chongming" CONTAINER="chongming"
PORT=8000 PORT=8000
DATA_DIR="${HOME}/chongming-data" DATA_DIR="${HOME}/chongming-data"
@@ -60,13 +77,27 @@ if ss -tlnp | grep -q ":${PORT}"; then
exit 1 exit 1
fi fi
# 根据架构设置默认 worker 数
case "$ARCH" in
aarch64|arm64)
DEFAULT_WORKERS=2
;;
*)
DEFAULT_WORKERS=4
;;
esac
# 4. 启动容器 # 4. 启动容器
log_info "启动容器..." log_info "启动容器..."
log_info "架构: $ARCH, workers: ${APP_WORKERS:-$DEFAULT_WORKERS}, 端口: $PORT"
docker run -d \ docker run -d \
-p "$PORT:8000" \ -p "$PORT:8000" \
-v "$DATA_DIR/data:/app/data" \ -v "$DATA_DIR/data:/app/data" \
-v "$DATA_DIR/uploads:/app/uploads" \ -v "$DATA_DIR/uploads:/app/uploads" \
-v "$DATA_DIR/logs:/app/logs" \ -v "$DATA_DIR/logs:/app/logs" \
-e "APP_WORKERS=${APP_WORKERS:-$DEFAULT_WORKERS}" \
--ulimit nproc=65535 \
--ulimit nofile=65535 \
--restart unless-stopped \ --restart unless-stopped \
--name "$CONTAINER" \ --name "$CONTAINER" \
"$IMAGE" "$IMAGE"
@@ -92,4 +123,4 @@ else
log_error "容器启动失败,查看日志:" log_error "容器启动失败,查看日志:"
docker logs "$CONTAINER" 2>/dev/null || true docker logs "$CONTAINER" 2>/dev/null || true
exit 1 exit 1
fi fi
+4 -1
View File
@@ -4,6 +4,7 @@
FastAPI chongming 示例 FastAPI chongming 示例
主入口文件,适配 chongming 打包系统 主入口文件,适配 chongming 打包系统
""" """
import os
import sys import sys
import platform import platform
from utils.launch import launch from utils.launch import launch
@@ -37,7 +38,9 @@ def app_run(app_config: dict, default_config: dict):
# 打包环境强制禁用重载 # 打包环境强制禁用重载
is_frozen = getattr(sys, "frozen", False) is_frozen = getattr(sys, "frozen", False)
reload = False if is_frozen else server_config.get("reload", False) reload = False if is_frozen else server_config.get("reload", False)
workers = server_config.get("workers", 4) # 支持通过环境变量 APP_WORKERS 覆盖 worker 数量(方便嵌入式设备调优)
default_workers = server_config.get("workers", 4)
workers = int(os.environ.get("APP_WORKERS", str(default_workers)))
# 显示启动信息 # 显示启动信息
env = app_config.get("env", "development") env = app_config.get("env", "development")
+4 -1
View File
@@ -302,7 +302,7 @@ async def init_database():
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from ..app.core.config import get_config from ..app.core.config import get_config
from ..app.task.dev_init_db import init_permission, dev_init_admin from ..app.task.dev_init_db import init_permission, init_default_roles, dev_init_admin
database = get_config()["database"] database = get_config()["database"]
database_type = database["type"] database_type = database["type"]
@@ -333,6 +333,9 @@ async def init_database():
async with async_session_maker() as session: async with async_session_maker() as session:
# await UserService.create_user(session, "admin", "admin", is_superuser=True) # type: ignore # await UserService.create_user(session, "admin", "admin", is_superuser=True) # type: ignore
await init_permission(session) await init_permission(session)
# 再初始化默认角色及权限关联
await init_default_roles(session)
# 最后初始化管理员用户
await dev_init_admin(session) await dev_init_admin(session)