需要用 Ansible 修改配置文件,其实就是在某个文件末尾添加几行内容。直观地想,直接用 shell
模块,echo
加 >>
就完事了。
但仔细一琢磨,很可能会引发一些意想不到的问题,比如:
- 如果需要添加的配置已经存在,
echo
仍会向配置文件底部添加同样的内容 - 如果添加配置的任务重复执行多次,则配置文件中也会多次出现重复的内容。无法做到幂等
- 如何做到,当对应的配置已经存在,则将该配置改为期望的值;当对应的配置不存在,不做任何操作(有就修改,没有就不动。好像可以用
sed
) - 如何安全地移除指定的配置项
诸如此类。运维工作常常要关系到生产环境。任何无法预期的效果都可能产生严重的影响。而单纯使用 echo
和 >>
向配置文件中添加内容,具有很大的不确定性。
当然可以形成一个 Shell 脚本,对各种边界进行足够的检查和判定,但这会导致代码量变大,结构复杂难以标准化;同时也容易出现遗漏的情况。
实际上 Ansible 内置的 lineinfile
就是专门用来处理上述任务的模块。
比如针对如下内容的配置文件 test_conf.ini
:1
2FIRST=true
SECOND=2
需要添加一行配置 THIRD=3
。
可以运行如下内容的 playbook change_config.yml
:1
2
3
4
5
6
7
8
9
10- name: change configuration
gather_facts: false
hosts: localhost
tasks:
- name: change content in test_conf.ini
lineinfile:
path: /home/starky/projects/ansible/practice/test_conf.ini
regexp: '^THIRD'
line: THIRD=3
其中 lineinfile
模块的 path
参数用于指定目标配置文件的路径;regexp
参数则用于指定对文件内容进行匹配时使用的正则表达式;最后的 line
参数表示希望在目标文件中出现的内容。
具体的步骤为:
- 检查
line
对应的内容是否存在于path
对应的目标文件中 - 若已经存在。则目标文件符合要求,不对该文件做任何操作
- 若不存在。通过
regexp
指定的正则表达式对目标文件进行匹配 - 若
regexp
匹配到文本行,则将该行内容修改为line
指定的内容 - 若
regexp
未匹配到文本行,则将line
对应的内容作为新的一行添加到目标文件末尾
运行效果:1
2
3
4
5
6
7
8
9$ ansible-playbook change_config.yml
PLAY [change configuration] *********************************************************************************************************
TASK [change content in test_conf.ini] **********************************************************************************************
changed: [localhost]
PLAY RECAP **************************************************************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
此时 test_conf.ini
配置文件的内容被修改为:1
2
3FIRST=true
SECOND=2
THIRD=3
若再次运行 ansible-playbook change_config.yml
命令:1
2
3
4
5
6
7
8
9$ ansible-playbook change_config.yml
PLAY [change configuration] *********************************************************************************************************
TASK [change content in test_conf.ini] **********************************************************************************************
ok: [localhost]
PLAY RECAP **************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可以看到修改配置文件的任务执行结果为 ok
,而不同于上一次的 changed
。这表示 lineinfile
模块对配置文件的内容进行了检查,发现需要添加的配置行已经存在,因此未做任何改动。符合幂等的原则。
假如将配置文件中的 THIRD=3
改为 THIRD=false
,再次运行 playbook:1
2
3
4
5
6
7
8
9$ ansible-playbook change_config.yml
PLAY [change configuration] *********************************************************************************************************
TASK [change content in test_conf.ini] **********************************************************************************************
changed: [localhost]
PLAY RECAP **************************************************************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
正则表达式 ^THIRD
会匹配到配置文件的第三行 THIRD=false
,再将该行内容替换为 THIRD=3
。
最终仍可以得到我们想要的内容:1
2
3FIRST=true
SECOND=2
THIRD=3
对于 Ansible playbook 而言,我们只需要关注期望达到的状态,而不用纠结为了达到该状态需要执行哪些步骤。
如 lineinfile
模块,line
指定的内容即为我们期望目标文件达到的状态。即该文件最终一定会包含一行与 line
相同的文本。
不管该行内容是本就已经存在的,还是通过修改 regexp
匹配到的文本行得到的,还是直接在目标文件末尾新增的。而我们只需要定义 path
、regexp
、line
三个参数即可。
其他用法
backrefs
lineinfile
默认的行为是若 line
指定的内容未存在,regexp
正则表达式也没有任何匹配,就在文件末尾添加一行 line
指定的内容。backrefs
参数可以修改此行为。当 backrefs
设定为 true
时,若 line
指定的内容不存在,正则表达式也没有匹配。则不做任何操作。
比如如下 playbook:1
2
3
4
5
6
7
8
9
10
11- name: change configuration
gather_facts: false
hosts: localhost
tasks:
- name: change content in test_conf.ini
lineinfile:
path: /home/starky/projects/ansible/practice/test_conf.ini
regexp: '^THIRD'
line: 'THIRD=3'
backrefs: true
当目标文件的内容如下:1
2FIRST=true
SECOND=2
playbook 实际不会对其做任何修改,不会在文件末尾添加 THIRD=3
。只有当文件中存在如 THIRD=false
这类内容时,playbook 才会完成匹配并替换对应的行。
没有 backrefs
表示匹配就替换,不匹配就在文件末尾添加;有 backrefs
表示匹配就替换,不匹配就不动。
删除一行内容
1 | - name: change configuration |
在匹配行前/后添加
1 | - name: change configuration |
1 | - name: change configuration |
需要注意以下两点:
- 当
line
指定的内容已经存在于目标文件中时,不管其具体在什么位置,目标文件都不会做任何修改 - 当
insertbefore
或insertafter
指定的正则表达式没有任何匹配时,都会在文件末尾添加line
指定的内容
官网示例
1 | # NOTE: Before 2.3, option 'dest', 'destfile' or 'name' was used instead of 'path' |