Skip to content

青龙面板:集中管理脚本、定时任务与运行日志

每天抓取一份公开数据、每小时检查一次服务状态、月底汇总一张报表:这些工作往往只需要几十行脚本。真正麻烦的是脚本多起来之后,要记住每份脚本放在哪里、读哪些环境变量、什么时候运行,以及失败时到哪找日志。

青龙把这些操作放进一个网页面板。你可以在线编辑 Python、JavaScript、Shell 和 TypeScript 脚本,设置运行时间,手动补跑任务,再从同一个入口查看输出。脚本里的业务逻辑仍由你编写,青龙负责管理运行所需的文件、配置、依赖和调度规则。

对已经有几份自用脚本的人,最值得尝试的动作是把一份脚本交给面板定时执行。下面从一个不需要外部账号的小任务开始,完整走过部署、运行、保留数据和恢复的过程。

GitHub 仓库信息

信息内容
项目标题青龙(Qinglong)
项目描述支持 Python3、JavaScript、Shell 和 TypeScript 的定时任务管理平台。
GitHub 仓库whyour/qinglong
官网地址青龙官方文档
主要开发语言TypeScript 81.33%、Shell 8.22%、JavaScript 7.03%
开源许可证Apache-2.0
最近代码更新2026-09-05(GitHub pushed_at;核对日期:2026-09-11)

从一条定时命令到日常管理

只看任务列表,青龙很像带网页的 crontab。实际使用时,能省下更多操作的是脚本周边的管理能力。

假设一份 Python 汇总脚本需要读取“报表目录”这个参数。把参数放进面板的环境变量后,脚本便可以通过 os.getenv() 读取;调整目录时不用改脚本。运行仍使用同一条命令,输出按任务记录到日志。青龙的环境变量生成逻辑任务包装器把这些环节连接起来。

如果脚本来自自己维护的 Git 仓库,可以用“订阅管理”同步脚本,再由“定时任务”决定脚本何时执行。同步代码和运行代码是两个动作:订阅解决文件更新,任务解决执行时间。自动增加、删除任务的选项也会影响现有任务列表,第一次订阅应限制到明确需要的文件,并检查同步日志。官方内置命令说明解释了 ql repo 的白名单、黑名单、分支和文件后缀参数。

依赖管理则负责脚本用到的 Node.js 包、Python 包和 Linux 软件包。缺少模块时,可以直接在面板中安装所需依赖,但脚本依赖的原生库仍受操作系统和 CPU 架构限制。官方 README建议:遇到 Alpine 不支持的依赖时考虑 Debian 镜像。这是选择执行环境的问题,重复点击“安装依赖”未必能解决。

适合放进青龙的任务

个人服务器、NAS 或小团队维护的一组短脚本,很适合用青龙管理:运行时间分散,需要临时补跑,希望用浏览器看日志,又不想额外部署数据库。官方单容器配置将面板、执行环境与本地 SQLite 数据放在一起,部署部件较少。

这种便利也意味着共享环境。任务以容器内的权限执行,能够接触挂载数据和供脚本使用的环境变量;订阅一个来源不明的脚本仓库,会把代码更新权交给对方。不要把青龙当成可以安全运行陌生代码的沙箱,也不要把多套互不信任的账号凭据混在同一个实例里。

如果只有一两条固定命令,系统自带的 cron 或 systemd timer 更省维护。如果希望保留现有 crontab、主要补上编辑和日志界面,可以看看 crontab-ui。如果需求已包含多个工作节点、故障转移、复杂任务依赖或隔离执行环境,应另行评估分布式调度和工作流平台;本文的单实例方案不提供这些保证。

Docker Compose 部署

项目版本:青龙 2.21.0。部署日期:2026-09-11(配置整理与核验日期,未实际启动服务)。

下面采用官方发布到 GHCR 的 Alpine 镜像,固定 2.21.0 标签及镜像摘要,避免相同标签后续变化影响复现。官方构建流程同时发布 Docker Hub 和 GHCR;本次已从 GHCR 读取镜像清单及 amd64 配置,未下载镜像层。

