From 450be840204fecf40b1279b38bf2ba5f433a0157 Mon Sep 17 00:00:00 2001 From: chakcy <947105045@qq.com> Date: Sun, 26 Apr 2026 15:26:01 +0800 Subject: [PATCH] =?UTF-8?q?modify:=20docker=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + README.md | 26 +++ dockerfile | 23 +++ public/main.py | 36 ++-- pyproject.toml | 2 + src/chongming/__init__.py | 17 ++ src/chongming/scripts/build.py | 236 ++++++++++++++++++-------- src/chongming/scripts/code_content.py | 1 + 8 files changed, 257 insertions(+), 87 deletions(-) create mode 100644 dockerfile diff --git a/.gitignore b/.gitignore index e1386f4..8d21926 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ output.md # resources uploads/ + +# docker +*.tar diff --git a/README.md b/README.md index ba1a1f1..1a6b673 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,29 @@ build cd build chongming # windows chongming.exe ``` + +## docker镜像 + +打包镜像 +```shell +docker build -t chongming:latest . +``` + +导出镜像 +```shell +docker save -o chongming_v1.0.tar chongming:v1.0 +``` + +导入镜像 +```shell +docker load -i chongming_v1.0.tar +``` + +运行镜像 +```shell +mkdir -p /data/chongming +docker run -d -p 8000:8000 \ + -v /data/chongming/database.db:/app/database.db \ + -v /data/chongming/uploads:/app/uploads \ + --name chongming chongming:v1.0 +``` diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..e913144 --- /dev/null +++ b/dockerfile @@ -0,0 +1,23 @@ +# 使用 Debian 的极简版,确保有基础的 glibc(推荐) +FROM debian:bookworm-slim + +# 可选:设置时区、安装 ca-certificates 等 +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# 复制打包好的可执行文件 +COPY build/chongming /app/ + +# 复制运行时需要的静态资源、配置文件、.mbank 等 +COPY build/config.toml /app/ +COPY build/resources/ /app/resources/ +COPY build/static.svfs /app/ + +# 暴露端口 +EXPOSE 8000 + +# 启动 +CMD ["./chongming"] \ No newline at end of file diff --git a/public/main.py b/public/main.py index d3dff49..1568db6 100644 --- a/public/main.py +++ b/public/main.py @@ -5,10 +5,12 @@ FastAPI chongming 示例 主入口文件,适配 chongming 打包系统 """ import sys +import platform from utils.launch import launch from multiprocessing import freeze_support import server +use_shell = platform.system() == "Windows" def app_run(app_config: dict, default_config: dict): """ @@ -18,7 +20,6 @@ def app_run(app_config: dict, default_config: dict): app_config: 应用配置 default_config: 默认配置 """ - import uvicorn from app.core.logger import get_logger # type: ignore logger = get_logger("app") @@ -47,17 +48,28 @@ def app_run(app_config: dict, default_config: dict): logger.info(f"👥 Workers: {workers}") logger.info("=" * 50) - uvicorn.run( - "server:app", - host=host, - port=port, - reload=reload, - workers=workers, - log_level="debug" if app_config.get("debug", False) else "info", - access_log=False, - timeout_keep_alive=timeout_keep_alive, - ) - + if use_shell: + import uvicorn + uvicorn.run( + "server:app", + host=host, + port=port, + reload=reload, + workers=workers, + log_level="debug" if app_config.get("debug", False) else "info", + access_log=False, + timeout_keep_alive=timeout_keep_alive, + ) + else: + from gunicorn.app.wsgiapp import run + sys.argv = [ + "gunicorn", + "server:app", + "-k", "uvicorn.workers.UvicornWorker", + "-w", str(workers), + "-b", f"{host}:{port}" + ] + run() def main(): freeze_support() diff --git a/pyproject.toml b/pyproject.toml index 06203c1..6e030a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,3 +48,5 @@ packages = ["src/chongming"] [project.scripts] build = "chongming.scripts.build:build" serve = "chongming:serve" +# gunicorn = "gunicorn chongming.app:app -k uvicorn.workers.UvicornWorker -w 4 -b 0.0.0.0:8000" +gunicorn = "chongming:gunicorn_serve" diff --git a/src/chongming/__init__.py b/src/chongming/__init__.py index 4fe8b3d..0833d01 100644 --- a/src/chongming/__init__.py +++ b/src/chongming/__init__.py @@ -13,6 +13,23 @@ def serve(): logget.info(f"启动日志: {log_config}") uvicorn.run("chongming.app:app", **server_config) +def gunicorn_serve(): + """使用 gunicorn 启动服务""" + import sys + from gunicorn.app.wsgiapp import run + config = load_config() + env = config["default"]["env"] + server_config = config[env]["server"] + + # 设置 gunicorn 参数 + sys.argv = [ + "gunicorn", + "chongming.app:app", + "-k", "uvicorn.workers.UvicornWorker", + "-w", f"{server_config.get('workers', 4)}", + "-b", f"{server_config.get('host', '0.0.0.0')}:{server_config.get('port', 8000)}" + ] + run() if __name__ == "__main__": serve() diff --git a/src/chongming/scripts/build.py b/src/chongming/scripts/build.py index 3a56432..b1b2474 100644 --- a/src/chongming/scripts/build.py +++ b/src/chongming/scripts/build.py @@ -1,3 +1,5 @@ +import platform + from module_bank import PythonToSQLite from module_bank.encryption import Encryption import os @@ -10,6 +12,8 @@ from sqlite_vfs.core import SQLiteVFS from sqlite_vfs.folder_packer import FolderPacker +is_windows = platform.system() == "Windows" + def build_vue_web(): """打包 Vue Web""" # 构建 Vue Web 命令 @@ -26,26 +30,37 @@ def build_vue_web(): cwd="./src/chongming-web", capture_output=True, text=True, - shell=True, + shell=is_windows, encoding="utf-8", errors="replace", ) if result.returncode != 0: print("打包 Vue Web 失败") - print(result.stderr) + print("标准输出:", result.stdout) + print("错误输出:", result.stderr) + return else: - vfs = SQLiteVFS(output_db, compress=False) - packer = FolderPacker( - sqlite_vfs=vfs, - exclude_patterns=[], - fs_name=fs_name, - ) + print("Vue Web 打包成功") + if result.stdout: + print(result.stdout) + + # 检查 dist 目录是否存在 + if not os.path.exists(dist_dir): + print(f"错误: dist 目录不存在: {dist_dir}") + return + + vfs = SQLiteVFS(output_db, compress=False) + packer = FolderPacker( + sqlite_vfs=vfs, + exclude_patterns=[], + fs_name=fs_name, + ) - try: - packer.pack_folder(dist_dir) - print(f"✅ 打包完成,文件保存为 {output_db}") - finally: - vfs.close() + try: + packer.pack_folder(dist_dir) + print(f"✅ 打包完成,文件保存为 {output_db}") + finally: + vfs.close() def run_pyarmor_obfuscate(): @@ -77,68 +92,139 @@ def run_pyinstaller(): """运行 PyInstaller 打包成可执行文件""" try: # 构建 PyInstaller 命令 - cmd = [ - sys.executable, - "-m", - "PyInstaller", - "--onefile", - "--name", - "chongming", - "--distpath", - ".", - "--workpath", - "./build_temp", - "--specpath", - "./specs", - "--clean", - # 隐藏导入 - "--hidden-import", - "utils", - "--hidden-import", - "utils.launch", - "--hidden-import", - "utils.config", - "--hidden-import", - "server", - "--hidden-import", - "module_bank", - "--hidden-import", - "fastapi", - "--hidden-import", - "fastapi.staticfiles", - "--hidden-import", - "fastapi.middleware.cors", - "--hidden-import", - "uvicorn", - "--hidden-import", - "sqlmodel", - "--hidden-import", - "aiosqlite", - "--hidden-import", - "aiofiles", - "--hidden-import", - "apscheduler", - "--hidden-import", - "diskcache", - "--hidden-import", - "passlib", - "--hidden-import", - "passlib.context", - "--hidden-import", - "passlib.handlers.bcrypt", - "--hidden-import", - "sqlite_vfs", - "--hidden-import", - "python_multipart", - "--hidden-import", - "sqlite_vfs.core", - "--hidden-import", - "jwt", - # 其他选项 - "--icon", - "../../public/chongming.ico", - "main.py", - ] + if is_windows: + cmd = [ + sys.executable, + "-m", + "PyInstaller", + "--onefile", + "--name", + "chongming", + "--distpath", + ".", + "--workpath", + "./build_temp", + "--specpath", + "./specs", + "--clean", + # 隐藏导入 + "--hidden-import", + "utils", + "--hidden-import", + "utils.launch", + "--hidden-import", + "utils.config", + "--hidden-import", + "server", + "--hidden-import", + "module_bank", + "--hidden-import", + "fastapi", + "--hidden-import", + "fastapi.staticfiles", + "--hidden-import", + "fastapi.middleware.cors", + "--hidden-import", + "uvicorn", + "--hidden-import", + "sqlmodel", + "--hidden-import", + "aiosqlite", + "--hidden-import", + "aiofiles", + "--hidden-import", + "apscheduler", + "--hidden-import", + "diskcache", + "--hidden-import", + "passlib", + "--hidden-import", + "passlib.context", + "--hidden-import", + "passlib.handlers.bcrypt", + "--hidden-import", + "sqlite_vfs", + "--hidden-import", + "python_multipart", + "--hidden-import", + "sqlite_vfs.core", + "--hidden-import", + "jwt", + # 其他选项 + "--icon", + "../../public/chongming.ico", + "main.py", + ] + else: + cmd = [ + sys.executable, + "-m", + "PyInstaller", + "--onefile", + "--name", + "chongming", + "--distpath", + ".", + "--workpath", + "./build_temp", + "--specpath", + "./specs", + "--clean", + # 隐藏导入 + "--hidden-import", + "utils", + "--hidden-import", + "utils.launch", + "--hidden-import", + "utils.config", + "--hidden-import", + "server", + "--hidden-import", + "module_bank", + "--hidden-import", + "fastapi", + "--hidden-import", + "fastapi.staticfiles", + "--hidden-import", + "fastapi.middleware.cors", + "--hidden-import", + "uvicorn", + "--hidden-import", + "sqlmodel", + "--hidden-import", + "aiosqlite", + "--hidden-import", + "aiofiles", + "--hidden-import", + "apscheduler", + "--hidden-import", + "diskcache", + "--hidden-import", + "passlib", + "--hidden-import", + "passlib.context", + "--hidden-import", + "passlib.handlers.bcrypt", + "--hidden-import", + "sqlite_vfs", + "--hidden-import", + "python_multipart", + "--hidden-import", + "sqlite_vfs.core", + "--hidden-import", + "jwt", + # gunicorn + "--hidden-import", + "gunicorn.glogging", + "--hidden-import", + "gunicorn.app", + "--hidden-import", + "gunicorn.app.wsgiapp", + # 其他选项 + "--icon", + "../../public/chongming.ico", + "main.py", + ] print(f"正在运行 PyInstaller: {' '.join(cmd)}") result = subprocess.run(cmd, cwd="./build", capture_output=True, text=True) diff --git a/src/chongming/scripts/code_content.py b/src/chongming/scripts/code_content.py index c8521aa..293add0 100644 --- a/src/chongming/scripts/code_content.py +++ b/src/chongming/scripts/code_content.py @@ -22,6 +22,7 @@ def generate_markdown_from_py_files(directory, output_file): or file.endswith(".ts") or file.endswith(".json") or file.endswith(".html") + or file == "dockerfile" ): file_path = os.path.join(root, file) if file == "output.md" or file == "package-lock.json":