Yantr:打造轻量级Docker应用商店,一键部署与升级自建服务

1. 为什么需要Yantr——那些“以为很简单”的部署痛点

我最早萌生做Yantr这个Docker应用商店和部署平台的念头,是在一台装满了乱七八糟服务的NAS上。那台机器上跑着十几个容器,来源各不相同:有从官方镜像拉的,有从某开发者的GitHub仓库直接build的,还有几个是几年前配好就再也不敢动的“祖传容器”。每次想升级,都得先翻半天笔记确认当初的端口映射和环境变量,稍不留神就把旧容器删了结果配置全丢。更别说换一台机器、帮朋友装一套环境,那简直就是把踩过的坑再踩一遍。

这种状态持续了很久,直到我决定做一个属于自己的“应用商店”:把常用的中间件、服务、工具全部收编到一个统一清单里,通过Docker Compose定义好端口、数据目录、环境变量、健康检查,然后一键部署、一键升级、一键回滚。Yantr就这么诞生了。说白了,它就是一套跑在自己服务器上的轻量级“应用市场”,把分散在各处的手动部署流程收敛成“点选-填参数-启动”三步操作。

Yantr适合的人群非常明确:用小主机、NAS跑自建服务的人,平时用Docker跑一堆服务、又经常被版本兼容和配置迁移折磨的人,以及那些看不惯命令行反复敲docker run、想给整个服务器做服务台账的人。它不是要替代Portainer这类管理面板,而是把“部署应用的体验”这件事单独拿出来做到极致。很多人问我,既然有现成的面板、有各类项目模板,为什么还要自己做一套?答案也很简单:市面上的方案要不功能太重,要不应用覆盖太少,要不就是绑定特定NAS系统,没有一套真正适合“普通用户 + 常用服务全家桶”的轻量级商店。

2. Yantr的核心设计:把仓库、市场、部署三件事理顺

2.1 应用商店与应用市场的本质区别

和“应用市场”那种纯展示、点一下就安装的界面不同,Yantr的核心是一套围绕Docker Compose文件的管理系统。它做的事情很朴素:维护一批可安装的应用定义文件,用户在界面上看到应用列表,选中后填写必要的配置项,平台自动生成compose配置并启动。

关键点在于,这里的应用列表不是人工硬编码在主页面的,而是通过“应用仓库”机制动态拉取。我把仓库设计成Git仓库结构:每个应用一个目录,里面包含compose.yaml、config.json和描述文档。平台启动时去拉取仓库列表,解析每个应用的元数据,渲染到商店页面里。这意味着,任何人都可以维护自己的应用仓库,Yantr只是一个“商店壳”,应用内容完全由仓库决定。这个设计参考了包管理器的思路,非常像Linux发行版里的软件源概念,而实际用下来,它带来的灵活性远比把所有应用写死在代码里要强得多。

2.2 为什么选用Compose作为部署引擎

Yantr的部署后端直接对接docker compose,这是我最坚持的一点。有人可能会说,直接用Docker API不就行了?甚至可以更底层一点,自己拼container config。但Compose有一个巨大优势:它把“多容器协同”“网络定义”“依赖顺序”“健康检查”都变成了声明式配置,而声明式配置最适合做模板化。

在Yantr里,每个应用都定义成一组Compose服务。比如要部署一套带数据库、Redis、Nginx的应用栈,不需要分别启动三个容器再手动接网络,而是直接一个compose文件搞定,Yantr负责解析、渲染变量、调用docker compose up。这样一来,应用的“一生只有一个入口”,无论是安装、升级、卸载还是回滚,操作对象都是同一份Compose定义。我甚至把用户填写的参数直接映射到Compose的环境变量和端口段里,保证每次部署生成的文件都可读、可追踪、可审计。

2.3 多架构、多环境的兼容策略

Docker部署最令人头疼的兼容性问题,集中体现在架构差异和镜像tag漂移上。Yantr在应用定义中显式声明支持架构,部署时读取宿主机的CPU架构,在商店中过滤掉不兼容的应用。某开发者的ARM小主机上,x86专用镜像不会出现在列表里,避免了那种点击安装后拉取失败、半天也找不到原因的挫败感。