准备一台安装 Docker Engine 和 Compose 插件的 Linux 主机,或使用 Docker Desktop 的 Linux 容器模式。宿主机要能访问 GHCR;日后安装脚本依赖,还需要对应软件包源的网络通路。官方资料没有给出统一的 CPU、内存和磁盘最低值,实际占用取决于脚本并发、依赖及日志量。作为少量短任务的试用起点,可先预留 1 个 CPU 核心、1 GiB 可用内存和数 GiB 磁盘,再观察使用情况;这只是容量规划建议。

已核验的镜像清单包含 linux/amd64linux/arm64linux/arm/v6linux/arm/v7linux/386linux/ppc64lelinux/s390x。清单存在不代表各架构都完成运行测试,脚本附加依赖还需要单独确认支持情况。

创建配置

下列命令在部署主机执行。使用专用空目录,不要覆盖已有青龙实例的数据:

bash
mkdir -p ~/qinglong
cd ~/qinglong
mkdir -p data backups
chmod 700 data backups
docker version
docker compose version

docker version 应能显示服务端信息;如果只显示客户端并提示无法连接 daemon,先启动 Docker 服务。后续命令均在 ~/qinglong 中执行;Linux 上如果当前用户没有 Docker 权限,按主机的权限策略使用 sudo

创建 compose.yaml

yaml
services:
  qinglong:
    image: ghcr.io/whyour/qinglong:2.21.0@sha256:e72f77d855a86cfabf774de76b030731c38540b251a0c888038810fbcf256c48
    restart: unless-stopped
    ports:
      - "127.0.0.1:5700:5700"
    environment:
      TZ: Asia/Shanghai
      QlBaseUrl: "/"
    volumes:
      - ./data:/ql/data
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

这份配置由官方 Compose 文件调整而来:固定镜像、限制入口、明确时区,并给 Docker 自身的容器日志设置轮转。

配置含义与调整方式
127.0.0.1:5700:5700只发布到部署主机的回环地址。端口冲突时改中间的宿主机端口,例如 127.0.0.1:15700:5700,并同步修改访问地址。
./data:/ql/data所有实例数据保留在当前目录的 data 中,包括脚本、数据库、配置、任务日志和依赖缓存。
TZ此处使用上海时区;任务时间与日志显示应在首次验收时核对。
QlBaseUrl网页部署路径,此处为根路径 /。子路径部署需要同步配置反向代理,不建议第一次使用时修改。
logging限制 Docker 收集的标准输出日志;不会清理 data/log 中的任务日志。

镜像继承官方健康检查,无需另装数据库或 Redis。Alpine 的 crond 需要 root 权限,这份配置保留官方默认容器用户;不要直接添加任意 user: 值。确需非 root 运行时,按官方文档改用 Debian 镜像,并重新核验该镜像的固定摘要、用户和数据目录权限。

启动与初始化

bash
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 qinglong

配置检查无输出且退出码为 0 表示可解析。拉取和启动成功后,容器应保持运行,健康状态会从 starting 转为 healthy;日志中如果反复出现权限错误或重启,先排查,暂不录入真实凭据。

在部署主机的浏览器访问 http://127.0.0.1:5700。远程服务器部署时,在自己的电脑另开终端建立 SSH 隧道:

bash
ssh -N -L 15700:127.0.0.1:5700 your-user@your-server

your-useryour-server 换成服务器登录用户和主机名,保持终端打开,然后在自己的电脑访问 http://127.0.0.1:15700。这里前一个端口属于电脑,目标 5700 属于服务器;远程访问不需要把青龙端口暴露到公网。

全新数据目录会进入初始化向导:开始安装,选择通知方式或跳过,再设置用户名和密码,最后去登录。账号由你创建,不套用旧教程中的默认密码。先完成账号初始化,再考虑增加其他访问入口;通知渠道的凭据以后可以按实际需要配置。

检查服务状态

在部署主机执行:

bash
curl --fail --silent --show-error http://127.0.0.1:5700/api/health

