Refactor service deployment to use direct process execution

- Remove bash wrapper script
- Update systemd service configuration
- Improve process management for gunicorn
- Simplify shared storage cleanup logic
- Update documentation for deployment
This commit is contained in:
yangdx 2025-10-29 18:55:47 +08:00
parent 6489aaa7f0
commit d5bcd14c6f
8 changed files with 40 additions and 53 deletions

View file

@ -1,4 +0,0 @@
#!/bin/bash
source /home/netman/lightrag-xyj/venv/bin/activate
lightrag-server

View file

@ -1,5 +1,5 @@
[Unit] [Unit]
Description=LightRAG XYJ Ollama Service Description=LightRAG XYJ Service
After=network.target After=network.target
[Service] [Service]
@ -8,10 +8,20 @@ User=netman
# Memory settings # Memory settings
MemoryHigh=8G MemoryHigh=8G
MemoryMax=12G MemoryMax=12G
# Using virtual enviroment created by miniconda
Environment="PATH=/home/netman/miniconda3/bin:/home/netman/lightrag-xyj/venv/bin"
WorkingDirectory=/home/netman/lightrag-xyj WorkingDirectory=/home/netman/lightrag-xyj
ExecStart=/home/netman/lightrag-xyj/lightrag-api # ExecStart=/home/netman/lightrag-xyj/venv/bin/lightrag-server
ExecStart=/home/netman/lightrag-xyj/venv/bin/lightrag-gunicorn
# Kill mode require ExecStart must be gunicorn or unvicorn main process
KillMode=process
ExecStop=/bin/kill -s TERM $MAINPID
TimeoutStopSec=60
Restart=always Restart=always
RestartSec=10 RestartSec=30
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -184,24 +184,16 @@ MAX_ASYNC=4
### 将 Lightrag 安装为 Linux 服务 ### 将 Lightrag 安装为 Linux 服务
从示例文件 `lightrag.service.example` 创建您的服务文件 `lightrag.service`。修改服务文件中的 WorkingDirectory 和 ExecStart 从示例文件 `lightrag.service.example` 创建您的服务文件 `lightrag.service`。修改服务文件中的服务启动定义
```text ```text
Description=LightRAG Ollama Service # Set Enviroment to your Python virtual enviroment
WorkingDirectory=<lightrag 安装目录> Environment="PATH=/home/netman/lightrag-xyj/venv/bin"
ExecStart=<lightrag 安装目录>/lightrag/api/lightrag-api WorkingDirectory=/home/netman/lightrag-xyj
``` # ExecStart=/home/netman/lightrag-xyj/venv/bin/lightrag-server
ExecStart=/home/netman/lightrag-xyj/venv/bin/lightrag-gunicorn
修改您的服务启动脚本:`lightrag-api`。根据需要更改 python 虚拟环境激活命令:
```shell
#!/bin/bash
# 您的 python 虚拟环境激活命令
source /home/netman/lightrag-xyj/venv/bin/activate
# 启动 lightrag api 服务器
lightrag-server
``` ```
> ExecStart命令必须是 lightrag-gunicorn 或 lightrag-server 中的一个,不能使用其它脚本包裹它们。因为停止服务必须要求主进程必须是这两个进程。
安装 LightRAG 服务。如果您的系统是 Ubuntu以下命令将生效 安装 LightRAG 服务。如果您的系统是 Ubuntu以下命令将生效

View file

@ -188,24 +188,18 @@ MAX_ASYNC=4
### Install LightRAG as a Linux Service ### Install LightRAG as a Linux Service
Create your service file `lightrag.service` from the sample file: `lightrag.service.example`. Modify the `WorkingDirectory` and `ExecStart` in the service file: Create your service file `lightrag.service` from the sample file: `lightrag.service.example`. Modify the start options the service file:
```text ```text
Description=LightRAG Ollama Service # Set Enviroment to your Python virtual enviroment
WorkingDirectory=<lightrag installed directory> Environment="PATH=/home/netman/lightrag-xyj/venv/bin"
ExecStart=<lightrag installed directory>/lightrag/api/lightrag-api WorkingDirectory=/home/netman/lightrag-xyj
# ExecStart=/home/netman/lightrag-xyj/venv/bin/lightrag-server
ExecStart=/home/netman/lightrag-xyj/venv/bin/lightrag-gunicorn
``` ```
Modify your service startup script: `lightrag-api`. Change your Python virtual environment activation command as needed: > The ExecStart command must be either `lightrag-gunicorn` or `lightrag-server`; no wrapper scripts are allowed. This is because service termination requires the main process to be one of these two executables.
```shell
#!/bin/bash
# your python virtual environment activation
source /home/netman/lightrag-xyj/venv/bin/activate
# start lightrag api server
lightrag-server
```
Install LightRAG service. If your system is Ubuntu, the following commands will work: Install LightRAG service. If your system is Ubuntu, the following commands will work:

View file

@ -129,11 +129,13 @@ def on_exit(server):
print("=" * 80) print("=" * 80)
print("GUNICORN MASTER PROCESS: Shutting down") print("GUNICORN MASTER PROCESS: Shutting down")
print(f"Process ID: {os.getpid()}") print(f"Process ID: {os.getpid()}")
print("=" * 80)
# Release shared resources print("Finalizing shared storage...")
finalize_share_data() finalize_share_data()
print("Gunicorn shutdown complete")
print("=" * 80)
print("=" * 80) print("=" * 80)
print("Gunicorn shutdown complete") print("Gunicorn shutdown complete")
print("=" * 80) print("=" * 80)

View file

@ -326,15 +326,14 @@ def create_app(args):
# Clean up database connections # Clean up database connections
await rag.finalize_storages() await rag.finalize_storages()
# In Gunicorn mode with preload_app=True, cleanup is handled by worker_exit/on_exit hooks
# Only perform cleanup in Uvicorn single-process mode
if "LIGHTRAG_GUNICORN_MODE" not in os.environ: if "LIGHTRAG_GUNICORN_MODE" not in os.environ:
# Clean up shared data # Only perform cleanup in Uvicorn single-process mode
logger.debug("Unvicorn Mode: finalize shared storage...") logger.debug("Unvicorn Mode: finalizing shared storage...")
finalize_share_data() finalize_share_data()
else: else:
# In Gunicorn mode with preload_app=True, cleanup is handled by on_exit hooks
logger.debug( logger.debug(
"Gunicorn Mode: don not finalize shared storage in worker process" "Gunicorn Mode: postpone shared storage finalization to master process"
) )
# Initialize FastAPI # Initialize FastAPI

View file

@ -45,9 +45,7 @@ def main():
check_and_install_dependencies() check_and_install_dependencies()
# Note: Signal handlers are NOT registered here because: # Note: Signal handlers are NOT registered here because:
# - Worker cleanup is handled by gunicorn_config.worker_exit() # - Master cleanup already handled by gunicorn_config.on_exit()
# - Master cleanup is handled by gunicorn_config.on_exit()
# This prevents race conditions when multiple processes try to finalize shared data
# Display startup information # Display startup information
display_splash_screen(global_args) display_splash_screen(global_args)

View file

@ -1444,7 +1444,7 @@ async def get_namespace_data(
return _shared_dicts[namespace] return _shared_dicts[namespace]
def finalize_share_data(shutdown_manager: bool = True): def finalize_share_data():
""" """
Release shared resources and clean up. Release shared resources and clean up.
@ -1453,10 +1453,6 @@ def finalize_share_data(shutdown_manager: bool = True):
In multi-process mode, it shuts down the Manager and releases all shared objects. In multi-process mode, it shuts down the Manager and releases all shared objects.
In single-process mode, it simply resets the global variables. In single-process mode, it simply resets the global variables.
Args:
shutdown_manager: If True, shut down the multiprocessing Manager.
Should be True only for the main process, False for worker processes.
""" """
global \ global \
_manager, \ _manager, \
@ -1483,8 +1479,8 @@ def finalize_share_data(shutdown_manager: bool = True):
f"Process {os.getpid()} finalizing storage data (multiprocess={_is_multiprocess})" f"Process {os.getpid()} finalizing storage data (multiprocess={_is_multiprocess})"
) )
# In multi-process mode, shut down the Manager only if requested # In multi-process mode, shut down the Manager
if _is_multiprocess and _manager is not None and shutdown_manager: if _is_multiprocess and _manager is not None:
try: try:
# Clear shared resources before shutting down Manager # Clear shared resources before shutting down Manager
if _shared_dicts is not None: if _shared_dicts is not None: