
这段时间身边好几个朋友都在折腾本地部署各种服务从大语言模型到知识库工具热度确实高。但有个需求一直没被好好满足手里攒了一堆时间序列数据想做标注却找不到趁手的工具。云端平台要么数据隐私不放心要么定制能力太弱。后来我找到了 TimeTagger一款专为时间序列数据设计的开源注解工具在 Linux 服务器上本地部署之后整个团队都能通过浏览器访问同一个标注工作台体验相当不错。这篇文章就把我的完整部署过程、以及从局域网到外部访问的打通经验都记录下来给同样有这个需求的你做个参考。1. TimeTagger 到底解决什么问题时间序列标注这件麻烦事1.1 时间序列标注的难点在哪先说清楚 TimeTagger 是干什么的。它是一款基于浏览器的时序数据可视化与注解工具专门用来给时间序列数据打标签。什么叫时间序列数据就是你按时间顺序采集到的连续数值点比如伺服电机的电流曲线、机房服务器的温湿度监控、心率手环采集的 PPG 信号、风力发电机的振动波形甚至是股票分钟级行情。做机器学习训练、故障诊断、异常检测这类任务时你首先需要一份打了标签的标准答案——什么样的波形是正常运行什么样的波形是故障前兆从哪里开始到哪里结束属于某个事件。这个过程就叫时间序列标注。说起来简单做起来麻烦。原始数据动辄几十万行 CSV逐条看根本不可能一定要可视化地看图说话在波形上直接框选区间、打标签。通用 BI 工具做不了这种交互Excel 更是看几万行数据就卡死。更麻烦的是协作问题三五个工程师各标各的最后合并标注结果时口径不统一你标的是normal我标的是normal_1后处理脚本直接爆炸。TimeTagger 解决的就是这个痛点它在浏览器里把时间序列数据画成波形你可以像在视频编辑器里剪片子一样拖拽选择一段区间给它打上自定义标签标注结果统一入库支持多人同时标注导出时是结构化 JSON直接喂给下游训练脚本。1.2 TimeTagger 的边界能力与功能清单先说清楚它能做什么、不能做什么避免你部署完发现货不对板。我实测下来TimeTagger 的核心能力集中在四块能力模块具体功能我的评价数据导入支持 JSON / CSV 格式的时序数据上传浏览器端直接解析适合单文件、中等规模数据超大文件建议先切片可视化浏览Zoon 缩放、拖拽平移、游标测量、波形自适应交互流畅度中上比 Matplotlib 导图效率高一个量级标签体系支持自定义标签类型区间型、点型、文本备注型覆盖了绝大多数标注场景字段可自定义颜色数据导出一键导出全部标注结果JSON 结构化输出与 sklearn / PyTorch 后处理衔接顺畅它不适合做什么如果你的数据不是显式的时间序列比如纯图像、纯文本那就另请高明如果你的标注需求极度复杂比如需要级联多级标签、需要跨数据源关联标注那应该去看商用的 Label Studio 或者 TagBox这类工具功能更重但部署和配置成本也高得多。TimeTagger 的定位是轻量、自托管、够用这个定位我很认可。1.3 结合当前本地部署热潮来看这个工具的价值最近本地部署这个词挺火大家越来越在意数据主权和隐私。我之所以专门挑了 TimeTagger 而不是直接用某个云标注平台就是因为在传感器数据和日志数据这个领域数据的敏感性往往比代码还高。部署在本地服务器所有数据进出都走内网标注接口不暴露到公网就没有第三方介入风险。TimeTagger 的部署方式对私有化环境非常友好它本身是前后端一体的把静态页面和 API 服务打包好配合 Docker 一条命令就能在任意 Linux 主机上跑起来不依赖外部 SaaS 服务。这一点正好切中了时序数据标注场景里既要工具完整又要数据可控的诉求。2. Linux 主机部署前的准备先想清楚再动手2.1 为什么我坚持用 Docker Compose 来部署TimeTagger 有原生的 Python 部署方式直接 pip 装依赖再用 python 启动也可以。但我最终选了 Docker Compose原因有三个第一依赖隔离干净。TimeTagger 依赖 MongoDB裸机部署意味着你主机上得装一套 MongoDB还要处理版本兼容、开机自启、systemd 服务等一堆事。容器化之后 MongoDB 和业务服务各跑各的互不污染删除也干净。第二迁移方便。你在这台机器上调试好的配置扔到生产服务器上只要docker compose up -d就能还原一模一样的环境。我后来把服务从测试机迁到正式服务器全程不到十分钟。第三多服务编排省心。同一个 compose 文件里把 TimeTagger 服务和 MongoDB 服务定义在一起统一网络、统一数据卷、统一重启策略不用我手工去记端口和 IP。如果你实在不想碰 Docker也可以走裸机路线但请记住一句话裸机部署爽一时迁移升级哭一场。Python 环境升级、MongoDB 版本不兼容、某个原生依赖编译失败这些坑我都踩过。容器化是现代自托管服务的事实标准不要绕远路。2.2 主机规划与目录结构设计先交代我的部署环境做一个参考项目配置操作系统Ubuntu 22.04 LTS内核 5.15CPU4 核内存8 GB建议至少 4 GB磁盘100 GB用于 MongoDB 数据卷 原始数据文件Docker24.0.7 Docker Compose v2主机拿到手之后第一步建议先做几个基础操作# 更新系统软件源并安装基础工具 sudo apt update sudo apt upgrade -y sudo apt install -y curl vim git ufw # 创建专用目录结构 sudo mkdir -p /opt/timetagger/{data,backup} sudo chown -R $USER:$USER /opt/timetagger目录结构这里值得多说一句。我把持久化数据单独放/opt/timetagger/data备份放/opt/timetagger/backup这样整个服务的家就是一个目录后面做快照、做迁移、做清理都只需要盯住这个目录。很多新手习惯把 Docker 数据卷丢在默认位置出问题的时候找半天都不知道数据在哪。2.3 提前想清楚数据从哪来、标注存到哪里部署之前我建议你先回答自己三个问题这三个问题的答案会直接影响后面的 Compose 配置原始时间序列数据放在哪我是在服务器上建了一个raw_data文件夹把 Excel 导出的 CSV 统一丢进去然后通过容器挂载让 TimeTagger 能读取。你也可以用浏览器直接上传但大批量数据走服务器文件系统更快更稳。标注结果存在哪TimeTagger 的标注数据默认持久化在 MongoDB 里。你需要规划好 MongoDB 的存储路径也就是数据卷映射到宿主机哪个目录。多人访问时的域名/IP 是什么如果是局域网使用直接写服务器 IP 加端口就行如果要做外部访问就要提前想好域名和反向代理方案这个我在第 4 章详细展开。我碰到过一个反面案例同事把 TimeTagger 装在云服务器上端口开在默认地址结果标注做到一半数据全丢了——后来发现 MongoDB 数据卷没有映射到宿主机容器一重建数据就蒸发。你部署完先别急着导入数据第一件事是验证数据持久化是否生效。3. 核心部署实操用 Compose 把 TimeTagger 和 MongoDB 拉起来3.1 编写 docker-compose.yml 配置直接上我调好的配置你可以基于这个改动。version: 3.8 services: mongodb: image: mongo:6.0 container_name: timetagger-mongo restart: unless-stopped environment: MONGO_INITDB_DATABASE: timetagger volumes: - /opt/timetagger/data/mongo:/data/db networks: - timetagger-net timetagger: image: timetagger/timetagger # 以官方仓库镜像名为准 container_name: timetagger-app restart: unless-stopped depends_on: - mongodb environment: TT_MONGODB_URI: mongodb://mongodb:27017/timetagger ports: - 8082:80 volumes: - /opt/timetagger/data/app:/root/.timetagger networks: - timetagger-net networks: timetagger-net: driver: bridge注意几个关键点网络通信MongoDB 服务没有公开端口映射只在内部网络timetagger-net里对 TimeTagger 可见。这样 MongoDB 不会暴露到宿主机和外部网络减少攻击面。TimeTagger 连接 MongoDB 的地址直接用服务名mongodb这是 Docker 内置 DNS 解析的便利不用写 IP。持久化卷这是保命配置。/opt/timetagger/data/mongo映射到 MongoDB 容器内的数据目录/opt/timetagger/data/app映射到 TimeTagger 应用目录。有了这两行容器删了重建数据还在。端口映射8082:80表示宿主机的 8082 端口转发到容器内的 80 端口。如果你服务器 8082 已经被占用后面全部命令里再换一个比如9082:80。提示镜像名称和具体环境变量在不同版本可能不同部署前一定先去仓库页面的 README 核对一下最新配置。我这里的配置是基于常规版本的合理默认值但工具迭代很快以官方文档为准永远是对的。3.2 启动前的环境变量核对清单不要急着up -d先花两分钟核对环境变量。我整理了一个清单照着过一遍能避免 90% 的启动后异常变量名作用我的取值说明TT_MONGODB_URITimeTagger 连接 MongoDB 的连接串使用 Docker 内网服务名mongodb端口 27017 是 MongoDB 默认端口TT_PORT容器内应用监听端口一般镜像内部固定为 80外部通过端口映射访问MONGO_INITDB_DATABASEMongoDB 初始化数据库名我这里设为timetagger与连接串保持一致TZ时区设置建议显式设置TZAsia/Shanghai否则时间轴会偏 8 小时标注区间全部错位时区这个坑我必须要重点提一下。时间序列工具的时间轴显示是基于浏览器本地时区的但后端记录标注时间用的是服务器时区。如果服务器用 UTC你在北京时间下午三点标注一段数据导出的 JSON 里时间戳全部比真实时间晚 8 小时。排查这种问题极其折磨因为它不影响功能只影响数据准确性等你跑完训练发现结果对不上才会回头来查时区。所以部署时就在 compose 里给 MongoDB 和 TimeTagger 都加上TZAsia/Shanghai一劳永逸。3.3 启动、验证与数据导入配置没问题之后开始启动cd /opt/timetagger docker compose up -d # 查看容器状态 docker compose ps # 查看日志确认没有报错 docker compose logs -f timetagger正常情况下你会看到 TimeTagger 容器处于Up状态日志末尾没有Traceback。然后浏览器访问http://你的服务器IP:8082能看到登录页面。首次登录会要求你创建管理员账号创建完就能用。下一步是导入数据我建议先用小数据量试通全流程在服务器上准备一个格式正确的 CSV 或 JSON 文件。CSV 至少要有两列时间列和数值列时间戳格式推荐 ISO 8601例如2025-01-01T00:00:00Z。在 TimeTagger 界面上传数据文件。确认波形渲染出来、时间轴正确后随便标注一段区间加一个标签。导出 JSON检查标注结果时间戳与原始数据对齐。按这个顺序走一遍你就能确认服务正常、数据解析正常、时间轴正常、MongoDB 写入正常。四项全过基础部署算完成。4. 让外部访问真正可用端口、防火墙与 Nginx 反向代理4.1 从 localhost 到局域网理解端口绑定默认情况TimeTagger 容器里的服务监听着80端口通过8082:80映射到宿主机后从局域网任何一台机器的浏览器访问http://192.168.x.x:8082就能打开页面。但如果你访问不了问题往往出在宿主机的监听地址上。我先解释一个小概念Docker 发布端口时如果不指定绑定 IP默认会监听宿主机所有网卡地址0.0.0.0。也就是说你不需要额外配置127.0.0.1:8082:80还是0.0.0.0:8082:80只要端口映射存在外部网络就看得见这个端口。这和裸机部署时服务默认只监听localhost不一样很多人第一次从裸机迁移到 Docker 时发现我的程序明明起来了外网怎么访问不了多半就是这个绑定的差异。知道这个原理之后你的排查思路就清楚了# 在服务器上检查端口监听状态 ss -tlnp | grep 8082如果输出显示0.0.0.0:8082在监听说明 Docker 层面已经开放如果这一步是通的但局域网还是访问不了那问题出在防火墙或者路由上。4.2 防火墙放行ufw 与 firewalld 两种路径Ubuntu 默认装的是 ufw。如果你像我一样习惯先开防火墙再部署服务那一定要记得放行端口# ufw 放行 8082 端口HTTP 直连场景 sudo ufw allow 8082/tcp # 如果后面要配 HTTPS 和 Nginx放行 80 和 443 sudo ufw allow 80/tcp sudo ufw allow 443/tcp # 查看防火墙规则 sudo ufw status numberedCentOS / Rocky Linux 用户用的是 firewalld命令略有不同sudo firewall-cmd --permanent --add-port8082/tcp sudo firewall-cmd --permanent --add-servicehttp sudo firewall-cmd --permanent --add-servicehttps sudo firewall-cmd --reload这里有一个很常见的坑你确认了 Docker 端口没问题确认了防火墙规则没问题但局域网还是访问不了。这时候去查云服务商的安全组。很多云服务器即使系统内部防火墙全开VPC 层面的安全组没放行也会把你挡在门外。我遇到过不止一次客户说端口都开了结果安全组入方向规则压根没加。4.3 用 Nginx 反向代理把服务藏到一个正经路径后面直接IP:8082访问虽然能用但不专业也不安全。外部访问场景下我强烈建议加一层 Nginx 反向代理。好处有三个统一入口端口只开 80/443、方便挂 TLS 证书、可以做访问鉴权。Nginx 安装很简单sudo apt install -y nginx然后创建站点配置server { listen 80; server_name timetagger.example.com; client_max_body_size 200m; location / { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }注意client_max_body_size 200m这一行。TimeTagger 标注的原始数据文件往往不小Nginx 默认限制1m上传大 CSV 会直接 413 报错。我被这个小坑绊过一次前前后后查了半小时才发现是代理层把上传文件掐了。配置文件写好之后sudo ln -s /etc/nginx/sites-available/timetagger /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx现在外部访问的高层链路是浏览器 → Nginx(80/443) → 反向代理 → TimeTagger(8082) → MongoDB(27017)。Nginx 把内网细节全部藏住外部只知道它在访问timetagger.example.com。5. 公网访问与 HTTPS 加固从外网能连到安全地让外网连5.1 公网访问的三种可行方案对比如果你部署的服务器本来就有公网 IP比如云服务器那反向代理配完就能直接从公网访问这个不在话下。但如果你的 Linux 机器是在家里或者公司内网想从外面访问就得考虑下面几种方案。我结合实际使用经验把它们列个表方案适用场景优点缺点是否需要额外软件路由器端口映射家庭/办公宽带有路由器管理权限不依赖第三方数据直连需要公网 IP部分运营商封 80/443 端口不需要内网穿透frp / nps 自建无公网 IP但有云服务器穿透能力强端口可控多一跳网络依赖云服务器稳定性需要 frps frpc云服务器反向代理已有多台云服务器架构清晰安全边界明确多一道开销带宽受限于云服务器不需要我自己的选择是自建 frp 隧道。原因是我们有多台边缘设备都在内网统一通过一台云服务器做流量转发这样只要一个入口就能访问所有内网服务。如果你只有 TimeTagger 一个服务需要公网访问那路由器端口映射是最轻量的方案。用 frp 的话核心配置逻辑是云服务器跑 frps监听一个公网端口内网 Linux 机器跑 frpc把 TimeTagger 的 8082 端口映射到云服务器的某个端口上。映射完成后外部访问云服务器IP:映射端口流量会经过 frps 转发到内网机器的 TimeTagger 上。5.2 用 certbot 一键签发免费 HTTPS 证书外部访问一旦要走公网明文 HTTP 就有点裸奔的意思了。标注数据在公网链路上传输中间任何一跳被截获都能看到明文内容。所以配 HTTPS 是必须的好在现在免费证书申请链路已经很成熟。如果你的服务已经有域名并且能通过 Nginx 正常访问用 certbot 是最省事的路径sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d timetagger.example.comcertbot 会自动修改 Nginx 配置加入 SSL 证书路径和 443 监听并且默认开启自动续期。你需要做的只是确认域名解析指向了你的服务器。如果基于某种原因不想用 certbot比如 frp 穿透后域名解析不在当前服务器也可以用 acme.sh 手动签发证书再把证书路径填到 Nginx 配置里。acme.sh 是纯 shell 实现的 ACME 协议客户端安装和使用都很轻量适合服务器环境。5.3 访问鉴权在 TimeTagger 前面再加一道锁TimeTagger 自身是有账号体系的多用户场景下每个人用自己的账号登录这没什么问题。但如果你的服务要暴露到公网我建议在 Nginx 这层再加一道 HTTP Basic Auth 做前置防线。这样即使有人拿到了 TimeTagger 的访问地址第一关都过不去应用层的弱口令攻击直接被挡在最外层。生成密码文件# 安装 apache2-utils 来用 htpasswd sudo apt install -y apache2-utils sudo mkdir -p /etc/nginx/conf.d sudo htpasswd -c /etc/nginx/conf.d/timetagger.htpasswd admin然后在 Nginx 站点的location /块里加上auth_basic Restricted Access; auth_basic_user_file /etc/nginx/conf.d/timetagger.htpasswd;nginx -t reload生效。再访问就要求输用户名密码了。这道锁看着简单实战价值很高——我见过不少人把数据分析平台直接裸奔在公网一个扫描脚本就能摸到登录页。6. 跑起来之后的事持久化、备份与真实使用中的坑6.1 MongoDB 数据卷与备份策略TimeTagger 的全部标注成果都存在 MongoDB 里。一旦 MongoDB 容器出问题而你又没有做数据卷映射那标注结果就是一条不剩。所以备份这件事我一定要单独拎出来讲。首先确认你自己的持久化配置没问题# 列出当前容器的挂载情况 docker inspect timetagger-mongo | grep -A 10 Mounts其次设计一个定期备份方案。最朴素也最可靠的是用mongodump导出再配合 crontab 做每日备份# 创建一个备份脚本 cat /opt/timetagger/backup/backup_mongo.sh EOF #!/bin/bash BACKUP_DIR/opt/timetagger/backup TIMESTAMP$(date %Y%m%d_%H%M%S) docker exec timetagger-mongo mongodump --out /tmp/mongo_backup_$TIMESTAMP docker cp timetagger-mongo:/tmp/mongo_backup_$TIMESTAMP $BACKUP_DIR/mongo_backup_$TIMESTAMP docker exec timetagger-mongo rm -rf /tmp/mongo_backup_$TIMESTAMP # 保留最近 7 天备份更早的删除 find $BACKUP_DIR -name mongo_backup_* -mtime 7 -exec rm -rf {} \; EOF chmod x /opt/timetagger/backup/backup_mongo.sh # 每天凌晨 3 点执行备份 crontab -e # 添加一行 # 0 3 * * * /opt/timetagger/backup/backup_mongo.sh /opt/timetagger/backup/backup.log 21还原备份的步骤也记录一下以防万一# 把备份文件拷贝回容器再执行 mongorestore docker cp /opt/timetagger/backup/mongo_backup_20250201_030000 timetagger-mongo:/tmp/ docker exec timetagger-mongo mongorestore /tmp/mongo_backup_20250201_030000备份这件事我的原则是宁可备了用不上不可用时没有备。特别是团队协作场景一个标注项目可能是几个人花了两周时间做出来的成果系统重来一次无所谓标注重做是要命的。6.2 我在实测中遇到的坑和完整排错链路部署和真实使用过程中我踩过几个问题每个都值得单拎出来说说排查思路而不是只给结论。坑一导入大文件时浏览器标签页无响应我一开始直接上传了一个 80MB 的 CSV 文件浏览器标签页卡了十几秒然后弹了无响应提示。这个问题的诱因是 TimeTagger 的前端解析逻辑在浏览器端运行超大文件需要先把整份数据 load 进浏览器内存再渲染。排查链路是这样的先用小文件测试确认服务端没问题 → 确认 Nginx 的client_max_body_size没顶到 → 结论是大文件超出了前端处理能力。我的解法是做数据切片用脚本把原始 CSV 按时间段切分成多个 5~10MB 的小文件分多次上传标注。标注完成导出时再在脚本里做合并。业务上影响不大可靠性却高了很多。坑二服务器重启后服务没起来有一次机房断电重启我巡检时发现 TimeTagger 访问不了。登录服务器一看docker 进程虽然起来了但容器都被停止了。排查链路先查容器状态docker ps -a确认Exited状态 → 再查 docker compose 配置发现我把restart: unless-stopped写到了 MongoDB 上但 TimeTagger 服务没写 → Docker 守护进程重启后没有自动拉起 TimeTagger 容器。解法很简单给每个服务都加上restart: unless-stopped重新docker compose up -d使配置生效。这个规则值得成为你的 compose 标配。坑三局域网能访问外部访问超时这个问题的排查链路最典型。我的配置是云服务器 frp 内网穿透现象是内网 IP:8082 没问题走公网域名就一直 timeout。第一步在本地机器telnet 云服务器IP 映射端口看端口通不通。结果显示超时。 第二步在云服务器上ss -tlnp | grep 映射端口确认 frps 端口在监听。 第三步查云防火墙安全组入方向没有放行这个公网端口。 放行之后访问恢复。这个问题的本质是流量没到达 frps 就被挡了排查思路核心是一层层往下切浏览器 → 公网入口 → frps → frpc → 内网服务每一跳单独验证。做运维排查的人应该都有体会网络问题十有八九是链路某一点出了问题按层定位是最具性价比的方法。6.3 团队协作使用时的配置建议如果 TimeTagger 不是你一个人用而是团队共享我建议你再做三件有性价比的事第一固定域名不要用 IP。不管你走不走 HTTPS给 TimeTagger 起一个域名比让同事记192.168.3.104:8082优雅得多。域名可以随便找个便宜的后缀或者在局域网自建一个 DNS 解析优先用域名在 Nginx 里做路由后续升级配置的时候不用改客户端。第二做好账号权限规划。TimeTagger 的权限模型不是特别细粒度更接近能登录的都能看。如果你的团队里有外部成员建议严格控制账号发放并且定期清理。外审人员用完后及时冻结账号避免长期挂着成为安全隐患。第三建立标注规范。这个虽然不是纯技术部署问题但直接影响协作效率。我建议在项目启动前就统一标签命名、时间区间边界规则和备注格式最好写成一个内部文档贴在标注页面的旁边。多标几个项目你就会发现工具的坑都是小事人与人之间标注口径不一致才是最大的坑。7. 最后一点实用建议给想直接上手的人说了这么多最后送几个实在的提示。第一按我前面给的 compose 配置正常情况下十五分钟之内就能把服务跑起来。你不需要完全理解每一行配置的含义但data目录的权限和TZAsia/Shanghai这两处一定要检查到位。第二外部访问这块先打通局域网再谈公网。我见过太多人一上来就想去申请域名、配 HTTPS、搞穿透结果基本步骤没做完最后连本地访问都报错。渐进式部署最稳妥先本机访问 → 再局域网 → 再加 Nginx → 再谈公网和证书。第三数据导入的格式问题建议部署完成后先用官方示例数据做一次全流程测试把自己要用的 CSV 格式调整好再正式导入业务数据。这样可以避免正式使用时才暴露格式问题白等半天。我在实际使用中还有一个体会TimeTagger 这类工具重要的不是功能有多少而是它的标注结果导出格式足够干净。这直接决定了你后续训练脚本要写多长。选工具的时候别只看交互好不好看多花点时间看它的导出 JSON 结构curl 一下 API 文档心里有数再下手能省下之后大量对接的力气。希望这篇文章能让你少走几步弯路把时间多花在数据本身。