Ubuntu 22.04下NodeJS Cronjob与日志及环境变量配置咨询
问题背景
我有一个使用morgan和morgan-daily-rotate实现日志功能的TypeScript项目,功能是从一台服务器拉取股票信息,再更新到另一台服务器。运行时会按YYYY-MM-DD格式生成日志文件夹。开发环境为Windows,部署到Linode的Ubuntu 22.04实例。
项目目录结构
build/ ... index.js logs/ 2022-10-04-20/ <- 根据运行日期时间生成 combined.log error.log warn.log src/ ... index.ts run.sh
项目部署在~/customerName/projectName/目录,部署时仅复制构建后的build文件夹和package.json到Linode实例以安装依赖。防火墙已开放http和https连接,ufw status显示规则正常。
在Windows开发环境及Ubuntu实例中直接从项目目录运行时,项目正常且日志正确生成在logs目录下的对应日期文件夹。但通过Cronjob执行命令*/5 * * * * cd /home/user/customerName/projectName/ && node index.js > /home/user/customerName/projectName/logs/cron.log 2>&1时,出现两个问题:
- 日志生成到
~/logs而非项目目录下的logs; - 日志中充斥
Error: connect ECONNREFUSED ::1:80的axios错误。
morgan日志传输配置示例
const infoTransport: DailyRotateFile = new DailyRotateFile({ level: "info", filename: "./logs/%DATE%/combined.log", datePattern: "YYYY-MM-DD-HH", maxSize: "20m", maxFiles: "14d", });
问题解答
1. 如何让Cronjob从~/customerName/projectName/目录执行脚本?
有三种可靠方案:
- 用绝对路径确保
cd生效:Cron环境里~不一定指向用户主目录,直接写完整绝对路径:*/5 * * * * cd /home/user/customerName/projectName/ && node index.js > /home/user/customerName/projectName/logs/cron.log 2>&1 - 封装成shell脚本执行:把切换目录、执行逻辑写到
run.sh,给脚本加执行权限(chmod +x run.sh),再让Cron调用脚本:run.sh内容:
Cron命令:#!/bin/bash cd /home/user/customerName/projectName/ node index.js > ./logs/cron.log 2>&1*/5 * * * * /home/user/customerName/projectName/run.sh - 修改日志配置为绝对路径:把
./logs/%DATE%/combined.log改成/home/user/customerName/projectName/logs/%DATE%/combined.log,彻底规避工作目录影响。
2. 手动运行正常,但Cronjob执行时为何出现Error: connect ECONNREFUSED ::1:80错误?
核心原因是Cron的环境变量和用户登录后的环境差异极大:
- 手动运行时,你有完整的环境变量(比如项目依赖的目标服务器地址、node路径),但Cron的环境变量非常精简,甚至连
PATH都只有默认几个目录; - 如果项目依赖
.env文件存储请求地址,手动运行时npm或脚本会自动加载,但Cron直接执行node index.js时,不会自动加载.env,导致请求地址缺失,默认指向localhost:80(IPv6对应::1:80),引发连接拒绝。
解决方法:
- 若用nvm安装node,在Cron命令或脚本里指定node绝对路径,比如
/home/user/.nvm/versions/node/v18.17.0/bin/node; - 在Cron命令里直接指定环境变量,比如:
*/5 * * * * cd /home/user/customerName/projectName/ && TARGET_SERVER=https://xxx.xxx node index.js > /home/user/customerName/projectName/logs/cron.log 2>&1 - 确保代码里加载
.env时指定绝对路径:require('dotenv').config({ path: '/home/user/customerName/projectName/.env' });
3. 如何为脚本设置运行时的环境变量?
常用的几种方式:
- Cron命令里直接定义:在
node命令前加环境变量,多个用空格分隔:*/5 * * * * cd /home/user/customerName/projectName/ && VAR1=value1 VAR2=value2 node index.js > /home/user/customerName/projectName/logs/cron.log 2>&1 - 通过
.env文件加载:用dotenv包并指定.env绝对路径,或在shell脚本里导出变量:#!/bin/bash cd /home/user/customerName/projectName/ export $(cat .env | xargs) node index.js > ./logs/cron.log 2>&1 - 在shell脚本里定义:把环境变量写在
run.sh头部:#!/bin/bash export NODE_ENV=production export TARGET_API=https://stock-server.example.com cd /home/user/customerName/projectName/ node index.js > ./logs/cron.log 2>&1 - 全局配置Cron环境变量:执行
crontab -e,在配置顶部添加全局环境变量,所有Cron任务都会继承:NODE_ENV=production TARGET_API=https://stock-server.example.com PATH=/home/user/.nvm/versions/node/v18.17.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
内容的提问来源于stack exchange,提问作者HotFix
相关产品推荐
相关产品推荐