预期响应的 code200data.statusok,并且 data.services.httpdata.services.grpc 均为 true

只看到 HTTP 200 或容器 healthy 还不够。 此版本的健康接口在正常返回检查结果时统一发送 HTTP 200,而镜像探针主要检查 HTTP 是否成功。因此仍要看响应正文,并通过下一节的实际任务验证调度和脚本执行。

让第一个任务留下结果

用一个只依赖 Python 标准库的脚本,把当前时间和运行标记写到持久化目录。这样既能验证环境变量,也能区分“面板打开了”和“任务确实跑过”。

先在“环境变量”中新增并启用 DEMO_LABEL,值填 qinglong-demo。这只是演示标记,不需要 Cookie、Token 或任何第三方账号。

在“脚本管理”的根目录新建 heartbeat.py,保存以下内容:

python
import json
import os
from datetime import datetime
from pathlib import Path

output_dir = Path("/ql/data/reports")
output_dir.mkdir(parents=True, exist_ok=True)
record = {
    "time": datetime.now().astimezone().isoformat(timespec="seconds"),
    "label": os.getenv("DEMO_LABEL", "missing-label"),
}
line = json.dumps(record, ensure_ascii=False)
with (output_dir / "heartbeat.jsonl").open("a", encoding="utf-8") as output:
    output.write(line + "\n")
print(line)

然后在“定时任务”中新建任务:

字段填写内容
名称青龙运行检查
命令task heartbeat.py
定时类型常规定时
定时规则*/5 * * * *

这里的五个字段依次是分、时、日、月、周,表示每小时的第 0、5、10 分钟等时间点触发;并非保存后严格等待五分钟。先手动点击“运行”,打开该任务日志,应出现带时间和 qinglong-demo 的一行 JSON。再在部署主机检查结果文件:

bash
docker compose exec qinglong tail -n 5 /ql/data/reports/heartbeat.jsonl

面板任务保持启用后,等待下一个五分钟刻度,文件应新增一行。如果输出为 missing-label,检查变量名称、启用状态并重新运行;如果没有结果文件,先看任务日志中的文件名或 Python 错误。需要从容器内排查时,可以运行官方包装命令:

bash
docker compose exec qinglong task heartbeat.py now

now 表示立即运行、跳过随机延迟。使用 task 能保留青龙的环境加载和日志处理,直接执行 python3 不等同于面板里的任务路径。演示完成后,可以禁用任务,避免样例长期累积输出。

重建后确认数据仍在

确认首次任务成功,再记录结果文件的行数并重建容器:

bash
docker compose exec qinglong wc -l /ql/data/reports/heartbeat.jsonl
docker compose up -d --force-recreate
docker compose ps
docker compose exec qinglong tail -n 5 /ql/data/reports/heartbeat.jsonl

待容器恢复健康后,用原账号登录,确认环境变量、任务和脚本仍在,文件保留旧记录,再手动运行一次。只有这一步也成功,才算验证了当前主机上的挂载与重建;只执行 restart 无法证明数据脱离容器可写层后仍然保留。

一次执行如何发生

界面中的一条任务,连接着持久化的任务配置、负责计时的调度器和真正运行脚本的进程。下面是上述示例涉及的关系,属于源码解释图:

Mermaid 流程图
查看源码
flowchart LR
  UI[网页面板] --> API[管理服务]
  API --> DB[(SQLite 任务与配置)]
  API --> FILES[脚本与环境变量]
  API --> TIMER[调度器]
  TIMER --> TASK[task 包装器]
  FILES --> TASK
  TASK --> PY[Python 脚本进程]
  TASK --> LOG[任务日志与状态]
  PY --> RESULT[持久化结果文件]

调度服务会按规则和调度模式选择执行路径。在默认 Alpine 环境中,普通五字段规则交给系统 cron;带秒字段或附加定时规则的任务走 Node 调度路径。六字段里的第一个字段是秒,例如 0 */5 * * * * 同样表示每五分钟的第 0 秒触发。不要直接复制带 ? 的 Quartz 表达式,当作所有 cron 方言都通用。

