Ansible 进阶 | synchronize 文件同步模块

准备从远程主机上拉取文件,这时我想到了 Ansible 中的 fetch 模块,但是发现 fetch 模块支持远程文件的拉取,而不支持目录!

于是,我就找到了 synchronize

synchronize 模块是对 rsync 的封装,实现控制机和目标机之间的数据同步。当然,你也可以在 command 模块中直接调用 rsync 命令,但是 synchronize 对其进行了封装,提供了一些规范化的东西,使用起来更加方便、高效,还可以增量同步。

简单了解下 rsync

rsync 是一个快速且功能非常丰富的文件拷贝工具。它可以在本地和远程之间通过 shell 或 rsync 服务互相拷贝文件。它提供了大量的选项来控制它各方面功能的行为,且在指定待拷贝文件方面非常有弹性。

它以其增量拷贝算法而出名,只拷贝源和目标不同的文件部分,因此减少网络间要传输的数据。rsync 被广泛用于做备份、镜像和当作升级版拷贝命令。

rsync 同步过程中由两部分模式组成:决定哪些文件需要同步的检查模式以及文件同步时的同步模式,也就是先检查哪些要同步,然后再进行同步。

  1. 检查模式是指按照指定规则来检查哪些文件需要被同步,例如哪些文件是明确被排除不传输的。默认情况下,rsync 使用 quick check 算法快速检查源文件和目标文件的大小、mtime(修改时间)是否一致,如果不一致则需要传输。当然,也可以通过在r sync 命令行中指定某些选项来改变 quick check 的检查模式,比如 --size-only 选项表示仅检查文件大小不同的文件作为待传输文件。rsync 支持非常多的选项,其中检查模式的自定义性是非常有弹性的。

  2. 同步模式是指在文件确定要被同步后,在同步过程发生之前要做哪些额外工作。例如上文所说的是否要先删除源主机上没有但目标主机上有的文件,是否要先备份已存在的目标文件,是否要追踪链接文件等额外操作。rsync 也提供非常多的选项使得同步模式变得更具弹性。

相对来说,为 rsync 手动指定同步模式的选项更常见一些,只有在有特殊需求时才指定检查模式,因为大多数检查模式选项都可能会影响 rsync 的性能。

rsync

总之,rsync 是非常强大的,参数选项非常多,能够实现非常具有弹性的功能,关于更完整更详细的选项说明可以参考:http://www.cnblogs.com/f-ck-need-u/p/7221713.html

synchronize

参数说明

  • src:必填,地址路径
  • dest:必填,目的地址路径
  • modemode=push,推送 ansible(src) -> 远程主机(dest);mode=pull,拉取,远程主机(src) -> ansible(dest),默认为 push
  • group:文件属组
  • owner:文件属主
  • archive:是否采用归档模式同步,即以源文件相同属性同步到目标地址,默认为 yes
  • delete:是否删除源中没有而目标存在的文件(即以推送方为主),默认为 no
  • compress:是否开启压缩,默认为 yes
  • rsync_opts:rsync 参数部分,--exclude:忽略同步文件、目录
  • rsync_timeout:指定 rsync 操作的 IP 超时时间,和 rsync 命令的 --timeout 参数效果一样

简单使用

本文中用两台主机进行试验:192.168.31.63(server端)、192.168.31.64(client端)。

我们现在 client 端随便新建一些文件:

1
2
3
4
5
6
7
8
9
# pwd
/tmp/client

# tree
.
└── sync_test
└── haha.t

1 directory, 1 file
  • 从远程主机 pull 文件

这时远程主机是源文件。

1
2
3
4
5
6
7
8
9
10
11
12
# ansible test -m synchronize -a "src=/tmp/client/ dest=/tmp/server mode=pull"
client | SUCCESS => {
"changed": true,
"cmd": "/usr/bin/rsync --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L root@client:/tmp/client/ /tmp/server",
"msg": "cd+++++++++ ./\ncd+++++++++ sync_test/\n>f+++++++++ sync_test/haha.t\n",
"rc": 0,
"stdout_lines": [
"cd+++++++++ ./",
"cd+++++++++ sync_test/",
">f+++++++++ sync_test/haha.t"
]
}

