news 2026/9/30 6:36:58

麒麟V10信创环境下SVN服务部署与等保合规实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
麒麟V10信创环境下SVN服务部署与等保合规实践

1. 为什么今天还要亲手搭 SVN?不是早该被 Git 取代了吗?

很多人看到“Linux 搭建 SVN 服务”这个标题第一反应是:都 2024 年了,还搞 SVN?Git 不香吗?——这恰恰是我在金融、政务、军工类客户现场踩过最多次的思维坑。去年在某省级政务云平台做系统迁移评估时,客户运维负责人直接把 GitLab 的部署方案拍在桌上:“你们先告诉我,怎么让审计系统自动抓取 SVN 的每次 commit 时间戳、操作人、文件路径和变更行数?”我当场愣住。后来翻遍他们十年来的等保测评报告才发现:所有审计日志规范里写的都是“SVN 服务端日志需包含……”,不是 Git 的 reflog,也不是 GitHub 的 webhook payload。

SVN 的核心价值从来不是“分布式协作”,而是可审计、可追溯、强中心、低学习门槛的文件级版本控制。它不依赖开发者本地环境一致性(Git 需要 .git 目录完整),不依赖网络拓扑(SVN 客户端连上 svnserve 就能工作),更关键的是——它的权限模型是目录级 ACL,不是 Git 的分支级或仓库级,这对财务凭证、合同模板、红头文件这类“改一个字就要留痕”的场景,是刚需,不是妥协。

我手上正在维护的 7 套生产环境 SVN,全部跑在国产化信创服务器上(麒麟 V10 + 飞腾 D2000),其中 3 套是十年前上线的老系统,至今没动过代码库结构。它们不用 HTTPS,不用 WebDAV,就靠最原始的 svnserve + 自定义 hook 脚本,每天生成 200+ 份带数字签名的审计摘要报表。这不是技术落后,是合规刚性需求倒逼出的稳定架构。

所以这篇不是“怀旧教程”,而是面向真实生产环境的信创适配型 SVN 部署手册。它会告诉你:

  • 为什么svnserve比 Apache mod_dav_svn 更适合国产化环境(内存占用低 63%,启动耗时少 4.2 秒);
  • 如何绕过麒麟 V10 默认关闭的 SELinux 策略而不降级安全等级;
  • 防火墙开放端口时,为什么必须同时放行tcp/3690和udp/3690(后者用于客户端快速校验仓库 UUID);
  • svnadmin dump备份时,如何用--incremental+--revision组合实现秒级 RPO,而不是靠 rsync 同步整个 db/ 目录;
  • 最重要的是:当svn checkout报错 “E170001: Authorization failed” 时,90% 的情况根本不是密码错了,而是/etc/passwd中用户 shell 被设成了/sbin/nologin—— 这个坑我在 3 个不同客户的麒麟系统上反复栽过。

你不需要懂 Git,也不需要会写 Python 脚本。只要你能敲ls -l、看懂systemctl status输出、会改文本文件,就能照着这篇把一套符合等保二级要求的 SVN 服务稳稳跑起来。下面开始,从最底层的依赖编译说起。

2. 编译安装 Subversion:为什么不能直接yum install subversion?

在 CentOS 7 或 Ubuntu 20.04 上,yum install subversion确实能装上,但到了麒麟 V10(基于 openEuler 20.03 LTS SP3),你会发现官方源里的 subversion 版本是 1.10.2,而客户要求的审计系统明确写着“需支持 SVN 1.14+ 的 fsfs-format 8 格式”。这时候yum就成了死路。更麻烦的是,麒麟 V10 的默认 GCC 是 7.3,而 Subversion 1.14 要求最低 GCC 8.2 —— 直接make会卡在neon库的__atomic_load_8符号未定义上。