镜像tag漂移问题更隐蔽。我习惯在仓库定义里锁定digest,而不是只用latest tag。去年有个项目,镜像作者在某天更新了latest指向的内容,接口发生了破坏性变化,所有依赖旧版本的人升级后直接起不来。从那以后我就规定,应用定义文件里必须写上具体tag和镜像SHA256摘要,Yantr部署时检查本机镜像摘要与定义是否一致,不一致则提示用户选择更新或保持现有版本。这种机制虽然牺牲了一点点“永远最新”的便利,但换来的稳定性,对于跑着家庭账本、照片库、监控系统的服务器来说,太重要了。

3. 部署与使用:一步步搭起你自己的Yantr

3.1 前置条件与安装步骤

先说我推荐的运行环境:一台Linux服务器或NAS,安装了Docker Engine 20.10以上版本和docker compose v2插件。内存建议不低于2GB,存储空间视应用数量而定,一般预留20GB以上比较从容。

Yantr本身的安装非常轻量,我把它打包成了一个容器,启动前只需要准备一个数据目录用于存放应用配置、备份和仓库缓存。这是我放在部署文档里的最小compose文件示例:

services: yantr: image: yantr/yantr-server:1.2.0 container_name: yantr restart: unless-stopped ports: - "8723:8723" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./yantr-data:/data environment: - TZ=Asia/Shanghai - YANTR_PORT=8723 - YANTR_STORE_DIR=/data/store - YANTR_REPO_URL=https://github.com/yantr-apps/stable

需要注意两件事。第一,挂载docker.sock是必须的,Yantr需要通过它调用宿主机的Docker守护进程来管理容器。如果你对安全性有更高要求,可以在Docker上配置TLS认证后再挂载,但家用场景下通过内网访问管理页面、不把端口暴露到公网,已经是比较稳妥的做法。第二,别小看YANTR_STORE_DIR这个配置,所有应用的可变参数、安装记录、升级历史都存这里,换机器时直接把整个目录打包带走,新环境里还原数据后,所有应用状态一目了然。

启动完毕之后,浏览器访问http://服务器IP:8723,首次进入会让你设置管理员账号。之后主界面就是商店首页,左侧是应用分类,右侧是应用卡片列表,顶部有搜索框和部署状态入口。整个界面走的是极简路线,我不想把它做成一个花哨的控制台,用户要的只是“快、稳、不出错”。

3.2 配置应用仓库与自定义软件源

商店首页默认带一份我维护的“稳定版”仓库,里面有大约三十个常用应用,覆盖了下载工具、媒体管理、智能家居中枢、监控面板、密码管理器、个人云盘等类别。但这个数字对很多人来说还不够,或者大家希望安装自己公司、团队内部开发的系统,这时候就需要明白仓库机制的扩展方式。

Yantr允许在后台设置里添加多个仓库源,每个仓库源对应一个Git仓库地址。仓库内的目录结构固定为如下格式:

apps/ ├── nextcloud/ │ ├── config.json │ ├── compose.yaml │ └── README.md ├── navidrome/ │ ├── config.json │ ├── compose.yaml │ └── README.md

config.json声明应用的基础信息,我这里用一个简化示例展示:

{ "name": "nextcloud", "displayName": "Nextcloud", "version": "28.0.1", "description": "自建网盘与协作平台", "categories": ["storage", "collaboration"], "arch": ["amd64", "arm64"], "ports": [8080], "healthCheck": "/status.php" }

Yantr定时拉取这些仓库的最新代码,同步应用列表。添加私有仓库时,可以填入带访问令牌的HTTPS地址。实际使用中我遇到过GitHub访问不稳定的情况,解决方案是把仓库同步到国内的代码托管平台上,再把仓库地址换成国内源,拉取速度会明显改善。这里不需要任何复杂操作,只需要注意仓库分支要写清楚,默认拉main分支,如果仓库主分支叫master,就要在配置里显式指定。

3.3 首次部署应用:参数与端口规划

应用列表里点开任意一个应用,会进入参数填写页。这一步是我的精心设计,也是Yantr最值得琢磨的地方。页面上不是简单展示一串环境变量让你自己猜,而是把每个字段都做了说明。以部署一个私人网盘服务为例,你会看到容器端口对应的宿主机端口、数据存储路径、管理员用户名、数据库密码等参数。