在 server 节点查看,数据已同步到 /tmp/server/ 目录下:

1
2
3
4
5
6
7
8
# pwd
/tmp/server
# tree
.
└── sync_test
└── haha.t

1 directory, 1 file
  • push 文件

我们在 haha.t 文件中随便输入一些内容,并新建一个文件:

1
2
3
4
5
6
7
# ll
total 4
-rw-r--r-- 1 root root 0 Aug 23 17:34 haha2.tt
-rw-r--r-- 1 liuhao root 7 Aug 23 17:33 haha.t
# cat haha.t
hah
ha

需要注意的是,push 时,src 需要填本地目录,dest 填远程主机目录:

1
2
3
4
5
6
7
8
9
10
11
12
# ansible test -m synchronize -a "src=/tmp/server/ dest=/tmp/client/"
client | SUCCESS => {
"changed": true,
"cmd": "/usr/bin/rsync --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L /tmp/server/ root@client:/tmp/client/",
"msg": ".d..t...... sync_test/\n<f.st...... sync_test/haha.t\n<f+++++++++ sync_test/haha2.tt\n",
"rc": 0,
"stdout_lines": [
".d..t...... sync_test/",
"<f.st...... sync_test/haha.t",
"<f+++++++++ sync_test/haha2.tt"
]
}
  • 单个文件冲突时

默认情况下会以 src 端的数据为准。

  • dest 端有多余文件时

默认情况下会保留 dest 端有,而 src 端没有的文件。可以通过 delete 参数设置为 yes 来删除 dest 端多余的文件。

1
2
3
4
5
6
7
8
9
10
# ansible test -m synchronize -a "src=/tmp/client/ dest=/tmp/server mode=pull delete=yes"
client | SUCCESS => {
"changed": true,
"cmd": "/usr/bin/rsync --delay-updates -F --compress --delete-after --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L root@client:/tmp/client/ /tmp/server",
"msg": "*deleting sync_test/other.t\n",
"rc": 0,
"stdout_lines": [
"*deleting sync_test/other.t"
]
}
  • 忽略文件
1
2
3
4
5
6
7
8
9
10
# ansible test -m synchronize -a "src=/tmp/server/ dest=/tmp/client/ rsync_opts='--exclude=sync_test/other.t'"
client | SUCCESS => {
"changed": true,
"cmd": "/usr/bin/rsync --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --exclude=sync_test/other.t --out-format=<<CHANGED>>%i %n%L /tmp/server/ root@client:/tmp/client/",
"msg": ".d..t...... sync_test/\n",
"rc": 0,
"stdout_lines": [
".d..t...... sync_test/"
]
}
  • 源主机有多个时

比如,一个主机组下配置了两个主机,并且都有 同一个目录 /tmp/client/,我们制造一个文件冲突的现场:

主机 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# tree
.
├── 21
└── 23

0 directories, 2 files

# ll
total 4
-rw-r--r-- 1 root root 4 Sep 5 11:39 21
-rw-r--r-- 1 root root 0 Sep 5 11:36 23

# cat 21
123

主机 2:

1
2
3
4
5
6
7
8
9
10
11
12
# tree
.
└── 21

0 directories, 1 file

# ll
total 4
-rw-r--r-- 1 liuhao root 4 Sep 5 11:29 21

# cat 21
456

可以看出,文件 21,不仅文件内容不同,文件权限也不同,那我们从两台主机进行拉取时,会得到什么样的结果呢?

