用 Ansible 真去改 RHEL
本实验在真正的 RHEL 系列系统上运行
VM 中运行着 AlmaLinux 9。systemd 以 PID 1 运行,SELinux 处于 Enforcing 状态,firewalld 也在运行。playbook 会真正修改系统,评分器则直接从系统中读取结果。
此前的 Ansible 课程在 Pod 中运行,只能做到语法检查。在这里,不做配置就会真的被阻止。
首次启动大约需要 2 分钟。
目标
把 RHCE 考试实际会考的内容走一遍:用户、服务、SELinux、防火墙、模板,以及幂等性。
为什么重要
RHCE(EX294)考的不是“是否懂 Ansible 语法”,而是能否把系统配置成想要的状态。所以大部分扣分并不来自语法,而是来自下面这些地方。
- 只写了
state: started,漏掉enabled: true,导致重启后服务没有启动 - firewalld 只用
permanent开放,导致现在没有开放;或者只用immediate开放,导致重启后消失 - 开启了 SELinux 布尔值,却没有
persistent,导致重启时恢复原状 - 无条件使用
command/shell,导致每次都出现 changed
最后一点尤其重要。不幂等的 playbook,就是每次运行都会改变点什么的 playbook。这样一来,就无法知道“这次运行改变了什么”,而且 handler 每次都会重启服务,造成不必要的中断。
步骤
- 在
/root/rhce/inventory.ini中,把这台主机放进[webservers]组,并创建/root/rhce/ansible.cfg。ansible webservers -m ping必须成功。 - 用
/root/rhce/users.yml创建webadmin组以及alice、bob用户(shell 为/bin/bash,附加组为webadmin)。请使用循环(loop)。 - 用
/root/rhce/service.yml安装httpd,并同时满足 started 和 enabled。curl http://127.0.0.1/必须返回 200。 - 用
/root/rhce/selinux.yml永久开启httpd_can_network_connect布尔值,并给/srv/web加上httpd_sys_content_t上下文。 - 用
/root/rhce/firewall.yml在防火墙中开放http服务。必须同时满足 runtime 和 permanent。 - 用
/root/rhce/templates/index.html.j2创建包含主机名的页面,并用/root/rhce/template.yml部署。请使用只在内容变化时才重启 httpd 的 handler。 - 创建调用以上全部内容的
/root/rhce/site.yml。然后故意把状态搞乱(停止服务、删除文件、删除账户)后运行两次,把changed的数量以changed_first=、changed_second=的形式写入/root/rhce/idempotent.txt。 第一次必须大于 0,第二次必须等于 0。如果不搞乱状态就去测量,第一次运行就是 0,这样就无法区分“修好之后变成了 0”和“本来就什么都没做”。 - 在
/root/rhce/report.md中写入selinux=Enforcing、idempotent=yes、playbooks=<개수>三行(最后一行的占位符为 playbook 的数量),以及说明。
参考
- SELinux 模块是
ansible.posix.seboolean和community.general.sefcontext。两个 collection 都需要,而且必须是与 ansible-core 版本匹配的版本——不匹配时只会给出警告,然后以couldn't resolve module/action报错退出,而这句话会让人怀疑是不是拼写错了。定义完上下文之后,必须把ansible.builtin.command: restorecon -R /srv/web与changed_when一起使用,才不会破坏幂等。 - firewalld 模块中
permanent和immediate是各自独立的。两者都为真,才是“现在已开放、重启后也开放”的状态。 - fact 写作
ansible_facts['hostname']或{{ ansible_hostname }}。 changed的数量用ansible-playbook site.yml | grep -oE 'changed=[0-9]+'统计。- 常见错误 1:无条件使用
command/shell。这样的任务始终是 changed。请用creates:、changed_when:给出条件。 - 常见错误 2:只写
state: started,漏掉enabled: true。现在能运行,但重启后不会启动。 - 常见错误 3:SELinux 布尔值漏掉
persistent: true。现在能开启,但重启时会恢复原状。 - 常见错误 4:两个任务以不同的内容管理同一个文件。 它们会互相撤销对方,两者永远都是 changed,而单独看每个任务都没有问题,所以最难找到。一个文件只能有一个所有者,如果用于引导,请用
force: false明确表示“仅在不存在时”。
inventory 与配置文件
在 /root/rhce/inventory.ini 中,把这台主机放进 [webservers] 组,并创建 /root/rhce/ansible.cfg。ansible webservers -m ping 必须成功。
以这台 VM 自己为目标。使用 ansible_connection=local 就可以不经过 SSH 直接完成。在 ansible.cfg 中写上 inventory 路径,就不必每次都加 -i。
创建用户和组
用 /root/rhce/users.yml 创建 webadmin 组以及 alice、bob 用户(shell 为 /bin/bash,附加组为 webadmin)。请使用循环(loop)。
先用 ansible.builtin.group,再用 loop 循环执行 ansible.builtin.user。在组不存在的状态下创建用户会失败。
服务必须满足两个条件
用 /root/rhce/service.yml 安装 httpd,并同时满足 started 和 enabled。curl http://127.0.0.1/ 必须返回 200。
state: started 管的是现在,enabled: true 管的是重启之后。两者是不同的事,也是考试中最常漏掉的。
SELinux 是开启的,而且真的会阻止
用 /root/rhce/selinux.yml 永久开启 httpd_can_network_connect 布尔值,并给 /srv/web 加上 httpd_sys_content_t 上下文。
布尔值用 ansible.posix.seboolean 加 persistent: true,文件上下文则在 community.general.sefcontext 之后使用 restorecon。
runtime 与 permanent 是不同的
用 /root/rhce/firewall.yml 在防火墙中开放 http 服务。必须同时满足 runtime 和 permanent。
permanent: true 作用于配置文件,immediate: true 作用于当前正在运行的防火墙。两者都要给出才算完成。
只在发生变化时才重启
用 /root/rhce/templates/index.html.j2 创建包含主机名的页面,并用 /root/rhce/template.yml 部署。请使用只在内容变化时才重启 httpd 的 handler。
在 template 任务上加 notify,并在 handlers 节中放置重启操作。handler 只在该任务为 changed 时才运行。
第二次运行必须是安静的
创建调用以上全部内容的 /root/rhce/site.yml。然后故意把状态搞乱(停止服务、删除文件、删除账户)后运行两次,把 changed 的数量以 changed_first=、changed_second= 的形式写入 /root/rhce/idempotent.txt。
第一次必须大于 0,第二次必须等于 0。如果不搞乱状态就去测量,第一次运行就是 0,这样就无法区分“修好之后变成了 0”和“本来就什么都没做”。
在 site.yml 中用 import_playbook 调用前面的 playbook,并运行两次。如果第二次的 changed 不是 0,就去找出那个任务。
学到了什么
在 /root/rhce/report.md 中写入 selinux=Enforcing、idempotent=yes、playbooks=<개수> 三行(最后一行的占位符为 playbook 的数量),以及说明。
除了 selinux=、idempotent=、playbooks= 这三行之外,还要整理出考试中会扣分的地方。