部署时最容易踩的坑就是端口冲突。很多新手喜欢把所有应用都映射到80端口,结果第二个应用就起不来了。我在参数页顶部做了一个智能检测:输入端口后,前端直接查询宿主机端口占用情况,一旦冲突立刻红框提示,并推荐一个空闲端口。这个功能实现起来并不复杂,Yantr后端调用宿主机Docker接口拿到已映射的端口列表,做一个差集运算就行,但它极大减少了部署失败率,也是我日常使用频率最高的功能之一。

另一个细节是卷路径规划。Yantr默认会在数据目录下按应用名创建子目录,比如/data/store/nextcloud,并且自动映射成容器内的数据卷。这样所有应用的持久化数据都有清晰归属。我的习惯是结合宿主机目录结构统一规划,比如所有下载类应用共享/downloads目录,所有备份数据汇总到/backup目录,这样即使Yantr本身出了问题,数据还在原位,可以直接用命令行接管恢复。

4. 常见问题与排查实录

4.1 商店页面应用列表拉取失败

这个问题出现得比较频繁,尤其是首次安装后或者长时间没启用时。表现为主界面一直转圈,或者提示仓库同步失败。

排查时我先看日志:docker logs yantr。如果日志里出现“failed to clone repo”或者“authentication failed”,基本可以确定是仓库地址问题。常见的坑包括:私有仓库令牌过期、分支名写错、仓库路径大小写不一致。有一次我调了两小时才发现是自己在仓库地址里把“Yantr”写成了“yantr”,而代码托管平台的仓库名是区分大小写的。

如果是网络环境导致的拉取超时,可以在后台设置里调大同步超时时间,或者把仓库换成国内镜像。这里还有个我个人的实操心得:不要频繁让平台自动同步,我习惯手动点“同步仓库”按钮,确认一次成功后再批量部署,避免仓库更新了一半导致应用元数据不完整。

4.2 容器健康检查判定失败、反复重启

Yantr部署时会自动为每个应用注入健康检查指令。这是好事,但健康检查也是一个经常出问题的点。最典型的案例:某个自带Web服务的镜像,健康检查路径配置成了根路径“/”,但实际服务响应需要用到指定Host头或者幂等性不达标,导致返回非200状态码,容器被判定为不健康,反复重启。

这种情况下,先到应用详情页看健康检查日志,确认具体是HTTP探测失败还是命令超时。然后去仓库定义里修改healthCheck配置,比如把路径改成“/status”或“/healthz”,调高start_period到60秒。健康检查的核心设计逻辑是:启动阶段别急着判定失败,给容器充分的初始化时间;运行阶段要保持探针频率,确保故障能被快速发现。

我还遇到过一种反向情况:健康检查本身没问题,但容器依赖数据库先启动,数据库又初始化很慢,导致应用容器一直处于starting状态。解决方案是利用Compose的depends_on加condition:service_healthy,让应用容器等待数据库健康后再启动。这个配置在Yantr内置应用模板中已经默认开启,但如果你导入第三方仓库的应用,一定要检查compose文件里有没有写健康检查链。

4.3 升级后配置丢失与恢复

升级功能是Yantr的高频操作,也是风险最高的操作。我遇到过最严重的一回:某应用升级时,模板里的环境变量名发生了变更,旧变量被丢弃,新变量的默认值覆盖了原来的配置,导致应用直接丢失了原有数据连接信息。

从那以后我对升级机制做了一个调整:Yantr在执行升级前,会把当前运行容器的完整compose配置导出成JSON快照,保存到数据目录的回滚备用区。一旦升级后的健康检查失败,或者应用无法正常对外提供服务,我可以一键回滚到快照版本。这个功能本质上就是把docker compose里的配置做深拷贝,再跟新版本做差异对比,但并不强制要求用户先看差异,而是默认允许回滚。

给所有使用者的建议:升级前先看一眼应用的发布说明,确认环境变量、配置文件格式是否有破坏性变更;升级后不要立刻离开,至少在页面观察两分钟,等健康检查状态变为“healthy”再放心。对于存储类应用,尤其是记录了大量数据索引的服务,升级前后都建议做一次备份目录的压缩存档,这是一种只需花几秒、但能救命的好习惯。

4.4 依赖镜像拉取缓慢或失败

自建服务离不开镜像拉取,而不同镜像源的线速区别很大。Yantr本身不直接提供镜像加速功能,但配置了宿主机级镜像加速器后,所有通过平台部署的应用也都会受益。我在部署文档里专门写过一个检查步骤:在宿主机上执行docker info查看Registry Mirrors配置,确认加速器生效。