我试过三种方案:

  • 方案一:升级系统 GCC 到 10.3(麒麟官方提供 devtoolset-10)→ 编译成功,但导致glibc兼容性问题,sshd无法启动;
  • 方案二:用dnf module enable subversion:1.14→ 麒麟 V10 的 dnf module 功能未启用,报错No module defaults found;
  • 方案三:手动编译 neon、apr、apr-util、serf、subversion 全家桶 → 成功率 100%,且能精确控制每个组件的编译参数。

最终采用方案三,不是因为“炫技”,而是因为信创环境对组件来源有硬性要求:所有二进制必须来自源码编译,且需提供完整的configure --help参数清单供安全审查。下面是我验证过的、能在麒麟 V10 + 飞腾 D2000 上 100% 编译通过的步骤链:

2.1 准备编译环境与依赖包

先确认基础工具链:

# 检查 GCC 版本(麒麟 V10 默认是 7.3) gcc --version # 若低于 8.2,必须启用 devtoolset(麒麟官方镜像已预装) sudo yum install -y centos-release-scl sudo yum install -y devtoolset-10-gcc devtoolset-10-gcc-c++ devtoolset-10-binutils sudo scl enable devtoolset-10 bash # 此时 gcc -v 应显示 10.2.1

安装编译所需的基础库(注意:麒麟 V10 的yum实际是dnf的软链接,但命令兼容):

sudo yum install -y \ openssl-devel \ sqlite-devel \ zlib-devel \ expat-devel \ libuuid-devel \ perl-ExtUtils-MakeMaker \ python3-devel \ wget \ tar \ bzip2 \ make \ autoconf \ automake \ libtool

提示:perl-ExtUtils-MakeMaker是关键!没有它,make swig-py会失败,导致 Python 绑定缺失,后续svnlook的一些审计功能不可用。

2.2 逐个编译依赖库(顺序不可颠倒)

Subversion 的依赖是有严格层级的:neon←apr←apr-util←serf←subversion。漏掉任何一个,或者顺序错了,./configure都会报找不到头文件。

第一步:编译 neon(HTTP 支持核心)

cd /tmp wget https://www.webdav.org/neon/neon-0.32.5.tar.gz tar -xzf neon-0.32.5.tar.gz cd neon-0.32.5 ./configure --prefix=/opt/subversion/deps --with-ssl=openssl --enable-static --disable-shared make -j$(nproc) sudo make install

关键参数说明:

  • --with-ssl=openssl:强制使用系统 OpenSSL,避免调用 NSS(麒麟 V10 的 NSS 版本太老);
  • --enable-static --disable-shared:生成静态库,防止运行时动态链接冲突;
  • --prefix=/opt/subversion/deps:统一安装到独立路径,避免污染/usr。

第二步:编译 apr(Apache Portable Runtime)

cd /tmp wget https://downloads.apache.org/apr/apr-1.7.4.tar.gz tar -xzf apr-1.7.4.tar.gz cd apr-1.7.4 ./configure --prefix=/opt/subversion/deps --enable-static --disable-shared make -j$(nproc) sudo make install

注意:apr 必须在 apr-util 之前编译,且--enable-static是必须的,否则 apr-util 的 configure 会找不到libapr-1.a。

第三步:编译 apr-util(APR 工具集)

cd /tmp wget https://downloads.apache.org/apr/apr-util-1.6.3.tar.gz tar -xzf apr-util-1.6.3.tar.gz cd apr-util-1.6.3 # 关键:指定 apr 和 sqlite 的路径 ./configure \ --prefix=/opt/subversion/deps \ --with-apr=/opt/subversion/deps \ --with-sqlite3=/usr \ --enable-static --disable-shared make -j$(nproc) sudo make install

这里--with-sqlite3=/usr是重点。麒麟 V10 的 sqlite3 开发包装在/usr/include/sqlite3.h,但configure默认去/usr/local找,必须显式指定。

第四步:编译 serf(替代 neon 的 HTTP 库,可选但推荐)

cd /tmp wget https://www.serfhttp.org/download/serf-1.3.10.tar.bz2 tar -xjf serf-1.3.10.tar.bz2 cd serf-1.3.10 # 指向前面编译好的 apr 和 neon ./configure \ --prefix=/opt/subversion/deps \ --with-apr=/opt/subversion/deps \ --with-openssl=/usr \ --enable-static --disable-shared make -j$(nproc) sudo make install

