Ansible模块的使用 文件模块 Files文件模块库包含的模块可以对Linux文件进行管理,如创建、删除、编辑和修改文件的权限与属性等。
模块
说明
blockinfile
插入、更新或删除由可定义标记线包围的多行文本块
lineinfile
确保特定行位于某个文件中,或使用反向引用正则表达式来替换现有行。此模块可以在想要更改某一个行的文本时使用
copy
将文件从本地或远程计算机复制到目标主机的某个位置。类似于file模块,copy模块还可以设置文件属性,包括SELinux上下文
fetch
该模块和copy类似,但以相反的方式工作。fetch用来从目标主机获取文件到本机控制节点上
file
设置权限、所有权、SELinux上下文以及常规文件、符号链接、硬连接、目录时间戳等。此模块还可以创建或删除常规文件、符号链接、硬连接和目录。其他过个与文件相关的模块支持与file模块相同的属性设置选项,包括copy模块
stat
检索文件的状态信息,与Linux stat命令相似
synchronize
对rsync命令的打包
文件模块使用示例 确保目标主机上存在文件 1 2 3 4 5 6 7 - name: Touch a file and set permissions file: path: /home/student/touch.me owner: student group: root mode: 0000 state: touch
如果目标主机已存在该文件,则会进行touch操作。上面的task除了确保文件存在以外,还会保证文件的权限为设定值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 File: touch.me Size: 14 Blocks: 8 IO Block: 4096 regular file Device: 801h/2049d Inode: 163550 Links: 1 Access: (0000/----------) Uid: ( 1000/ student) Gid: ( 0/ root) Context: unconfined_u:object_r:user_home_t:s0 Access: 2020-06-02 17:11:40.642364330 -0400 Modify: 2020-06-02 17:11:40.642364330 -0400 Change: 2020-06-02 17:11:40.644530998 -0400 Birth: - ------------------------------ File: touch.me Size: 14 Blocks: 8 IO Block: 4096 regular file Device: 801h/2049d Inode: 163550 Links: 1 Access: (0000/----------) Uid: ( 1000/ student) Gid: ( 0/ root) Context: unconfined_u:object_r:user_home_t:s0 Access: 2020-06-02 17:18:59.746867300 -0400 Modify: 2020-06-02 17:18:59.746867300 -0400 Change: 2020-06-02 17:18:59.746867300 -0400 Birth: -
修改文件属性 使用file模块,确保新的或现有文件具有正确的文件属性和SELinux类型。
1 2 3 4 - name: SELinux type is set to samba_share_t file: path: /home/student/touch.me setype: samba_share_t
修改前touch.me文件的setype属性是user_home_t
,使用file模块处理后setype属性已经改变为samba_share_t
。
1 2 3 4 $ ls -lZ ----------. 1 student root unconfined_u:object_r:user_home_t:s0 14 Jun 2 17:18 touch.me $ ls -lZ ----------. 1 student root unconfined_u:object_r:samba_share_t:s0 14 Jun 2 17:18 touch.me
永久更改SELinux文件上下文属性 设置上下文属性时,file模块的行为和chcon类似。通过运行restorecon可能会意外的撤销使用该模块对文件上下文所做的更改。当使用file设置上下文后,可以使用System模块集合中的sefcontext来更新SELinux策略,如semanage fcontext
。
1 2 3 4 5 - name: SELinux type is persistently set to samba_share_t sefcontext: target: /home/student/touch.me setype: samba_share_t state: present
可以看得到在SELinux上下文策略中目标的默认上下文已经更改为samba_share_t
。
1 2 /home/student/touch.me all files system_u:object_r:samba_share_t:s0
注意:sefcontext模块只更新SELinux策略中目标的默认上下文,并不更改当前现有文件的上下文。
在目标主机上复制和编辑文件 使用copy模块时,模块假定设置了force: yes
。这会强制copy模块覆盖远程文件(如果存在并且包含于与当前要发送的文件内容不同)。如果手动设置force: no
,则它仅会在目标主机不存在要复制的这个文件时才会进行复制。
1 2 3 4 5 - name: Copy a file to managed hosts copy: src: file dest: /home/student/touch.me force: yes
如果要从目标主机上拉取文件到本机,则使用fetch模块。
1 2 3 4 - name: Retrieve SSH key from reference host fetch: src: "/home/{{ user }} /.ssh/id_rsa.pub" dest: "files/keys/{{ user }} .pub"
要确保现有文件中存在某行文本,可以使用lineinfile模块。
1 2 3 4 5 - name: Add a line of text to file lineinfile: path: /home/student/touch.me line: "Can you touch me?" state: present
如果要将文本块插入到文档中,应使用blockinfile模块。
1 2 3 4 5 6 7 8 - name: Add additional lines to a file blockinfile: path: /home/student/touch.me block: | This is the block of first line. And This is the block of third line. state: present
使用blockinfile模块时,注释块标记插入到块的开头和结尾,用来让Ansible识别和保持幂等性。
1 2 3 This is the first line.
在目标主机上删除文件 在大多数情况下,如果控制目标主机文件的删除使用file模块的state: absent
参数来控制。
1 2 3 4 - name: Removed file in the server file: dest: /home/student/touch.me state: absent
检索文件的详细信息 使用stat模块可以查看文件的详细信息,并返回文件的事实。你可以利用这些Facts对文件进行检索和校验。stat模块类似于Linux系统中的stat
命令。
1 2 3 4 5 6 7 - name: Check all stat of /etc/passwd stat: path: /etc/passwd register: results - debug: vars: results
同步控制节点和受控节点之间的文件 使用synchronize模块来操作同步主机间的文件。synchronize对rsync工具进行打包,它简化了playbook中常见文件管理任务。使用该模块要求双方主机安装rsync工具。
1 2 3 4 - name: synchronize local file to server file synchronize: src: /etc dest: /home/student/
使用Jinja2模版部署自定义文件 可以利用Jinja2模版语法通过和变量与事实相配合,对固定地方的值进行覆盖和编辑。来实现定制化修改配置文件。
1 2 3 使用`{% EXPR %}`用于表达式或逻辑(循环、判断) 使用`{{ EXPR }}`用于输出最终表达式或变量的结果 使用`{# COMMENT #}`注释,注释的内容不会出现在最终的结果里
构建Jinja2模版 Jinja2模版由多个元素组成:数据、变量和表达式。在呈现Jinja2模版时,这些变量和表达式被替换为对应的值。模版中使用的变量可以在playbook的vars部分中指定。可以将目标主机的事实作为模版中使用的变量。
可以使用ansible all -m setup
来查看目标主机中全部的Fact事实。模版文件没有固定的文件拓展名,只要是文本文件即可,但是为了方便记忆理解,通常使用.j2
来代表文本文件是Jinja2的模版文件。
部署Jinja2模板 我们刚刚创建好了Jinja2模版,现在要利用这些模版。我们需要使用template
模块。src参数指的是模版文件在控制节点中的路径,dest的值是在目标主机的指定目录生成文件。
1 2 3 4 5 6 tasks: - name: template render template: src: motd.j2 dest: /etc/motd backup: true
template和file模块一样支持对文件权限进行设置。
标示配置文件由Ansible管理 我们使用模版生成文件后,为了避免管理员用户手动的修改这些配置文件,我们最好在模版的开头写上声明。虽然template不会自动地帮我们完成,但是我们可以在模版文件的开头手动引入设定好的提醒文本,使用Jinja2语法将变量的内容填写到配置文件中。
可以使用ansible_managed
指令中默认设置的 Ansible managed
字符串来执行此操作。这不是一个正常的变量,但是可以在模版中用作一个变量。ansible_managed
指令在ansible.cfg
文件中的设置:
ansible_managed = Ansible managed: modified on %Y-%m-%d %H:%M:%S
要将在ansible.cfg
文件中配置的ansible_managed
字符串包含在Jinja2模版内,使用下面的引用变量语法即可。
通过模版生成的配置文件开头存在了Ansible managed: modified on 2020-06-10 15:55:29
。这样就能对修改此文件的人有一个提示的作用。
控制结构 可以在Jinja2模版中使用控制结构,以减少重复的输入。为Play中每个主机能够动态的生成条目,或者有条件的将文本插入到文件中。
循环 Jinja2使用for
语句来提供循环功能。
1 2 3 {% for user in users %} {{ user }} {% endfor %}
下面的示例模版使用for逐一遍历users变量中的所有值,将myuser替换为各个值,但值为root时除外。
1 2 3 4 {# for statement #} {% for myuser in users if not myuser == "root" %} User number is {{ loop.index }} - {{ myuser }} {% endfor %}
loop.index变量是循环到当前处的索引号。他在循环第一次执行时的值为1,每一次迭代递增1。
下面的例子是生成hosts文件。
1 2 3 {% for host in groups['all'] %} {{ hostvars[host]['ansible_facts']['default_ipv4']['address'] }} {{ hostvars[host]['ansible_facts']['fqdn'] }} {{ hostvars[host]['ansible_facts']['hostname'] }} {% endfor %}
1 2 3 4 5 6 7 8 9 --- - name: generate hosts hosts: all tasks: - name: generate template template: src: hosts.j2 dest: /home/student/hosts ...
条件语句 Jinja2使用if语句来提供条件控制。如果满足某些条件,则会按照语句块内的规则继续生成。
1 2 3 {% if finished %} {{ result }} {% endif %}
Jinja2仅能用于模版,不能用于Playbook。条件语句和循环语句可以相互嵌套。
变量过滤器 可以使用Jinja2提供的过滤器改变原有变量输出的格式,例如将字符串转换为JSON或YAML。
如果要转换为json格式时,使用to_json过滤器进行输出。
如果要转换为yaml格式时,使用to_yaml过滤器进行输出。
1 2 {{ output | to_json }} {{ output | to_yaml }}
如果要使结构更适于人类阅读,可以使用下面的过滤器来输出人类可读格式。
1 2 {{ output | to_nice_json }} {{ output | to_nice_yaml }}
变量测试 在Ansible Playbook中与when子句一同使用的表达式是Jinja2表达式。用于测试返回值的内置Ansible测试failed、changed、succeeded、skipped四种。
1 2 3 4 5 tasks: ....... - debug: msg: "The task was aborted" when: returnvalue is faild
软件包管理 使用dnf的Ansible模块可以在受控主机上控制dnf软件包管理器。dnf是RHEL8使用的默认包管理器用于替代yum。不过也可以使用yum模块来对RHEL8进行操作。
下面示例中使用task任务来代替原有的dnf包管理器指令:
dnf install httpd -y
1 2 3 4 - name: Install the httpd packages dnf: name: httpd state: present
state关键字有如下参数:
present 如果软件包不存在,则安装软件包
absent 如果已安装,则删除软件包
latest 如果软件包不是最新版本,则会对软件包进行更新。要是没有安装则会安装最新版本的软件包
name关键字有以下使用方式:
直接填写某一个或以列表的形式填写多个软件包名称
使用'*'
配合latest可以进行更新系统全部软件包
若要管理模块或组需要使用@
符号
安装Development Tools
软件组的示例:
1 2 3 4 - name: Install the Development Tools group dnf: name: ‘@Development Tools’ state: present
安装postgresql
数据库模块:
1 2 3 4 - name: Install the postgresql module dnf: name: '@postgresql:9.6/client' state: present
如果包管理器不是yum或者dnf。可以使用package
模块替代yum/dnf
模块。package
模块可以自动检测并使用受控主机的包管理器去安装配置的软件包。
1 2 3 4 - name: Install httpd package: name: httpd state: present
收集已安装的软件包信息 使用package_facts
模块就能获取到受控主机中已经安装的软件包。它会将获取到的全部软件包信息存入ansible_facts.packages
变量中。
使用package_facts
模块并进行输出的示例:
1 2 3 4 5 6 7 8 9 --- - name: Output hosts: prod tasks: - name: Check packages package_facts: manager: auto - debug: var: ansible_facts.packages.httpd
package_facts
模块有两个选项:
manager 选择软件包管理器。默认auto自动识别
strategy 策略
返回prod主机组中已安装的httpd软件包信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ok: [ serverc] => { "ansible_facts.packages.httpd" : [ { "arch" : "x86_64" , "epoch" : null , "name" : "httpd" , "release" : "10.module+el8+2764+7127e69e" , "source" : "rpm" , "version" : "2.4.37" } ] } ok: [ serverd] => { "ansible_facts.packages.httpd" : [ { "arch" : "x86_64" , "epoch" : null , "name" : "httpd" , "release" : "10.module+el8+2764+7127e69e" , "source" : "rpm" , "version" : "2.4.37" } ] }
配置yum仓库 添加yum仓库 使用yum_repository
模块来控制第三方yum仓库。在添加仓库时即可配置GPG密钥。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --- - name: Create yum repo hosts: prod tasks: - name: Create yum repo yum_repository: name: rpmforge description: RPMforge YUM repo file: test gpgkey: http://materials.example.com/yum/repository/RPM-GPG-KEY-example baseurl: http://materials.example.com/yum/repository/ enabled: yes present: yes gpgcheck: yes
可以直接使用gpgkey选项而不需要使用其他模块进行配置。
用户管理和身份认证 如何管理用户和用户组,以及配置ssh-key。
用户模块 使用user
模块可以管理主机上的用户以及它们的许多参数。也可以删除用户、设置主目录、设置UID、关联的用户组等很多参数。
如果需要创建可以登录的计算机用户,需要使用password参数和password_hash('sha512')
搭配生成Hash后的密码才可以登陆系统。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --- - name: Add user hosts: dev tasks: - name: User modul user: name: natsumi shell: /bin/bash groups: wheel append: yes state: present password: "{{ pwd | password_hash('sha512') }} " vars: pwd: test123
组模块 group
模块可以管理受控主机中的用户组。
1 2 3 4 5 6 7 8 --- - name: Add group hosts: dev tasks: - name: change group group: name: devuser state: present
group
模块参数: gid,local,name,state,system(如果设置为yes,则表示创建的组是系统组)
系统调度 at一次性任务 使用at
模块来创建一个一次性任务。可以安排任务在未来的某一个时间点执行一次。
at
模块参数:
参数
选项
说明
command
-
计划要运行的命令
count
-
单位数字。必须和units一同使用
script_file
-
计划要执行的现有脚本文件
state
absent/present
添加或删除命令或脚本的状态
unique
yes/no
如果任务已在运行,则不会再次执行
units
minutes/hours/days/weeks
时间单位
at
模块使用示例:
1 2 3 4 5 6 7 8 9 10 --- - name: Using at make task hosts: dev tasks: - name: create task at: command: "echo 'rick' > lala.txt" count: 1 units: minutes unique: yes
目前使用at
模块总会在文件名末尾拼接上marcinDELIMITERxxxx
字符串,经过Google发现这一串文本是用来在at pool中用来标示任务的。可是在这里为什么将他们输出了出来目前还不得而知。
cron计划任务 要完成重复性的任务,也可以使用cron
模块来创建计划任务。
1 2 3 4 5 6 - name: Ensure a job that runs at 2 and 5 exists. Creates an entry like "0 5,2 * * ls -alh > /dev/null" cron: name: "check dirs" minute: "0" hour: "5,2" job: "ls -alh > /dev/null"
想要在特殊时间点,比如系统重启后运行一个任务,可以使用special_time
参数来配置cron计划任务。
1 2 3 4 5 - name: Ensure a job that runs at system reboot. Creates an entry like "@reboot ls -alh > /dev/null" cron: name: "check dirs" special_time: reboot job: "ls -alh > /dev/null"
reboot模块 使用reboot模块重新启动比直接用shell模块发起关机更安全,使用shell模块关闭受控主机后,它会等待再次开机恢复运行,才会继续向下执行其他任务和Play。
对受控主机重启后并持续等待180s。如果受控主机恢复运行则继续执行接下来的任务。
1 2 3 - name: Reboot a slow machine that might have lots of updates to apply reboot: reboot_timeout: 180
如果超出运行时间,则执行出错。接下来的task和play都不会继续执行。
fatal: [servera]: FAILED! => {"changed": false, "elapsed": 19, "msg": "Timed out waiting for last boot time check (timeout=18)", "rebooted": true}