手里设备一多Excel 就撑不住了。尤其是笔记本、显示器、测试手机、软件授权这类东西借出去半年没人记得等想起来的时候不是丢了就是过期了。我两年前开始找自托管的资产跟踪方案试过好几套最后留下的是 DumbAssets。这项目名字虽然看着随意实际做资产跟踪器却非常顺手上手配合 Docker 做本地部署相当省心。这篇文章就把我从零部署到实现外网访问的完整过程、踩过的坑和个人建议都写出来给正准备搞设备台账的运维和做大扫除的团队做参考。1. 为什么我用 DumbAssets 做资产跟踪1.1 Excel 管理设备的三个痛点早期我所在的小团队用共享表格管资产表面看一人一行写得很整齐实际上问题一大堆。最典型的就是多人编辑冲突两个人同时登记同一台设备的去向后保存的人直接把前一个人的记录覆盖掉。第二个痛点是提醒机制完全缺失保修期过了、租赁到期了、软件许可证要续费了这些都不会主动跳出来全靠人肉记住。第三个痛点是统计口径混乱有人写“笔记本”有人写“NB”有人写“联想的本子”月底想拉个报表都不知道该按哪列汇总。这些痛点不是靠更复杂的表格技巧能解决的核心问题是缺一个真正状态化的资产库。DumbAssets 这类资产跟踪器解决的正是这个问题每台设备有独立档案每次借出和归还都是状态变更字段统一由提前定义好的模板控制统计视图实时刷新根本不需要人为维护汇总表。1.2 DumbAssets 的技术栈与设计思路DumbAssets 给我最深的印象是它没搞花里胡哨的前后端分离架构而是老实用了 Django PostgreSQL Redis 这套非常成熟的技术栈。后端框架自带 Admin 后台、权限管理和表单渲染开发效率极高PostgreSQL 承担主数据存储支撑复杂查询绰绰有余Redis 用来做缓存和会话共享多实例部署时不会出现登录状态不同步的问题。这套架构对自托管特别友好三个组件全是开源生态里的标准件任何有点 Docker 基础的人都能快速部署。功能侧面上看DumbAssets 几乎覆盖了资产全生命周期管理——自定义资产字段、分类模板、分配与回收记录、维护工单、保修到期提醒、软件许可证管理、二维码标签打印、CSV 导入导出、REST API 接口。我最常用的是两个功能一是自定义字段能根据团队需求给资产增加“成本中心”“采购单号”这类业务字段二是标签打印一条命令生成带二维码的 PDF贴到设备上之后扫一下就能看到这台设备的所有关联信息。1.3 同类开源工具横向对比选型阶段我对比了市面上几个常见的开源资产跟踪方案包括 Snipe-IT、GLPI 和 DumbAssets。Snipe-IT 功能很全用户群体大但界面相对传统导航层级多小团队使用成本偏高。GLPI 适合做 IT 服务台一体化运维有工单、知识库、网络设备自动发现功能和规模都很大对于只想管好设备的团队来说偏重型了。DumbAssets 的定位刚好卡在中间比纯 Excel 正规得多又没 GLPI 那么庞大界面走的是清爽路线上手路径短。方案技术栈定位部署难度适合规模DumbAssetsDjango PostgreSQL Redis轻量资产跟踪低Docker Compose 即用个人/中小团队Snipe-ITLaravel MySQL通用资产管理中配置项较多中小型企业GLPIPHP MySQLIT 运维 资产综合平台较高模块多中大型企业我当时选 DumbAssets 还有一个实际理由它的 API 设计简洁后续如果要做钉钉或者企业微信的借还审批流对接成本不会太高。对于先解决“把账记清楚”这个核心问题再慢慢叠加外接功能的团队来说这种轻量但留有余地的方案最合适。2. 本地部署Docker Compose 拉起整套服务2.1 部署前的环境准备部署 DumbAssets 需要的硬件门槛很低我用的是一台吃灰的旧 Mini 主机4 核 CPU8G 内存一块 256G 的 SSD。这套配置跑一个 Django 应用加 PostgreSQL、Redis 绰绰有余。如果你手头只有一台普通的 Windows 笔记本也可以装 Docker Desktop 后跑起来但建议长期运行还是放到专门的主机、NUC 或者云服务器上毕竟资产台账这种系统追求的是 7x24 小时稳定。软件层面需要提前准备好三样东西Linux 环境我用的是 Debian 12、Docker Engine 20.10 以上版本、docker compose 插件。安装 Docker 部分可以直接参考官方文档这里不再赘述。有一个细节值得强调Debian 系系统通过 apt 安装的 docker 包可能比较旧建议使用 Docker 官方源安装避免出现 compose 语法不兼容的问题。目录规划也很重要。我在/opt/dumbassets下建了三个子目录/opt/dumbassets ├── compose.yaml # Docker Compose 配置文件 ├── .env # 环境变量不放进 git ├── backup/ # 定时备份脚本和产物 └── logs/ # 容器日志落盘目录把环境变量单独放.env文件而不是直接写在 compose 里是我一直坚持的习惯。这样配置可以提交到 git 追踪变更密钥和密码留在本地避免一次手误泄露整个服务凭据。2.2 compose 配置逐行说明DumbAssets 官方仓库提供了最新的 Docker 部署模板我基于实际环境微调后得到下面的核心配置。写注释的几个字段是必须修改的关键项。# compose.yaml services: db: image: postgres:16-alpine container_name: dumbassets-db restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - db_data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: dumbassets-redis restart: unless-stopped command: [redis-server, --appendonly, yes] volumes: - redis_data:/data healthcheck: test: [CMD, redis-cli, ping] interval: 10s timeout: 5s retries: 5 app: image: ghcr.io/dumbwareio/dumbassets:latest container_name: dumbassets-app restart: unless-stopped depends_on: db: condition: service_healthy redis: condition: service_healthy env_file: - .env volumes: - static_data:/app/staticfiles - media_data:/app/mediafiles ports: - 8000:8000.env文件里最核心的几项DJANGO_SECRET_KEY请用openssl rand -hex 32生成 DJANGO_DEBUGFalse DJANGO_ALLOWED_HOSTS192.168.1.100,assets.example.com POSTGRES_DBdumbassets POSTGRES_USERdumbassets POSTGRES_PASSWORD换成强密码 REDIS_URLredis://redis:6379/1为什么要把数据持久化出来这是新手最容易忽略的地方。如果不挂载db_data卷容器一旦被删除重建所有数据库内容直接蒸发。我见过不止一次有人docker compose down之后发现半年台账全部归零。static_data和media_data同理静态文件和上传的图片、附件必须独立保存否则升级镜像时资产照片全丢。另一个细节是depends_on里的condition: service_healthy。默认的depends_on只保证容器启动顺序不保证服务真正可用。我第一版没加 healthcheck出现了好几次应用容器先起来、数据库还没就绪应用进程直接报连接错误的状况。加了健康检查之后应用容器会等数据库和 Redis 都 ready 再启动问题彻底消失。2.3 初始化管理员账号服务启动后接下来就是初始化数据库结构并创建管理员账号。不同版本的 DumbAssets 命令格式略有差异以官方 README 为准。我实际操作时的流程是docker compose up -d docker compose exec app python manage.py migrate docker compose exec app python manage.py createsuperusermigrate是把 Django 内置表和 DumbAssets 的业务表建出来新部署一定要先执行。createsuperuser按提示输入用户名、邮箱、密码这个账号就是之后登录后台的管理员入口。有一点要提醒首次登录后立刻把默认密码改掉并且建议直接关掉注册入口。资产跟踪器的访问权限一旦被放开任何人都能看到公司设备流向、采购价格、员工信息这属于典型的数据安全事故别等到出了问题再补救。2.4 局域网内访问与验证服务起来之后在服务器本机执行curl -I http://localhost:8000能看到 200 响应说明应用正常。局域网的访问场景我很常用同一办公室的同事用手机浏览器打开http://192.168.1.100:8000扫码盘点或者快速查询设备信息都很方便。如果局域网内其他设备打不开按这个顺序排查。先确认服务器防火墙是否放行 8000 端口ufw allow 8000/tcp。再看本机能否访问本机能而其他设备不能多半是监听地址绑定了 127.0.0.1检查ports映射是否写的8000:8000而不是127.0.0.1:8000:8000。最后看DJANGO_ALLOWED_HOSTS是否包含访问用的 IP 或域名漏掉这个会直接触发 Django 的 DisallowedHost 报错页面上是一大段红色错误信息。3. 外网访问域名、公网入口与 HTTPS3.1 先想清楚要不要把服务暴露到公网本地部署完成之后紧接着的问题就是出差在外怎么看数据。这一步我只建议按需操作如果只有办公室局域网使用完全没必要开公网如果有远程查库存、给外地同事分配资产的需求再考虑外网访问。把服务暴露到公网之前必须先认清风险边界。资产台账是典型的高敏感业务数据包含设备序列号、供应商信息、员工姓名和部门结构。裸奔式的端口直连风险太大任何暴露在公网的端口都会成为扫描器的目标Django 的管理后台如果防护不严就可能被爆破。我的思路是默认不开任何多余端口只保留一个 443 入口其余流量全部挡在防火墙外面再叠加 HTTPS 加密和访问限流。3.2 动态公网 IP 的 DDNS 方案做外网访问第一步是拥有一个域名和可解析的公网 IP。如果你用的是家庭宽带头一件事是确认运营商分配的是不是公网 IPv4 地址。登录路由器看 WAN 口 IP和手机 4G 网络下的 IP 对比一下如果不一致大概率是运营商大内网。拿到公网 IPv4 后由于家庭宽带通常是动态 IP需要用 DDNS 把域名动态解析到当前 IP 上。购买一个便宜的国际域名然后在路由器后台找到 DDNS 设置填入域名服务商的 API 密钥或账号密码路由器会在 IP 变化时自动更新解析记录。这样你永远只用记住assets.example.com不用每次去翻路由器的 WAN IP。如果你所在的宽带没有公网 IPv4可以试试申请开通或者使用 IPv6 DDNS。现在的手机网络和家庭宽带很多都支持 IPv6DumbAssets 三个组件对外只暴露应用端口配好 IPv6 地址和防火墙规则后同样可以远程访问。IPv6 方案需要额外注意一点家用路由器默认可能开启 IPv6 防火墙记得放行对应端口。3.3 Nginx 反向代理用 443 入口统一接管流量外网流量进入服务器后直接打到 8000 端口的 Django 应用上不是不行但有几个问题证书没法加、端口不标准、后端日志和访问控制缺一层统一入口。我的做法是在前面加一层 Nginx 反向代理这里的反向代理是 Web 服务架构里的标准组件负责把 443 端口的 HTTPS 请求转发给本地的 8000 端口和应用本身的对外网络访问没有关系。Nginx 配置片段如下server { listen 443 ssl; server_name assets.example.com; ssl_certificate /etc/nginx/ssl/assets.example.com.pem; ssl_certificate_key /etc/nginx/ssl/assets.example.com.key; client_max_body_size 20m; location / { proxy_pass http://127.0.0.1:8000; 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; } } server { listen 80; server_name assets.example.com; return 301 https://$host$request_uri; }这里有一个最容易踩的坑Django 这类框架启动时并不感知外部域名它读取的是请求头里的Host。必须把proxy_set_header Host $host和X-Forwarded-Proto正确传过去否则 Django 会认为请求来源不合法抛出 400 错误。我在第一次配 Caddy 的时候漏掉了X-Forwarded-Proto这一项结果页面能打开但所有静态资源全部加载失败折腾了半天。如果你不想手动管理证书续期建议直接用 Caddy它内置自动申请和续签 HTTPS 证书的能力配置比 Nginx 短很多assets.example.com { reverse_proxy 127.0.0.1:8000 }Caddy 会自动完成证书获取、HTTP 跳转、反向转发三件事对不想把精力花在证书运维上的人非常友好。我用 Nginx 是因为早就在服务器上跑着其他站点顺手复用同一套配置新部署项目的话直接上 Caddy 能省不少事。3.4 安全加固清单端口映射完成、HTTPS 配好后再花几分钟做几件事能显著降低被攻击的概率。只向内网开放 8000 端口公网仅开放 443 和 80。在服务器防火墙层面把 8000 的访问来源限制为内网网段或云安全组白名单确保公网来的请求必须走网页入口。其次为 DumbAssets 的管理后台设置强密码开启两因素认证如果项目支持并限制登录失败次数。第三部署 fail2ban 对 SSH 和 Nginx 登录日志做暴力破解防护自定义规则把连续失败的 IP 自动封禁一段时间。最后定期更新容器镜像Django 框架和应用代码会持续发布安全补丁docker compose pull docker compose up -d这个命令值得养成每月运行一次的习惯。公网入口的安全维护说到底是一层层叠出来的没有哪一项能单独保证绝对安全但把端口收敛、加密传输、访问限流、及时更新都做到之后小规模自托管系统的安全水位基本就够了。4. 初始化资产库让台账真正滚起来4.1 设计资产分类与自定义字段系统部署完不等于能用起来真正决定资产跟踪器使用体验的是初始数据建模。我在正式录入前花了一个晚上梳理资产台账结构最终沉淀下来的方案是资产类别先分五个大类每个大类下再通过标签做细分。大类示例自定义字段电脑设备序列号、型号、采购日期、保修截止、成本中心、供应商显示与外设尺寸、接口类型、归属人移动设备IMEI、运营商、锁屏密码登记人软件许可证授权类型、授权数、到期日期、采购订单号办公家具尺寸、楼层、工位位置自定义字段的命名规范一定要提前定好我是全部用小写英文字母加下划线例如purchase_date避免后续 API 对接时出现大小写混用的低级问题。录入规则也要定清楚“序列号必填”“采购日期格式统一为 YYYY-MM-DD”“归属人必须关联员工档案”缺一不可。规则不定的后果是三个月后数据一塌糊涂想清理都无从下手。4.2 借还与维护流程资产数据建好之后日常操作就是借出和归还。DumbAssets 里每台设备都能关联一个使用者借出时记录借用人和预计归还时间归还时再更新状态。我建议每个月初做一次借用未归清单核查把超期未还的记录统一提醒一遍逐渐养成团队内部“先查再借、借完必还”的流程。维护工单也建议顺手记录。设备出现故障时在后台新建一条维护记录关联到具体资产以后这台设备的维修历史一目了然是判断“修还是换”的重要依据。这些数据累积到年底直接能拉出各型号故障率报表。4.3 二维码标签打印与盘点标签功能是我推荐 DumbAssets 的重要理由。系统能根据资产信息生成带二维码的标签页打印成 PDF 后裁切贴上设备机身。日常盘点根本不需要翻纸质台账拿手机对着设备扫一下产品名、编号、归属人、位置全部出来。批量盘点时一台一台扫过去记录直接形成电子盘点单和系统里的存量数据一比对账实差异就能定位到具体设备。标签纸选择上建议用亚银 PET 材质比普通铜版纸耐摩擦和耐油污贴在笔记本外壳上能撑很久。标签粘贴位置统一贴在底部或者侧面避免覆盖出厂序列号。4.4 数据导入导出与备份如果之前已经有 Excel 台账不需要手工逐条录入。DumbAssets 支持 CSV 导入把 Excel 导出成 CSV 后再调整列名和系统字段对齐一次批量导入就能完成旧数据迁移。我导入时先导入 5 条测试数据核对字段映射正确后再全量导入避免大批量数据错位后清洗成本过高。导出功能也很重要我每月自动导出一次完整台账放到内部网盘作为系统故障时应急恢复的底牌。考虑到资产管理系统的数据来之不易我在服务器上配置了 cron 定时任务每天凌晨备份 PostgreSQL 数据库每周打包一次 media 文件目录保留最近 30 天的备份版本。备份命令大致是pg_dump导出 SQL 文件后 gzip 压缩再配合 rsync 同步到另一台主机。5. 常见问题与排查实录5.1 重启后服务起不来部署完成后第一次重启服务器最容易遇到的就是容器没起来。我的排查思路是固定三步走先docker compose ps看容器状态再docker compose logs app看应用日志最后docker compose logs db看数据库启动情况。遇到过最典型的原因是数据库没有完成初始化应用容器反复重启。我在 2.2 节加了 healthcheck 之后这种问题就很少出现了因为应用容器会等数据库就绪后再启动。如果仍然出问题检查一下宿主机的时区时区和数据库时区是否一致某些版本的 PostgreSQL 在时区差异下会对时间字段做奇怪的处理。5.2 静态文件 404页面没有样式页面能打开但是纯文本CSS 和图片全部加载失败这是 Django 部署中经典的老问题。原因是生产模式下 Django 不会自己服务静态文件需要执行 collectstatic 把静态文件收集到统一目录。命令是docker compose exec app python manage.py collectstatic --noinput。执行完成后把static_data卷暴露给 Nginx 或 Caddy 即可正常加载样式。这个问题我两次踩到一次是首次部署时忘了收集静态文件另一次是升级版本后没有重新执行 collectstatic新版本新增的静态资源没同步过来。5.3 外网访问打不开的排查顺序域名配好了但家里外网打不开别急着怀疑系统先从近到远逐层排查。我的习惯顺序是本机 curl 8000 端口确认应用正常局域网内改用 IP 访问确认端口监听正常手机切换到 4G/5G 网络访问确认不是本地网络问题然后 ping 域名看解析是否指向当前公网 IP再检查路由器端口映射是否生效最后确认云防火墙或光猫防火墙是否放行 443 端口。绝大多数情况是运营商 NAT 导致没有公网入口或者路由器端口映射配错 IP。为了方便排查我把这个顺序整理成了一张表出现问题时逐项打勾检查项命令/操作预期结果应用状态docker compose psapp 容器 Up本地响应curl -I http://127.0.0.1:8000HTTP/1.1 200局域网访问手机访问 http://内网IP:8000页面打开域名解析nslookup assets.example.com解析到公网 IP路由器映射查看路由器端口转发列表443 指向内网服务器防火墙状态firewall-cmd --list-all443/tcp 放行5.4 忘记管理员密码后的重置如果你和我一样习惯把密码交给密码管理器管理不太会忘但如果密码真的丢了不需要重装系统。在应用容器里执行 Django 提供的 shell 命令直接重置密码即可具体命令格式以项目文档为准原理是通过manage.py进入交互式 shell查找到用户对象后调用set_password方法并保存。这个操作也提醒我们把数据库备份定时做好万一操作失误还能从备份中找回数据。5.5 备份恢复的正确姿势恢复备份最忌讳的是在应用还在运行时直接覆盖数据库。我的标准流程是先docker compose down停掉整套服务然后用 gzip 解压备份的 SQL 文件并通过psql导入数据库导入完成后启动容器验证数据。数据库备份要同时关注 PostgreSQL 和 media 文件二者不是一个体系分开备份分开恢复。如果只恢复了数据库而忘记恢复 media所有资产照片和上传附件会全部出现文件损坏这种事故一旦发生追悔莫及。6. 最后聊聊实操中的体会说实话部署 DumbAssets 本身难度不高真正难的是让一套资产台账系统在团队里持续被使用。我的体会有三条分享给你参考。第一条初始数据要一次性做干净定好字段规范再批量导入不要边用边补导致历史数据失控。第二条每天或者每周安排固定时间扫码盘点比想象中更能培养团队的使用习惯。第三条标签打印和扫码路径一定要简单任何人用手机扫一下就能看到信息系统才会被当成日常工具而非负担。如果后续有需求可以继续深挖 REST API 和外部系统打通。比如把资产数据接到会议室预约系统或者办公用品采购流程设备入库后自动同步到企业微信群这些都是在 DumbAssets 数据扎牢根基之后自然而然能长出来的能力。先从一台台设备录起台账这个笨功夫下到位了后面所有自动化才有真正可靠的数据支撑。