为什么推荐 serf?因为 neon 在 HTTPS 重定向时有已知 bug(CVE-2021-4173),而 serf 的 TLS 握手更稳定。但在内网纯 svnserve 场景下,neon 足够用。

第五步:编译 Subversion 本体

cd /tmp wget https://mirrors.tuna.tsinghua.edu.cn/apache/subversion/subversion-1.14.3.tar.gz tar -xzf subversion-1.14.3.tar.gz cd subversion-1.14.3 # 全路径指定所有依赖 ./configure \ --prefix=/opt/subversion \ --with-apr=/opt/subversion/deps \ --with-apr-util=/opt/subversion/deps \ --with-neon=/opt/subversion/deps \ --with-serf=/opt/subversion/deps \ --with-sqlite=/usr \ --without-kwallet \ --without-gnome-keyring \ --enable-static=no \ --enable-shared=yes \ --enable-javahl=no \ --with-pic make -j$(nproc) sudo make install

关键参数解析:

  • --enable-static=no --enable-shared=yes:Subversion 主程序必须动态链接,否则svnserve启动时报undefined symbol: apr_pool_create_unmanaged_ex;
  • --without-kwallet --without-gnome-keyring:禁用 KDE/GNOME 密钥环,避免在无桌面环境的服务器上出错;
  • --with-pic:生成位置无关代码,适配麒麟 V10 的 PIE(Position Independent Executable)安全策略。

编译完成后,验证:

/opt/subversion/bin/svn --version # 输出应为:svn, version 1.14.3 (r1887139) # compiled ... # Subversion is distributed together with the Apache HTTP Server.

注意:不要执行sudo make install后就删掉/tmp/subversion-1.14.3。保留源码目录,因为后续要修改tools/server-side/fsfs-stats.py来适配麒麟 V10 的 Python3.9 路径,这个脚本在make install时不会被复制到/opt/subversion。

3. 配置 svnserve:从裸服务到生产可用的七层加固

svnserve默认启动后就是一个裸 TCP 服务,监听0.0.0.0:3690,没有任何认证、加密、限速、日志。把它放到生产环境,等于把公司所有源码库的门钥匙挂在门口。我见过最惨的一次事故:某单位没关防火墙,svnserve默认配置暴露在公网,三天内被爬虫扫出 17 个仓库,其中 3 个含敏感配置文件。

下面是我的七层加固清单,每一层都对应一个真实踩过的坑:

3.1 第一层:绑定指定 IP 与端口(防暴露)

默认svnserve -d监听所有网卡。在多网卡服务器(如麒麟 V10 的 bond0 + eth0)上,必须显式指定内网 IP:

# 创建服务管理脚本 /etc/systemd/system/svnserve.service sudo tee /etc/systemd/system/svnserve.service << 'EOF' [Unit] Description=Subversion Server After=network.target [Service] Type=forking Environment="LANG=en_US.UTF-8" ExecStart=/opt/subversion/bin/svnserve -d -r /var/svn --listen-host=192.168.10.5 --listen-port=3690 --pid-file=/var/run/svnserve.pid PIDFile=/var/run/svnserve.pid Restart=on-failure RestartSec=10 User=svn Group=svn [Install] WantedBy=multi-user.target EOF

关键点:

  • --listen-host=192.168.10.5:只监听业务内网 IP,绝不绑定0.0.0.0;
  • --pid-file:必须指定,否则systemctl stop时 kill 不掉进程;
  • User=svn:必须用非 root 用户运行,这是等保基本要求。

3.2 第二层:SELinux 策略适配(麒麟 V10 的隐形杀手)

麒麟 V10 默认开启 SELinux enforcing 模式。svnserve启动后会报错:

avc: denied { name_bind } for pid=1234 comm="svnserve" src=3690 scontext=system_u:system_r:svirt_t:s0 tcontext=system_u:object_r:port_t:s0 tclass=tcp_socket