任务包装器根据扩展名选择解释器,配合环境加载、工作目录处理和日志记录,把一次脚本执行接入面板。SQLite 的数据库位置也在数据目录内,所以这套部署没有独立数据库服务。

秒级触发能缩短等待时间,但不会自动让长任务安全并发。任务可能还没跑完就再次触发,写文件、扣减额度或调用外部接口的脚本尤其要自行处理重复执行。先观察实际耗时,再决定频率和多实例选项,不能把“执行结束”直接当作业务操作成功。

数据备份与恢复

青龙的数据边界是整个 data 目录。路径配置包含 dbconfigscriptsrepologdep_cache 等目录;本例还有 reports。只备份脚本会丢掉任务规则和账号配置,只备份数据库也会丢掉脚本与结果文件。

下面采用停机冷备份:先在面板禁用自动任务和订阅,等待正在运行的任务结束,再停服务。这样避免脚本或 SQLite 正在写入时,直接打包得到不一致的副本。记下此前启用的任务,恢复后逐个复验再启用。

在 Linux 部署主机的 ~/qinglong 执行;sudo tar 用来读取容器 root 写入的文件并保留所有者信息:

bash
docker compose stop
backup_stamp=$(date +%Y%m%d-%H%M%S)
backup_file="backups/qinglong-${backup_stamp}.tar.gz"
sudo tar -czpf "$backup_file" data compose.yaml
sudo chmod 600 "$backup_file"
sudo tar -tzf "$backup_file" > /dev/null
docker compose start

每一步确认成功后再继续;备份或校验失败时,该文件不能作为恢复依据。tar -tzf 只能证明归档可读取,恢复演练才验证备份是否足够。Docker Desktop 上的宿主机文件权限表现可能不同,但归档仍必须覆盖完整数据目录。

备份包含登录信息、脚本变量和通知配置,应加密保存一份到另一块磁盘或另一台受控主机。本机 backups 目录只能应对误操作,无法应对整块磁盘损坏。容器内部手工安装到系统目录的软件包不在此归档中,重建后需重新安装;把必要的包名和版本单独记录。

恢复到一个新目录

先确认原实例已停止或已移除可能重复执行的任务,新实例也不能连接真实业务后就立即全量运行。下面的命令在目标 Linux 主机执行,将备份绝对路径替换为实际值:

bash
mkdir -p ~/qinglong-restored
cd ~/qinglong-restored
sudo tar -xzpf /path/to/qinglong-backup.tar.gz
sudo test -f compose.yaml
sudo test -d data/db
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps

若原实例仍占用主机 5700,先停原实例,或在恢复目录将绑定改为 127.0.0.1:15700:5700。按新端口检查健康接口,确认原账号可登录、任务规则和脚本存在、历史结果可读取,最后只手动运行样例任务。两个实例即使使用不同网页端口,也可能同时执行同一业务任务,不能因此认为业务已隔离。

升级、回滚与日志维护

升级前查看目标版本的变更记录;可以从本文版本记录进入仓库,再切换到目标 tag 或 commit 查看对应文件,并结合官方发布信息核对 Python/Node 版本、依赖兼容、数据变更和登录行为。固定镜像摘要也会固定旧代码,之后的安全修复需要主动评估升级。

先暂停任务和订阅,完成上一节的冷备份,再复制当前 Compose:

bash
cp compose.yaml compose.before-upgrade.yaml

compose.yamlimage 替换为已经核验的目标版本及对应摘要,不能只改冒号后的版本而保留旧摘要。然后执行:

bash
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 qinglong

青龙的启动初始化会同步数据表并尝试补充字段。新版本启动后,先检查健康正文、登录、脚本依赖和一条低风险任务,再逐步恢复定时任务。

如果新版已经接触数据,回滚应恢复“旧镜像配置 + 升级前的数据副本”。先 docker compose down 停止新版并保留当前目录,用上一节的方法把升级前备份恢复到新目录,再用归档内的旧 Compose 启动。单独恢复 compose.before-upgrade.yaml 无法撤销数据库变化;也不要用旧归档直接覆盖仍在运行的新数据目录。

