代码同步方法汇总

程序开发过程中可能会遇到运行环境和开发环境不是同一台机器的情况。本文主要介绍在过去几年我在开发过程中使用的同步工具的变更历程(血泪折腾史)。

SFTP客户端手动上传

正常Linux服务器都会开ssh服务,最简单粗暴的方法就是用各种sftp客户端,比如Filezilla,每次修改就上传修改后的文件覆盖已有文件。
优点很明显,配置方便,操作直白易懂。对于简单的项目,不需要大量修改代码,远程测试,这是最简单方便的方法。
缺点也很明显,修改->上传->运行。中间的上传过程需要先找到改过的文件,然后鼠标操作上传,等上传结束后运行,过程十分痛苦。
代码同步方法汇总_第1张图片

sshfs(或者其他方法)挂载远程文件系统

其实也可以直接把代码放在运行环境,然后通过sshfs挂载到本地。

Linux 系统

如果本地环境也是Linux的话,就很简单了,从软件源安装 sshfs 包。然后直接运行:

sshfs [user@]host:[dir] mountpoint [options]

详细参数移步 sshfs --help
由于Linux系统有个比较灵活的FUSE(Filesystem in Userspace)层,mount各种远程目录和文件系统都特别容易。

Windows 系统

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访问。

挂载远程文件系统到本地的优点很明显:

  • 任何修改通过文件系统底层的实现直接同步到远端,不用手动上传
  • 所见即所得,远程文件和本地文件一致

但是也有一些问题:

  • 网络情况不佳的时候不能挂载,不能继续工作
  • 部分IDE依赖Inotify (inode notify) 这样的功能来监听代码变化,以便高效及时的更新补全列表、检查代码错误、检查拼写等。而远程挂载的文件系统是没有这类特性的。
  • 性能问题,打开大文件(图片、数据表)的时候,网络延迟和带宽限制可能会带来很大的困扰。

各种Sync命令行

Linux下利用SSH来传输文件的工具很多,简单介绍SCP和rsync

scp

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

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同样也有缺点:

  • 上传和运行输出在一起,很混乱
  • 大量文件修改的情况下(比如本地跑生成代码的脚本,或者本地进行部分编译),上传可能会很慢(当然也可以少量修改的时候就提前手动上传,然后切回来继续改)
  • 每次重连ssh,如果远程机器带宽足够但延迟较高,就会比较慢(毕竟ssh建立链接需要经过 tcp3次握手、ssh协议版本协商、密钥和算法协商、认证请求、会话请求这些繁琐的程序,延迟高的话一套下来可能需要数秒才能开始传输)

Inotify和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相比,还是有很大进步的:

  • 第一次实现了自动上传
  • 很多文件修改的时候可以后台上传

但是缺点很多

  • inotifywait在收到第一个文件修改事件就退出并进入rsync阶段,然而现实是经常大量文件在很短的时间内修改,这样就有可能在传输开始后有新的文件修改,传输结束后,本地文件和远程文件实际是不同步的,但是新的inotify忽略了这些事件,所以直到下次修改才能上传这次的改动。这样有时候就会产生一些奇怪的bug,当时的解决方案也很简单,保存完本地修改以后估摸着传输完毕了,再保存一次,这样又开始了一次传输,改动就被同步了。
  • 性能地下,在持续有文件修改的时候,对很多次的链接ssh传输文件(按照上面的设置,1s多一次)。

但是无论如何,总算实现了 自动, 还是很开心的用了很久,直到后来发现了 lsyncd。

lsyncd(Live Syncing Daemon)

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设计的功能是一台机器分发到多台机器,第一想到了这些方案:

  • 两台机器同时跑lsyncd给对方同步?(有毒吧,设置不对可能两端疯狂检测到文件修改给对方疯狂同步,冲突问题怎么解决,网络不佳如何处理)
  • git? 小改动每次commit仅仅用来同步?不太合适吧
  • 前面介绍的所有方法?辣鸡

是时候换新同步软件了。现在需求比较明确:

  • 多端同步
  • 不依赖公共服务
  • 能解决依赖问题
  • 足够灵活的配置
  • 稳定
  • 监听文件变动及时同步

后来搜索发现了 syncthing,像是一个合适的工具

Syncthing

