ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

群晖NAS实战:用Docker搭建SVN版本控制服务器

群晖NAS实战:用Docker搭建SVN版本控制服务器 先聊两句背景。不少朋友家里已经有一台群晖NAS平时就是存照片、挂下载、跑点小服务总觉得性能还有富余却又不知道还能干点啥正经事。我自己的这台DSM7.2平时利用率也不高直到有一段时间帮朋友带个小项目几个人改同一份文档、互相传压缩包传到心态爆炸才想起来把NAS变成团队内部的SVN服务器。这个方案跑了大半年稳定、省电、好维护同事到现在都以为我租了一台云服务器。这篇文章就把我在DSM7.2上从零部署SVN服务的完整过程写出来包括方案选型、具体命令、权限规划和日常维护的坑给想在家里或者办公室搭建版本控制环境的朋友一个可以直接抄作业的参考。1. 方案选型为什么我最终选择了Docker部署SVN1.1 群晖当版本服务器的三个硬道理先说一个经常被忽略的事实版本控制服务其实并不吃硬件。SVN最核心的服务进程就是svnserve内部跑的是文件读写和访问控制逻辑对CPU、内存的要求很低但对磁盘稳定性和网络可达性有要求。这些东西恰好都是NAS的强项。我自己选NAS当SVN服务器的核心理由有三个一是7x24小时不关机群晖的功耗大概在十几瓦到二十几瓦比常年挂一台PC可便宜太多而且不需要额外买UPS也能扛住短暂的断电恢复二是存储天然冗余共享文件夹可以放在RAID空间上硬盘坏一块不至于版本库直接蒸发三是内网访问方便同一个局域网里的同事直接连NAS的IP就能用不需要暴露公网端口安全面小了不少。如果你手头已经有一台DSM7.2或者DSM7.x的群晖等于已经具备了一个性能溢出的版本服务器剩下的只是软件层面的配平问题。另外还要说清楚一个概念SVN和Git不一样它是集中式版本控制所有版本数据都存在服务器端一个叫“仓库”的目录里客户端需要联网才能提交和更新。这种模式适合团队规模不大、协作文件以Office文档、设计稿和中小型代码库为主的场景。目录级权限控制比Git原生做得细学习门槛也更低新同事上手半天就能用明白。这也决定了在NAS上跑SVN是一件完全匹配需求的事。1.2 原生套件与Docker镜像怎么选才不后悔群晖系统里安装软件有三种常见思路直接在套件中心装官方套件、添加第三方社群源安装套件、用Container Manager就是以前的Docker套件跑容器。DSM7.2官方的套件中心没有原生SVN套件第三方社群源里确实有subversion-server之类的包但我个人强烈不推荐在主系统里装第三方源套件。原因有几个第一DSM7.x的安全策略比6.x严格很多第三方套件需要非官方签名每次系统大版本升级都可能被禁用或失灵第二套件直接跑在群晖的系统目录里一旦要卸载或重装残留下来的配置、依赖、启动脚本非常难清理干净长期来看对系统稳定性是个隐患第三SVN的版本更新不算频繁但第三方源不一定跟得上有些旧套件在DSM7.2上会出现权限失效或者启动失败的问题。所以我的最终方案是用Container Manager跑一个独立的SVN容器。Docker容器的好处是隔离、干净、可迁移镜像随用随拉仓库数据通过Volume持久化在共享文件夹里就算容器删了重建只要数据卷还在版本库毫发无损。这也是目前社区里在群晖上装SVN的主流实践网上能找到的教程和踩坑记录最多遇到问题可参考的案例也多。提示如果你只是临时用一下、不打算长期维护装套件也行但凡是打算正经跑几个人的代码和文档我都建议一步到位用Docker后面维护会舒服很多。2. 部署前的环境准备DSM7.2上有几样东西必须先弄明白2.1 存储空间与共享文件夹规划在正式拉镜像创建容器之前我建议先花十分钟把存储和用户权限规划好这一步能避免后面出现“容器跑起来了但仓库目录无法访问”之类的低级问题。首先确定仓库存储位置。我习惯在一开始就新建一个独立的共享文件夹名字就叫svnrepo。打开群晖的“控制面板 共享文件夹”点“新增”选择所在存储池时尽量选在RAID卷或Basic卷而不是M.2缓存盘上因为SVN版本库是持久化数据不适合放在缓存盘上。共享文件夹默认的权限先不用改太细因为你实际是通过容器内部的svnserve来做认证的没必要在共享文件夹层面拦截Docker映射出来的用户。其次要理解一个关键点Docker容器内运行的用户和群晖系统用户不是同一个概念。假如容器以root用户启动那么它在容器内创建的文件落在挂载目录上时文件所有权会变成root。后续你用群晖的File Station去看svnrepo里的文件可能会提示无权限这是正常现象。只要你不需要通过SMB直接操作仓库文件就不用理会这个显示问题。如果你确实想在宿主机侧方便地备份可以在创建容器时指定--user参数把运行用户映射成群晖的admin但这又会带来文件权限混乱的风险我的建议是保持容器默认用户别动这个设置。最后检查一下DSM的防火墙规则。如果你用的是群晖自带防火墙在“控制面板 安全性 防火墙”里需要放行3690端口也就是svnserve默认监听端口。这一步经常被忽略导致局域网内其他机器怎么都连不上而本地却能访问。端口不通的错误和认证错误的报错完全是两回事后面排查时会单独展开。2.2 Container Manager的安装与镜像选择DSM7.2预装了“Container Manager”也就是原来叫Docker的套件。如果你的系统里没有先到套件中心搜索Container Manager并安装安装完成后打开你会看到“容器”、“映像”、“项目”、“注册表”等几个模块。接下来就是选镜像。Docker Hub上SVN相关的镜像不少但质量参差不齐。我建议优先选这两种之一轻量型选garethflowers/svn-server它只包含svnserve和subversion工具镜像小、启动快适合纯内网svn://协议使用全功能型选elleflorio/subversion或者带Apache的版本它集成了mod_dav_svn能直接提供http://访问适合需要走Web、跨网段或者对接外部客户端的场景。我自己一开始用轻量版后来因为要临时给外地同事开放访问才切换到带HTTP功能的镜像从中得到的教训是如果预先想清楚“将来要不要外网访问”这一步就能选对省得后面迁移。在Container Manager的“注册表”搜索框输入镜像名下载即可。不建议使用Desktop版本的镜像那是给图形界面用的体积大且没有实际意义。下载完成后先别急着点“启动”因为创建Container时还需要设置一批参数我们放在下一节详细做。3. 核心实操从创建容器到仓库上线照着抄就行3.1 创建并配置SVN容器镜像下载好之后点击“新增”开始创建容器这一步是全程最关键的环节。以garethflowers/svn-server为例你需要配置三部分内容端口、存储空间和重启策略。端口映射上容器内部的3690端口要映射到群晖宿主机的3690端口。如果宿主机3690被占用你自己可以换一个外部端口例如3691但后面客户端连接时URL就要改成svn://192.168.1.10:3691/仓库名。这里我更推荐保持3690不动因为SVN客户端默认端口就是3690换端口等于让每个接入的人都多记一个非标准端口纯粹给自己添麻烦。存储空间映射上最关键的是把群晖的/volume1/svnrepo挂载到容器里的某个数据目录。不同的镜像数据目录名不一样garethflowers/svn-server默认的数据目录是/home/svn你可以把仓库直接建在该目录下elleflorio/subversion一般用/var/svn。如果你的镜像说明文档里没有直接写可以进容器看环境变量或者阅读镜像首页说明。环境变量部分也值得看一眼。有的镜像支持通过环境变量预设用户名密码例如SVN_REPO、SVN_USER等。这样启动时自动创建仓库虽然方便但我个人不太喜欢因为密码会出现在容器环境变量里不够透明。我更倾向于启动容器后自己手动svnadmin create后面解释原因。最后是重启策略。在Container Manager里创建容器时一定要把“自动重新启动”改成“自动”或者“总是重启”。如果你不设置群晖重启之后容器处于停止状态SVN服务自然就断了。默认容器创建页面经常不会自动勾选这项这是新手最容易踩的坑。一切配置好后启动容器然后进入下一步。3.2 在容器内初始化仓库和认证体系容器跑起来后打开Container Manager的“容器”页面选中SVN容器点击“终端机”选择“新增”就能进入一个bash/ash终端界面。如果你的容器没有自带bash可能只有sh输入命令时注意适配。我习惯先把容器内的目录结构摸清楚。进入后执行ls -l /home/svn正常情况下这个目录对应的是群晖的svnrepo共享文件夹里面目前应该是空的。接下来创建第一个项目仓库假设团队叫testprojectsvnadmin create /home/svn/testproject创建完成后这个目录下会出现conf、hooks、db等子目录。SVN仓库的核心配置都在conf目录里包括三个文件svnserve.conf、passwd、authz。先编辑svnserve.confvi /home/svn/testproject/conf/svnserve.conf这里要特别注意SVN配置文件本身对格式极其敏感每项配置前面不能有空格且#号和配置项之间不能随便留多个空格。最常见的报错就是“Line X expected ”十有八九是格式问题尤其从网页复制内容时容易带上头部空格。最简配置我来写一遍并说明每个字段的作用[general] anon-access none auth-access write password-db passwd authz-db authz realm testprojectanon-access none禁止匿名访问强制所有用户都必须登录避免仓库内容在内网裸奔。auth-access write通过认证的用户拥有读写权限。password-db passwd指定账号密码文件passwd。authz-db authz指定权限控制文件authz。realm认证领域名称客户端连接时显示的提示名称建议和仓库名保持一致避免多个仓库被客户端缓存混淆。然后编辑passwd文件放在[users]段下面一行一个用户[users] admin StrongPassw0rd alice Alice123 bob Bob456注意这里的密码是明文存储安全级别不高但SVNsvnserve协议本身就没有做加密传输。如果只在可信内网使用问题不大如果开放到公网我建议考虑配置Apache作为前端启用TLS这一点后面会说。接着编辑authz文件把用户和仓库的权限关系写清楚。最简单的全局权限配置是[/] admin rw alice rw bob r[/]表示仓库根目录。用户alice可读写bob只读。后续如果要增加目录级别细化可以在同一个文件里继续加[/trunk]、[/branches]之类的块。配置完成后在终端里执行kill -1 1或者直接重启容器。因为svnserve在启动时会读取配置文件部分参数修改后需要重启进程。kill -1 1是给容器的1号进程发送HUP信号有些镜像里的启动脚本不一定支持热加载所以最稳妥的就是在Container Manager界面重启一下容器。重启后仓库就正式上线了。3.3 客户端连接与TortoiseSVN/IDEA里的使用在群晖上把服务端搭好只是完成了40%的工作客户端这边怎么连过去才是大多数人最容易卡住的环节。我先用Windows上最常见的TortoiseSVN走一遍完整流程。先去TortoiseSVN官网下载安装包俗称“小乌龟”安装时按默认选项一路点下一步即可注意把“command line client tools”这个选项勾上否则后续在IDEA里配置subversion命令行时会找不到svn.exe。安装完成后在本地任意目录点击右键选择“SVN Checkout”在“URL of repository”一栏输入svn://192.168.1.10/testproject这里的192.168.1.10要换成你的群晖局域网IP。如果你改了外部端口要写成svn://192.168.1.10:3691/testproject。弹出的认证窗口输入passwd里配置好的用户名密码勾选保存认证仓库内容就会拉到你指定的目录。如果你经常用JetBrains系IDEIDEA、PhpStorm、WebStorm等配置方式也很简单。打开“Settings Version Control Subversion”首先把“Use command line client”指向刚才安装TortoiseSVN时自带的svn.exe通常路径是C:\Program Files\TortoiseSVN\bin\svn.exe。然后在主界面点“Get from VCS”选择Subversion输入仓库URL之后的操作就和TortoiseSVN一致了。提交文件的标准动作也别搞混本地修改完文件后要先“Update”更新拉取队友的最新改动再“Commit”提交自己的修改。如果直接提交遇到冲突TortoiseSVN会给出冲突文件列表需要手动合并处理。SVN的冲突处理没有Git那么复杂一般右键冲突文件选“Edit conflicts”逐个对比左右版本即可。4. 权限体系与进阶玩法目录级权限、HTTP访问、提交钩子4.1 目录级权限的精细控制SVN区别于Git的一个核心卖点就是对目录级权限的支持。比如你有一个项目仓库希望普通开发人员只能提交/trunk下的代码不能动/branches/release目录传统SVN根本不需要额外插件直接改authz就能实现。我给一个实际示例假设仓库结构是testproject ├── trunk ├── branches │ ├── dev │ └── release └── docsauthz文件可以这样写[groups] developers admin, alice releasers admin, bob [/] admin rw * [/trunk] developers rw * r [/branches/release] releasers rw * r [/docs] developers rw releasers r注意这里* 后面是空的表示“除了前面显式授权的用户或组其他人没有任何权限”。这个用法很容易踩坑很多人只写* r结果匿名用户都能随便读取权限形同虚设。要注意authz中的匹配规则遵循“就近覆盖”原则[/]的权限是基础子目录的权限会覆盖父目录的设置。所以例子里[/trunk]让developers可读写而* r只影响其他人不会影响已经显式授权的组。修改完authz后保存重启容器新的权限规则即刻生效。客户端那边已经连接的用户可能会因为仓库缓存了旧认证信息而报错最直接的解决办法是在TortoiseSVN里选择“Clear Auth Cache”重新认证。4.2 从svn://到http://让外网和浏览器也能访问svn://协议简单高效但在某些场景下并不够用。比如同事在外地需要走公网访问或者你只是想临时用浏览器看看代码变更历史并不想打开TortoiseSVN。这个时候就需要把服务升级为HTTP访问。这里要区分一个概念svnserve本身也支持svn://和svnssh://但它不能直接提供HTTP协议。要提供HTTP访问需要前端挂一个WebDAV服务主流实现是Apache的mod_dav_svn模块。因此在镜像选型上如果你预见到将来有HTTP需求尽量从一开始就用带Apache的镜像例如前面提到的elleflorio/subversion。以这个镜像为例创建容器时除了映射3690端口还要再映射一个80端口给容器内的Apache。例如宿主机的8080端口映射到容器的80端口那么在浏览器地址栏访问http://192.168.1.10:8080就能看到SVN仓库列表页面。仓库的HTTP访问URL通常长这样http://192.168.1.10:8080/svn/testproject/同一套authz、passwd文件在Apache模式下也能生效因为Apache会调用SVN自带的认证组件读取这些文件。更安全的方式是配置Apache使用Digest认证替代Basic认证不过对于小团队内网使用Basic认证配合同一局域网的可信环境已经够用。如果真要暴露到公网再强调一次最好通过反代例如群晖自带的Reverse Proxy功能挂上HTTPS证书不要把裸HTTP端口直接映射到路由器。群晖的“登录门户 高级 反向代理服务器”可以方便地把一个域名端口转发到容器内再套一层Lets Encrypt证书实际体验接近云托管服务。4.3 用Hook脚本实现提交通知和自动备份SVN的hooks目录是很多人容易忽略的宝藏功能。仓库创建后在/home/svn/testproject/hooks/下会生成一堆.tmpl模板文件把它们直接改名为可执行文件并编写脚本就能在特定时机自动触发动作。最常见的是post-commit.tmpl用于在每次提交后执行自定义逻辑。我做过两个比较实用的脚本这里分享一下思路。第一个是提交通知脚本。每次有代码或文档提交时向团队聊天工具发一条消息内容包含提交人、版本号、文件列表。团队用的飞书和企业微信都支持Webhook脚本本质上就是组装一个JSON用curlPOST到Webhook地址。由于容器里的base镜像不一定带了curl你可能需要进容器安装或者用wget替代。脚本核心调用大概是REPOS$1 REV$2 AUTHOR$(svnlook author -r $REV $REPOS) CHANGED$(svnlook changed -r $REV $REPOS)然后把变量拼进Webhook的JSON模板里发送出去新同事看到群里出现了提交记录等于自动养成了团队协作的习惯。第二个是自动备份脚本。版本库最怕数据丢失我写了个post-commit钩子每次提交后用svnadmin hotcopy把仓库快照到一个备份目录REPOS$1 REV$2 BACKUP_DIR/home/svn/backups/testproject-$REV svnadmin hotcopy $REPOS $BACKUP_DIR --clean-logshotcopy是官方推荐的在线备份方式不需要停服直接把当前仓库复制一份。配合群晖自带的“快照复制”功能等于有一层系统级快照加上一层SVN版本库级快照双保险。钩子脚本记得要赋予可执行权限并在开头加上#!/bin/sh否则不生效。5. 常见问题与排查技巧实录5.1 端口不通、连不上服务器的问题这里列几个我在实际部署中遇到的典型问题按排查优先级排个序。现象可能原因解决方法本机Telnet 3690端口通局域网其他电脑不通群晖防火墙未放行3690控制面板 安全性 防火墙放行3690端口局域网内所有电脑都连不上容器端口映射未设置Container Manager里确认容器外部端口映射到了3690连上后一直卡在连接中客户端和服务端SVN版本差异过大升级TortoiseSVN到最新版有时能连有时连不上群晖硬盘休眠导致容器响应慢在硬盘设置里关闭休眠或保持容器轻量运行还有一个隐藏坑如果群晖同时安装了两个SVN相关容器两个容器都映射了3690端口后启动的那个会创建失败或者出现外部端口占用报错。这时要用netstat -tlnp在终端里查一下哪个进程占用了3690然后把多余的容器删掉。5.2 权限认证报错与匿名访问陷阱SVN连接时报权限错误的频率非常高而且报错信息通常很模糊我总结了几种常见情况。E170013: Unable to connect to a repository at URL这种多为网络层问题不是权限问题。优先检查端口、防火墙和IP能否ping通。E170001: Authentication failed这种是账号密码不对或者服务端passwd文件格式错乱。注意passwd文件每行用户名和密码之间必须用等号连接不能有分号也不能用系统密码替代。E220001: Item is not readable这种通常是匿名访问被拒绝但你输入的账号没有对应仓库的读权限。回到authz里检查[/]块确认该用户是否被显式授权。如果authz文件中的[/]块完全不存在有些SVN版本会视作“所有人无权限”这时候即使账号密码正确也会报各种诡异错误。E160013: File not found通常不是文件真丢了而是你访问的URL少了一层路径。比如你创建的是testproject仓库但用它来访问svn://IP/当然找不到东西。仓库根URL必须写到仓库名这一级。注意修改passwd和authz之后一定要重启容器或让svnserve重新加载。我自己就遇到过改完密码后客户端怎么都登不上最后发现是没重启服务旧配置还在内存里运行。5.3 迁移、升级与容器自启动细节容器方案最大的好处是迁移方便但也有人因为操作顺序不对导致数据丢失这里多说两句。迁移前先把容器停掉然后用File Station或命令行把整个svnrepo共享文件夹打包复制到新NAS上。之后在新NAS上重新拉镜像、创建容器确保存储空间映射路径和旧的一致把打包的数据解压到对应目录再启动容器即可。这里的关键是目录结构必须完全一致尤其是容器内数据目录如果新容器映射了不同的路径仓库就找不到配置文件。升级镜像时最稳妥的做法也不是在Container Manager里直接更新容器。先把现有的容器导出配置Container Manager支持导出JSON配置然后用新的镜像重新创建一个新容器挂载同一个数据卷。启动成功后先连上仓库看一眼历史日志确认正常再删除旧容器。只要数据卷指向没变容器更新对仓库来说是无感的。容器自启动的问题前面提过但还有一层如果群晖本身被误设为来电自启但硬盘模式导致容器启动时间不稳定建议在任务计划程序里加一条“开机触发”的脚本执行docker start 容器名双保险。脚本内容很简单就是一行命令但加上之后确实能避免家里断电恢复后SVN服务不可用的情况。写在最后的话关于SVN服务本身我觉得部署这件事真不难难的是想清楚你为什么要用它、你的团队需要什么样的权限模型、你的数据要怎样备份。我最初就是图省事直接装套件后来因为升级DSM版本被禁用过一次才彻底转向Docker方案。一个最实际的建议无论如何都要把svnrepo共享文件夹设置为群晖快照或Hyper Backup的备份对象SVN仓库这种数据本身有历史版本但仓库文件损坏或者误删整个目录的时候历史版本可救不了你。备份习惯养成这服务才真正算跑得踏实。如果你们也在折腾过程中遇到什么新问题欢迎把报错信息发出来交流这个坑群越多人踩后面的人就越少交学费。
返回列表