介绍
在本实验中,你将学习使用 Ansible 在 Red Hat Enterprise Linux (RHEL) 系统上部署和管理文件的基本技能。你将亲手实践一些最常用且强大的用于文件操作的 Ansible 模块,从基本的文件部署到更高级的内容操作和状态管理。
你将首先使用 ansible.builtin.copy 模块来传输静态文件并设置其属性。接下来,你将使用 lineinfile 和 blockinfile 修改文件内容,并使用 ansible.builtin.template 模块生成自定义的 MOTD。本实验还涵盖了创建符号链接、使用 stat 验证文件状态、使用 fetch 检索日志以及清理已管理的文件,全面概述了 Ansible 的文件管理能力。
使用 ansible.builtin.copy 模块复制静态文件并设置属性
在本步骤中,你将学习如何使用最基础的 Ansible 模块之一:ansible.builtin.copy。此模块用于将文件从你的控制节点(LabEx VM)传输到托管主机上的指定位置。在我们的例子中,托管主机将是 localhost 本身。除了复制文件,copy 模块还允许你精确控制文件的属性,例如其所有者、组和权限模式,这对于正确的系统配置至关重要。
首先,让我们设置项目环境。我们所有的工作都将在 ~/project 目录下进行。
导航到项目目录并为源文件创建一个子目录。 这是一个保持项目组织的常见做法。
安装
ansible-core包。sudo dnf install -y ansible-core然后,导航到项目目录并为源文件创建一个子目录。
cd ~/project mkdir files接下来,创建一个简单的文本文件供我们复制。 我们将使用
cat命令配合“here document”在files目录中创建info.txt文件。cat << EOF > ~/project/files/info.txt This file was deployed by Ansible. It contains important system information. EOF现在,创建一个 Ansible inventory 文件。 inventory 文件告诉 Ansible 要管理哪些主机。对于本次实验,我们将管理本地机器。创建一个名为
inventory.ini的文件。cat << EOF > ~/project/inventory.ini localhost ansible_connection=local EOF在此 inventory 中,
localhost是我们要定位的主机。变量ansible_connection=local指示 Ansible 直接在控制节点上执行任务,而无需使用 SSH。创建你的第一个 Ansible playbook。 这个 playbook 将包含复制文件的指令。使用
nano或cat创建一个名为copy_file.yml的文件。nano ~/project/copy_file.yml将以下内容添加到文件中。这个 playbook 定义了一个任务:将
info.txt复制到/tmp/目录并设置其属性。--- - name: Deploy a static file to localhost hosts: localhost tasks: - name: Copy info.txt and set attributes ansible.builtin.copy: src: files/info.txt dest: /tmp/info.txt owner: labex group: labex mode: "0640"让我们分解一下
copy任务中的参数:src: files/info.txt: 源文件在控制节点上的路径,相对于 playbook 的位置。dest: /tmp/info.txt: 文件将在托管主机上放置的绝对路径。owner: labex: 将文件的所有者设置为labex用户。group: labex: 将文件的组设置为labex组。mode: '0640': 设置文件的权限。0640表示所有者可读/写,组可读,其他人无权限。
使用
ansible-playbook命令执行 playbook。-i标志指定了我们的 inventory 文件。ansible-playbook -i inventory.ini copy_file.yml你应该会看到类似以下的 playbook 执行成功的输出:
PLAY [Deploy a static file to localhost] *************************************** TASK [Gathering Facts] ********************************************************* ok: [localhost] TASK [Copy info.txt and set attributes] **************************************** changed: [localhost] PLAY RECAP ********************************************************************* localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0最后,验证文件是否已正确复制 并具有正确的属性。使用
ls -l命令检查权限、所有者和组。ls -l /tmp/info.txt输出应显示
labex是所有者和组,权限为-rw-r-----。-rw-r----- 1 labex labex 72 Jul 10 14:30 /tmp/info.txt你还可以查看文件的内容,以确保它已完整复制。
cat /tmp/info.txtThis file was deployed by Ansible. It contains important system information.
你已成功使用 ansible.builtin.copy 模块在本地系统上部署了文件并配置了其属性。
使用 lineinfile 和 blockinfile 修改文件内容
在本步骤中,你将学习如何在不替换整个文件的情况下修改托管主机上的现有文件。Ansible 为此目的提供了强大的模块:ansible.builtin.lineinfile 用于管理单行,ansible.builtin.blockinfile 用于管理多行文本块。这些模块在更改配置文件或向日志文件添加条目等任务中非常有用。
我们将继续使用你在上一步创建的 info.txt 文件,该文件位于 /tmp/info.txt。
首先,确保你在项目目录中。
cd ~/project创建一个名为
modify_file.yml的新 playbook。这个 playbook 将包含两个任务:一个用于添加单行,另一个用于向现有文件添加文本块。nano ~/project/modify_file.yml将以下内容添加到你的
modify_file.ymlplaybook 中。 这个 playbook 目标是localhost,并使用lineinfile和blockinfile向/tmp/info.txt追加内容。--- - name: Modify an existing file hosts: localhost tasks: - name: Add a single line of text to a file ansible.builtin.lineinfile: path: /tmp/info.txt line: This line was added by the lineinfile module. state: present - name: Add a block of text to an existing file ansible.builtin.blockinfile: path: /tmp/info.txt block: | ## BEGIN ANSIBLE MANAGED BLOCK This block of text consists of two lines. They have been added by the blockinfile module. ## END ANSIBLE MANAGED BLOCK state: present让我们来分析一下使用的模块:
ansible.builtin.lineinfile: 此模块确保文件中存在特定行。如果该行已存在,Ansible 将不执行任何操作,从而使任务具有幂等性。path: 要修改的文件。line: 要确保存在于文件中的文本行。state: present: 这确保行存在。你可以使用state: absent来删除它。
ansible.builtin.blockinfile: 此模块管理一个文本块,该文本块由标记行(例如## BEGIN ANSIBLE MANAGED BLOCK)包围。这对于管理配置部分非常理想。path: 要修改的文件。block: 要插入的多行字符串。|是 YAML 中用于字面量块的语法,会保留换行符。state: present: 确保块存在。
使用
ansible-playbook命令和你的inventory.ini文件执行 playbook。ansible-playbook -i inventory.ini modify_file.yml输出将显示两个任务都对文件进行了更改。
PLAY [Modify an existing file] ************************************************* TASK [Gathering Facts] ********************************************************* ok: [localhost] TASK [Add a single line of text to a file] ************************************* changed: [localhost] TASK [Add a block of text to an existing file] ********************************* changed: [localhost] PLAY RECAP ********************************************************************* localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0最后,通过查看
/tmp/info.txt的内容来验证更改。cat /tmp/info.txt你应该会看到原始内容,然后是新添加的行和新的文本块。
This file was deployed by Ansible. It contains important system information. This line was added by the lineinfile module. ## BEGIN ANSIBLE MANAGED BLOCK This block of text consists of two lines. They have been added by the blockinfile module. ## END ANSIBLE MANAGED BLOCK如果你再次运行 playbook,Ansible 将报告
ok=3和changed=0,因为内容已存在,这展示了这些模块的幂等性。
使用 ansible.builtin.template 模块生成自定义 MOTD
在本步骤中,你将从复制静态文件进阶到使用 ansible.builtin.template 模块生成动态文件。该模块利用 Jinja2 模板引擎,根据 Ansible 从托管主机收集的变量和系统信息(称为“facts”)来创建定制化文件。我们将创建一个动态的每日消息 (MOTD),以显示特定于系统的相关信息。
首先,确保你在
~/project目录中,并创建一个专门用于存放模板的子目录。 将 Jinja2 模板存储在templates目录中是 Ansible 的标准最佳实践。cd ~/project mkdir templates接下来,创建 Jinja2 模板文件。 这个文件
motd.j2将包含我们 MOTD 的结构,其中包含动态数据的占位符。.j2扩展名是 Jinja2 模板的常用约定。nano ~/project/templates/motd.j2将以下内容添加到文件中。请注意
{{ ... }}语法,它表示一个变量或 fact 的占位符。################################################################# ## Welcome to {{ ansible_facts['fqdn'] }} # ## This is a {{ ansible_facts['distribution'] }} system. ## System managed by Ansible. # ## For support, contact: {{ admin_email }} #################################################################在此模板中:
{{ ansible_facts['fqdn'] }}将被替换为主机的完全限定域名 (Fully Qualified Domain Name)。{{ ansible_facts['distribution'] }}将被替换为 Linux 发行版的名称(例如 RedHat)。{{ admin_email }}是一个自定义变量,我们将在 playbook 中定义它。
现在,创建一个名为
template_motd.yml的新 playbook。此 playbook 将使用模板生成/etc/motd文件。nano ~/project/template_motd.yml添加以下内容。此 playbook 需要提升的权限 (
become: true) 才能写入/etc目录。它还定义了自定义的admin_email变量。--- - name: Deploy a custom MOTD from a template hosts: localhost become: true vars: admin_email: admin@labex.io tasks: - name: Generate /etc/motd from template ansible.builtin.template: src: templates/motd.j2 dest: /etc/motd owner: root group: root mode: "0644"此 playbook 中的关键参数:
become: true: 这告诉 Ansible 使用sudo来执行任务,这对于写入/etc/motd是必需的。vars: 此部分是我们定义自定义变量(如admin_email)的地方。ansible.builtin.template: 处理 Jinja2 模板的模块。src指向我们的.j2文件,而dest是托管主机上的目标文件。
执行 playbook。
ansible-playbook -i inventory.ini template_motd.yml输出应确认任务已成功完成。
PLAY [Deploy a custom MOTD from a template] ************************************ TASK [Gathering Facts] ********************************************************* ok: [localhost] TASK [Generate /etc/motd from template] **************************************** changed: [localhost] PLAY RECAP ********************************************************************* localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0验证结果。 检查新生成的
/etc/motd文件的内容。cat /etc/motd你将看到渲染后的输出,其中 Jinja2 占位符已被实际的系统 facts 和你定义的自定义变量替换。
fqdn将与你的实验环境的主机名匹配。################################################################# ## Welcome to host.labex.io # ## This is a RedHat system. ## System managed by Ansible. # ## For support, contact: admin@labex.io #################################################################
你现在已经成功使用模板创建了一个定制化文件,这是基础设施自动化中的一项核心技能。
使用 copy 和 file 模块部署支持文件并创建符号链接
在本步骤中,你将结合 copy 模块的知识和一个新的、功能强大的模块:ansible.builtin.file。copy 用于传输内容,而 file 则用于管理托管主机上文件、目录和符号链接的状态。你将使用它来创建目录、设置权限,以及(在本实验中最重要的)创建符号链接。
我们的场景是配置系统显示的登录前消息。在许多 Linux 系统中,/etc/issue 会显示给本地终端用户,而 /etc/issue.net 会显示给远程用户(例如通过 SSH)。我们将部署一个单独的 issue 文件,然后创建一个符号链接,使 /etc/issue.net 指向 /etc/issue,从而确保它们始终显示相同的消息。
首先,确保你在
~/project目录中,并为你的 issue 消息创建源文件。 我们将把这个文件放在你之前创建的files子目录中。cd ~/project cat << EOF > ~/project/files/issue Authorized access only. All connections are logged and monitored. EOF创建一个名为
deploy_issue.yml的新 playbook。此 playbook 将包含两个任务:一个用于复制issue文件,另一个用于创建符号链接。nano ~/project/deploy_issue.yml将以下内容添加到你的
deploy_issue.ymlplaybook 中。 此 playbook 需要提升的权限 (become: true) 才能管理/etc/目录中的文件。--- - name: Configure system issue files hosts: localhost become: true tasks: - name: Copy custom /etc/issue file ansible.builtin.copy: src: files/issue dest: /etc/issue owner: root group: root mode: "0644" - name: Ensure /etc/issue.net is a symlink to /etc/issue ansible.builtin.file: src: /etc/issue dest: /etc/issue.net state: link force: yes让我们分析新的
ansible.builtin.file任务:src: /etc/issue: 当state为link时,src指定符号链接应指向的文件。dest: /etc/issue.net: 这是创建符号链接本身的目标路径。state: link: 这个关键参数告诉file模块创建符号链接,而不是普通文件或目录。force: yes: 这是一个有用的选项,可以确保幂等性。如果/etc/issue.net已作为普通文件存在,Ansible 将删除它并创建链接。如果没有force: yes,在这种情况下 playbook 将会失败。
执行 playbook。
ansible-playbook -i inventory.ini deploy_issue.yml输出将显示两个任务都成功地进行了更改。
PLAY [Configure system issue files] ******************************************** TASK [Gathering Facts] ********************************************************* ok: [localhost] TASK [Copy custom /etc/issue file] ********************************************* changed: [localhost] TASK [Ensure /etc/issue.net is a symlink to /etc/issue] ************************ changed: [localhost] PLAY RECAP ********************************************************************* localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0使用
ls -l命令验证结果。 此命令提供详细列表,清晰地显示符号链接。ls -l /etc/issue /etc/issue.net输出应显示
/etc/issue是一个普通文件,而/etc/issue.net是一个指向它的符号链接。/etc/issue.net权限开头的l表示它是一个链接。-rw-r--r--. 1 root root 65 Jul 10 15:00 /etc/issue lrwxrwxrwx. 1 root root 10 Jul 10 15:00 /etc/issue.net -> /etc/issue
你现在已经成功部署了一个配置文件,并使用 ansible.builtin.file 模块创建了一个符号链接,这是管理系统配置的一种常见且强大的模式。
使用 stat 验证文件状态并使用 fetch 检索日志
在本步骤中,你将学习两个重要的数据收集模块:ansible.builtin.stat 和 ansible.builtin.fetch。stat 模块用于检查托管主机上文件或目录的状态——例如,查看它是否存在、其权限是什么,或者最后修改时间。它不会更改任何内容,因此非常适合用于检查和条件逻辑。fetch 模块的作用与 copy 相反:它从托管主机检索文件,并将它们保存在你的控制节点上,这对于备份配置或收集日志文件进行分析非常理想。
我们将创建一个 playbook,该 playbook 首先检查你之前创建的 /etc/motd 文件是否存在,然后将 DNF 包管理器日志文件 (/var/log/dnf.log) 检索到你的 LabEx VM 上的本地目录。
首先,确保你在
~/project目录中,并创建一个新的子目录来存储你将要检索的文件。cd ~/project mkdir fetched_logs创建一个名为
check_and_fetch.yml的新 playbook。此 playbook 将包含检查文件和检索日志的任务。nano ~/project/check_and_fetch.yml将以下内容添加到你的
check_and_fetch.ymlplaybook 中。 此 playbook 使用stat获取文件详细信息,使用register将这些详细信息存储在变量中,使用debug显示变量,并使用fetch检索日志文件。--- - name: Check file status and fetch logs hosts: localhost become: true tasks: - name: Check if /etc/motd exists ansible.builtin.stat: path: /etc/motd register: motd_status - name: Display stat results ansible.builtin.debug: var: motd_status.stat - name: Fetch the dnf log file from managed host ansible.builtin.fetch: src: /var/log/dnf.log dest: fetched_logs/ flat: yes让我们分解一下关键概念:
register: motd_status: 这是 Ansible 的一个关键功能。它获取任务的整个输出,并将其保存到一个名为motd_status的新变量中。ansible.builtin.debug: 此模块用于在 playbook 运行期间打印值。在这里,我们打印了已注册变量 (motd_status.stat) 中的stat对象,以查看文件的属性。ansible.builtin.fetch: 此模块从托管主机检索文件。src: 要从托管主机检索的文件的路径。dest: 控制节点(你的 LabEx VM)上保存文件的目录。flat: yes: 默认情况下,fetch会创建与主机和源路径匹配的子目录结构。flat: yes通过将文件直接复制到dest目录而不添加任何额外的子目录来简化此过程。
执行 playbook。 由于我们要读取系统日志文件,因此使用
become: true来获取必要的权限。ansible-playbook -i inventory.ini check_and_fetch.yml输出将显示 debug 任务中
stat检查的结果,然后是fetch任务。PLAY [Check file status and fetch logs] **************************************** TASK [Gathering Facts] ********************************************************* ok: [localhost] TASK [Check if /etc/motd exists] *********************************************** ok: [localhost] TASK [Display stat results] **************************************************** ok: [localhost] => { "motd_status.stat": { "exists": true, "gid": 0, "isreg": true, "mode": "0644", "path": "/etc/motd", ... } } TASK [Fetch the dnf log file from managed host] ******************************** changed: [localhost] PLAY RECAP ********************************************************************* localhost : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0验证日志文件是否已成功检索。 列出
fetched_logs目录的内容。ls -l ~/project/fetched_logs/你应该会看到
dnf.log文件,现在它已本地存储在你的控制节点上。total 4 -rw-r--r--. 1 labex labex 1234 Jul 10 15:30 dnf.log
你现在已经学会了如何在不进行更改的情况下检查文件属性,以及如何将托管系统中的重要文件检索回你的控制节点。
使用 file 模块清理已管理主机上的文件
在最后这个步骤中,你将学习如何使用 ansible.builtin.file 模块来确保文件和目录 不存在 于系统上。配置管理的一个关键部分不仅在于创建和修改资源,还在于清理它们。通过将 state 参数设置为 absent,你可以指示 Ansible 删除文件、符号链接甚至整个目录。
为了完成本次实验,我们将编写一个单一的“清理”playbook,它将删除我们在前几个步骤中创建的所有文件:/tmp/info.txt、/etc/motd、/etc/issue 以及 /etc/issue.net 符号链接。
首先,确保你在
~/project目录中。cd ~/project创建一个名为
cleanup.yml的新 playbook。此 playbook 将包含恢复我们更改所需的所有任务。nano ~/project/cleanup.yml将以下内容添加到你的
cleanup.ymlplaybook 中。 此 playbook 使用任务列表,每个任务都针对我们创建的文件之一。请注意,become: true是在 play 级别设置的,因此所有任务都将以提升的权限运行。--- - name: Clean up managed files from the system hosts: localhost become: true tasks: - name: Remove the temporary info file ansible.builtin.file: path: /tmp/info.txt state: absent - name: Remove the custom MOTD file ansible.builtin.file: path: /etc/motd state: absent - name: Remove the custom issue file ansible.builtin.file: path: /etc/issue state: absent - name: Remove the issue.net symbolic link ansible.builtin.file: path: /etc/issue.net state: absent此 playbook 的关键在于每个任务中的
state: absent参数。这告诉file模块确保指定path的项不存在。如果找到文件,它将删除它。如果文件已不存在,它将不做任何操作,从而保持幂等性。执行清理 playbook。
ansible-playbook -i inventory.ini cleanup.yml输出将显示每个任务通过删除文件成功进行了更改。
PLAY [Clean up managed files from the system] ********************************** TASK [Gathering Facts] ********************************************************* ok: [localhost] TASK [Remove the temporary info file] ****************************************** changed: [localhost] TASK [Remove the custom MOTD file] ********************************************* changed: [localhost] TASK [Remove the custom issue file] ******************************************** changed: [localhost] TASK [Remove the issue.net symbolic link] ************************************** changed: [localhost] PLAY RECAP ********************************************************************* localhost : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0验证文件是否已被删除。 你可以使用
ls命令检查它们是否存在。该命令将报告无法访问它们,因为它们已不存在。ls /tmp/info.txt /etc/motd /etc/issue /etc/issue.net预期的输出是一系列错误,这证实了清理工作已成功完成。
ls: cannot access '/tmp/info.txt': No such file or directory ls: cannot access '/etc/motd': No such file or directory ls: cannot access '/etc/issue': No such file or directory ls: cannot access '/etc/issue.net': No such file or directory
你现在已经成功使用 Ansible 删除了文件并清理了系统,完成了从创建到删除的文件管理全生命周期。
总结
在本实验中,你学习了使用 Ansible 在 RHEL 系统上进行文件管理的基础知识。你首先使用 ansible.builtin.copy 模块将静态文件传输到托管主机,同时设置了特定的所有权和权限。然后,你通过使用 lineinfile 确保特定行存在,并使用 blockinfile 管理多行文本块,探索了如何修改现有文件。涵盖的一项关键技能是使用 ansible.builtin.template 模块和 Jinja2 语法生成动态文件内容,以创建包含系统事实的自定义每日消息 (MOTD)。
此外,你还练习了使用 ansible.builtin.file 模块部署支持文件和创建符号链接。为了确保你的部署成功,你使用了 stat 模块来验证文件的状态和属性,并使用 fetch 模块从托管主机检索文件(如日志)到控制节点。最后,你学习了如何执行清理操作,通过使用带有 state: absent 的 file 模块来删除在整个实验过程中创建的文件和目录,从而确保托管主机处于干净的状态。