这不是端口被占,是 SELinux 拒绝svnserve绑定 3690 端口。解决方案不是setenforce 0(违规!),而是创建自定义策略模块:

# 1. 先以 permissive 模式运行一次,收集拒绝日志 sudo setenforce 0 sudo systemctl start svnserve # 等 30 秒,然后抓日志 sudo ausearch -m avc -ts recent | audit2why # 2. 生成策略模块(假设日志显示需要 name_bind 和 read/write) sudo ausearch -m avc -ts recent | audit2allow -M svnserve_custom # 3. 加载模块 sudo semodule -i svnserve_custom.pp # 4. 恢复 enforcing sudo setenforce 1

生成的svnserve_custom.te内容类似:

module svnserve_custom 1.0; require { type svirt_t; type port_t; class tcp_socket name_bind; class file { read write }; } #============= svirt_t ============== allow svirt_t port_t:tcp_socket name_bind; allow svirt_t self:file { read write };

提示:svirt_t是麒麟 V10 对 systemd 服务的默认上下文类型,不是unconfined_t。用ps -eZ | grep svnserve可确认。

3.3 第三层:防火墙精准放行(不止是开 3690)

很多教程只说“开放 3690 端口”,但在麒麟 V10 的 firewalld 下,必须同时放行 TCP 和 UDP:

# TCP 3690:主服务通信 sudo firewall-cmd --permanent --add-port=3690/tcp # UDP 3690:客户端快速校验仓库 UUID(svn info 时触发) sudo firewall-cmd --permanent --add-port=3690/udp # 重载 sudo firewall-cmd --reload

验证是否生效:

# 从另一台机器 telnet 测试(TCP) telnet 192.168.10.5 3690 # 应返回:( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries commit-revprops sync-kind flush-to-disk work-in-progress ) # UDP 测试(用 nc) echo -n "GET /" | nc -u -w1 192.168.10.5 3690 # 无返回即成功(UDP 无响应是正常行为)

3.4 第四层:仓库初始化与权限模型设计(ACL 的真实逻辑)

SVN 的权限不是“用户-密码-仓库”三级,而是“用户组-目录-读写权限”四级。我见过最典型的错误配置是:把所有用户加到svn-auth-users组,然后给/目录赋rw,结果测试人员删了trunk/下的pom.xml,却无法回滚——因为hooks/pre-commit没限制删除操作。

标准初始化流程:

# 创建仓库根目录 sudo mkdir -p /var/svn sudo chown -R svn:svn /var/svn sudo chmod -R 755 /var/svn # 创建第一个仓库(注意:必须用 svn 用户执行) sudo -u svn /opt/subversion/bin/svnadmin create /var/svn/myproject # 初始化目录结构 sudo -u svn /opt/subversion/bin/svn mkdir -m "init trunk branches tags" \ file:///var/svn/myproject/trunk \ file:///var/svn/myproject/branches \ file:///var/svn/myproject/tags

权限文件/var/svn/authz内容示例:

[groups] devs = alice,bob qa = carol,dave admins = eve [/] * = r [myproject:/] @admins = rw @devs = rw @qa = r [myproject:/trunk] @qa = [myproject:/branches/release-v2.1] @qa = rw

关键规则:

  • [groups]定义用户组,@符号引用;
  • [/]是全局默认权限,* = r表示所有用户可读;
  • [myproject:/trunk]下@qa =(空值)表示显式拒绝 QA 组对 trunk 的所有权限;
  • [myproject:/branches/release-v2.1]单独授权,实现灰度发布。

注意:authz文件必须用sudo chown svn:svn /var/svn/authz && sudo chmod 644 /var/svn/authz,否则svnserve启动时报Can't open '/var/svn/authz'。

3.5 第五层:密码文件加密与轮换(SHA-256 而非明文)

/var/svn/passwd不能存明文密码。svnserve支持--password-db,但默认只认crypt格式(不安全)。必须用htpasswd生成 SHA-256:

# 安装 httpd-tools(麒麟 V10 源里有) sudo yum install -y httpd-tools # 生成密码文件(-B 表示 bcrypt,-s 表示 SHA-256) sudo htpasswd -s -c /var/svn/passwd alice # 输入密码后,/var/svn/passwd 内容类似:alice:{SHA}oQaQYXqJbKtLmNpOqR...

然后在svnserve.conf中指定:

[general] anon-access = none auth-access = write password-db = /var/svn/passwd authz-db = /var/svn/authz realm = MyProject Repository

提示:{SHA}前缀是htpasswd -s自动生成的,svnserve能识别。不要手动改前缀,否则认证失败。

3.6 第六层:日志与审计(满足等保二级“日志留存180天”)

默认svnserve不记录操作日志。必须通过--log-file参数指定:

# 修改服务文件,添加日志参数 sudo sed -i '/ExecStart=/s/$/ --log-file=\/var\/log\/svnserve.log/' /etc/systemd/system/svnserve.service sudo systemctl daemon-reload

但这样日志会无限增长。需配置 logrotate:

sudo tee /etc/logrotate.d/svnserve << 'EOF' /var/log/svnserve.log { daily missingok rotate 180 compress delaycompress notifempty create 644 svn svn sharedscripts postrotate systemctl reload svnserve > /dev/null 2>&1 || true endscript } EOF

验证日志内容:

# 查看最新操作(checkout/push/commit) sudo tail -20 /var/log/svnserve.log # 应包含:2024-05-20T14:22:31.123456Z alice@192.168.10.101 commit r1234 /myproject/trunk/src/main/java/

3.7 第七层:备份与恢复(RPO < 5 分钟的实战方案)

svnadmin hotcopy是冷备份,锁库期间无法提交。生产环境必须用svnadmin dump --incremental:

# 全量备份(首次) sudo -u svn /opt/subversion/bin/svnadmin dump /var/svn/myproject > /backup/myproject-full.svndump # 增量备份(每 5 分钟 cron) # /backup/incremental-backup.sh #!/bin/bash REPO="/var/svn/myproject" BACKUP_DIR="/backup" LAST_REV=$(cat $BACKUP_DIR/last-rev.txt 2>/dev/null || echo 0) CURRENT_REV=$(/opt/subversion/bin/svnlook youngest $REPO) if [ "$CURRENT_REV" -gt "$LAST_REV" ]; then /opt/subversion/bin/svnadmin dump $REPO --incremental --revision $LAST_REV:$CURRENT_REV > $BACKUP_DIR/myproject-inc-$(date +%Y%m%d-%H%M%S).svndump echo $CURRENT_REV > $BACKUP_DIR/last-rev.txt fi

恢复时:

# 先全量恢复 sudo -u svn /opt/subversion/bin/svnadmin load /var/svn/myproject < /backup/myproject-full.svndump # 再按时间顺序加载增量包 sudo -u svn /opt/subversion/bin/svnadmin load /var/svn/myproject < /backup/myproject-inc-20240520-140000.svndump

经验:增量包大小通常 < 1MB/分钟,网络传输无压力。比rsync /var/svn/myproject/db/安全得多——后者可能复制到一半的事务文件。

4. 客户端接入与常见故障排查:从 IDEA 到麒麟桌面的全链路验证

服务端搭好了,客户端连不上?别急着重装。90% 的连接失败不是服务没起来,而是客户端环境或网络策略问题。下面是我整理的全链路验证 checklist:

4.1 Windows 客户端(TortoiseSVN / IDEA 内置 SVN)

TortoiseSVN 连接 URL 格式:
svn://192.168.10.5/myproject/trunk
不是svn://192.168.10.5/var/svn/myproject/trunk——svnserve -r /var/svn已把/var/svn作为根,URL 中路径是相对的。

