本文涉及rsync、stunnel、cron这3个软件。rsync实现同步,stunnel加密rsync传输的数据,cron让同步自动化。
先前已实现了MySQL数据库的自动同步,现在要实现的是网站文件同步。这样一来,既是对网站数据的自动备份(谁让我太懒),同时也能在主服务器需要维护时临时切换服务器,尽量减少服务中断时间,甚至将来可能实现负载均衡及服务器自动切换,让网站不间断运行。
目录
1 操作系统及软件版本
- 操作系统
- 主从服务器都是Debian 7。
- 软件版本
- rsync 3.0.9,stunnel 4.53,vixie cron 3.0。
2 设置stunnel
stunnel可以建立SSL通道,让不支持SSL的程序通过这通道通信,来保证通信的安全。
通过下面命令安装stunnel:
# apt-get install stunnel4
安装后,编辑/etc/default/stunnel4,把“ENABLED”的值改为“1”,允许stunnel开机自动启动。
2.1 生成SSL证书与密钥
证书及密钥的生成参考为MySQL生成SSL证书和密钥。在/etc/stunnel中创建certs目录,把生成后的密钥置于其中。确认certs目录及密钥的所有者和用户组都为root,然后设置certs目录权限为700,密钥文件的权限为400。
2.2 主服务器设置
我的目的是让rsync通过stunnel传输数据,主服务器定义为运行rsync服务(使用rsync --daemon启动rsync)的主机。
在/etc/stunnel新建“stunnel.conf”文件,包含以下内容:
# Provide the full path to your certificate & key file CAfile = /etc/stunnel/certs/cacert.pem cert = /etc/stunnel/certs/server-cert.pem key = /etc/stunnel/certs/server-key.pem # Verify peer certificate if present. verify = 2 # lock the process into a chroot jail chroot = /var/run/stunnel4/ # and create the PID file in this jail pid = /stunnel4.pid # change the UID and GID of the process for security reasons setuid = stunnel4 setgid = stunnel4 # Disable support for insecure SSLv2 protocol options = NO_SSLv2 # for testing purpose #debug = 7 #foreground = yes [rsync] accept = 273 connect = 127.0.0.1:873
- CAfile = /etc/stunnel/certs/cacert.pem
- cert = /etc/stunnel/certs/server-cert.pem
- key = /etc/stunnel/certs/server-key.pem
- 证书文件的路径。CAfile,CA证书;cert,服务端证书;key,服务端证书的私钥。
- verify = 2
- 使用CA证书验证客户端证书。此选项仅用于访问控制(access control),不用于授权(authorization)。
- chroot = /var/run/stunnel4/
- 把stunnel进程的根目录限定于/var/run/stunnel4/。/var/run/stunnel4为stunnel安装过程中系统自动创建,目录所有者和用户组都是stunnel4。
- pid = /stunnel4.pid
- pid文件的路径,stunnel进程必须对其所在目录具有写权限。此路径相对于chroot路径,上面设置的意思是stunnel4.pid文件位于/var/run/stunnel4。
- setuid = stunnel4
- setgid = stunnel4
- 设置进程的uid和gid。用户stunnel4和用户组stunnel4为stunnel安装过程中系统自动创建,若这里要改为其他值,必须相应修改chroot目录的权限,否则会因为无法创建pid文件而启动不了stunnel服务。
- options = NO_SSLv2
- 禁用不安全的SSLv2协议。
- #debug = 7
#foreground = yes - 让stunnel进程前台运行,并显示调试信息。仅用于测试及问题排除,正常使用时这两个选项应删除或注释掉。
- [rsync]
- 表示开始了一个服务的定义。“rsync”是服务名,可随意设置。
- accept = 273
- 接受某地址的连接,若未指定,默认接受所有IPv4地址的连接。这里设置为监听273端口的传入连接。
- connect = 127.0.0.1:873
- 连接到一个远端主机;若未指定,默认连接localhost。与上面accept选项一起,表示把273端口接收到的数据转发到本机873端口,即rsync服务的默认端口。
启动stunnel服务:
# service stunnel4 start
2.3 从服务器设置
从服务器每小时执行一次rsync命令,从主服务器同步数据。
在/etc/stunnel新建“stunnel.conf”文件,包含以下内容:
# Provide the full path to your certificate & key pair file CAfile = /etc/stunnel/certs/cacert.pem cert = /etc/stunnel/certs/client-cert.pem key = /etc/stunnel/certs/client-key.pem # Verify peer certificate verify = 2 # lock the process into a chroot jail chroot = /var/run/stunnel4/ # and create the PID file in this jail pid = /stunnel4.pid # change the UID and GID of the process for security reasons setuid = stunnel4 setgid = stunnel4 # Disable support for insecure SSLv2 protocol options = NO_SSLv2 # enable client mode client = yes # for testing purpose #debug = 7 #foreground = yes [rsync] accept = 127.0.0.1:873 connect = caibaoz.com:273
- CAfile = /etc/stunnel/certs/cacert.pem
cert = /etc/stunnel/certs/client-cert.pem
key = /etc/stunnel/certs/client-key.pem - CAfile是与服务器相同的CA证书,cert和key是客户端证书及其私钥。
- client = yes
- 启用客户端模式。
- accept = 127.0.0.1:873
connect = caibaoz.com:273 - 这两个选项一起,表示把本机873端口接收的数据,转发到caibaoz.com的273端口。
启动stunnel服务:
# service stunnel4 start
3 设置rsync
rsync是一个快速、多用途的文件传输软件。它使用“rsync算法”,只传输文件不同的部分,可大大加快传输速度。使用它可更新整个目录树或者整个文件系统,还可选择性保留符号链接、硬链接、文件所有者、文件权限、设备文件,以及文件的时间信息。
通过下面命令安装rsync:
# apt-get install rsync
3.1 主服务器设置
编辑/etc/default/rsync。把“RSYNC_ENABLE”的值改为“true”,表示启用rsync服务(rsync daemon);把 “RSYNC_OPTS”的值改为“'-v'”,-v参数让rsync在日志中记录更多信息;把“RSYNC_NICE”的值改为“'10'”,让 rsync服务不那么占用资源。
设置logrotate,让日志文件不会无限增大。rsync软件包提供有现成文件,直接拷贝过来即可:
# cp /usr/share/doc/rsync/examples/logrotate.conf.rsync /etc/logrotate.d/rsync
把rsync的配置文件示例拷贝到/etc:
# cp /usr/share/doc/rsync/examples/rsyncd.conf /etc
编辑/etc/rsyncd.conf,按照下面内容对其进行修改(只对我用到的选项进行解释):
# sample rsyncd.conf configuration file # GLOBAL OPTIONS #motd file=/etc/motd log file=/var/log/rsyncd.log # for pid file, do not use /var/run/rsync.pid if # you are going to run rsync out of the init.d script. # The init.d script does its own pid file handling, # so omit the "pid file" line completely in that case. pid file=/var/run/rsyncd.pid #syslog facility=daemon #socket options= # MODULE OPTIONS [mysite] #comment = public archive path = /var/www/mysite use chroot = yes max connections=5 lock file = /var/lock/rsyncd.lock # the default for read only is yes... read only = yes list = no uid = nobody gid = nogroup # exclude = # exclude from = # include = # include from = auth users = username secrets file = /etc/rsyncd.secrets strict modes = yes hosts allow = localhost # hosts deny = ignore errors = no ignore nonreadable = yes transfer logging = yes # log format = %t: host %h (%a) %o %f (%l bytes). Total %b bytes. timeout = 200 refuse options = checksum dry-run dont compress = *.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz *.rar *.xz *.7z *.ace *.avi *.gpg *.jpeg *.jpg *.lz *.lzma *.lzo *.mov *.mp3 *.mp4 *.ogg *.png *.rzip *.tlz *.txz
- log file=/var/log/rsyncd.log
- 日志文件路径。一定要是这个位置,先前logrotate的设置才有效果。
- pid file=/var/run/rsyncd.pid
- pid文件路径。
- [mysite]
- 表示一个模块的开始。mysite是模块名,可随意指定。每个模块代表一个用来同步的目录,
rsync somehost.mydomain.com::
可显示rsync服务端允许列出的所有模块。 - path = /var/www/mysite
- 这个模块所对应的目录。
- use chroot = yes
- 给客户端传输文件前,把根目录限定在“path”(指代上面path选项的值)中,以增强安全性。此选项要求以超级用户权限运行rsync服务。使用此选项后,会无法跟随(follow)指向绝对路径的,和目标在“path”之外的符号链接。
- max connections=5
- 最大连接数。我理解为允许同时连接的客户端数量。
- lock file = /var/lock/rsyncd.lock
- 这个文件用来支持max connection选项,rsync服务使用这个文件中的记录确保共享此文件的模块连接数未超限。
- read only = yes
- yes表示不允许客户端上传文件。
- list = no
- no表示不允许客户端列出此模块(可参考上面“[mysite]”的说明)。
- uid = nobody
gid = nogroup - 当使用root用户运行rsync服务时,uid指定传输文件时使用的用户,gid指定传输文件时使用的用户组。uid和gid一起使用,决定了何种文件权限是可用的。(假设[mysite]模块中有一个文件“afile”,其所有者和用户组都是question,文件权限是644,我的理解是就算read only选项的值是no,客户端照样不能修改afile。未实际测试,暂不知这样理解是否有误。)
- auth users = username
- 允许访问此模块的用户。可随意指定,不必是系统中的实际用户。如果要指定多个用户,用英文逗号(,)或空格隔开即可。
- secrets file = /etc/rsyncd.secrets
- 包含auth users选项的用户名以及对应此用户的密码。格式是“用户名:密码”,一行一个用户名密码对。下面是一个简单的示例:
username:password user2:123456
- strict modes = yes
- 假设使用root用户运行rsync服务(用其他用户也一样),设置为yes则必须把secrets file的权限改为除root外其他用户不能访问,否则rsync服务会拒绝连接。
- hosts allow = localhost
- 允许连接的客户端。可以是IP地址或主机名。由于我们打算让rsync通过stunnel通信,这里设置为只允许本地主机连接。
- ignore errors = no
- 这一选项告诉rsync服务,在执行文件传输的删除操作时是否忽略I/O错误。通常情况下,如果遇到I/O错误,rsync会跳过--delete阶段。假如系统是由于临时性资源短缺或其他I/O错误导致文件无法访问,这可以阻止大量文件惨遭删除。设置为no则不忽略I/O错误。
- ignore nonreadable = yes
- 告诉rsync服务完全忽略用户无法读取的文件。(需结合uid和gid选项进一步理解。)
- transfer logging = yes
- 是否在日志中记录传输了哪些文件。yes记录,no不记录。
- timeout = 200
- 如果200秒没有活动则断开连接。
- refuse options = checksum dry-run
- 设置要拒绝哪些客户端选项。默认情况下,rsync对比服务端和客户端文件的大小和修改日期来决定一个文件是否需要传输,checksum让rsync对比文件的大小和校验和(rsync 3.0.0及以后版本使用MD5值进行对比)决定一个文件是否需要传输。需要注意的是rsync总是会生成一个全部文件(whole-file)的校验和,客户端在文件传输结束后会对比这个校验和来检验文件是否被正确地重建。dry-run用来试运行rsync命令,一般结合-v(--verbose) 和-i(--itemize-changes)参数,在传输前查看rsync命令要做什么,但不真的传输数据。这两个选项是示例配置文件默认的,我保持它 不变。
- dont compress = *.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz *.rar *.xz *.7z *.ace *.avi *.gpg *.jpeg *.jpg *.lz *.lzma *.lzo *.mov *.mp3 *.mp4 *.ogg *.png *.rzip *.tlz *.txz
- 当服务端向客户端传输数据时,不压缩这些后缀名的文件。此为rsync 3.1.0的默认列表,我借来使用。
启动rsync服务:
# service rsync start
3.2 从服务器设置
单就rsync来说,客户端不需要设置,只需要决定使用rsync的哪些命令行选项,以及以哪个用户的身份运行rsync命令。具体的设置在4.3 设置rsync每小时同步一次,这里只对rsync命令进行说明。
因为是同步网站文件,我选择让www-data用户运行rsync命令,每一个小时从主服务器同步一次。使用的rsync命令如下:
rsync -rlptvz --delete --password-file /etc/rsync.password username@localhost::mysite /var/www/mysite
- -r
- 或--recursive,递归拷贝目录。也就是拷贝所有目录下的所有文件。
- -l
- 或--links,拷贝符号链接本身。也就是说如果遇到符号连接,在目标主机重建此符号连接。
- -p
- 或--perm,让rsync的接收端按照源文件的权限设置目标文件的权限。
- -t
- 或--times,在传输过程中保留文件的修改时间,并把目标文件的修改时间改为与源文件相同。
- -v
- 或--verbose,让程序在文件传输过程中提供更多信息。
- -z
- 或--compress,对传往目标主机的文件数据进行压缩,以减少传输的数据量。
- --delete
- 删掉接收端主机(相比发送端主机)上多出来的文件。只针对被同步的目录(你必须让rsync发送整个目录,例如“dir”或“dir/”,而不是使用通配符表示的目录中的内容,例如“dir/*”。因为通配符会被shell扩展,其结果是rsync实际上被要求传输单个文件,而不是文件的父目 录。)执行此操作。排除在传输之外的数据不会被删除。
- --password-file
- 允许以文件的形式提供访问rsync服务端所需要的密码。文件应该在第一行只包含密码(不需要用户名,且其他行会被忽略)。如果文件对所有用户可读,或者以root用户运行rsync但密码文件所有者不是root,rsync会报错并自动退出。
- username@localhost::mysite
- 因为通过stunnel传输数据,所以要连接的远端主机是本机,“localhost”。
3.3 rsync命令简介
命令格式:
本地: rsync [选项...] 源... [目标] 通过远程shell(如ssh)访问: 取文件: rsync [选项...] [用户@]主机:源... [目标] 推文件: rsync [选项...] 源... [用户@]主机:目标 通过rsync daemon访问: 取文件: rsync [选项...] [用户@]主机::源... [目标] rsync [选项...] rsync://[用户@]主机[:PORT]/源... [目标] 推文件: rsync [选项...] 源... [用户@]主机::目标 rsync [选项...] 源... rsync://[用户@]主机[:PORT]/目标
如果只提供了“源”没提供“目标”,则列出所有将拷贝的源文件。
rsync有两种工作方式:
- 使用远程shell(如ssh、rsh等)传输数据。在这一工作方式下,默认使用ssh传输数据,且远端主机文件传输的默认目录是ssh用户的家目录。
- 通过rsync daemon传输数据。远端主机运行rsync daemon(用rsync --daemon命令运行rsync,默认配置文件路径是/etc/rsyncd.conf。),传输过程中数据未加密。
也可以通过远程shell,让远端主机使用一次性的rsync daemon传输数据,从而可以利用rsync daemon的部分特性。方法是用“-e(或--rsh)”参数指明要使用的远程shell,并在远端主机的主机名或IP地址后使用两个冒号。注意当通过rsync daemon访问时,上面命令格式中的“用户”指rsync的用户,而不是远程shell的用户,远程shell的用户应在“-e”参数中指定。还要注意/etc/rsyncd.conf里面的pid file必须对ssh的用户可写,否则无法成功连接。我们把 3.2中的命令修改一下做为示例:
rsync -rlptvz --delete -e "ssh -l ssh用户" --password-file /etc/rsync.password username@localhost::mysite /var/www/mysite
还有一个需要注意的地方。
rsync -avz foo:src/bar /data/tmp
这一命令会把远端主机上bar目录中的内容拷贝到本地的/data/tmp/bar目录中。(参数-a等价于-rlptgoD,表示递归拷贝,原样拷贝符号链接,保留文件的权限、修改时间、用户组、所有者信息,拷贝设备文件和特殊文件。)
rsync -avz foo:src/bar/ /data/tmp
这一命令会把远端主机上bar目录中的内容拷贝到本地的/data/tmp目录中。也就是说,下面两个命令是等价的:
rsync -av /src/foo /dest rsync -av /src/foo/ /dest/foo
源目录后如果没有斜杠(/),代表“拷贝这个目录”;源目录后加上斜杠,代表“拷贝这个目录中的文件”。
不过,如果拷贝远端主机默认目录中的文件,或者拷贝rsync模块中的文件,不需要斜杠。下面两个命令都是把远端主机目录中的内容拷贝到“/dest”:
rsync -av host: /dest rsync -av host::module /dest
4 设置cron
cron是Linux中的计划任务执行程序,最小时间粒度是1分钟。可按分钟、天、周、月、年的频率执行计划任务。
4.1 cron简介
当进入多用户runlevel时,cron会自动从/etc/init.d启动。cron的配置文件被称为“crontab”。
cron会在spool区域(/var/spool/cron/crontabs)读取每个用户自己的crontab文件(以/etc/passwd中的用户名命名)。注意应使用crontab命令访问并更新这个目录中的文件,而不是直接访问和修改。
cron还会读取/etc/crontab文件,这是cron的系统配置文件,其格式与spool区域的crontab稍有差别。在Debian中,/etc/crontab被预先设置为运行/etc/cron.hourly,/etc/cron.daily,/etc/cron.weekly和/etc/cron.monthly中的程序,这是Debian特有的设置(详见man cron
)。
此外,Debian中的cron还会读取/etc/cron.d目录中的文件,cron以对待/etc/crontab的方式对待其中的文件,且其中文件的格式与/etc/crontab相同。然而,/etc/cron.d中的文件独立于/etc/crontab,它们不会继承在/etc/crontab中设置的环境变量。这也是Debian特有的设置(详见man cron
)。
cron会每分钟“苏醒”一次,检查所有crontab中的命令,看是否需要执行。cron也会每分钟检查一次spool目录和/etc/crontab的修改时间,并重新加载修改后的配置文件,所以修改crontab后,不需要重启cron服务重新加载配置。cron也会检查/etc/cron.d目录的变化。通常情况下,系统管理员应使用/etc/crontab而不是/etc/cron.d。
4.2 crontab文件格式
crontab告诉cron daemon“在这一天的这一时间运行这一命令”。每个用户有自己的crontab,crontab中的命令会以其拥有者的身份执行。
crontab中的每一行是一个命令。每行以5个时间和日期区域开始,后面跟着一条要执行的命令,最后是一个换行符('\n')。系统crontab(/etc/crontab)使用相同的格式,区别是在时间区域之后,在命令之前需要给出执行此命令的用户名。区域之间可使用Tab键或空格键分隔开。
当分钟、小时和月区域匹配当前时间,以及至少两个日区域(月的某日,周的某日)中的一个匹配当前时间时,cron会执行命令。下面是时间和日期区域:
区域 | 允许的值 |
---|---|
minute(分钟) | 0-59 |
hour(小时) | 0-23 |
day of month(月的某日) | 1-31 |
month(月) | 1-12(或使用月份名) |
day of week(周的某日) | 0-7(0或7是Sun,或使用周名) |
星号(*)表示所有允许的值(first-last)。数字加连字符(-)表示取值范围,例如小时区域的8-11表示要在8、9、10、11点执行命令。可以给出英文逗号分隔的取值列表,例如“1,2,5,9”,“0-4,8-12”。
可以配合取值范围使用“步进”的值。格式是取值范围后面跟着一个“/<数字>”,表示每隔这一取值范围中每隔多少数字取一次值。例如 “0-23/2”可以用在小时区域,表示每隔两小时执行一次命令(等价于“0,2,4,6,8,10,12,14,16,18,20,22”)。也可以用 星号配合步进,例如“*/2”代表每隔两小时。
月和周的某日可以用名称代替。使用月份或周名称的前三个字母(大小写无所谓)。在这种情况下不允许使用取值范围及取值列表。
如果既指定了月的某日,又指定了周的某日(不是*,必须是具体的值)。当前时间不管与它俩哪一个相匹配,都会执行命令。例如“30 4 1,15 * 5”的结果是命令既会在每月1号及15号的4:30 am执行,也会在每个星期五执行。
crontab不支持任何形式的命令换行(把一个命令写成两行或更多行,例如shell的反斜杠“\”),每一行的命令区域必须是一个完整的命令。 命令以换行符或%结尾,%会被转换为换行符。第一个%后如果有数据,会作为标准输入发送给这条命令。如果要输入%,需用反斜杠进行转义。
还可以使用下面的特殊字符串代替前面5个时间和日期区域:
字符串 | 含义 |
---|---|
@reboot | 执行一次,在系统启动时。 |
@yearly | 每年执行一次,“0 0 1 1 *”。 |
@annually | (与@yearly相同) |
@monthly | 每月执行一次,“0 0 1 * *”。 |
@weekly | 每周执行一次,“0 0 * * 0”。 |
@daily | 每天执行一次,“0 0 * * *”。 |
@midnight | (与@daily相同) |
@hourly | 每小时执行一次,“0 * * * *”。 |
要注意的是,如果使用了@reboot,命令的执行时间是在启动cron daemon的时候。每台机器的启动顺序都可能会有差别,因此命令有可能在某些系统daemon(或其他设施)启动前执行。
4.3 设置rsync每小时同步一次
以root用户(你当然不会允许www-data登录系统)执行下面命令,打开www-data的crontab:
# crontab -u www-data -e
- -u
- 指出这是哪个用户的crontab。若没有使用此参数,默认打开当前用户的crontab。
- -e
- 表示要编辑这个crontab。
此外还有-l参数,列出当前crontab的内容;-r参数,移除当前crontab。
在打开的文件底部加上这样一行并保存:
0 * * * * rsync -rlptvz --delete --password-file /etc/rsync.password username@localhost::mysite /var/www/mysite
5 防火墙
需要在主服务器的防火墙中打开stunnel端口,本例是273端口。rsync的873端口不需要打开,这样可以阻止未经加密的rsync传输。
6 参考文档
6.1 rsync tutorial。
6.2 rsync and stunnel。
6.3 Secure Communication with Stunnel。
6.5 man rsync
,man rsyncd.conf
,man stunnel4
,man cron
,man crontab
,man 5 crontab
。
Visits: 833