程序开发过程中可能会遇到运行环境和开发环境不是同一台机器的情况。本文主要介绍在过去几年我在开发过程中使用的同步工具的变更历程(血泪折腾史)。
正常Linux服务器都会开ssh服务,最简单粗暴的方法就是用各种sftp客户端,比如Filezilla,每次修改就上传修改后的文件覆盖已有文件。
优点很明显,配置方便,操作直白易懂。对于简单的项目,不需要大量修改代码,远程测试,这是最简单方便的方法。
缺点也很明显,修改->上传->运行。中间的上传过程需要先找到改过的文件,然后鼠标操作上传,等上传结束后运行,过程十分痛苦。
其实也可以直接把代码放在运行环境,然后通过sshfs挂载到本地。
如果本地环境也是Linux的话,就很简单了,从软件源安装 sshfs 包。然后直接运行:
sshfs [user@]host:[dir] mountpoint [options]
详细参数移步 sshfs --help
由于Linux系统有个比较灵活的FUSE(Filesystem in Userspace)层,mount各种远程目录和文件系统都特别容易。
Windows系统对FUSE一直没有官方的支持(据我所知)。对文件系统的抽象,个人认为是不如Linux的(仅个人观点一家之言)。很久以前用过Windows的SSHFS,具体项目名字已经不记得了,但是后面Windows升级到win10之后就没法用了。不过幸运的是还是有大佬在新系统上实现的FUSE的API。安装sshfs-win可以实现挂载SSHFS到本地系统。
SSHFS-Win is a minimal port of SSHFS to Windows. Under the hood it uses Cygwin for the POSIX environment and WinFsp for the FUSE functionality.
安装WinFsp 和 SSHFS-Win以后就可以挂载远程文件系统了。使用格式:
\\sshfs\[locuser=]user@host[!port][\path]
居然还有一个GUI的前端软件SiriKali,在Windows系统上支持Cryfs, Securefs, Encfs and Sshfs。
看起来很靠谱的样子,但是我也没用过,安装方法等移步github。
除了sshfs,也有很多其他方法挂载远程文件系统,比如NFS(Network File System),FTP(File Transfer Protocol),SMB(Server Message Block),尤其SMB是Windows原生支持的文件共享协议(Linux对以上描述的协议都良好支持)。但是问题在于这些服务需要开放新的端口,可能需要安装新的软件包,进行各种配置。然而在很多情况下,可能作为使用者的你没有root权限,没有开放端口的权限,或者机器在NAT中,而你没有上层路由的配置权限无法进行端口转发等操作(虽然也可以使用ssh tunnel等方法,但是仍然可能带来其他问题)。所以在此仅介绍SSHFS。因为基本上你能使用的机器都会给你ssh访问。
挂载远程文件系统到本地的优点很明显:
但是也有一些问题:
Linux下利用SSH来传输文件的工具很多,简单介绍SCP和rsync
scp和cp很像,区别在于scp 的 source和target中有一个是远程系统,命令格式:
scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file] [-l limit] [-o ssh_option] [-P port] [-S program] source ... target
详细解释见 man scp
功能很简单,无脑复制source到target。
rsync is a file transfer program capable of efficient remote update
via a fast differencing algorithm.
rsync的自我描述很清楚,高效的远程文件更新软件,能够高效进行差分更新。基本使用格式:
Usage: rsync [OPTION]... SRC [SRC]... DEST
or rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST
or rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST
or rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST
or rsync [OPTION]... [USER@]HOST:SRC [DEST]
or rsync [OPTION]... [USER@]HOST::SRC [DEST]
or rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]
下面是一些常用的参数:
-a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)
-r, --recursive recurse into directories
-u, --update skip files that are newer on the receiver
--progress show progress during transfer
一般情况,raync -a -u localPath remoteDomain:remotePath
就能结局大部分问题。
至此同步文件发方法已经有了很大的提高。
Rsync相比前面的几种方案,已经很好了,大部分shell支持按上键返回上一个命令,这样一次上传使用两次键盘按键,不用记住、查找改过的名字,不用断网等挂载远程系统的问题。同时可以使用
rsync source target && ssh remoteMathing command
这样的操作,将上传和在远程机器上运行两个命令绑在一起,这样可以将 上传->运行 这个操作简化到两次按键。
但是rsync同样也有缺点:
自己曾经被这个问题困扰,研究了一个简单的对Rsync的改进:使用Inotify监听文件变化,有变化就调用rsync传输。
首先介绍下主角:inotifywait
inotifywait efficiently waits for changes to files using Linux’s inotify(7) interface. It is suitable for waiting for changes to files from shell scripts. It can either exit once an event occurs, or continually execute and output events as they occur.
inotifywait用于等待文件变化,可以配置为文件变化立即结束或者持续执行,输出inotify事件。
使用格式:
notifywait [-hcmrq] [-e <event> ] [-t <seconds> ] [--format <fmt> ] [--timefmt <fmt> ] <file> [ ... ]
其中可以监听的事件及详细说明移步 man inotifywait.
然后介绍配角: watch
watch - execute a program periodically, showing output fullscreen
watch也是Linux的一个命令,可以等间隔的执行一个命令。使用格式:
watch [options] command
options请移步man watch。
所以思路是这样的:
首先执行 notifywait,等文件变化 notifywait 结束,然后执行 rsync上传。最外层用 watch 包起来,每次上传完成,等一个很小的事件(比如1s)然后开始新的监听。
大概是这样写的(年代久远,不记得细节了):
watch -n 1 notifywait -e sourcePath && rsync -a -u sourcePath remoteServer
Windows下也有 inotifywait的移植:https://github.com/thekid/inotify-win。记忆中cygwin的包里面就有。watch和rsync当然也有Windows的移植,cygwin中应该都有的。用Windows Subsystem for Linux也许也是个不错的选择。
和Rsync相比,还是有很大进步的:
但是缺点很多
但是无论如何,总算实现了 自动, 还是很开心的用了很久,直到后来发现了 lsyncd。
https://github.com/axkibe/lsyncd
---- 其实很久以前搜索的时候就发现lsyncd,奈何当时在略显复杂的配置文件面前感到了无助。
Lsyncd watches a local directory trees event monitor interface (inotify or fsevents). It aggregates and combines events for a few seconds and then spawns one (or more) process(es) to synchronize the changes. By default this is rsync. Lsyncd is thus a light-weight live mirror solution that is comparatively easy to install not requiring new filesystems or block devices and does not hamper local filesystem performance.
本质上,lsyncd就是我写的watch+inotify+rsync组合的高级版。同样监听文件变动,然后收集数秒的文件变化,用scp,rsync等工具上传。听起来很简单,难点在于,为了尽可能的增强这个框架的灵活性,他的配置文件使用 Lua 写。用Lua写没问题,很多配置都用Lua写,他的配置文件分了4层,作者把整个流程抽象成了4层,每一层都可以进行相当灵活的配置。可以用它传文件、做备份、甚至自己加命令做测试运行各种操作都没有问题。
如作者所说:
Lsyncd is designed to synchronize a slowly changing local directory tree to a remote mirror. Lsyncd is especially useful to sync data from a secure area to a not-so-secure area.
用来同步代码,应该很适合。
下面是我现在正在使用的一个配置文件。具体配置方法详见(manual)[https://axkibe.github.io/lsyncd/]
--- -
-- User configuration file for lsyncd.
--
-- a Simple example for default rsync.
--
settings {
logfile = "/tmp/lsyncd.log",
statusFile = "/tmp/lsyncd-status.log",
nodaemon = true,
insist = true,
statusInterval = 60,
maxDelays = 2
}
-- Slave server configuration
exclude_files = {
'.git', '__pycache__', '.ipynb_checkpoints',
'.vendor', 'db_storage', '.idea',
'.stfolder', '*.pyc','pic2', 'runs', 'log', 'tmp',
'node_modules'
}
sync {
default.rsync,
delete = false,
source = "/home/username/source_path",
target = "node1:target_path",
exclude = exclude_files,
delay = 1,
rsync = {
binary = "/usr/bin/rsync",
archive = true,
compress = true
}
}
sync {
default.rsync,
delete = false,
source = "/home/username/source_path",
target = "node2:target_path",
exclude = exclude_files,
delay = 1,
rsync = {
binary = "/usr/bin/rsync",
archive = true,
compress = true
}
}
最开始的settings
是所有层都包含的daemon的设置,包含出错是否退出,logfile存放地方,是否后台,更新状态时间间隔,最大延迟。。。
后面exclude_files
是不同步的文件列表,wildcard匹配,还是很好读的。
后面的 sync
是Layer4,也就是最顶层的配置,使用rsync,本地删除时不删除远程文件,然后设置source和target,排除的文件,rsync相关的信息,看起来并不复杂。好处是可以设置很多个sync
,同步到多台机器。
运行也很简单:
lsyncd your_configure_file.lua
用了lsyncd以后很久没有再换过,configuration改了又改,无非换个地址,加点排除文件之类的。不得不说,这已经是一个很优秀的同步软件了。
优点:
缺点,在这个使用环境中lsyncd几乎没有缺点,知道后来又购入一台机器,现在有两台机器可能会编辑代码,此时出现了一个新需求:在两台机器之间相互同步代码。而lsyncd设计的功能是一台机器分发到多台机器,第一想到了这些方案:
是时候换新同步软件了。现在需求比较明确:
后来搜索发现了 syncthing,像是一个合适的工具
Syncthing replaces proprietary sync and cloud services with something open, trustworthy and decentralized. Your data is your data alone and you deserve to choose where it is stored, if it is shared with some third party and how it’s transmitted over the Internet.
大概这是我喜欢开源软件一个重要的原因吧,软件的作者愿意展示软件的所有,从不尝试故意限制软件的某些功能,从不故意增加额外的需求,愿意让使用者以他喜欢的方式使用而不加任何限制。
Syncthing主要吸引我的特性:
启动很容易,直接运行 syncthing
可执行文件,或者 systemctl start
,软件源安装的话应该会给默认的systemctl
入口。
配置很简单,WebUI,打开浏览器默认 http://localhost:8384。可以添加目录和机器,目录设置里面可以选择共享的机器。此处仅需要注意这些问题:
软件默认使用22000端口进行数据交互。至少需要一台机器22000端口可访问。虽然添加机器后配置机器IP时可以使用dynamic
,支持自动uPnP穿透什么的,但是NAT里面的机器还是可能存在问题。基本我的解决方案是使用OpenVPN
,这样所有要同步的机器在一个共同的虚拟子网里面,相互访问很容易。当然可以使用ssh tunnel
,然而tunnel问题多多,比如经常掉线,尤其是国内的网络环境下。
修改文件以后在短时间内快速同步是我的刚需之一,在高级设置里面可以找到 Fs Watcher Enabled
,设置True
就可以监听文件系统改动了。目测同步速度并不比rsync慢。其实在我把界面从IDE切到命令行这一过程中完成同步就达到使用要求了。基本在1s左右完成同步就可以有良好的使用体验。
为了让实在搞不定网络问题的用户使用,syncthing用户可以自发建立relayserver,官方维护server列表,我使用中实际并不希望使用relayserver,因为他总会比内网慢,所以直接关掉了。其他诸如localdiscovery,global discovery都直接关掉了。
优点:
缺点:
现在,lsyncd和syncthing都在使用,毕竟lsyncd有他的优点,轻巧,易访问(只要ssh就行),用于同步部署服务的机器,syncthing用来做两台主力机器的同步,合理配置后,使用起来已经对sync这一操作无感了。
最后发点牢骚 人不应该将就。不断折腾的过程中,东西用得越来越舒服,开发体验稳步提升。也许看起来这是个没什么用的东西,然而人活着,有意思就好,何必事事总追求个实用意义,何必事事只想着那点钱权名。 世人都晓神仙好,惟有功名忘不了!古今将相在何方?荒冢一堆草没了。 世人都晓神仙好,只有金银忘不了!终朝只恨聚无多,及到多时眼闭了。 世人都晓神仙好,只有娇妻忘不了!君生日日说恩情,君死又随人去了。 世人都晓神仙好,只有儿孙忘不了!痴心父母古来多,孝顺儿孙谁见了? 哈哈哈,从意识到生命由自己控制到现在,上下求索,依然想不透人活着到底为什么,依然在探索和思考。有些人,活的比我长,却看似从未想过人生的终极奥义,有的人,也许这辈子也跳不出那些金框框银笼笼。高喊着梦想与使命,嘴里呼出的气却满是铜臭;高喊着一切为了你好,手里却哗哗点着卖劳动力赚的钱;人前谈笑风生,人后悄悄来句:"老师说的对吧?"。这般沉浸在名利里,认真得连满嘴胡话,厚颜无耻都不自知的人,我也是头一次见。