代码同步方法汇总_第2张图片
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主要吸引我的特性:

  • WebUI, 虽然configuration file也蛮好的,但是配置这种事情,有UI毕竟还是方便些,尤其第一次配置
  • 多端同步,刚需
  • 安全性,这个其实我不是特别在乎,大多时候也就校内网两台机器互传,虽然都是公网IP,但自己的东西也没什么保密必要。
  • File Versioning,非刚需,但是有也是蛮好的。
  • 支持inotify监听并快速上传文件,刚需(虽然主页和github都没列,但是manual里面有说明)
  • 跨平台,有也蛮好,虽然我主力平台都是Linux,Windows大多情况都是打游戏用的。
  • Android客户端,至今未试过,也许可以用来备份手机

启动,配置

启动很容易,直接运行 syncthing可执行文件,或者 systemctl start,软件源安装的话应该会给默认的systemctl入口。
配置很简单,WebUI,打开浏览器默认 http://localhost:8384。可以添加目录和机器,目录设置里面可以选择共享的机器。此处仅需要注意这些问题:

网络可访问问题

软件默认使用22000端口进行数据交互。至少需要一台机器22000端口可访问。虽然添加机器后配置机器IP时可以使用dynamic,支持自动uPnP穿透什么的,但是NAT里面的机器还是可能存在问题。基本我的解决方案是使用OpenVPN,这样所有要同步的机器在一个共同的虚拟子网里面,相互访问很容易。当然可以使用ssh tunnel,然而tunnel问题多多,比如经常掉线,尤其是国内的网络环境下。

inotify

修改文件以后在短时间内快速同步是我的刚需之一,在高级设置里面可以找到 Fs Watcher Enabled,设置True就可以监听文件系统改动了。目测同步速度并不比rsync慢。其实在我把界面从IDE切到命令行这一过程中完成同步就达到使用要求了。基本在1s左右完成同步就可以有良好的使用体验。

relay server

为了让实在搞不定网络问题的用户使用,syncthing用户可以自发建立relayserver,官方维护server列表,我使用中实际并不希望使用relayserver,因为他总会比内网慢,所以直接关掉了。其他诸如localdiscovery,global discovery都直接关掉了。

优点:

  • 最大的优点就是满足了目前我的所有需求
  • 足够快,虽然他看起来没有那么轻量,传输之前做了很多操作。但速度确实满足需求了
  • 多端同步
  • Conflict虽然不像git那么Pro,但是至少文件不会丢,手动同解决下也不是不可以,并且这种情况极少出现
  • File Versioning,安全性上比rsync高多了,防手滑删库必备
  • WebUI 配置真方便

缺点:

  • 需要额外的端口来提供服务。网络访问可能存在困难,多用户可能会端口冲突,不希望在路由器上弄一整页几十个端口转发
  • 不够轻量化,但是提供了这么多服务,也没什么吐槽的
  • 初始同步的时候比较慢。大概建索引哈希之类的。相比rsync,初始同步速度被完爆。不过初始毕竟就一次,还可以接受

总结

现在,lsyncd和syncthing都在使用,毕竟lsyncd有他的优点,轻巧,易访问(只要ssh就行),用于同步部署服务的机器,syncthing用来做两台主力机器的同步,合理配置后,使用起来已经对sync这一操作无感了。

最后发点牢骚 人不应该将就。不断折腾的过程中,东西用得越来越舒服,开发体验稳步提升。也许看起来这是个没什么用的东西,然而人活着,有意思就好,何必事事总追求个实用意义,何必事事只想着那点钱权名。 世人都晓神仙好,惟有功名忘不了!古今将相在何方?荒冢一堆草没了。 世人都晓神仙好,只有金银忘不了!终朝只恨聚无多,及到多时眼闭了。 世人都晓神仙好,只有娇妻忘不了!君生日日说恩情,君死又随人去了。 世人都晓神仙好,只有儿孙忘不了!痴心父母古来多,孝顺儿孙谁见了? 哈哈哈,从意识到生命由自己控制到现在,上下求索,依然想不透人活着到底为什么,依然在探索和思考。有些人,活的比我长,却看似从未想过人生的终极奥义,有的人,也许这辈子也跳不出那些金框框银笼笼。高喊着梦想与使命,嘴里呼出的气却满是铜臭;高喊着一切为了你好,手里却哗哗点着卖劳动力赚的钱;人前谈笑风生,人后悄悄来句:"老师说的对吧?"。这般沉浸在名利里,认真得连满嘴胡话,厚颜无耻都不自知的人,我也是头一次见。

你可能感兴趣的:(Linux,Tips)