执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ansible test -m synchronize -a "src=/tmp/client/ dest=/tmp/server/ mode=pull"
主机1 | SUCCESS => {
"changed": true,
"cmd": "/usr/bin/rsync --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L root@主机1:/tmp/client/ /tmp/server/",
"msg": ".d..t...... ./\n>f.st...... 21\n",
"rc": 0,
"stdout_lines": [
".d..t...... ./",
">f.st...... 21"
]
}
主机2 | SUCCESS => {
"changed": true,
"cmd": "/usr/bin/rsync --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L root@1主机2:/tmp/client/ /tmp/server/",
"msg": ".d..t...... ./\n>f..t.o.... 21\n",
"rc": 0,
"stdout_lines": [
".d..t...... ./",
">f..t.o.... 21"
]
}

然后我们看结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ll
total 4
-rw-r--r-- 1 liuhao root 4 Sep 5 11:29 21
-rw-r--r-- 1 root root 0 Sep 5 11:36 23

# tree
.
├── 21
└── 23

0 directories, 2 files

# cat 21
456

显然,后面执行的主机 2 的文件内容,覆盖了前面主机 1 的文件内容。

注意点

  1. 本地和远程系统必须安装 rsync 包,否则无法使用这个模块;
  2. 对于 synchronize 模块,本地主机是同步任务发起的主机,目标主机是同步时被连接的主机;

  3. 也可以使用 delegate_to 将本地主机更改为其他主机。这样可以在两个远程主机之间进行复制,或者在一台远程机器上执行两个目录的同步。

delegate_to

需要注意的是,使用 delegate_to 授权机进行 synchronize,需要保证授权机能密钥访问远程机。因为 delegate_to 时,使用的帐户权限是授权机的,而非 ansible host 的。

1
2
3
4
5
6
7
8
9
# Synchronization of src on delegate host to dest on the current inventory host.
# 同步『授权机(delegate host)』的 src 目录到远程机器
# 注:需要指定 rsync_opts。
# 参考 https://github.com/ansible/ansible/issues/7250
- synchronize:
src: /first/absolute/path
dest: /second/absolute/path
rsync_opts: '-e "ssh -p -i /home/delegate_host_user/.ssh/id_rsa"'
delegate_to: delegate.host
  1. src 的所属用户和权限是本地主机上运行 Ansible 任务的用户和权限(如果配置了 delegate_to,那就是授权机上的 remote_user 的);
  2. dest 的所属用户和权限是目标主机上 remote_user 的用户和权限,如果配置了 become=yes,则为 become_user的;
  3. 即使使用了 sudo, dest=~/x 也会变成 ~<remote_user>/x
  4. 如果 inventory 文件中使用 ansible_ssh_pass 进行用户名密码认证,在使用 synchronize 模块时由于模块使用的是独立的 ssh 通道,因此会再次提示输入密码,在大规模文件下发场景中使用体验较差,可以考虑通过其它途径实现。

和 copy/fecth 的区别

其实有点类似于 rsyncscp 的区别,Rsync 有着更丰富的特性,并且更快捷,当然,Rsync 使用起来相对复杂些,因为参数较多。

总结来看就是:

  • copy 模块不支持从远端到本地的拉去操作,fetch 模块支持,但是 src 参数不支持目录递归,只能回传具体文件;
  • copy 模块的 remote_src 参数是指定从远端服务器上往远端服务器上复制,相当于在 shell 模块中执行 copy 命令;
  • synchronize 则支持文件下发和回传,分别对应的 push 和 pull 模式。synchronize 模块的功能依赖于 rsync,但是功能不依赖于 rsync 配置文件中定义的模块;
  • copy 模块适用于小规模文件操作,synchronize 支持大规模文件操作。
hoxis wechat
一个脱离了高级趣味的程序员,关注回复1024有惊喜~
赞赏一杯咖啡
  • 本文作者: hoxis | 微信公众号【不正经程序员】
  • 本文链接: https://hoxis.github.io/ansible-synchronise.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!
  • 并保留本声明和上方二维码。感谢您的阅读和支持!
0%