自建Leanote知识库:MongoDB部署避坑与数据主权实践
1. 为什么选择自建Leanote而不是直接用云笔记1.1 从一次数据丢失说起前阵子帮一个做技术文档的朋友迁移笔记系统他之前用的某云笔记突然调整了免费版功能导出格式还变成了私有加密格式几百篇技术笔记差点变成“数字遗产”。这件事让我重新审视了自建知识库的价值——数据在自己手里格式开放迁移自由。Leanote就是在这个背景下进入视野的。Leanote是一款开源的笔记应用支持Markdown编辑、多端同步、笔记分享、博客发布等功能。它的核心卖点很明确数据完全自主可控支持Markdown原生存储可以一键导出为通用格式。对于长期积累技术文档、项目笔记、个人知识库的人来说这种“数据主权”比任何花哨功能都重要。但Leanote的部署有个绕不开的坎——它依赖MongoDB作为数据存储。很多人在这一步卡住要么是MongoDB版本不兼容要么是认证配置出错要么是数据目录权限问题。我前后部署过三次Leanote踩过的坑足够写一篇完整的避坑指南。这篇文章就把整个部署过程拆开揉碎从架构选型到参数配置从初始化到故障排查全部讲清楚。1.2 适合谁来参考这套方案这套方案适合三类人第一类是有一定Linux基础、想搭建个人知识库的技术从业者第二类是需要团队内部共享文档、但又不想把数据放在第三方平台的小团队第三类是正在学习MongoDB和Web应用部署的学生或转行者Leanote的部署过程本身就是一个很好的全栈实践案例。需要提前说明的是Leanote的官方维护节奏比较慢最新版本停留在几年前但这不影响它的可用性。社区版功能已经足够完整而且代码结构清晰二次开发的门槛不高。如果你追求的是“开箱即用、功能炫酷”那Leanote可能不适合你但如果你看重的是“数据可控、格式开放、长期可用”那它值得花时间折腾。2. 部署前的整体设计与选型考量2.1 架构拆解Leanote和MongoDB各自扮演什么角色Leanote的架构其实很清晰它是一个典型的Web应用后端用Go语言编写前端是传统的HTMLJavaScript。整个系统分为三个核心部分Web服务层负责处理HTTP请求和页面渲染业务逻辑层处理笔记的增删改查、用户认证、同步逻辑数据存储层则完全交给MongoDB。MongoDB在这里的角色是“唯一的数据持久化层”。Leanote的所有数据——用户信息、笔记内容、附件元数据、配置项——全部存在MongoDB的集合Collection里。这意味着MongoDB的稳定性和性能直接决定了Leanote的可用性。我见过有人把MongoDB和Leanote装在同一台低配机器上结果笔记一多就卡顿这就是没有做好资源隔离的后果。从数据流的角度看用户在浏览器里编辑一篇Markdown笔记Leanote后端会把笔记内容、创建时间、修改时间、标签等信息打包成一个BSON文档写入MongoDB的notes集合。附件文件则存在文件系统的files目录下MongoDB里只存路径引用。这种“元数据入库、文件落盘”的设计很常见好处是数据库不会因为大文件而膨胀备份时也可以分开处理。2.2 版本选择的坑为什么我不推荐最新版MongoDB这是部署Leanote时最容易踩的坑。Leanote官方文档里写的MongoDB版本要求是2.x或3.x但很多人直接装了最新的5.x或6.x结果启动时报各种驱动不兼容的错误。原因在于Leanote使用的MongoDB Go驱动版本较老不支持新版MongoDB的认证协议和连接握手方式。我实测下来MongoDB 3.6到4.0之间的版本兼容性最好。3.6是官方测试过的版本4.0在大多数场景下也能正常工作但需要额外注意认证配置。再往上4.2开始引入了新的连接协议Leanote的驱动会直接报“unsupported wire version”错误。所以我的建议是如果追求稳定直接用3.6如果机器上已经有4.0且不想降级也可以尝试但要做好认证配置的调试准备。另一个版本相关的坑是操作系统。Leanote的二进制包对glibc版本有要求在一些较新的Linux发行版上比如Ubuntu 22.04直接运行官方编译的二进制可能会报“GLIBC_2.xx not found”。解决办法要么是用Docker运行要么是自己从源码编译。我后面会详细讲这两种方案。2.3 部署方式对比二进制、Docker还是源码编译三种方式我都试过各有优劣。二进制部署最简单下载解压就能跑但受限于编译时的glibc版本在新系统上容易出问题。Docker部署隔离性好版本可控但需要额外配置数据卷和网络而且Leanote的官方Docker镜像更新也不及时。源码编译最灵活可以自己改代码、调参数但需要Go环境编译过程对新手不太友好。我的推荐是如果你只是想快速跑起来用Docker如果你想长期维护、方便升级用二进制固定版本MongoDB如果你有二次开发需求那就源码编译。下面我会以二进制部署为主线因为它的过程最透明能让你看清楚每一步在做什么遇到问题也最容易排查。3. 核心细节解析与实操要点3.1 MongoDB的安装与关键配置项先装MongoDB。以CentOS 7为例添加官方源后直接yum安装指定版本# 添加MongoDB 3.6的yum源 cat /etc/yum.repos.d/mongodb-org-3.6.repo EOF [mongodb-org-3.6] nameMongoDB Repository baseurlhttps://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.6/x86_64/ gpgcheck1 enabled1 gpgkeyhttps://www.mongodb.org/static/pgp/server-3.6.asc EOF # 安装 yum install -y mongodb-org-3.6.0 mongodb-org-server-3.6.0 mongodb-org-shell-3.6.0安装完成后不要急着启动先改配置文件。MongoDB的配置文件在/etc/mongod.conf有几个关键项必须调整# /etc/mongod.conf 关键配置 storage: dbPath: /data/mongodb/data # 数据目录建议单独挂盘 journal: enabled: true # 生产环境必须开启 systemLog: destination: file path: /data/mongodb/logs/mongod.log logAppend: true net: port: 27017 bindIp: 127.0.0.1 # 只监听本地Leanote和MongoDB同机时最安全 security: authorization: enabled # 开启认证后面会创建用户这里有几个点值得展开说。dbPath一定要放在独立的磁盘分区上因为MongoDB的写入模式是“先写journal再写数据”如果和数据盘混在一起IO竞争会很严重。bindIp设为127.0.0.1是最安全的做法因为Leanote和MongoDB通常在同一台机器上不需要对外暴露数据库端口。如果确实需要远程连接再改成0.0.0.0并配合防火墙规则。authorization开启后MongoDB就要求所有连接必须认证。但这里有个顺序问题第一次启动时不能直接开认证否则你连创建用户的权限都没有。正确的流程是先以无认证模式启动创建管理员用户和Leanote专用用户然后关闭服务修改配置开启认证再重启。# 第一步无认证启动 systemctl start mongod # 第二步创建管理员 mongo --port 27017 use admin db.createUser({ user: admin, pwd: 你的管理员密码, roles: [{role: root, db: admin}] }) # 第三步创建Leanote专用用户 use leanote db.createUser({ user: leanote, pwd: 你的Leanote密码, roles: [ {role: readWrite, db: leanote}, {role: dbAdmin, db: leanote} ] }) # 第四步关闭服务修改配置开启认证重启 systemctl stop mongod # 修改 /etc/mongod.conf 中 security.authorization: enabled systemctl start mongod注意Leanote的用户名和密码在后续配置文件中要对应填写密码不要包含特殊字符如、#否则在连接字符串里需要转义容易出错。3.2 Leanote的目录结构与配置文件详解Leanote的二进制包解压后目录结构是这样的leanote/ ├── bin/ # 可执行文件 │ ├── leanote # 主程序 │ └── run.sh # 启动脚本 ├── conf/ # 配置文件 │ └── app.conf # 核心配置 ├── files/ # 附件存储目录 ├── logs/ # 日志目录 ├── public/ # 静态资源 └── messages/ # 国际化语言包核心配置全在conf/app.conf里。这个文件是INI格式但Leanote用的是自己的配置解析库有些写法比较特殊。我挑几个最容易出错的配置项说明# app.conf 关键配置 appname leanote httpport 9000 runmode prod # 数据库配置 db.host 127.0.0.1 db.port 27017 db.dbname leanote db.username leanote db.password 你的Leanote密码 # 站点配置 site.url http://你的域名或IP:9000 site.name 我的知识库 # 附件存储 files.dir /path/to/leanote/filesrunmode这个参数很关键。设为dev时Leanote会输出详细的调试日志但性能较差设为prod时日志精简性能更好。生产环境一定要用prod。site.url必须和实际访问地址完全一致包括协议和端口否则登录后会出现跳转错误或静态资源加载失败。还有一个隐藏的坑Leanote的配置文件里密码如果包含#或;会被当成注释截断。我见过有人设了带#的密码结果Leanote连不上数据库排查了半天才发现是配置文件解析的问题。所以密码尽量用字母数字组合避免特殊符号。3.3 数据目录权限与SELinux的隐形陷阱Linux下部署Web应用权限问题永远是绕不开的。Leanote运行时需要读写files目录和logs目录如果权限不对要么启动时报“permission denied”要么运行中附件上传失败。我的做法是创建一个专用系统用户来跑Leanote# 创建leanote用户不允许登录 useradd -r -s /sbin/nologin leanote # 设置目录归属 chown -R leanote:leanote /path/to/leanote chmod -R 750 /path/to/leanote # 数据目录单独设置 chown -R leanote:leanote /data/leanote/files chmod -R 755 /data/leanote/files如果你用的是CentOS或RHEL还有一个SELinux的问题。SELinux默认会阻止Web应用访问非标准目录表现就是Leanote能启动但上传附件时报“500 Internal Server Error”。解决办法有两种要么关闭SELinux不推荐要么给目录打上正确的SELinux标签# 查看当前SELinux状态 getenforce # 如果返回Enforcing需要设置标签 semanage fcontext -a -t httpd_sys_rw_content_t /data/leanote/files(/.*)? restorecon -Rv /data/leanote/files提示semanage命令需要先安装policycoreutils-python包。如果不想折腾SELinux临时设为Permissive模式也可以但重启后会失效需要修改/etc/selinux/config永久生效。4. 完整部署流程与核心环节实现4.1 从零开始的逐步部署记录假设你有一台干净的CentOS 7服务器IP是192.168.1.100我们要把Leanote跑在9000端口。下面是完整的操作序列我按实际执行顺序记录第一步系统准备# 更新系统 yum update -y # 安装必要工具 yum install -y wget tar git # 创建数据目录 mkdir -p /data/mongodb/{data,logs} mkdir -p /data/leanote/{files,logs}第二步安装MongoDB 3.6按3.1节的步骤安装并配置MongoDB创建好admin和leanote用户开启认证确认服务正常。# 验证MongoDB认证是否生效 mongo -u leanote -p 你的密码 --authenticationDatabase leanote --eval db.stats()如果这条命令能返回数据库统计信息说明MongoDB配置正确。第三步部署Leanote# 下载Leanote二进制包 cd /tmp wget https://sourceforge.net/projects/leanote-bin/files/2.6.1/leanote-linux-amd64-v2.6.1.bin.tar.gz # 解压到目标目录 tar -zxvf leanote-linux-amd64-v2.6.1.bin.tar.gz -C /data/ mv /data/leanote /data/leanote-app # 导入初始数据 mongorestore -u leanote -p 你的密码 --authenticationDatabase leanote \ --drop -d leanote /data/leanote-app/mongodb_backup/leanote_install_data/mongorestore这一步是导入Leanote的初始数据包括默认主题、配置项等。--drop参数表示先删除已有集合再导入第一次部署时加上没问题但如果是升级或迁移千万不要加这个参数否则会清空现有数据。第四步修改配置文件# 编辑app.conf vim /data/leanote-app/conf/app.conf需要修改的项httpport 9000 runmode prod db.host 127.0.0.1 db.port 27017 db.dbname leanote db.username leanote db.password 你的密码 site.url http://192.168.1.100:9000 files.dir /data/leanote/files第五步设置权限并启动# 设置归属 chown -R leanote:leanote /data/leanote-app chown -R leanote:leanote /data/leanote # 启动 su - leanote -s /bin/bash -c /data/leanote-app/bin/run.shrun.sh脚本会读取app.conf并启动Leanote。如果一切正常你会看到类似“Leanote started on port 9000”的输出。第六步验证访问浏览器打开http://192.168.1.100:9000应该能看到Leanote的登录页面。默认管理员账号是admin密码是abc123。登录后第一件事就是修改密码这个默认密码是公开的不改等于把知识库大门敞开。4.2 用systemd管理Leanote服务用run.sh启动的方式有个问题终端一关进程就没了。生产环境必须用systemd托管。创建服务文件# /etc/systemd/system/leanote.service [Unit] DescriptionLeanote Note Service Afternetwork.target mongod.service [Service] Typesimple Userleanote Groupleanote WorkingDirectory/data/leanote-app ExecStart/data/leanote-app/bin/leanote Restarton-failure RestartSec5 [Install] WantedBymulti-user.target这里直接调用bin/leanote而不是run.sh因为run.sh里有一些交互式的逻辑不适合systemd。Restarton-failure保证进程崩溃后自动拉起Aftermongod.service确保MongoDB先启动。# 重载systemd配置 systemctl daemon-reload # 启动并设置开机自启 systemctl start leanote systemctl enable leanote # 查看状态 systemctl status leanote4.3 反向代理与HTTPS配置直接暴露9000端口不太优雅也不安全。用Nginx做反向代理是标准做法# /etc/nginx/conf.d/leanote.conf server { listen 80; server_name your-domain.com; location / { proxy_pass http://127.0.0.1:9000; 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 50m; }如果要上HTTPS用Lets Encrypt的certbot自动签发证书即可。但要注意开启HTTPS后app.conf里的site.url也要改成https://否则Leanote生成的链接还是HTTP的会导致混合内容警告。注意Nginx的client_max_body_size默认是1M如果不改上传稍大的附件就会报413错误。这个坑我踩过当时以为是Leanote的问题排查了半天才发现是Nginx的限制。5. 常见问题与排查技巧实录5.1 启动失败类问题速查现象可能原因排查方法解决方案启动报“connection refused”MongoDB未启动或端口不对systemctl status mongod启动MongoDB检查db.port配置启动报“authentication failed”数据库用户名密码错误用mongo命令手动连接测试核对app.conf中的用户名密码启动报“no reachable servers”MongoDB bindIp限制netstat -tlnp | grep 27017确认bindIp包含127.0.0.1启动后立即退出配置文件格式错误查看logs目录下的日志检查app.conf是否有语法错误报“GLIBC not found”系统glibc版本过低ldd --version升级系统或改用Docker部署5.2 运行中的典型故障与处理附件上传失败是最常见的问题。表现是笔记能正常编辑保存但上传图片或文件时报错。排查思路分三层先看Nginx的client_max_body_size是否够大再看files.dir目录的权限是否正确最后检查SELinux是否拦截。我遇到过一次前两项都正常最后发现是SELinux的问题用ausearch -m avc -ts recent命令才定位到。笔记同步冲突是另一个高频问题。Leanote的多端同步逻辑比较简单如果两台设备同时修改同一篇笔记后提交的会覆盖先提交的。避免方法是养成“编辑前先同步”的习惯或者在不同设备上编辑不同笔记。如果确实需要多人协作建议升级到Leanote的团队版或者改用支持OT算法的协作工具。MongoDB磁盘占用过大也值得关注。Leanote的notes集合里每次修改笔记都会生成一条新记录类似版本历史时间长了数据量会膨胀。可以定期清理旧版本// 在mongo shell中执行删除30天前的笔记历史 use leanote db.note_histories.deleteMany({ CreatedTime: {$lt: new Date(Date.now() - 30*24*60*60*1000)} })提示执行删除前先备份或者先用find命令确认要删除的数据范围。我一般会先跑一遍count看看影响多少条记录心里有数再删。5.3 性能优化的几个实操技巧Leanote在笔记数量超过几千篇后列表加载会变慢。除了升级硬件还有几个软件层面的优化点第一给MongoDB建索引。Leanote默认只建了基础索引可以手动补充use leanote db.notes.createIndex({UserId: 1, UpdatedTime: -1}) db.notes.createIndex({UserId: 1, NotebookId: 1})第二调整MongoDB的缓存大小。默认情况下MongoDB会占用大量内存做缓存如果机器内存有限可以在mongod.conf里限制storage: wiredTiger: engineConfig: cacheSizeGB: 1 # 根据实际内存调整一般设为可用内存的50%第三开启Nginx的静态资源缓存。Leanote的public目录下有很多CSS和JS文件让Nginx直接返回并设置缓存头可以减轻后端压力location ~* \.(css|js|png|jpg|gif|ico)$ { root /data/leanote-app/public; expires 7d; add_header Cache-Control public, immutable; }6. 数据备份与迁移的完整方案6.1 备份策略数据库和文件要分开处理Leanote的数据分两部分MongoDB里的结构化数据和files目录下的附件。备份必须两者都覆盖否则恢复后笔记在但图片丢了等于白备。数据库备份用mongodump# 全量备份 mongodump -u leanote -p 你的密码 --authenticationDatabase leanote \ -d leanote -o /backup/mongodb/$(date %Y%m%d) # 压缩备份 tar -czf /backup/mongodb/leanote-db-$(date %Y%m%d).tar.gz \ /backup/mongodb/$(date %Y%m%d)附件备份用rsync做增量同步rsync -avz --delete /data/leanote/files/ /backup/leanote-files/--delete参数表示删除目标目录中源目录已不存在的文件保持两边一致。但第一次用的时候要小心确认源目录是对的否则可能误删备份。6.2 迁移到新服务器的操作顺序迁移时最容易犯的错误是“先停服务再备份”结果备份过程中用户还在写入导致数据不一致。正确的顺序是在旧服务器上停止Leanote服务systemctl stop leanote执行mongodump备份数据库用rsync同步files目录在新服务器上安装相同版本的MongoDB和Leanote用mongorestore恢复数据库同步files目录到新服务器的对应位置修改新服务器的app.conf更新site.url和files.dir启动服务并验证整个过程中MongoDB的版本要一致Leanote的版本也要一致。跨大版本迁移比如从2.4升到2.6需要先看官方的升级说明有些版本之间有数据格式变更。6.3 恢复演练确保备份真的能用备份不做恢复演练等于没备份。我建议每季度做一次恢复测试找一台测试机按照迁移流程走一遍确认笔记能正常打开、附件能正常显示、用户能正常登录。只有实际恢复成功过才能在真正出问题时不慌。恢复时如果遇到“collection already exists”错误说明目标数据库里已有数据。要么加--drop参数先清空要么换一个数据库名恢复后再对比。我一般会恢复到临时数据库确认数据完整后再决定是否覆盖生产库。7. 我踩过的那些坑与最终建议第一次部署Leanote时我犯了一个低级错误MongoDB没有开认证直接暴露在公网上。第二天发现数据库里多了几个莫名其妙的集合显然是被人扫到了。幸好只是测试环境没有重要数据。从那以后我养成了习惯任何数据库服务安装后第一件事就是开启认证并限制监听地址。第二次踩的坑是glibc版本。当时在一台Ubuntu 20.04上跑Leanote的二进制包启动就报错。查了半天才发现官方包是在CentOS 6上编译的glibc版本太老。后来改用Docker部署问题迎刃而解。所以如果你用的是较新的Linux发行版Docker方案可能比二进制更省心。第三次是附件目录权限。Leanote以leanote用户运行但files目录的归属是root结果上传附件一直失败。日志里只写“write error”没有具体路径排查了很久。后来用strace跟踪系统调用才发现是权限问题。这个教训告诉我部署任何应用先把目录权限理清楚能省掉后面80%的诡异问题。如果让我给新手一个建议那就是不要追求一步到位。先把MongoDB和Leanote跑起来能登录、能记笔记然后再逐步加Nginx、加HTTPS、加备份。每一步都验证通过再进入下一步这样出问题时排查范围小容易定位。一上来就全套配置一旦某个环节出错你根本不知道是哪里的问题。最后分享一个实用小技巧Leanote的日志默认输出到logs/leanote.log但如果你用systemd管理日志会进journal。用journalctl -u leanote -f可以实时看日志比翻文件方便。如果日志级别不够详细把app.conf里的runmode临时改成dev重启后就能看到更详细的调试信息。排查完再改回prod避免影响性能。