【运维进阶】部署文件到受管主机
部署文件到受管主机
实验环境
[lth@controller ~ 21:13:54]$ mkdir web && cd web[lth@controller web 21:16:40]$ cat > ansible.cfg <<'EOF'
[defaults]
remote_user = lth
inventory = ./inventory[privilege_escalation]
become = True
become_user = root
become_method = sudo
become_ask_pass = False
EOF[lth@controller web 21:16:44]$ cat > inventory <<'EOF'
controller
node1
node2
node3
node4
EOF
修改文件并将其复制到主机
Files 模块库包含的模块允许您完成与Linux文件管理相关的大多数任务,如创建、复制、编辑和修改文件的权限和其他属性,下表列出了常用的Files模块库中使用的模块。
- file:设置权限、所有权、SELinux上下文以及常规文件、符号链接、硬链接和目录的时间戳等属性。此模块还可以创建或删除常规文件、符号链接、硬链接和目录。
- sefcontext,设置持久selinux上下文。
- lineinfile:确保特定行位于某个文件中,或使用反向引用正则表达式来替换现有行。
- replace:查找文件中行,一次性替换成对应内容。
- blockinfile:插入、更新或删除多行文本块。
- stat:检索文件的状态信息,类似于Linux stat命令。
- copy:将文件从本地或远程计算机复制到受管节点上的某个位置。
- synchronize:围绕rsync命令的一个程序,可加快和简化常见任务。
- fetch:用于从远程计算机获取文件到控制节点。
file 模块
**示例:**创建文件或修改文件属性
[lth@controller web 21:16:56]$ vim playbook.yml
[lth@controller web 21:17:10]$ cat playbook.yml
---
- hosts: node1gather_facts: notasks:- name: Touch a file and set permissionsfile:path: /tmp/testfileowner: lthgroup: wheelmode: 0640state: touch[lth@controller web 21:17:26]$ ansible-playbook playbook.yml PLAY [node1] ****************************************************************************************TASK [Touch a file and set permissions] *************************************************************
changed: [node1]PLAY RECAP ******************************************************************************************
node1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0[root@node1 ~ 21:18:21]# ls -l /tmp/testfile
-rw-r----- 1 lth wheel 0 8月 18 21:18 /tmp/testfile
mode选项: 必须使用前导0 (‘0644’ or ‘01777’) 或者引起来 (‘‘644’’ or ‘‘1777’’) 。如果直接写640,则会640当做10进制,并转换成二进制1 010 000 000,最终文件权限是-w- — --T。
lineinfile 模块
示例1:确保文件中存在特定行
[lth@controller web 21:22:35]$ vim playbook.yml
[lth@controller web 21:22:40]$ cat playbook.yml
---
- hosts: node1gather_facts: notasks:- name: add linelineinfile:path: /tmp/testfileline: 'Add this line to file'state: presentcreate: yes[lth@controller web 21:22:51]$ ansible-playbook playbook.yml PLAY [node1] ****************************************************************************************TASK [add line] *************************************************************************************
changed: [node1]PLAY RECAP ******************************************************************************************
node1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0[root@node1 ~ 21:23:02]# ls -l /tmp/testfile
-rw-r--r-- 1 root root 22 8月 18 21:23 /tmp/testfile
还可以在特定位置插入:
- insertbefore,最后一个匹配到前插入
---
- hosts: node1gather_facts: notasks:- name: add linelineinfile:path: /etc/httpd/conf/httpd.confline: 'Listen 82'insertbefore: 'Listen 80'state: present
- insertafter,最后一个匹配到后插入
---
- hosts: node1gather_facts: notasks:- name: add linelineinfile:path: /etc/httpd/conf/httpd.confline: 'Listen 82'insertafter: 'Listen 80'state: present
示例2:替换文本行
---
- hosts: node1gather_facts: notasks:- name: replace linelineinfile:path: /tmp/testfileregexp: 'Add'line: 'replace'state: present
---
- hosts: node1gather_facts: notasks:- name: add linelineinfile:path: /etc/httpd/conf/httpd.confline: '#Listen 80'regexp: '^Listen 80'state: present
示例3:替换成多行文本
---
- hosts: node1gather_facts: notasks:- name: add linelineinfile:path: /tmp/testfileline: |line 1line 2regexp: 'replace'state: present
replace 模块
该模块使用正则表达式匹配内容,将匹配的内容替换成指定的内容。匹配的多个地方都会被替换掉。
示例:
[lth@controller web 21:31:10]$ cat playbook.yml
---
- hosts: node1gather_facts: notasks:- name: replace multi linereplace:path: /tmp/testfileregexp: '^Hello World.*'replace: 'Hello Lth'[lth@controller web 21:31:23]$ ansible-playbook playbook.ymlPLAY [node1] ************************************************************************************TASK [replace multi line] ***********************************************************************
ok: [node1]PLAY RECAP **************************************************************************************
node1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@node1 ~ 21:31:51]# cat /tmp/testfile Hello WorldHello Lth
blockinfile 模块
示例:将文本块添加到现有文件
[lth@controller web 21:33:20]$ vim playbook.yml
[lth@controller web 21:33:36]$ cat playbook.yml
---
- hosts: node1gather_facts: notasks:- name: add lineblockinfile:path: /tmp/testfileblock: |line 1 in fileline 2 in filestate: present[lth@controller web 21:33:41]$ ansible-playbook playbook.yml ......[root@node1 ~ 21:33:46]# cat /tmp/testfile
Hello World
Hello Lth
# BEGIN ANSIBLE MANAGED BLOCK
line 1 in file
line 2 in file
# END ANSIBLE MANAGED BLOCK
stat 模块
**stat 模块检索文件的信息,类似于Linux stat命令。**参数提供检索文件属性、确定文件校验和等功能。
stat 模块返回一个包含文件状态数据的值的散列字典,允许您使用单独的变量引用各条信息。
示例:
[lth@controller web 21:38:52]$ vim playbook.yml
[lth@controller web 21:38:59]$ cat playbook.yml ---- hosts: node1gather_facts: notasks:- stat:path: /tmp/testfilechecksum_algorithm: md5register: result- debug:msg: "/tmp/testfile md5 is {{ result.stat.checksum }}"- debug:var: result[lth@controller web 21:39:12]$ ansible-playbook playbook.ymlPLAY [node1] ****************************************************************************************TASK [stat] *****************************************************************************************ok: [node1]TASK [debug] ****************************************************************************************ok: [node1] => {"msg": "/tmp/testfile md5 is 610c7f16071ca402c0cba45e4b2af437"}TASK [debug] ****************************************************************************************ok: [node1] => {"result": {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "failed": false, "stat": {"atime": 1755169454.854485, "attr_flags": "", "attributes": [], "block_size": 4096, "blocks": 8, "charset": "us-ascii", "checksum": "610c7f16071ca402c0cba45e4b2af437", "ctime": 1755169447.1454144, "dev": 64768, "device_type": 0, "executable": false, "exists": true, "gid": 0, "gr_name": "root", "inode": 67588143, "isblk": false, "ischr": false, "isdir": false, "isfifo": false, "isgid": false, "islnk": false, "isreg": true, "issock": false, "isuid": false, "mimetype": "text/plain", "mode": "0644", "mtime": 1755169446.892412, "nlink": 1, "path": "/tmp/testfile", "pw_name": "root", "readable": true, "rgrp": true, "roth": true, "rusr": true, "size": 22, "uid": 0, "version": "1806842840", "wgrp": false, "woth": false, "writeable": true, "wusr": true, "xgrp": false, "xoth": false, "xusr": false}}}PLAY RECAP ******************************************************************************************node1 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
copy 模块
示例:将控制节点上文件拷贝到受管理节点,类似于Linux中scp命令。
[lth@controller web 21:40:05]$ vim playbook.yml
[lth@controller web 21:40:10]$ cat playbook.yml
---
- hosts: node1gather_facts: notasks:- name: copy /tmp/testfile to remote nodecopy:src: /tmp/testfiledest: /tmp[lth@controller web 21:40:17]$ echo "hello from controller" > /tmp/testfile
[lth@controller web 21:40:23]$ ansible-playbook playbook.yml PLAY [node1] ****************************************************************************************TASK [copy /tmp/testfile to remote node] ************************************************************
changed: [node1]PLAY RECAP ******************************************************************************************
node1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@node1 ~ 21:42:47]# cat /tmp/testfile
hello from controller
说明:此模块假定设置了force: yes,强制覆盖远程文件,类似scp命令。如果设置force: no, 则不会出现覆盖。
fetch 模块
从受管节点检索文件,例如将被管理节点文件先取到控制节点,然后用于分发到其他节点。诸如SSH公钥之类的文件。
示例:
[lth@controller web 21:42:43]$ vim playbook.yml
[lth@controller web 21:42:47]$ cat playbook.yml
---
- hosts: node1gather_facts: notasks:- name: fetch file from remote nodefetch:src: /tmp/testfiledest: /tmp
[lth@controller web 21:42:58]$ ansible-playbook playbook.yml[lth@controller web 21:43:13]$ tree /tmp/node1/
/tmp/node1/
└── tmp└── testfile1 directory, 1 file
使用JINJA2模板部署文件
JINJA2 模板介绍
Jinja2 就像个智能 “文本裁缝”,用{{ }}
塞变量、{% %}
做判断循环,能把固定模板和动态数据缝合成你要的样子,Ansible 配配置、网页写内容都离不开它,简单说就是 "让文本变聪明的小工具"~
示例1:部署web服务器,主页内容显示为Welcome to HOSTNAME。HOSTNAME为受管主机完全主机名。
playbook内容如下:
---
- name: Enable intranet serviceshosts: node1tasks:- name: ensure latest version of httpd yum:name: httpdstate: latest- name: test html page is installed# template模块: 复制文件到受管主机时,根据jinja2语法替换template:# 指定 Jinja2 模板来源src: index.html.j2# 指定要在目标主机上创建的文件dest: /var/www/html/index.html- name: httpd enabled and runningservice:name: httpdenabled: truestate: restarted
...
index.html.j2 内容如下:
Welcome to {{ ansible_fqdn }}
执行:
[lth@controller web 21:47:10]$ ansible-playbook playbook.yml PLAY [Enable intranet services] *********************************************************************TASK [Gathering Facts] ******************************************************************************ok: [node1]TASK [ensure latest version of httpd] ***************************************************************ok: [node1]TASK [test html page is installed] ******************************************************************changed: [node1]TASK [httpd enabled and running] ********************************************************************changed: [node1]PLAY RECAP ******************************************************************************************node1 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
剧本执行完成后,index.html内容如下:
Welcome to node1.xiexin.cloud
Jinja2 模板语法
Jinja2 模板由多个元素组成:数据、变量和表达式。在呈现Jinja2模板时, 这些变量和表达式被替换为对应的值。模板中使用的变量可以在playbook的vars部分中指定,也可以使用受管主机FACTS。
变量和逻辑表达式置于分隔符之间:
- {{ EXPR }},用于装载表达式,比如变量,运算表达式,比较表达式。
- {% EXPR %},用于装载控制语句,比如if,for等。
- {# #},用于装载注释,模板文件中的注释不会包含在最终生成文件中。
for 语句
Jinja2使用for语句来提供循环功能。
示例1:
---
- name: test templatehosts: node1vars:users:- tom- jack- Snoopy- lucytasks:- name: test templatetemplate:src: testfile.j2dest: /tmp/testfile
...
testfile.j2内容如下:
{% for user in users %}
{{ user }}
{% endfor %}
for用于声明循环,{% endfor %} 表示结束。user变量会遍历users变量中所有值。
生成的/tmp/testfile内容如下:
tom
jack
Snoopy
lucy
验证:
[lth@controller web 21:49:03]$ ansible-playbook playbook.yml PLAY [test template] *******************************************************************************TASK [Gathering Facts] *****************************************************************************ok: [node1]TASK [test template] *******************************************************************************changed: [node1]PLAY RECAP *****************************************************************************************node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@node1 ~ 21:49:17]# cat /tmp/testfile
tom
jack
snoopy
lucy
filter 过滤器
Jinja2还提供filter,对输出的结果进行格式化输出。
- “{{ output | to_json }}”,使用JSON格式输出。
- “{{ output | to_yaml }}”,使用YAML格式输出。
- “{{ output | to_nice_json }}”,使用人们更加可读的JSON格式输出。
- “{{ output | to_nice_yaml }}”,使用人们更加可读的YAML格式输出。
- “{{ output | from_json }}”,把output当做JSON格式解析。
- “{{ output | from_yaml }}”,把output当做YAML格式解析。
字符串处理过滤器:
- “{{ testvar | upper }}” ,将字符串转换成纯大写。
- “{{ testvar | lower }}” ,将字符串转换成纯小写。
- “{{ testvar | capitalize }}”,将字符串变成首字母大写,之后所有字母纯小写。
- “{{ testvar | reverse }}”,将字符串反转。
- “{{ testvar | first }}”,返回字符串的第一个字符。
- “{{ testvar | last }}”,返回字符串的最后一个字符。
- “{{ testvar | trim }}”,将字符串开头和结尾的空格去除。
- “{{ testvar | center(width=30) }}”,将字符串放在中间,并且设置字符串的长度为30,字符串两边用空格补齐30位长。
- “{{ testvar | length }}”,返回字符串长度,length与count等效,可以写为count。
- “{{ testvar | list }}”,将字符串转换成列表,每个字符作为一个元素。
列表处理过滤器:
- “{{ testvar | first }}”,返回列表中的第一个值。
- “{{ testvar | last }}”,返回列表中的最后一个值。
- “{{ testvar | min }}”,返回列表中最小的值。
- “{{ testvar | max }}”,返回列表中最大的值。
- “{{ testvar | sort }}”,将列表升序排序输出。
- “{{ testvar | sort(reverse=true) }}”,将列表降序排序输出。
- “{{ testvar | sum }}”,返回纯数字非嵌套列表中所有数字的和。
- “{{ testvar | join }}”,将列表中的元素合并成一个字符串。
- “{{ testvar | join(’ , ') }}”,将列表中的元素使用指定分隔符合并成一个字符串。
- “{{ testvar | random }}”,从列表中随机返回一个元素。
- “{{ testvar | unique }}”,去掉列表中重复的元素,重复的元素只留下一个。
数字处理过滤器:
- {{ ‘a’ | int }}",将对应的值转换成int类型,如果无法转换,默认返回0。使用int(default=6)或者int(6)时,如果无法转换则返回指定值6。
- “{{ ‘a’ | float }}”,将对应的值转换成浮点型,如果无法转换,默认返回’0.0’。使用float(default=8.88)或者float(8.88)时,如果无法转换则返回默认值’8.88’。
- “{{ testvar4 | abs }}”,获取对应数值的绝对值。
- “{{ 12.5 | round }}”,四舍五入。
- “{{ 3.1415926 | round(5) }}”,取小数点后五位。
- “{{ 100 | random }}”,从0到100中随机返回一个随机数。
- “{{ 10 | random(start=5) }}”,从5到10中随机返回一个随机数。
- “{{ 15 | random(step=5) }}”,从0到15中随机返回一个随机数,这个随机数是5的倍数。
- “{{ 15 | random(start=5,step=3) }}”,从5到15中随机返回一个随机数,步长为3。