长期使用时,关注 data/log、脚本自建输出和订阅缓存的增长。官方命令 ql rmlog 7 会清理超过保留天数的任务日志;确认取证和保留要求后,可以在部署目录执行:

bash
docker compose exec qinglong ql rmlog 7

这不会代替 Docker 日志轮转,也不会自动清理本文脚本生成的 reports/heartbeat.jsonl。需要停用实例时,docker compose down 会移除容器和网络,绑定挂载的 data 仍留在宿主机;删除该目录才会丢弃持久化数据,应在备份确认后单独处理。

常见问题

端口连不上。 先用 docker compose ps 确认容器运行,再在部署主机请求健康接口。本例只绑定回环地址,另一台电脑直接访问服务器 IP 会失败;检查 SSH 隧道,或按自己的网络方案配置受控入口。改端口后,浏览器地址、隧道目标和健康请求都要一起改。

容器反复重启,日志提示不能写数据。 检查 data 的宿主机权限、磁盘剩余空间和挂载配置。NAS ACL、NFS root squash 或 SELinux 也可能拒绝容器写入。先确认运行用户和具体拒绝原因,再按平台要求修正目录所有者或标签,不要递归 chmod 777。重新启动后再验证文件持久化。

脚本提示 ModuleNotFoundError 或找不到 Node 模块。 打开依赖管理,核对包名、语言类型和安装日志;Python 导入名未必等于包安装名。若错误来自原生库或架构,检查 Alpine 兼容性,必要时另行验证 Debian 镜像。重新运行原任务,以实际输出确认修复。

手动运行正常,定时没有结果。 检查任务是否启用、五字段和六字段是否写对,再在容器中执行 docker compose exec qinglong date 核对时间。查看面板运行记录和容器日志;复杂表达式或附加规则还涉及 Node 调度路径。把规则暂时缩小为本文的五分钟示例,确认触发后再恢复目标规则。

页面可用,但健康正文异常。data.statuserror 或某个服务字段为 false,读取容器启动日志,定位 HTTP 或内部 gRPC 服务初始化问题。此时单看 healthy 无法确认可用性,不应继续批量导入任务。

使用边界与下一步

面板能执行脚本,也能管理变量和文件,应按具有执行权限的管理入口保护。保持回环绑定并通过 SSH 或受控私网访问,是个人部署较容易维护的方式。需要多人远程访问时,再配置 HTTPS、入口访问控制和可信代理;HTTPS 负责传输保护,不会让不可信脚本获得隔离。不要给容器挂 Docker socket、宿主机根目录或与任务无关的敏感目录。

青龙采用 Apache-2.0 许可证,但订阅脚本、第三方依赖和调用的外部服务各有自己的授权与使用条件。每次引入新脚本,先读清参数、网络目标和写入位置,再给最少的凭据。备份和日志也要避免成为凭据的第二个泄露入口。

下一步可以把 heartbeat.py 换成自己的一项低风险汇总或检查任务,保留明确的成功输出。需要发送通知时,再阅读官方内置 API中的 QLAPI.systemNotify;不要把配置了通知渠道等同于所有脚本失败都会自动报警。

版本与验证范围

本文最高验证等级为 image-checked:已核对官方文档与镜像关联源码,读取 GHCR 镜像索引、摘要和 amd64 配置,并完成 Compose 静态解析。镜像标签与 Git tag 不应默认视为同一快照,正文源码链接采用本次镜像配置声明的源码修订。未启动青龙容器,未实测浏览器初始化、调度、依赖安装、持久化、备份恢复或升级;文中的运行结果是读者验收目标。

Docker Hub 的标签和 Registry 查询在本次网络下发生 TLS 连接错误,因此没有验证 Docker Hub 副本与 GHCR 内容一致。GitHub Releases 接口本次返回空列表,版本依据是 Git tag、版本文件和镜像清单。外部文档与动态仓库信息的核对日期均为 2026-09-11。

参考资料

全部公开文章由同一个站点构建和发布。