资讯详情

资讯详情

银河麒麟源码编译Subversion 1.8.14与svnserve部署排障

简介这份资源面向在银河麒麟国产 Linux系统上需要搭建版本控制服务的管理员与开发人员围绕「从源码编译安装 SVN 环境」这一实际问题给出一条可落地的完整安装路线。包中共 1 个 docx 文档约 202KB以文字步骤与命令片段为主体积小、查阅方便适合作为部署时的对照手册。内容覆盖预编译依赖 apr、apr-util 与 sqlite 的下载与 configure/make 安装Subversion 源码编译/etc/profile 环境变量配置与 svnserve 版本验证以及版本库目录创建、svnadmin create 建库等环节并说明 svnserve.conf、passwd、authz 三个配置文件的关键项写法包括匿名访问关闭、鉴权用户读写、用户口令与群组权限设置还涉及服务启动与开机自启脚本、Windows 客户端访问地址格式等延伸细节。已有 2798 人学习适合需要在内网或离线环境自建 SVN 服务、希望减少编译与配置踩坑的读者参考。1. 银河麒麟上装 SVN系统源自带的版本为什么常常不能直接用一台银河麒麟服务器要接回一套跑了很多年的版本管理流程运维给过来的要求很朴素地址不变、账号不变、hook 脚本别动。真正动手才发现系统源里的 subversion 版本和这套流程对不上svnadmin dump出来的东西在两边行为不一致钩子脚本里的路径假设也不一样。老项目维护最怕版本漂移所以我一般不从源里装而是把 subversion 1.8.14 连同 apr、apr-util、sqlite 一起源码编译锁死在/usr/local/svn这个前缀下。这条路适合两类人一是在国产化平台上必须把 svnserve 跑起来、又拿不到合适软件包的运维二是需要固定版本、机器在内网、只能靠 U 盘或跳板机往里传包的开发。整个过程不难难的是依赖顺序、动态库路径和那三个配置文件里的细节下面按我实际走通的顺序拆。2. 依赖三件套apr、apr-util、sqlite 的编译顺序与参数怎么定subversion 不是一个能单独站住的程序。它把文件操作、内存池、字符串处理、网络收发几乎全部压在 Apache Portable Runtime 上所以 apr 和 apr-util 必须先就位而 1.8 这一支又把 sqlite 的源码直接塞进自己的构建流程里用一份 amalgamation 源码现场编不依赖系统里的 libsqlite3。三者的关系是一条链顺序错了 configure 就会在探测阶段直接退出。2.1 为什么必须先 apr 后 apr-util再轮到 subversionapr-util 的 configure 脚本第一件事就是找 apr 的头文件和库找不到就报错退出。而 subversion 的 configure 会同时找 apr 和 apr-util两个都齐了才继续往下探测。所以顺序只能是 apr → apr-util → subversion没有别的排列。这里还有个容易被忽略的点apr 和 apr-util 的版本要成套。apr-1.6.5 配 apr-util-1.6.1 是同一代的组合跨代混搭时 configure 未必报错但编译到一半出现结构体成员不匹配的情况我见过不止一次。手上没有成套的包就先停下来把版本对齐别硬编。安装前缀我习惯分开写/usr/local/apr、/usr/local/apr-util、/usr/local/svn。全都塞进/usr/local看着省事实际是给自己埋雷——头文件和库全糊在一起将来想单独升级或卸掉其中一个根本分不清哪些文件是谁的。# 全程 root源码包统一放在 /home/dmdba/svn 下 cd /home/dmdba/svn # 1) 编译 apr只依赖系统自带的 gcc / make cd apr-1.6.5 ./configure --prefix/usr/local/apr make -j4 make install # 2) 编译 apr-util--with-apr 指向 apr 的安装前缀不是源码目录 cd ../apr-util-1.6.1 ./configure --prefix/usr/local/apr-util --with-apr/usr/local/apr make -j4 make install--prefix决定make install把东西放到哪后面 subversion 的--with-apr必须和它一字不差。-j4是按 CPU 核数给的编译机上核多可以给到 8核少就给 2给太高在内存紧张的虚机上会因为 OOM 直接杀掉编译进程表现成莫名其妙的make: *** [xxx] Killed。2.2 apr-util 的--with-apr写错会怎么表现--with-apr后面跟的必须是 apr 的安装前缀/usr/local/apr不是解压出来的源码目录/home/dmdba/svn/apr-1.6.5。写成源码目录时 configure 有时也能过因为它能在源码树里找到apr.h但make install之后 apr-util 里记录的路径是错的等到 subversion 链接阶段才会以cannot find -lapr-1的形式爆出来。判断方法很直接configure 跑完看它打印的摘要行./configure --prefix/usr/local/apr-util --with-apr/usr/local/apr 21 | tail -20正常的输出里会出现checking for apr-config... /usr/local/apr/bin/apr-1-config这样的行。如果这一行显示的是源码目录或者干脆是no就说明路径给错了别急着 make先回去改。另外如果系统里缺 expat 开发包apr-util 会跳过 expat 支持继续编。这对 svnserve 本身影响不大但如果后面要接 Apache httpd 做 http 访问就会缺 DAV 相关能力。想一次到位编译前把expat-devel、zlib-devel、openssl-devel装上能省掉后面重编 apr-util 的返工。2.3 sqlite-amalgamation 不用编译但改名和位置是硬约束subversion 1.8.14 的构建脚本不调用系统的 sqlite3而是在源码根目录下找sqlite-amalgamation/sqlite3.c这个文件找到之后现场编进去。这个约定非常死目录名差一个字符都不认。从官网下下来的是sqlite-amalgamation-3071501.zip解压出来是同名目录。必须把它挪到 subversion 源码根目录下并且改名为sqlite-amalgamationcd /home/dmdba/svn unzip sqlite-amalgamation-3071501.zip # 挪进 subversion 源码树并改成构建脚本认的名字 mv sqlite-amalgamation-3071501 subversion-1.8.14/sqlite-amalgamation # 确认文件在位这一步别省 ls -l subversion-1.8.14/sqlite-amalgamation/sqlite3.c最后那条ls是我每次都会敲的。少敲这一下等到 subversion 编了十几分钟才报sqlite3.c: No such file返工的时间远比这一行命令贵。2.4 离线传包与解压后的目录规划内网机器最常见的传包方式是跳板机 scp 或 U 盘挂载。不管哪种落地之后统一解压到同一个父目录路径才好写cd /home/dmdba/svn tar -zxvf apr-1.6.5.tar.gz tar -zxvf apr-util-1.6.1.tar.gz tar -zxvf subversion-1.8.14.tar.gz unzip sqlite-amalgamation-3071501.zip ls -d apr-1.6.5 apr-util-1.6.1 subversion-1.8.14 sqlite-amalgamation-3071501四个包解压后应该平级出现在/home/dmdba/svn下这是后面所有cd命令的前提。传包过程中最怕的是压缩包被截断tar -zxvf报unexpected EOF就是这个原因重传一遍即可别怀疑包本身。提示银河麒麟的默认 shell 和 coreutils 与新版本发行版存在差异脚本里凡是涉及通配符和路径拼接的地方宁可写全路径也别依赖相对路径的推断。3. subversion 1.8.14 编译configure 三个--with与 make 中断的处理依赖齐了之后subversion 这一关卡人的地方集中在两个阶段configure 的依赖探测和 make 的链接阶段。前者报错很干脆一行红字告诉你缺什么后者往往要滚几百行才露出真正的原因得会从日志尾部往回翻。3.1 configure 阶段依赖探测的三个关键参数subversion 的 configure 参数不少真正必须显式给的就三个安装前缀、apr 路径、apr-util 路径。cd /home/dmdba/svn/subversion-1.8.14 ./configure \ --prefix/usr/local/svn \ --with-apr/usr/local/apr \ --with-apr-util/usr/local/apr-util \ 21 | tee /tmp/svn-configure.log三个参数的含义务必分清--prefix是自己要装到哪--with-apr和--with-apr-util是依赖在哪。前一个写错装完找不到命令后两个写错configure 直接报APR not found或APR-UTIL not found然后退出。tee那一段不是多余的。configure 输出动辄几百行滚屏之后想回头找具体是哪个探测失败只能靠日志。养成把 configure 和 make 的输出都落一份到/tmp的习惯出问题时grep -i error一下就能定位。configure 顺利跑完时末尾会打印一段摘要里面能看到APR、APR-UTIL、SQLite各自的版本和路径。这一段建议截图或复制留存将来排查版本不一致时是唯一的对照依据。3.2 make 中断先看这三处不要急着重装系统make 失败的原因五花八门但在麒麟上装 1.8.14 这个组合九成跑不出下面三种第一处sqlite3.c 找不到。前面 2.3 节说过目录名和位置不对就会在编译到 sqlite 那一层时中断。解决办法是把sqlite-amalgamation目录放回 subversion 源码根目录并确认文件名。第二处链接阶段报undefined reference to一串 apr 符号。这基本是 apr-util 编译时没真正链上 apr回头重编 apr-util--with-apr指向安装前缀别指向源码目录。第三处编译器把警告当错误。新版 gcc 对某些老代码的隐式类型转换会给出-Werror级别的提示1.8.14 这样的老代码在新工具链上偶尔会撞上。这种情况不是代码坏了是编译选项太严可以在 configure 时加一句放宽# 编译失败时先把完整日志留下来再决定怎么改 make -j4 21 | tee /tmp/svn-make.log # 从日志尾部往前找第一个 error而不是从头读 grep -n -i error /tmp/svn-make.log | head -20grep出来的第一条 error 往往不是根因真正的根因通常在它上面几十行。用sed -n 起始行,结束行p /tmp/svn-make.log把上下文打出来看比一行行往上翻快得多。3.3 动态库路径/etc/ld.so.conf.d里那两行省不得make install成功不代表能跑。subversion 的可执行文件链接的是/usr/local/svn/lib下的libsvn_*apr 的库在/usr/local/apr/lib这些目录都不在系统默认的库搜索路径里。不处理的话svnserve --version会以error while loading shared libraries: libsvn_client-1.so.0收场。正确做法是交给 ldconfig 管理而不是在/etc/profile里堆LD_LIBRARY_PATHcat /etc/ld.so.conf.d/svn.conf EOF /usr/local/apr/lib /usr/local/apr-util/lib /usr/local/svn/lib EOF # 让新配置生效 ldconfig # 验证库是否已被系统识别 ldconfig -p | grep -E libsvn_client|libapr-1三行路径按依赖顺序排列ldconfig会把它们加进缓存。用 conf.d 的好处是干净、可回滚将来升级只需改这一个文件再ldconfig。相比之下往/etc/profile里追加LD_LIBRARY_PATH只对交互式登录的 shell 生效systemd 拉起的服务和 cron 任务读不到是典型的“手动能跑、开机就挂”。3.4 PATH 生效与svnserve --version的冒烟验证最后一步是把/usr/local/svn/bin加进 PATH。写在/etc/profile末尾对所有用户生效cat /etc/profile EOF # Subversion export PATH$PATH:/usr/local/svn/bin EOF # 当前会话立即生效 source /etc/profile # 三件事一起验命令找得到、版本对得上、动态库加载正常 which svnserve svnserve --version svn --version --quietwhich svnserve应该输出/usr/local/svn/bin/svnserve。如果输出的是/usr/bin/svnserve说明系统里原本就装着 subversionPATH 顺序不对把/usr/local/svn/bin提到前面或者干脆把系统自带的卸掉避免两套混用。svnserve --version会打印版本号和编译时间同时把依赖的 apr、apr-util、sqlite 版本一并列出来。这一屏信息极其有用后面遇到任何行为异常第一件事就是回来核对这三个版本号。注意svn --version --quiet只输出版本号适合写进脚本做版本断言svnserve --version输出更全适合人工核对。4. 版本库与权限svnadmin create之后的三个配置文件怎么落地服务能启动只是开始真正决定这套 SVN 好不好用的是版本库结构和权限模型。svnadmin create一条命令就能建库但它生成的conf目录里那三个文件任何一个改错都会让客户端连不上而且报错信息相当不友好。4.1svnadmin create的实际产物与目录规划版本库根目录的选择有讲究。放在/home/svn下方便备份但要注意系统安全策略对这个路径的读取限制放在/var/svn下更符合常规权限管理也更顺。我这边沿用/home/svn因为备份脚本早就按这个路径写死了。# 版本库的父目录注意这一步只是普通目录 mkdir -p /home/svn # 项目目录作为单个版本库存在 mkdir -p /home/svn/project # 关键一步把普通目录变成 SVN 版本库 svnadmin create /home/svn/project # 看它生成了什么 ls -l /home/svn/project执行完svnadmin create之后/home/svn/project下会多出conf、db、hooks、locks、format、README.txt。db是 FSFS 格式的实际存储日常不用碰hooks放提交前后的钩子脚本老流程里那些“提交后发邮件”的逻辑就挂在这conf是接下来要改的三个文件所在。有一点要提前说清楚/home/svn是所有版本库的父目录/home/svn/project才是一个版本库。这个层级关系决定了后面svnserve -r参数该指到哪搞混了客户端就会连到空路径。4.2svnserve.conf四行配置的作用域和顶格规则conf/svnserve.conf是服务端的总开关里面的配置项全部要顶格写行首不能留空格注释行用#开头。这不是格式洁癖解析器对前导空白极敏感行首多一个空格就会导致启动失败而且报错信息只说“配置错误”不告诉你是哪一行。[general] anon-access none auth-access write password-db passwd authz-db authz realm /home/svn五行的含义逐条过一遍anon-access none匿名用户什么都不能做。默认是read意味着不登录也能看代码这个默认值在公网环境是重大风险必须改。auth-access write通过认证的用户可以读写。只想给某些人读权限靠下面的 authz 文件控制这一行给write是上限。password-db passwd账号密码文件。这里写的是相对路径相对于conf目录所以实际读的是conf/passwd。authz-db authz权限控制文件同样是相对conf的路径。realm /home/svn认证域。这一行原配置里没有建议加上。不设的话客户端会用默认域缓存凭据同一台机器连多个不同 SVN 服务时账号密码会互相覆盖表现成“密码明明对却一直认证失败”。password-db和authz-db也可以写绝对路径。写绝对路径的好处是不受conf目录位置影响坏处是迁移版本库时要跟着改。我一般保持相对路径把整个版本库目录一起搬走就能用。4.3passwd与authz分组、路径前缀与匹配顺序passwd只管账号不管权限格式极简[users] admin 123456 dev01 123456 dev02 123456等号两边可以有空格解析器会裁掉。密码是明文存储这一点要有心理准备——conf目录的权限必须收紧到700否则同机器上任何用户都能看到密码。authz才是真正决定谁能访问哪里的地方它支持分组、路径和权限三元组[groups] g_admin admin g_dev dev01,dev02 [/] g_admin rw g_dev r * 逐段解释[groups]段定义组。一个用户可以属于多个组组名前面加引用。[/]表示版本库根路径。注意这个斜杠是相对版本库根不是文件系统根。如果只想放开某个子目录写成[/trunk/docs]这种形式。g_admin rw管理员组读写。g_dev r开发组只读。* 除上面列出的组之外的所有用户权限为空也就是拒绝。这一行必须写不写的话未列出的用户会落到默认策略容易出现“谁都能进”的意外。匹配顺序是从具体到宽泛路径越长的规则优先同一条路径上用户规则的优先级高于组规则。所以给某个特定用户单独开权限时直接把用户名写进去比如zhangsan rw它会覆盖所在组的限制。改完这三个文件不需要重启服务svnserve 每次请求都会重新读配置。这一点很省事但也意味着改错了会立刻生效改之前先备份一份。4.4svnserve -d -r的-r到底停在哪一层启动命令是svnserve -d -r /home/svn。-d是守护进程模式-r是版本库根路径。关键在于-r要指到所有版本库的父目录不是某个具体版本库# 正确指到父目录 /home/svn svnserve -d -r /home/svn # 错误指到具体版本库客户端 URL 会多一层且其他库访问不到 # svnserve -d -r /home/svn/project # 确认进程和端口 ps -ef | grep svnserve ss -lntp | grep 3690-r /home/svn之后客户端访问地址是svn://服务器IP/project。如果写成-r /home/svn/project地址就变成svn://服务器IP/而且再想加第二个项目就得重启服务换路径。端口默认 3690启动后必须确认防火墙放行# firewalld 环境 firewall-cmd --permanent --add-port3690/tcp firewall-cmd --reload firewall-cmd --list-portsps能看到进程但客户端连不上八成就是防火墙这一层。这个坑后面还会细说。5. 避坑与排查五条在麒麟上真踩过的记录下面的每一条都是实际遇到的按“现象 → 原因 → 解决”的顺序写遇到同类问题时可以直接对号入座。5.1 启动即退出日志里只有一行 “Option expected”现象执行svnserve -d -r /home/svn后进程没起来前台跑一次就输出svnserve: /home/svn/project/conf/svnserve.conf:12: Option expected然后退出。原因svnserve.conf里某个配置项前面留了空格或制表符。这个文件是 ini 风格解析但解析器对行首空白零容忍。常见的触发场景是从网页或聊天窗口复制配置粘贴时带了缩进。解决用cat -A conf/svnserve.conf把所有不可见字符打出来行首出现^I制表符或一串空格的就是元凶。把所有配置项改成顶格注释行统一用#开头改完再启动。养成习惯改完这个文件先cat -A扫一眼再敲启动命令。5.2 客户端 Connection refused服务端ps却能看到进程现象服务器上ps -ef | grep svnserve有进程端口也在监听但 Windows 客户端连svn://192.168.10.20/project报Unable to connect to a repository at URL底层是连接被拒。原因系统防火墙没放行 3690。另一种情况是svnserve监听在 IPv6 而客户端走 IPv4ss -lntp的输出里如果只有[::]:3690而没有0.0.0.0:3690就是这个原因。解决先放行端口firewall-cmd --permanent --add-port3690/tcp firewall-cmd --reload再确认监听地址ss -lntp | grep 3690应能看到0.0.0.0:3690。如果只监听 IPv6检查/etc/hosts和系统是否禁用了 IPv4 回环。5.3 密码没错却一直认证失败现象客户端反复弹认证框输入admin/123456始终提示认证失败服务端日志里没有明显报错。原因最常见的是passwd文件里的用户名带了前后空格或者authz里根本没有为这个用户配置任何路径权限。还有一种隐蔽情况是客户端缓存了旧凭据清掉缓存前怎么输都不对。解决先用svn auth --show-passwords或客户端自带的“清除认证缓存”把本地缓存清掉再核对authz里是否有[/]段对这个用户或所在组授权最后检查passwd文件的编码必须是 UTF-8 无 BOM带 BOM 时第一行用户名会解析失败。5.4 重启后 SVN 没起来rc.local里的脚本像没执行现象手动执行/root/svn.sh一切正常重启机器后ps里没有 svnserve 进程脚本里的日志也没写。原因/etc/rc.d/rc.local这个文件在新版系统上默认没有执行权限systemd 不会去跑它。另一个原因是脚本里写的是相对路径svnserve而开机时 PATH 还没加载到/usr/local/svn/bin。解决chmod x /etc/rc.d/rc.local给上执行权限脚本里改用绝对路径/usr/local/svn/bin/svnserve -d -r /home/svn。查绝对路径用which svnserve。更稳妥的做法是舍弃 rc.local写成 systemd 服务单元下一章展开。5.5svn命令能跑svnserve却说找不到 libsvn_client现象交互式登录后svn --version正常但svnserve启动报error while loading shared libraries: libsvn_client-1.so.0: cannot open shared object file。原因动态库搜索路径没配。svn和svnserve链接的库不完全相同前者可能命中了系统自带的旧库后者走的是/usr/local/svn/lib。解决按 3.3 节写/etc/ld.so.conf.d/svn.conf把三个 lib 目录列进去执行ldconfig再用ldconfig -p | grep libsvn确认。改完必须重开一个会话旧会话的环境变量还是老的。6. 把 svnserve 交给 systemd一次写对之后不用再碰 rc.localrc.local那套做法在老系统上能用但在麒麟这一类新系统上它依赖“被手动赋予执行权限”这个前提换台机器部署就得重新记得敲一次chmod x属于典型的靠人肉记忆维持的配置。我现在的做法是直接写 systemd 单元一次写对后面复制过去就能用。先看单元文件。放在/etc/systemd/system/svnserve.service[Unit] DescriptionSubversion Server Afternetwork.target [Service] Typeforking ExecStart/usr/local/svn/bin/svnserve -d -r /home/svn ExecStop/usr/bin/pkill -f svnserve -d -r /home/svn Restarton-failure RestartSec3 [Install] WantedBymulti-user.target几个关键点说明一下。Typeforking是因为svnserve -d会自己 fork 到后台父进程立刻退出systemd 需要知道这一点才不会误判服务已死。ExecStart里用的是绝对路径绕开 PATH 未加载的问题。ExecStop用pkill -f按完整命令行匹配比按进程名杀更精确避免误伤其他 svn 相关进程。Restarton-failure加RestartSec3是给自己留的后悔药——进程意外退出时 3 秒后自动拉起比人肉发现再登录处理快得多。启用和验证按这个顺序走# 重新加载单元文件让新写的 service 生效 systemctl daemon-reload # 设为开机自启并立即启动 systemctl enable --now svnserve # 看状态重点看 Active 那一行是不是 active (running) systemctl status svnserve --no-pager # 从外部验证端口真的在监听 ss -lntp | grep 3690systemctl status的输出里如果出现Active: active (running)说明服务起来了如果是failed用journalctl -u svnserve -n 50 --no-pager看最近 50 行日志比翻/var/log/messages快得多。服务起来之后验证要分三层做别只做最上面一层层次验证命令通过标准进程与端口ss -lntp | grep 3690显示0.0.0.0:3690处于 LISTEN服务端自检svn info svn://127.0.0.1/project输出仓库 UUID 和 Revision客户端拉取svn checkout svn://192.168.10.20/project /tmp/proj-test检出成功能提交一次测试文件三层里最容易漏的是第二层。很多人直接拿 Windows 客户端试连不上时分不清是服务端问题还是网络问题。先在服务器本机用svn info打一次本机通了再排查网络问题范围立刻缩小一半。最后说个我自己的习惯。每次交付这类环境我都会把svnserve.conf、passwd、authz三个文件连同 systemd 单元一起打包到版本库的docs/目录下写清楚版本号和改动日期。不是怕自己忘是怕半年后接手的人对着一个跑着的服务完全不知道它当初是怎么配起来的——这台机器会变成一个黑匣子。从那以后我每次装完 SVN都会强制走一遍“本机 svn info → 外部 checkout → 重启机器再验一次”这三步缺一步都不算交付完成。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →