通过Apache Write-through proxy实现SVN Master-Slave主从架构


参考资料:
http://svnbook.red-bean.com/en/1.5/svn.serverconfig.httpd.html#svn.serverconfig.httpd.extra.writethruproxy

背景/环境:
目前有一台SVN服务器供全公司使用,但由于分公司数量的增加,且处于不同的地理区域的网络中,造成了以下问题:
1.部分分公司的网络状况不佳,每次更新和提交代码的时候都非常慢;
2.SVN服务器的压力越来越大;

同时,很多分公司的领导都提出,希望能在本地建立一台总部SVN服务器的镜像,提升访问速度;

方案/设计:
通过Apache的Write-through proxy使SVN服务器实现与MySQL Master-Slave类似的一主多从的架构。
如下图所示:

安装与配置:

1.安装配置Apache与Subversion - Master&Slave
注:以下操作需要在Master与Slave上进行

首先,需要安装和集成Apache与Subversion,具体的步骤可以参考我的这篇文章:
http://heylinux.com/archives/917.html
其中 第1段 和 第3段 分别讲述了Apache与Subversion的安装与配置方法。

2.检查Apache是否加载proxy与proxy_http模块 - Master&Slave
注:以下操作需要在Master与Slave上进行

# grep proxy /opt/apache2/conf/httpd.conf
如果出现以下内容,则表明加载成功:
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

如果没有出现,则需要通过以下方式添加:
进入Apache源码包的modules目录
# cd httpd-2.2.18/modules
# /opt/apache2/bin/apxs -c -i -a proxy/mod_proxy.c proxy/proxy_util.c
# /opt/apache2/bin/apxs -c -i -a proxy/mod_proxy_http.c

然后重启Apache使模块生效
# /opt/apache2/bin/apachectl restart

3.配置读写分离 - Slave
注:以下操作仅需要在Slave上进行

进入仓库的主目录
# cd /data/svn_repo

删除在第1步中创建的仓库
# rm -rf project1

创建新的空白仓库
# /opt/subversion/bin/svnadmin create project1

更改仓库目录的属主
# chown -R apache:apache project1/

创建pre-revprop-change钩子文件
# cd project1/hooks/
# cp -p pre-revprop-change.tmpl pre-revprop-change
# chmod +x pre-revprop-change

# vim pre-revprop-change
删除以下部分内容:

if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi

echo "Changing revision properties other than svn:log is prohibited" >&2
exit 1

更改Apache配置文件
# vim /opt/apache2/conf/extra/httpd-svn.conf

<Location /svn>
DAV svn
SVNListParentPath On
SVNParentPath /data/svn_repo
SVNMasterURI http://master.heylinux.com/svn  ##将master.heylinux.com替换为Master服务器的IP地址或域名
AuthType Basic
AuthName "Subversion Repository"
AuthUserFile /opt/subversion/conf/svn_passwdfile
AuthzSVNAccessFile /opt/subversion/conf/svn_accessfile
Require valid-user
</Location>

<Location /svn-proxy-sync>
DAV svn
SVNListParentPath On
SVNParentPath /data/svn_repo
Order deny,allow
Deny from all
Allow from 192.168.203.133  ##将192.168.203.133替换为Master服务器的IP地址
</Location>

配置详解:
首先,在>Location /svn>中加入 SVNMasterURI http://master.heylinux.com/svn 指定主SVN服务器的URL;
然后,创建一个新的>Location /svn-proxy-sync>,取消认证功能,并加入地址验证,仅允许来自Master的请求;这样,Master就可以通过 http://slave.heylinux.com/svn-proxy-sync/project1将代码同步到Slave中去,避免了直接提交到http://slave.heylinux.com/svn/project1后会产生死循环的问题;
虽然,我们平时使用的是Slave - http://slave.heylinux.com/svn/project1,但在commit的时候,代码会通过代理自动提交到Master - http://master.heylinux.com/svn/project1,然后再由Master同步到Slave中;
也就是说,我们在commit的时候,速度依然会受到与总公司Master服务器之间网络的影响,但是本着80%与20%的读写比例原理,大多数时间下,我们都是以update为主,而在执行Update动作的时候,我们的请求只会在Slave上,因此速度会很快。

4.配置镜像同步 - Master
注:以下操作仅需要在Master上进行

进入仓库的主目录
# cd /data/svn_repo

创建pre-revprop-change钩子文件
# cd project1/hooks/
# cp -p pre-revprop-change.tmpl pre-revprop-change
# chmod +x pre-revprop-change

# vim pre-revprop-change
删除以下部分内容:

if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi

echo "Changing revision properties other than svn:log is prohibited" >&2
exit 1

初始化镜像
# /opt/subversion/bin/svnsync init http://slave.heylinux.com/svn-proxy-sync/project1 file:///data/svn_repo/project1
执行后显示以下信息则表明成功:
Copied properties for revision 0.

注意:如果是绝对路径,file://后面还要加一个斜杠/,因此总共是三个斜杠,否则会出现以下错误:
svnsync: Unable to open an ra_local session to URL
svnsync: Local URL 'file://data/svn_repo/project1' contains unsupported hostname

进行初次同步
# /opt/subversion/bin/svnsync sync http://slave.heylinux.com/svn-proxy-sync/project1
执行后显示以下信息则表明成功:
Transmitting file data .........
Committed revision 1.

创建 post-commit 钩子文件
# cd /data/svn_repo/project1/hooks
# vim post-commit

#!/bin/sh
# Post-commit script to replicate newly committed revision to slaves
/opt/subversion/bin/svnsync sync http://slave.heylinux.com/svn-proxy-sync/project1 > /dev/null 2>&1

更新 pre-revprop-change 钩子文件
# vim pre-revprop-change

#!/bin/sh
# Post-revprop-change script to replicate revprop-changes to slaves

REV=${2}
/opt/subversion/bin/svnsync copy-revprops http://slave.heylinux.com/svn-proxy-sync/project1 ${REV} > /dev/null 2>&1

更改钩子文件的属主与权限
# chmod +x post-commit pre-revprop-change
# chown apache:apache post-commit pre-revprop-change

5.测试
配置完成,接下来便可以通过以下步骤检查是否搭建成功。
a. 从Slave上checkout - 从Master上checkout - 向Slave上commit - 到Master上update
b. 向Master上commit - 从Slave上udpate

如果向Slave提交的代码可以从Master上update下来,则表明Write-through proxy代理配置成功;
如果向Master提交的代码可以从Slave上update下来,则表明镜像同步配置成功;

6.其它
该文章中仅配置了一个Slave,如果在实际情况中需要配置多个Slave的话,只需要重复以上步骤并将Slave全部添加到Master的post-commit和pre-revprop-change钩子文件中即可。

,

  1. #1 by mcsrainbow on 2012/03/14 - 18:12

    加个小笔记,对SVN进行hotcopy方式的完整备份方法:
    /opt/subversion/bin/svnadmin hotcopy /data/svn_repo/project1 /data/backups/project1
    这样备份出来的/data/backups/project1可以在出现问题时直接使用;
    而svnadmin dump则会耗费大量的时间和精力,即使使用增量备份的参数,所备份出来的文件也和完整备份的大小相差很小,非常不推荐。

  2. #2 by shirphy on 2012/03/19 - 15:13

    2边的svn用户配置文件是否必须一样?master和slave的用户列表如果不同,那么在slave上checkout、commit应该会有问题吧

    • #3 by mcsrainbow on 2012/03/19 - 18:15

      尽量保持完全一样,我的建议是可以通过rsync,让每台从服务器从主服务器上同步过来。当然如果变更不频繁的话,也可以手动处理。

  3. #4 by shirphy on 2012/03/22 - 16:35

    我在windows上和域集成,现在碰到一个问题是:不能识别用户,在触发器中对用户进行判断,实际执行时发现user为空,导致后续的操作都不能进行。
    和mudole有关吗?

    • #5 by mcsrainbow on 2012/03/23 - 11:19

      我对Windows域集成这方面不熟悉,恐怕帮不了你了。

  4. #6 by shirphy on 2012/03/23 - 19:59

    通常slave和master服务器在2个不同的地方,那么这2个地方的用户是否必须是相同的?比如在slave所在节点有个用户usera,那么这个用户在master节点上也必须存在?否则usera在slave服务器上commit时,写代理要写入master服务器上,他的这个用户必须经过master服务器上的鉴权才行吧?如果是这样,那么我在master和slave服务器上分别集成了2个不同的域,在commit时如果存在用户名不能被识别,那就commit不了。您这个环境下的用户如何管理?会保持master和slave的用户信息保持同步吗?

    • #7 by mcsrainbow on 2012/03/25 - 19:38

      用户和权限文件同步这个是必须的。

  5. #8 by shirphy on 2012/03/26 - 10:59

    OK ,谢谢
    我在测试环境下用同一个域能配置,还有一种可能的方案是使用混合认证,同时用域和其他用户,在slave server上使用非域用户,同时和master上的非域用户认证文件保持同步,不过我没有测试这种方案。

  6. #9 by Jack on 2012/04/11 - 23:39

    看到大作,很有收获,谢谢!
    请教一个问题,能否帮忙回答一下?
    我目前创建了master-slave的svn,主库的数据也可以通过post-commit传到备库。可是我发现,如果在备库working copy执行lock命令时,lock的是备库,而主库并没有被lock。此时主库的数据就无法svnsync到备库了(检测到备库与主库不一致,多了一个lock)。只有当手动删除备库的lock后才能解决。
    请问大侠关于这个问题有好的解决方法吗?能否使在备库working copy执行lock时,直接lock主库,而不是备库吗?

    • #10 by mcsrainbow on 2012/04/12 - 14:52

      抱歉,我之前没有遇到过这个问题,在国外的一些主流论坛上也未能找到相应的解决办法。

  7. #11 by Jack on 2012/04/20 - 23:02

    我去svn社区看了一下,1.61解决了这个bug。
    但是现在还有一个bug,就是主库的svn lock不能同步至备库(手动hook没用,token会不一致)。这个bug挂了两年了,还没解决。

(will not be published)
*