如果某个镜像无论如何都拉不动,我一般会先手动用docker pull单独测试,判断是网络问题还是镜像本身不存在。曾经有个应用的镜像tag在远程仓库已经被删除,但应用定义文件里还锁着旧的digest,导致每次部署都报manifest unknown。这种情况只能联系镜像维护者或者换一个可用的tag,而Yantr的报错信息里会把“拉取失败”和“镜像不存在”明确区分开,这一点帮我省了很多排查时间。

5. 我为什么会坚持用Yantr——几个容易被忽略的设计细节

5.1 安全默认值与应用隔离

自建平台最怕安全问题,而安全不一定来自外部攻击,更多时候是配置不当带来的暴露风险。Yantr在设计上把“默认安全”做进了模板里:所有应用默认不开放到宿主机0.0.0.0,而是挂在平台自带的内网桥接网络上;需要对外访问的服务,必须手动勾选“对外发布端口”,系统才生成对应的端口映射。

权限方面,official模板全部使用非root用户运行容器,镜像内创建专用用户,并将数据卷的属主设置为该用户ID。这套机制避免了常见的“容器内文件全是root”历史包袱,用户后续做数据管理时不用每次都在root权限下操作。

另一个小细节是,Yantr自动为每个应用创建独立的Docker网络。不同应用之间的容器默认不能互相访问,只有明确配置了关联关系才允许互通。比如部署了数据库容器和应用容器,需要在页面上做一次“网络连接”操作,二者才能通信。这种微隔离的思路虽然给初次使用者多了一步操作,但从安全角度看非常值得。

5.2 模板变量与批量部署的体验

批量部署是很多人没注意到的隐藏功能。我在维护一套多媒体家庭服务时,需要在内网三台NAS上部署相同配置的多个应用。手动一台台填参数太痛苦,Yantr支持导出应用配置模板,把一次填好的参数保存为一个“部署蓝图”。换一台设备时,导入蓝图,确认端口和路径没有冲突就能一键复现。

模板变量的处理上,Yantr的逻辑很直接:每个变量都有默认值、描述和类型,部署蓝图只记录用户填写过的值,其余从仓库默认值读取。这样当应用有新增环境变量时,不填写的部分自动跟随上游更新,不会因为拷贝旧蓝图而卡住版本升级。

有一次我帮别人远程部署一套家庭监控系统,全程没有SSH命令行操作,只让对方把Yantr部署好,然后我导出一份蓝图发过去,对方导入、点安装,五分钟内整套监控服务就起来了。这种体验让我确认了自己的方向是对的:Docker本身的复杂度不需要每个人都完全掌握,有了好的定义和管理层,部署完全可以是“点选式”的。

5.3 适合什么样的人上手

我接触过三类人,用得最舒服。第一类是NAS玩家,他们的诉求是稳定跑起一两个下载器、媒体服务器、相册备份工具,不希望碰命令行,Yantr的应用商店正好提供这种操作界面。第二类是开发者,自己公司或团队有多个内部服务需要统一管理,用Yantr的自定义仓库可以沉淀内部部署模板,新员工入职后不用问“这个服务怎么起”这种问题。第三类是折腾型玩家,可能同时跑着十几种服务,需要一个全局的“应用台账”,清楚知道自己这台机器上到底装了什么、用了哪些端口、哪些数据存在哪里。

如果只是偶尔跑一两个容器,Yantr对这类用户来说可能略重,它们更适合直接使用系统自带面板管理。但只要能感知到“部署许多服务、升级频繁、配置易丢”这些痛点,Yantr一定能带来明显的效率提升。

我在实际维护这套平台的过程中,最大的体会是:不要把部署工具想得越复杂越厉害,真正好的平台是让使用者感觉“没在干活”,应用就自己跑起来了。Yantr目前仍然在持续迭代,我给自己定的原则是每加一个应用,都要走一遍从零部署、升级、回滚的完整流程,确保模板经得起日常操作而不是只适用于初始安装。最后再分享一个小技巧:每次升级平台本体之前,先把数据目录打一个tar包,这条习惯已经帮我避过两次数据目录损坏的坑,成本几秒钟,收益却可能是整台服务器服务的延续。

相关新闻