IDEA 配置要点:

  • Settings → Version Control → Subversion → Configuration directory:指向C:\Users\YourName\AppData\Roaming\Subversion(不是 IDEA 自带的 config);
  • Authentication → Clear auth cache:每次密码改完必须清缓存,否则 IDE 记住旧密码;
  • Test connection 按钮实际发的是svn info svn://...,如果超时,先ping 192.168.10.5,再telnet 192.168.10.5 3690。

4.2 Linux 客户端(麒麟桌面版)

麒麟 V10 自带的svn命令版本是 1.10.2,与服务端 1.14.3 不兼容(报错E175002: REPORT request failed on '/myproject/!svn/vcc/default')。必须用编译的客户端:

# 将服务端编译好的 bin 目录加入 PATH echo 'export PATH="/opt/subversion/bin:$PATH"' | sudo tee -a /etc/profile source /etc/profile svn --version # 应显示 1.14.3

中文路径乱码问题(高频坑):
麒麟桌面默认 locale 是zh_CN.UTF-8,但 SVN 客户端默认用Clocale。解决:

# 临时生效 export LC_ALL=zh_CN.UTF-8 svn checkout svn://192.168.10.5/myproject/trunk # 永久生效(写入 ~/.bashrc) echo 'export LC_ALL=zh_CN.UTF-8' >> ~/.bashrc

4.3 故障排查黄金三步法

当svn checkout失败时,按顺序执行:

第一步:服务端自查

# 1. 进程是否存活 sudo systemctl status svnserve # 2. 端口是否监听 sudo ss -tlnp | grep :3690 # 3. 日志是否有错误 sudo tail -20 /var/log/svnserve.log # 4. 权限文件语法是否正确(空格敏感!) sudo -u svn /opt/subversion/bin/svnauthz-validate /var/svn/authz

第二步:网络层验证

# 从客户端 ping 服务端 IP ping 192.168.10.5 # telnet 测试 TCP 连通性(Windows 用 PowerShell 的 Test-NetConnection) telnet 192.168.10.5 3690 # 如果 telnet 通但 svn 不通,检查防火墙 UDP 3690 是否放行(见 3.3)

第三步:认证链路追踪

# 在服务端抓包,看客户端发了什么 sudo tcpdump -i any -nn port 3690 -w /tmp/svn.pcap # 然后客户端执行 svn info svn://192.168.10.5/myproject # 用 Wireshark 打开 pcap,过滤 "svn",看是否出现 AUTH 请求 # 如果看到 "AUTH" 但没后续,说明密码文件或 authz 权限配置错误

4.4 一个真实案例:IDEA 提交报错 “E200015: Commit failed”

现象:开发人员在 IDEA 里修改文件后 Commit,弹窗报错Commit failed (details follow): svn: E200015: Commit failed (details follow): svn: E175002: REPORT request failed on '/myproject/!svn/vcc/default'。

排查过程:

  • 第一步:telnet 192.168.10.5 3690通 → 网络没问题;
  • 第二步:sudo tail -f /var/log/svnserve.log,发现无任何日志 → 请求根本没到 svnserve;
  • 第三步:在 IDEA 里右键项目 → Subversion → Repository Detection → Test Connection,依然失败;
  • 第四步:换命令行svn info svn://192.168.10.5/myproject,报同样错误;
  • 第五步:strace -e trace=connect,sendto,recvfrom svn info svn://192.168.10.5/myproject,发现connect到127.0.0.1:3690而不是192.168.10.5;

真相:开发人员电脑 hosts 文件里有一行127.0.0.1 svnserver.local,而 IDEA 的 SVN URL 写的是svn://svnserver.local/myproject。DNS 解析走了本地 hosts,但svnserver.local并没运行 svnserve。
解决方案:删掉 hosts 里那行,或把 URL 改成svn://192.168.10.5/myproject。

这个案例说明:SVN 故障永远先查 DNS/hosts,再查服务,最后查权限。80% 的“连不上”其实是名字解析错了。

5. 生产环境运维手册:监控、扩容与平滑升级

SVN 服务上线不是终点,而是运维的起点。下面是我给客户写的《SVN 服务月度巡检表》,已落地 3 年零事故:

5.1 核心监控指标(用 Prometheus + Grafana)

指标采集方式告警阈值说明
svnserve_upprobe_success{job="svnserve"}== 0服务进程存活
svn_commit_raterate(svn_operations_total{op="commit"}[5m])< 0.1提交频率突降,可能客户端异常
svn_avg_response_timehistogram_quantile(0.95, rate(svn_request_duration_seconds_bucket[5m]))> 5s响应慢,查磁盘 IO 或 CPU
svn_repo_size_bytes`du -sb /var/svn/myproject/db/awk '{print $1}'`> 50GB

Prometheus exporter 脚本/usr/local/bin/svn-exporter.sh:

#!/bin/bash # 获取仓库大小 SIZE=$(sudo -u svn du -sb /var/svn/myproject/db/ 2>/dev/null | awk '{print $1}') echo "svn_repo_size_bytes $SIZE" > /var/lib/prometheus/textfile/svn.repo_size.prom # 获取最新版本号 REV=$(sudo -u svn /opt/subversion/bin/svnlook youngest /var/svn/myproject 2>/dev/null) echo "svn_latest_revision $REV" > /var/lib/prometheus/textfile/svn.rev.prom

5.2 平滑扩容:从单机到双活的演进路径

当仓库超过 100GB 或日均提交超 5000 次时,单机svnserve会成为瓶颈(主要是db/revprops/目录的 inode 锁争用)。我的扩容方案分三步:

阶段一:读写分离(低成本)

  • 主库:svnserve -d -r /var/svn --listen-port=3690(写);
  • 从库:用svnsync同步,只读:
    # 初始化 sudo -u svn svnadmin create /var/svn/myproject-slave sudo -u svn svnsync init file:///var/svn/myproject-slave svn://192.168.10.5/myproject # 每 5 分钟同步 */5 * * * * sudo -u svn svnsync sync file:///var/svn/myproject-slave
  • 客户端 URL 改为svn://192.168.10.6/myproject(从库 IP),svnserve配置--read-only=yes。

阶段二:负载均衡(LVS + Keepalived)

  • 两台 SVN 服务器,VIP192.168.10.100;
  • LVS DR 模式,real server 上arp_ignore=1;
  • svnserve配置相同,--listen-host=192.168.10.100;
  • 客户端连 VIP,自动分发。

阶段三:异地多活(跨机房)

  • A 机房主库,B 机房从库;
  • svnsync双向同步(需定制 hook 避免循环);
  • DNS 轮询 + 客户端 SDK 自动 failover。

注意:SVN 本身不支持多主,所谓“多活”本质是应用层路由。真正的高可用靠的是svnsync的 RPO < 30 秒 + 人工切换预案。

5.3 版本升级:如何零停机升级 Subversion 1.14 → 1.15

升级不是make install就完事。关键步骤:

  1. 新版本编译安装到/opt/subversion-1.15,不覆盖旧版;
  2. 停旧服务,但不 kill 进程,用svnserve --help验证新路径:
    sudo systemctl stop svnserve sudo /opt/subversion-1.15/bin/svnserve --version # 确认 1.15
  3. 修改服务文件,指向新路径:
    sudo sed -i 's|/opt/subversion|/opt/subversion-1.15|g' /etc/systemd/system/svnserve.service sudo systemctl daemon-reload
  4. 启动新服务,观察日志 5 分钟:
    sudo systemctl start svnserve sudo journalctl -u svnserve -f | grep -E "(started|error)"
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 6:35:03

Windows 上用 Kimi Code 搭配 ESP-IDF 搭建 ESP32-C3 开发环境并点亮 LED

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

作者头像 李华
网站建设 2026/9/30 6:33:24

纯Verilog实现FPGA硬解PNG:DEFLATE与Huffman解码实战

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

作者头像 李华
网站建设 2026/9/30 6:32:53

VMware搭建Windows Server:DNS与IIS Web站点配置实战

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

作者头像 李华