简介:这份文档面向在银河麒麟操作系统上部署版本控制服务的技术人员,聚焦源码方式安装Subversion及其依赖环境,适合需要离线编译、内网部署或按步骤排查安装问题的运维与开发人员参考。压缩包内共1个docx文件,约202KB,篇幅紧凑,便于随项目归档和查阅。目前已有2798人学习,说明其在国产化系统运维场景中具有一定参考价值。内容围绕apr、apr-util、SQLite与Subversion的编译安装展开,覆盖环境变量配置、版本库创建、用户与权限设置、svnserve服务启动,以及Windows客户端访问和开机自启等收尾环节,并穿插配置项说明与常见注意事项。读者可据此梳理银河麒麟下SVN环境搭建的完整流程,快速定位编译依赖、权限配置和服务启动中的易错点。
1. 银河麒麟上源码编译 SVN:老版本 1.8.14 为什么反而更省事
某公司内网要做版本管理,服务器是银河麒麟,外网只开了镜像,系统源里的 subversion 版本和桌面端 TortoiseSVN 对不上,提交时直接提示工作副本版本过高。折腾一圈后发现最省事的路径不是升级客户端,而是源码编译一套固定版本的 subversion 和 svnserve,把服务端版本钉死。这套东西解决的是「内网无外网、系统源版本不可控」场景下自建版本库的问题,适合手里有几台麒麟机器、要给一个小团队开 trunk/branches、又不想引入额外中间件的人。下面按依赖、编译、建库、权限、排错、自启的顺序,把实际走通的流程摊开写,命令都是能直接抄的。
2. apr、apr-util 与 sqlite 的编译顺序:三个包谁先谁后决定成败
源码编译 SVN 最容易卡住的地方不是 SVN 本身,而是三个依赖包的顺序和路径。subversion 1.8.14 依赖 APR(Apache Portable Runtime)、APR-UTIL 和 SQLite,三者有严格的先后关系:APR 必须先装,APR-UTIL 编译时要通过--with-apr找到 APR 的安装目录,而 subversion 的 configure 又要同时找到这两个的 config 脚本。顺序错了,报错信息往往很含糊,只告诉你「not found」,不会告诉你是哪个路径写错了。
2.1 离线环境下的源码包准备与版本核对
内网机器没法直接dnf install,源码包要提前在能上网的机器上下好,再拷进麒麟环境。老版本在 Apache 的归档站上比在镜像站上更稳,因为镜像站只保留当前版本,1.6.x 这类老包在镜像里经常已经下架。我一般统一放到/home/dmdba/svn/下,一个目录管到底,后面编译、排错、回滚都不用到处找文件。
# 以 root 操作,源码统一放在 /home/dmdba/svn 下,避免文件散落 mkdir -p /home/dmdba/svn cd /home/dmdba/svn # apr 与 apr-util 从 Apache 归档站取,老版本在归档里长期存在 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 # sqlite amalgamation 是 zip 包,解压后只有 sqlite3.c 和 sqlite3.h 两个核心文件 unzip sqlite-amalgamation-3071501.ziptar -zxvf里的z表示 gzip 解压,x是解包,v输出文件列表方便确认包没坏,f指定文件名。unzip不带参数会解到当前目录,解出来的目录名是sqlite-amalgamation-3071501,这个目录名后面要用到,别随手改。
版本对应关系建议先核对一遍再动手,这套组合是 1.8.x 分支上比较稳的一组:
| 组件 | 版本 | 作用 | 安装前缀 |
|---|---|---|---|
| apr | 1.6.5 | 跨平台运行时库,SVN 的内存池、文件操作都走它 | /usr/local/apr |
| apr-util | 1.6.1 | APR 的扩展库,XML、数据库抽象层 | /usr/local/apr-util |
| sqlite amalgamation | 3071501(即 3.7.15.1) | SVN 版本库的底层存储引擎 | 编进 SVN,不单独装 |
| subversion | 1.8.14 | 服务端与命令行客户端 | /usr/local/svn |
拷贝进内网后先做一次完整性校验,归档站每个包旁边都有.md5文件,比对不上就别往下走了,编译到一半报「gzip: stdin: unexpected end of file」多半就是包没传完整。
cd /home/dmdba/svn md5sum apr-1.6.5.tar.gz apr-util-1.6.1.tar.gz subversion-1.8.14.tar.gz # 与同目录下 .md5 文件里的值逐行比对,不一致就重新拷贝提示:麒麟环境如果是最小化安装,
gcc、make、unzip可能都没有,先确认gcc --version和make --version有输出,再往下走。
2.2 apr 与 apr-util 的 configure 参数与安装验证
APR 的编译是最简单的一步,但--prefix一定要想清楚,后面 apr-util 和 subversion 都要引用这个路径,中途改前缀等于前面的活白干。我固定用/usr/local/apr,不带尾部斜杠,带斜杠在某些 configure 版本里会拼出双斜杠路径。
cd /home/dmdba/svn/apr-1.6.5 ./configure --prefix=/usr/local/apr make -j"$(nproc)" make install--prefix决定make install把库、头文件、apr-1-config脚本放到哪;make -j"$(nproc)"用满 CPU 核数并行编译,麒麟机器核少的话去掉-j也行,差别只在耗时;make install要 root 权限,因为目标目录在/usr/local下。装完立刻验证,别等后面报错再回头查:
/usr/local/apr/bin/apr-1-config --version ls /usr/local/apr/include/apr-1/apr.hapr-1-config是给下游包用的探测脚本,apr-util 和 subversion 的 configure 都会调用它拿编译参数。这个脚本不在,后面一定失败。接着编 apr-util,关键是--with-apr必须指向刚装好的/usr/local/apr,而不是源码目录:
cd /home/dmdba/svn/apr-util-1.6.1 ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr make -j"$(nproc)" make install # 验证 apr-util 的探测脚本也装好了 /usr/local/apr-util/bin/apu-1-config --version--with-apr写成源码目录../apr-1.6.5是最常见的错误,configure 阶段可能不报错,但编译到链接时找不到libapr-1.so,报错信息里一堆 undefined reference,看着像代码问题,其实是路径问题。如果麒麟系统上缺 expat 相关头文件,apr-util 的 configure 会提示找不到 XML 解析器,常见做法是装expat-devel,或者加--with-expat=builtin让 apr-util 用自带的 expat 源码编,内网不方便装包时后者更省事。
2.3 sqlite amalgamation 放进源码树的位置与理由
SQLite 这块是 subversion 1.8 编译里最容易翻车的一环。subversion 的构建脚本约定在源码树根目录下找sqlite-amalgamation/目录,里面放sqlite3.c。它不通过系统libsqlite3链接,而是把这个 C 文件直接编进libsvn_subr,目的是版本可控、避免和系统里其他程序的 SQLite 符号冲突。所以不能只把 zip 解压了就完事,得把文件放到约定的位置。
cd /home/dmdba/svn # subversion 1.8 的构建脚本只认源码树里的 sqlite-amalgamation 目录 mkdir -p subversion-1.8.14/sqlite-amalgamation cp sqlite-amalgamation-3071501/sqlite3.c subversion-1.8.14/sqlite-amalgamation/ cp sqlite-amalgamation-3071501/sqlite3.h subversion-1.8.14/sqlite-amalgamation/ # 确认位置正确,文件名必须是 sqlite3.c,大小写都不能错 ls -l subversion-1.8.14/sqlite-amalgamation/mkdir -p的-p保证父目录不存在时一起建;拷贝时只认sqlite3.c这个名字,把整个目录改名成sqlite-amalgamation放在源码树里也行,效果一样。sqlite3.h一起拷进去不是必须的,但排错时有用,编译报缺头文件时能第一时间确认是不是这里的问题。
另一种做法是 configure 时用--with-sqlite=/usr/local/sqlite指向单独编译的 SQLite,适合系统里已经有匹配版本的场景。但在麒麟上,系统自带的libsqlite3版本往往偏新或偏旧,和 subversion 1.8 的 API 不一定对得上,编译期不报错、运行期svnadmin create报 schema 异常的情况我见过不止一次。离线环境下用 amalgamation 是后悔药最少的选择,多拷两个文件,省掉一晚上的排查。
3. Subversion 本体编译:从 configure 摘要到 svnserve --version 的四个检查点
依赖装完,subversion 的编译反而快了。这一步的核心不是敲命令,而是看懂 configure 最后输出的摘要,以及make install之后库文件到底落在哪。很多人卡在「装完了,svnserve --version却报找不到库」,问题基本都出在这一章。
3.1 configure 参数逐条拆解与摘要解读
编译参数不多,但每一个都对应一个依赖路径,写错一个就前功尽弃。下面这条命令是实际跑通的版本:
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--prefix=/usr/local/svn是 SVN 的安装根,二进制在bin/,库在lib/;--with-apr和--with-apr-util分别指向前面两个依赖的安装前缀,configure 会去这两个目录下找apr-1-config和apu-1-config。三个参数之间用反斜杠换行,实际执行时是一整行,复制到终端里不会出问题。
configure 跑完,最后会打印一段摘要,重点看这几行:APR和APR-UTIL的版本号是否显示出来、SQLite是否显示为internal(说明用的是源码树里的 amalgamation)、serf和ssl的状态。如果 SQLite 那一行显示not found,回头检查 2.3 里的目录名和文件名;如果 APR 显示not found,基本是--with-apr路径写错了。
常见追加参数有两个:要让svn客户端能拉https://的仓库,加--with-ssl,前提是系统有openssl-devel;只在内网走svn://协议的话可以不加,减少一层依赖。另一个是--with-zlib,压缩传输用,系统有zlib-devel就加上,没有也不影响svn://的基本使用。内网环境我一般只加--with-ssl,其余保持默认,参数越少,出问题时变量越少。
3.2 make install 之后的目录布局与动态库处理
编译和安装命令本身没有悬念,关键是装完之后要知道东西在哪:
make -j"$(nproc)" make install装完之后,/usr/local/svn/bin/下会多出一组命令,常用的是这几个:
| 命令 | 用途 |
|---|---|
| svn | 命令行客户端,拉取、提交、查看日志 |
| svnadmin | 建库、备份、校验版本库 |
| svnserve | 轻量级服务端,走 svn:// 协议 |
| svnlook | 直接查看版本库内容,排错时很有用 |
| svnsync | 版本库镜像同步 |
库文件在/usr/local/svn/lib/下,是一组libsvn_*.so.1。问题来了:svnserve启动时要加载这些库,以及/usr/local/apr/lib和/usr/local/apr-util/lib下的 APR 库。默认情况下动态链接器只认/lib、/usr/lib这些标准路径,/usr/local下的子目录不在搜索范围里,于是就会出现「编译装完了,一执行就报 cannot open shared object file」。
先确认依赖有没有断:
ldd /usr/local/svn/bin/svnserve | grep "not found"如果有输出,说明有库找不到。解决办法是把三个库目录登记到动态链接器配置里,而不是靠临时export LD_LIBRARY_PATH:
cat > /etc/ld.so.conf.d/svn.conf <<'EOF' /usr/local/svn/lib /usr/local/apr/lib /usr/local/apr-util/lib EOF # 让新配置立即生效 ldconfig # 再次检查,这次应该没有 not found 输出 ldd /usr/local/svn/bin/svnserve | grep "not found"/etc/ld.so.conf.d/下的.conf文件会被ldconfig统一读取,写在这里是全局生效、重启不丢;LD_LIBRARY_PATH只对当前 shell 有效,换个终端或者被 systemd 拉起就失效,属于典型的「本地好的、上线就挂」。三个路径一行一个,别用冒号拼在一行,ld.so.conf的格式是按行解析的。
3.3 环境变量写 /etc/profile 还是 profile.d:一个容易漏的差异
svnserve --version要在任意目录下都能敲,就得把/usr/local/svn/bin加进 PATH。直接改/etc/profile是原始做法,能在登录 shell 里生效,但有两个隐患:一是文件越改越长,多人维护时容易冲突;二是某些非登录 shell(比如ssh host 'command'这种)不读/etc/profile,命令就找不到。更规范的做法是单独放一个/etc/profile.d/svn.sh:
cat > /etc/profile.d/svn.sh <<'EOF' # 只追加 SVN 的 bin 目录,保留系统原有 PATH export PATH="$PATH:/usr/local/svn/bin" EOF chmod +x /etc/profile.d/svn.sh # 当前已登录的 shell 需要手动 source 一次,新开的终端会自动加载 source /etc/profile.d/svn.shexport不能省,只写PATH=$PATH:...在当前 shell 里看着有效,子进程拿不到;chmod +x不是必须的,但加上没坏处,某些发行版的 profile 加载逻辑会跳过没有执行位的脚本。source只影响当前终端,已经开着的其他窗口要各自执行一次,或者退出重登。
验证分三步,缺一步都可能误判:
# 1. 命令能否找到 which svnserve # 2. 版本号是否正常输出,同时验证动态库加载没问题 svnserve --version # 3. 新开一个终端再敲一次,确认 profile.d 生效which svnserve输出/usr/local/svn/bin/svnserve才算 PATH 配对了;svnserve --version除了打印版本,还会列出支持的协议(svn://、svn+ssh://),如果只有前者没有后者,说明编译时 SSH 相关依赖没找到,内网自用影响不大。第三步最容易漏,很多人当前终端好了就以为完事,结果运维同事登录上去发现命令不存在,又回来查半天。
4. 版本库与权限三件套:svnadmin create 之后的三个配置文件
SVN 服务端起不来,十次里有七次不是二进制的问题,是conf/下那三个配置文件的问题。svnserve.conf管匿名访问和认证开关,passwd管用户密码,authz管路径权限。三个文件各管一摊,但互相引用,改错一个就可能出现「能连上但一直弹认证」或者「认证过了但提交被拒」这类玄学现象。
4.1 svnserve.conf 的最小可用写法与 realm 的作用
先建库。版本库根目录我选/home/svn,下面每个项目一个子目录,svnserve -r /home/svn一次带起所有项目,后面加项目不用重启服务:
# 版本库根目录 mkdir -p /home/svn # 具体项目目录,-p 保证父目录一起建 mkdir -p /home/svn/project # 把 project 目录初始化为 SVN 版本库 svnadmin create /home/svn/project # 建库后 conf 目录下会生成三个配置文件 ls -l /home/svn/project/conf/svnadmin create执行完,目录里会出现conf/、db/、hooks/、locks/、format这几项,db/是真正的数据存储,conf/是配置。接下来编辑svnserve.conf,最小可用配置只有五行:
[general] anon-access = none auth-access = write password-db = passwd authz-db = authz realm = /home/svn/projectanon-access = none表示未认证用户什么都干不了,这是内网版本库该有的默认值;auth-access = write给认证用户读写权限;password-db和authz-db分别指向同目录下的passwd和authz,写相对路径就行;realm是认证域,客户端会按 realm 缓存凭据,不写的话默认用版本库路径,多个项目共用一套账号时建议显式写清楚,否则切换项目时客户端可能拿着旧凭据反复弹窗。
注意:
[general]这一行必须顶格,前面不能有空格,文件首行也不能留空行。svnserve 的配置解析器对缩进很敏感,多一个空格就报「Invalid config file」或者直接忽略整段配置,表现为匿名可以读、认证用户反而被拒。
原文里提到「首行不可以留空,要顶行」,这是血泪经验,不是格式洁癖。用vi编辑时按i进入插入模式,改完ESC再:wq保存,别用带自动缩进的编辑器插件,很容易在[general]前插一个制表符。
4.2 passwd 与 authz 的群组授权:路径到底相对谁
passwd文件管账号,格式很简单,一个用户一行:
[users] admin = 123456[users]段名不能改,用户名和密码之间用等号,等号两边可以有空格但别留多余空白字符,密码末尾带空格是最隐蔽的坑,登录时怎么输都不对。这里用弱密码只是为了演示,实际部署一定要换掉,并且把文件权限收紧到 600,passwd是明文存储,同机器上有读权限的人都能看到。
authz文件管权限,群组写法能省很多重复配置:
[groups] g_admin = admin [/] @g_admin = rw * =[groups]段定义群组,g_admin包含admin一个用户,多个用户用逗号分隔;[/]表示版本库根目录,@g_admin = rw给这个群组读写权限,* =表示除上面列出的群组外,其他所有人无任何权限,等号后面留空就是「无权限」,不是「继承」。
这里有个特别容易理解错的地方:authz里的/是版本库根,不是svnserve -r指定的那个目录。当服务用svnserve -d -r /home/svn启动、客户端访问svn://ip/project时,authz里的/对应的是project这个版本库的根,而不是/home/svn。如果要控制版本库内部的子目录,写[/trunk]、[/branches]这种形式。把/当成/home/svn来写[/project],结果就是权限怎么配都不对,客户端不是 403 就是能读不能写。
4.3 svnserve 启动参数与客户端连通验证
配置改完,启动服务:
# -d 后台运行,-r 指定版本库根目录,注意路径只到 /home/svn svnserve -d -r /home/svn # 确认进程在 ps -ef | grep "[s]vnserve" # 确认 3690 端口在监听 ss -lntp | grep 3690-d让 svnserve 以守护进程方式跑在后台,前台不占终端;-r是版本库根,客户端 URL 里的第一段路径会拼在它后面,所以只写到/home/svn,不能写成/home/svn/project,否则访问路径就变成svn://ip/project/project。ps -ef | grep "[s]vnserve"里方括号是为了排除 grep 自己那一行,看输出更干净。
本机先自测,排除网络因素:
svn list svn://127.0.0.1/project --username admin --password 123456能列出空目录说明服务端配置、认证、权限三件事都通了。如果这一步报Authorization failed,问题在authz的路径或passwd的账号;如果报Unable to connect,问题在进程或端口。
| 验证点 | 命令 | 期望结果 |
|---|---|---|
| 进程存在 | ps -ef | grep svnserve | 有一条 svnserve 进程 |
| 端口监听 | ss -lntp | grep 3690 | LISTEN 状态 |
| 本机认证 | svn list svn://127.0.0.1/project | 列出目录内容 |
| 远端连通 | 客户端连 svn://服务器IP/project | 弹认证框,输入后能浏览 |
远端连不上先查防火墙,麒麟默认可能开着 firewalld:
# 放通 3690 端口,--permanent 保证重启后仍生效 firewall-cmd --permanent --add-port=3690/tcp firewall-cmd --reload firewall-cmd --list-ports--permanent不加的话规则只在当前运行期有效,重启就没了,这是另一个常见误解。--reload让规则立即生效但不断开现有连接。客户端地址写成svn://服务器IP/project,注意是双斜杠加 IP 再加项目名,少一个斜杠或者多写一层路径都会连到错误的位置。
5. 避坑与排查:麒麟上装 SVN 常见的五个翻车现场
这一章按「现象 → 原因 → 解决」整理,都是实际踩过的,顺序按出现频率从高到低。排错时建议从下往上倒推:先确认进程和端口,再看配置,最后才怀疑编译。
5.1 编译期:找不到 apr-util 与 sqlite3.h
现象一:configure 报APR-UTIL not found,但 apr-util 明明装过了。原因基本是--with-apr-util指向了源码目录,或者 apr-util 装到了非默认前缀而参数没跟着改。configure 找的是apu-1-config这个脚本,不是libaprutil-1.so。 解决:先跑/usr/local/apr-util/bin/apu-1-config --version确认脚本存在,再检查 subversion 的 configure 参数里--with-apr-util是不是写的/usr/local/apr-util。如果 apr-util 装在了别处,把参数改成实际路径,别用软链接糊弄,configure 会解析脚本里的绝对路径。
现象二:configure 报An appropriate version of sqlite could not be found。原因是源码树里的sqlite-amalgamation/sqlite3.c不存在,或者目录名拼错(比如写成sqlite-amalgamation-3071501)。 解决:ls subversion-1.8.14/sqlite-amalgamation/sqlite3.c确认文件在,目录名必须是sqlite-amalgamation,文件名必须是sqlite3.c,大小写敏感。改完目录结构要重新跑 configure,makefile 是 configure 生成的,光改文件不重跑没用。
现象三:make 阶段报gcc: command not found或编译到一半被 OOM Killer 杀掉。原因是麒麟最小化安装没带编译工具链,或者make -j开的并行数超过了内存承受能力。 解决:先gcc --version确认工具链,缺就装gcc、make、glibc-devel;内存小的机器把-j"$(nproc)"改成-j2甚至去掉-j单线程编,慢一点但不会中途崩。
5.2 运行期:配置首行、权限与端口
现象四:服务起来了,客户端一直弹认证框,输对密码也进不去。原因通常是svnserve.conf文件首行或段名前有空格,解析器把整段[general]忽略了,匿名访问策略回落到默认值,认证配置根本没生效;另一个可能是passwd里密码尾部带了空格。 解决:sed -n '1,10p' /home/svn/project/conf/svnserve.conf看前几行有没有缩进,有就删掉;cat -A passwd看行尾有没有$之外的空白字符。改完必须重启 svnserve,配置不会热加载。
现象五:客户端能浏览,但提交报Permission denied,服务端日志里是Can't open file 'db/txn-current-lock'。原因是版本库目录的属主和运行 svnserve 的用户不一致。用 root 建库、用普通用户起服务,或者反过来,都会导致写锁文件失败。这是权限问题,不是authz配置问题,很多人会往authz上查,方向就错了。 解决:ps -ef | grep svnserve看运行用户,ls -ld /home/svn/project/db看目录属主,用chown -R 同一用户:同一组 /home/svn/project统一,然后重启服务。日常操作也别用 root 直接往版本库里提交,混用身份是这类问题的根源。
补充一条端口问题:本机svn list通、远端连不上,九成是防火墙没放通 3690,或者-r的路径写多了一层。先用ss -lntp | grep 3690确认监听地址是0.0.0.0而不是127.0.0.1,再看防火墙规则。svnserve 默认监听所有网卡,如果只监听本地,检查启动命令里有没有多写--listen-host。
6. 开机自启与长期维护:把 svn.sh 换成 systemd 单元
用rc.local加一个svn.sh脚本是最原始的做法,能跑,但有几个现实问题:rc.local在新版系统上默认没有执行权限、脚本里如果依赖 PATH 会因为环境没加载而失败、服务挂了没有自动拉起。麒麟上更稳的做法是写一个 systemd 单元,把启动路径写死成绝对路径,彻底绕开环境变量的问题。
先确认svnserve支持前台运行,1.8 版本带--foreground参数:
/usr/local/svn/bin/svnserve --help | grep foreground有输出就按下面的单元文件写,Type=simple配合前台运行,systemd 能直接托管进程,停止、重启、看状态都规范:
# /etc/systemd/system/svnserve.service [Unit] Description=Subversion Serve After=network.target [Service] Type=simple # 绝对路径,不依赖 /etc/profile 里的 PATH ExecStart=/usr/local/svn/bin/svnserve --foreground -r /home/svn Restart=on-failure RestartSec=3 [Install] WantedBy=multi-user.targetAfter=network.target保证网络就绪后再起服务;--foreground让 svnserve 不 fork,进程直接由 systemd 管;Restart=on-failure是rc.local方案给不了的,进程异常退出 3 秒后自动拉起;WantedBy=multi-user.target决定systemctl enable时的启动级别。如果你的版本不支持--foreground,退回-d并把Type改成forking,同时用ExecStop=/usr/bin/pkill -f svnserve收尾,但不如前台方案干净。
启用和验证一条龙:
systemctl daemon-reload systemctl enable --now svnserve systemctl status svnserve --no-pager # 重启机器后再验一次,这一步别省 rebootdaemon-reload在改完单元文件后必须执行,否则 systemd 读的还是旧配置;enable --now同时做开机自启和立即启动;status看Active: active (running)才算成功。重启验证是唯一能证明自启真的生效的手段,我在这个环节翻过车:单元文件里的路径写成了相对路径,手动systemctl start一切正常,重启后直接失败,journalctl -u svnserve里只有一行No such file or directory。
最后给一个日常巡检的习惯:每次改完svnserve.conf、passwd、authz,先svnadmin verify /home/svn/project确认版本库本身没坏,再systemctl restart svnserve,最后用svn list走一遍认证。三步都过再通知同事使用,比等别人报「提交不了」再回头查要省事得多。从那以后我每次动配置都强制走这三步,再没出现过改完忘了重启、同事对着旧配置排查半天的情况。希望帮到你。
本文还有配套的精品资源,点击获取