Artcraft:数字时代艺术与工艺融合的创作方法论

Artcraft:数字时代艺术与工艺融合的创作方法论

1. 项目概述:当“艺术”与“工艺”在数字时代重新握手“artcraft”这个词乍看像一个生造词,但拆开来看——art(艺术) craft(工艺),它其实精准戳中了当下创作领域最真实的一股暗流:人…

2026/10/20 15:18:01 阅读更多 →
diagram-design:系统性思维的可视化工程实践

diagram-design:系统性思维的可视化工程实践

1. 为什么“diagram-design”不是画图,而是一场系统性思维重构“diagram-design”这个词组乍看像一个工具操作指令——打开绘图软件、拖拽几个矩形、连几根箭头、加点文字,导出PNG完事。我见过太多团队把这当成交付物:需求评审会上&#xff0…

2026/10/20 15:18:01 阅读更多 →
Titanic实战:GBDT与LLM对比,表格数据建模的取舍

Titanic实战:GBDT与LLM对比,表格数据建模的取舍

1. 先把话说透:GBDT和LLM在表格数据上的真实处境 聊到表格数据建模,业内一直有个心照不宣的结论:凡是结构化数据,GBDT系模型(LightGBM、XGBoost、CatBoost)依然是性价比最高、最稳的默认选择。我做了几年机…

2026/10/20 15:18:01 阅读更多 →

最新新闻

前端彩蛋进阶:用字符画和console实现嫦娥奔月动效

前端彩蛋进阶:用字符画和console实现嫦娥奔月动效

前端职业生涯里,有些需求真的让人又爱又恨。Leader发来一句话:“马上中秋节了,你在网站的console面板中画一个嫦娥奔月,要有逼格。”第一反应是打开控制台敲几行彩色的 log 做个静态图交差,但冷静下来想一想&#xff0…

2026/10/20 16:09:30 阅读更多 →
C++23 std::basic_stacktrace与自定义分配器:生产级调用栈捕获实践

C++23 std::basic_stacktrace与自定义分配器:生产级调用栈捕获实践

1. 项目概述1.1 核心需求解析:当调试信息变成一等公民如果你写过一段时间 C,大概率经历过这种场景:线上服务崩了,日志里只有一行Segmentation fault,连个调用栈都没有,只能靠猜。传统做法是挂 gdb、加backt…

2026/10/20 16:09:30 阅读更多 →
自定义轮廓与3D球面滚动:高保真词云可视化实战

自定义轮廓与3D球面滚动:高保真词云可视化实战

简介:本资源是一套融合多种实现方式的词云图可视化方案,面向前端开发者、数据可视化学习者及Web交互效果实践者,解决传统词云图样式单一、缺乏动态交互与自定义能力的问题。压缩包共8个文件(78KB),含3个核心…

2026/10/20 16:09:30 阅读更多 →
RV1103边缘NPU部署实战:图像分类模型INT8量化与性能对比

RV1103边缘NPU部署实战:图像分类模型INT8量化与性能对比

1. 为什么要在RV1103上折腾图像分类模型 1.1 这个实验到底在做什么 把训练好的图像分类模型塞进一块指甲盖大小的芯片里,让它能离线跑推理,这件事听起来简单,做起来全是坑。我这次拿到的硬件平台是瑞芯微RV1103,一颗定位在低功耗…

2026/10/20 16:09:30 阅读更多 →
移动化ERP落地全攻略:场景重构、技术选型与实战避坑

移动化ERP落地全攻略:场景重构、技术选型与实战避坑

上周我去一家制造企业做回访,刚好撞见这样的场景:仓管员为了核对一张出货单,抱着笔记本电脑在仓库和办公室之间来回跑,碰上仓库角落没网线,急得满头汗。旁边销售总监正举着手机打电话确认库存,说自己三天没…

2026/10/20 16:09:30 阅读更多 →
LLM推理服务如何利用Prefill与Decode功耗不对称性降低电费成本

LLM推理服务如何利用Prefill与Decode功耗不对称性降低电费成本

1. 从"电费账单"说起:为什么LLM推理服务需要看电网脸色 第一次看到这个标题的时候,我脑子里冒出来的第一个疑问是:一个做LLM推理服务优化的论文,为什么标题里会出现"Demand Response"这种电力系统里的术语&am…

2026/10/20 16:08:29 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →