背景配置 一个用于启动 flask 服务的 sh 脚本方式,start.sh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/bin/bash source ~/tools/miniforge3/etc/profile.d/conda.shconda activate vltenvLinux which pythonpython --version flask --version gunicorn --version export $(grep -v '^#' .env | xargs)TIMESTAMP=$(date '+%Y_%m_%d_%H_%M_%S' ) ENABLE_SCHEDULER=true gunicorn -w 4 -b 0.0.0.0:5001 app:app \ --access-logfile "logs/gunicorn_access_${TIMESTAMP} .log" \ --error-logfile "logs/gunicorn_error_${TIMESTAMP} .log" \ --log-level info \ --timeout 120 \ --keep-alive 5 \ --max-requests 1000 \ --max-requests-jitter 100 \ --preload
启动方式:nohup ./start.sh >nohup.log 2>&1 &
转为 systemd 管理 创建 systemd 服务文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 (vltenvLinux) [autotest@dgvxl2905 vlt_track]$ cat vlt_track_app.service [Unit] Description=VLT TRACK Flask Application with Gunicorn After=network.target [Service] Type=notify User=autotest WorkingDirectory=/home/autotest/vlt/vlt_track Environment="PATH=/home/autotest/tools/miniforge3/envs/vltenvLinux/bin:/usr/local/bin:/usr/bin:/bin" EnvironmentFile=/home/autotest/vlt/vlt_track/.env Environment="ENABLE_SCHEDULER=true" ExecStart=/home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn \ -w 4 \ -b 0.0.0.0:5000 \ app:app \ --access-logfile /home/autotest/vlt/vlt_track/logs/gunicorn_access.log \ --error-logfile /home/autotest/vlt/vlt_track/logs/gunicorn_error.log \ --log-level info \ --timeout 120 \ --keep-alive 5 \ --max-requests 1000 \ --max-requests-jitter 100 \ --preload ExecReload=/bin/kill -USR1 $MAINPID Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
1 2 mkdir -p /home/autotest/vlt/vlt_track/logssudo ln -s /home/autotest/vlt/vlt_track/vlt_track_app.service /etc/systemd/system/vlt_track_app.service
创建 logrotate 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 (base) [root@dgvxl2905 vlt_track]# cd /etc/logrotate.d/ (base) [root@dgvxl2905 logrotate.d]# cat vlt_track /home/autotest/vlt/vlt_track/logs/gunicorn_*.log { su autotest autotest daily rotate 30 compress delaycompress notifempty missingok dateext dateformat _%Y%m%d create 0644 autotest autotest sharedscripts postrotate /bin/systemctl reload vlt_track_app.service > /dev/null 2>&1 || true endscript }
测试 logrotate 配置
1 2 3 4 5 6 7 8 9 10 11 (base) [root@dgvxl2905 logrotate.d]# logrotate -d /etc/logrotate.d/vlt_track reading config file /etc/logrotate.d/vlt_track Allocating hash table for state file, size 15360 B Handling 1 logs rotating pattern: /home/autotest/vlt/vlt_track/logs/gunicorn_*.log after 1 days (30 rotations) empty log files are not rotated, old logs are removed switching euid to 5991 and egid to 5991 considering log /home/autotest/vlt/vlt_track/logs/gunicorn_access_2025_08_06_11_21_11.log log does not need rotating (log has been already rotated)considering log /home/autotest/vlt/vlt_track/logs/gunicorn_access_2025_08_06_11_48_12.log
启动服务 1 2 3 systemctl daemon-reload systemctl start vlt_track_app systemctl status vlt_track_app
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (base) [root@dgvxl2905 vlt_track]# systemctl status vlt_track_app ● vlt_track_app.service - VLT TRACK Flask Application with Gunicorn Loaded: loaded (/home/autotest/vlt/vlt_track/vlt_track_app.service; linked; vendor preset: disabled) Active: active (running) since Fri 2025-10-17 10:17:41 CST; 2s ago Main PID: 6208 (gunicorn) Status: "Gunicorn arbiter booted" Tasks: 8 Memory: 54.3M CGroup: /system.slice/vlt_track_app.service ├─6208 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... ├─6213 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... ├─6228 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... ├─6229 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... └─6230 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... Oct 17 10:17:40 dgvxl2905 systemd[1]: Starting VLT TRACK Flask Application with Gunicorn... Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.336 | INFO | utils.api_scanner:start_scheduler:297 - API扫描-定时任务已启动 Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.336 | INFO | utils.api_scanner:start_scheduler:298 - API扫描-定时任务时间: 1点0分 Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.336 | INFO | utils.api_scanner:init_api_scanner_scheduler:564 - API扫描-定时任务已…PID: 6208) Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.338 | INFO | utils.statistics_collector:start_scheduler:37 - 统计数据-定时任务已启…PID: 6208) Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.338 | INFO | utils.statistics_collector:init_statistics_collector_scheduler:70 - …(PID: 6208) Oct 17 10:17:41 dgvxl2905 systemd[1]: Started VLT TRACK Flask Application with Gunicorn. Hint: Some lines were ellipsized, use -l to show in full.
注意点 1. 路径相关 常见错误
1 2 3 4 5 6 7 WorkingDirectory =~/vlt/vlt_trackEnvironment ="PATH=~/tools/bin:$PATH" ExecStart =./start.shWorkingDirectory =$HOME /app
正确做法
1 2 3 4 WorkingDirectory =/home/autotest/vlt/vlt_trackEnvironment ="PATH=/home/autotest/tools/miniforge3/envs/vltenvLinux/bin:/usr/bin:/bin" ExecStart =/home/autotest/vlt/vlt_track/start.sh
2. 环境变量 常见错误
1 2 3 4 5 6 Environment ="PATH=$CONDA_HOME/bin:$PATH" Environment ="VAR1=value1 VAR2=value2" Environment =PATH="/usr/bin:/bin"
正确做法
1 2 3 4 5 6 7 8 9 10 Environment ="PATH=/home/autotest/tools/miniforge3/envs/vltenvLinux/bin:/usr/bin:/bin" Environment ="ENABLE_SCHEDULER=true" Environment ="FLASK_ENV=production" EnvironmentFile =/home/autotest/vlt/vlt_track/.envEnvironment ="PATH=/usr/bin:/bin"
3. Type 类型选择 1 2 3 4 5 6 7 8 9 10 11 12 13 Type =simpleType =forkingPIDFile =/var/run/myapp.pidType =notifyType =on eshotRemainAfterExit =yes
注意 :
gunicorn 使用 Type=notify 需要安装 systemd 支持
如果 gunicorn 没有 systemd 支持,用 Type=simple 代替
4. 用户权限 1 2 3 4 5 6 7 8 9 10 User =autotestGroup =autotestNoNewPrivileges =true PrivateTmp =true
注意 :
确保用户对 WorkingDirectory 和日志目录有权限
确保用户对可执行文件有执行权限
5. 重启策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Restart =alwaysRestart =on -failureRestart =no RestartSec =10 StartLimitBurst =5 StartLimitIntervalSec =60
6. 服务文件位置 1 2 3 4 5 6 7 8 9 10 sudo ln -s /home/autotest/vlt/vlt_track/vlt_track_app.service /etc/systemd/system/sudo cp vlt_track_app.service /etc/systemd/system/mkdir -p ~/.config/systemd/user/cp vlt_track_app.service ~/.config/systemd/user/systemctl --user enable vlt_track_app
注意 :
软链接的源文件如果权限不对,systemd 可能拒绝加载
系统服务放在 /etc/systemd/system/
用户服务放在 ~/.config/systemd/user/
7. 文件权限 1 2 3 4 5 6 7 8 sudo chmod 644 /etc/systemd/system/vlt_track_app.servicechmod +x /home/autotest/vlt/vlt_track/start.sh
8. ExecStart 命令 常见错误
1 2 3 4 5 6 7 8 9 ExecStart =/usr/bin/app > /var/log/app.log 2 >&1 ExecStart =/usr/bin/app && echo "started" ExecStart =$HOME /bin/appExecStart =/usr/bin/gunicorn \ -w 4 \
正确做法
1 2 3 4 5 6 7 8 9 10 11 ExecStart =/home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn app:appExecStart =/home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn \ -w 4 \ -b 0.0.0.0:5000 \ app:app ExecStart =/bin/bash /home/autotest/vlt/vlt_track/start.sh
9. 修改后的操作流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 sudo systemctl daemon-reloadsudo systemctl restart vlt_track_appsudo systemctl status vlt_track_appsudo systemctl status vlt_track_app -lsudo journalctl -u vlt_track_app -fsudo journalctl -u vlt_track_app -n 50 --no-pager
10. 依赖关系 1 2 3 4 5 6 7 8 9 10 11 12 [Unit] After =network.targetAfter =postgresql.serviceRequires =postgresql.service
11. 常见排错 1 2 3 4 5 6 7 8 9 10 11 systemd-analyze verify /etc/systemd/system/vlt_track_app.service systemctl list-dependencies vlt_track_app systemd-analyze blame systemd-analyze critical-chain vlt_track_app
12. 配置文件名称规范 1 2 3 4 5 6 7 vlt_track_app.service my-web-app.service vlt_track.service (与其他配置中的引用要保持一致) vlt-track-app.service (虽然可以,但下划线更清晰)
13. EnvironmentFile 注意事项 1 2 3 4 5 6 7 8 9 10 DATABASE_URL =postgresql://localhost/dbENABLE_SCHEDULER =true export DATABASE_URL =postgresql://localhost/db DATABASE_URL ="postgresql://localhost/db"
14. 关于 logrotate 配置 14.1 基本轮转流程 logrotate 的核心工作分为两步,都由配置指令驱动,无需手写 mv/touch:
重命名旧日志 (内置行为)
匹配到的文件如 gunicorn_access.log 会被自动改名为 gunicorn_access.log_20251019(由 dateext + dateformat _%Y%m%d 决定)。
rotate 30 表示只保留最近 30 份改名后的旧日志,更早的会被删除。
创建同名空文件 (由 create 指令显式触发)
配置行 create 0644 autotest autotest 告诉 logrotate: 在重命名完成后,立即以相同文件名、指定权限/属主创建一个新的空日志文件,供 Gunicorn 继续写入。
14.2 postrotate 里为什么 reload Gunicorn 仍持有已重命名文件的文件描述符,会继续往“旧”文件写日志。 systemd 单元里已定义:
1 ExecReload =/bin/kill -USR1 $MAINPID
USR1 是 Gunicorn 的“重新打开日志”信号。 因此在 postrotate 段执行:
1 /bin/systemctl reload vlt_track_app.service
即可让 Gunicorn 关闭旧描述符、打开新空文件,服务进程本身不会被杀死和重启 ,实现不丢连接、不重启服务 的日志切换。
14.3 命令尾部细节
> /dev/null 2>&1 —— 屏蔽标准输出与错误,避免 cron/logrotate 自身日志被污染。
|| true —— 即使服务临时未运行导致 reload 失败,也视为 postrotate 成功,保证轮转任务本身不会中断退出。
14.4 与 systemctl start 的区别
systemctl start 只在服务未运行 时启动它;
systemctl reload 在服务已运行 时向其发送 USR1,完成日志热切换。 二者职责不同,完全不会重复。
14.5 配置校验与手动演练 1 2 3 4 5 6 7 8 sudo logrotate -d /etc/logrotate.d/vlt_tracksudo logrotate -f /etc/logrotate.d/vlt_tracktail -f /home/autotest/vlt/vlt_track/logs/gunicorn_access.log
重命名 是 logrotate 内置行为;create 指令负责生成新空文件;reload 负责让进程切换到新文件——三者缺一不可,才能在线上实现平滑、自动的日志轮转。
它与 systemctl start 各司其职,一个负责 启动服务 ,一个负责在服务 不中断的情况下处理日志文件更新 ,两者完美配合,没有冲突。
核心原则
绝对路径优先 - 避免任何路径歧义
权限最小化 - 用普通用户运行,除非必须 root
日志要完善 - 便于排查问题
重启策略 - 生产环境务必配置自动重启
修改后 reload - daemon-reload 是必须的
服务名一致性 - service 文件名和引用要匹配