RHCE8-EX294 试题解析记录

RHCE-EX294


勘误:leancode@189.cn

合计 16 题,校验 16

建议完成时间:1 小时

【更新】修正部分文字描述和错误

【更新】更新了新题型


前置信息

将使⽤以下虚拟系统进⾏此考试:

System IP address Role
control.domainx.example.com 172.24.250.254 Ansinle Server
node1.domainx.example.com 172.25.250.9 Ansible Client
node2.domainx.example.com 172.25.250.10 Ansible Client
node3.domainx.example.com 172.25.250.11 Ansible Client
node4.domainx.example.com 172.25.250.12 Ansible Client
node5.domainx.example.com 172.25.250.13 Ansible Client

所有系统的root密码都是 redhat

请勿更改root密码。

安装和配置 Ansible

照下方所述,在控制节点 control.domainx.example.com 上安装和配置 Ansible:

  1. 安装所需的软件包
  2. 创建名为 /home/greg/ansible/inventory 的静态清单文件,以满足以下要求:
    • nodel 是dev 主机组的成员
    • node2 是 test 主机组的成员
    • node3 和 node4 是 prod 主机组的成员
    • node5 是 balancers 主机组的成员
    • prod 组是 webservers 主机组的成员
  3. 创建名为 /home/greg/ansible/ansible.cfg 的配置文件,以满足以下要求:
    • 机清单文件为 /home/greg/ansible/inventory
    • playbook 中使用的角色的位置包括 /home/greg/ansible/roles
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 连接到控制节点
ssh root@control
# 检查软件仓库
yum repolist
# 配置软件仓库 http://content.example.com/rhe18.0/x86_64/ucfupdates/
yum-config-manager --add-repo=http://content.example.com/rhel8.0/x86_64/ucfupdates/
echo "gpgcheck=0" >>/etc/yum.repo.d/content.example.com_rhe18.0_x86_64_ucfupdates.repo
# 安装软件包
yum install -y ansible
ansible --version
exit
# 使用普通用户登录
ssh control
mkdir ansible
cd ansible/
mkdir group_vars
vim inventory
vim ansible.cfg
vim group_vars/all

ansible/inventory完整文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
node1
node2
node3
node4
ndoe5

[dev]
node1

[test]
node2

[prod]
node3
node4

[balancers]
node5

[webservers:children]
prod

ansible/ansible.cfg 完整文件(远程使用普通用户)

1
2
3
4
5
6
7
8
9
10
11
[defaults]
inventory = /home/greg/ansible/inventory
roles_path = /home/greg/ansible/roles:/usr/share/ansible/roles
remote_user = greg
ask_pass = false

[privilege_escalation]
become=true
become_method=sudo
become_user=root
become_ask_pass=false

ansible/ansible.cfg 远程用户指定为 root 时,无需配置提权部分(使用如下的配置)

1
2
3
4
[defaults]
inventory = /home/greg/ansible/inventory
remote_user = root
roles_path = /home/greg/ansible/roles

ansible/group_vars/all完整文件(被控制节点未做免密登录时可以设置此变量)

1
ansible_ssh_pass: redhat
1
2
3
4
5
6
7
8
9
10
# 检查 inventory 文件
ansible -i inventory --list dev
ansible -i inventory --list test
ansible -i inventory --list prod
ansible -i inventory --list balancers
ansible -i inventory --list webservers
# 测试连接
ansible all -m ping
# 请确保显示的 id 为 root,以校验正确的提权
ansible all -a "id"

创建和使用 ansible 的临时命令

创建一个名为 /home/greg/ansible/adhoc.sh 的 shell 脚本该脚本将使用 Ansible 临时命在各个受管节点上安装 yum 存储库

存储库1:

  • 储库的名称为 EX294_BASE
  • 描述为 EX294 base software
  • BaseURL为 http://repo.domainx.example.com/BaseOS
  • GPG 签名检查为后用状态
  • GPG密钥URL为http://repo.domainx.example.com/RPM-GPG-KEYredhat-release
  • 存储库为启用状态-

存储库2:

  • 存储库的名称为 EX294_STREAM
  • 描述为 EX294 stream software
  • BaseURL为 http://rhgls.domainx.example.com/AppStream
  • GPG 签名检查为启用状态
  • GPG密钥 URL为http://rhgls.domainx.example.com/RPM-GPG-KEYredhat-release
  • 储库为启用状态

adhoc.sh 完整文件

1
2
3
#!/bin/bash
ansible all -m yum_repository -a 'name="EX294_BASE" description="EX294 base software" baseurl="http://repo.domainx.example.com/BaseOS" gpgcheck="yes" gpgkey="http://repo.domainx.example.com/RPM-GPG-KEYredhat-release"'
ansible all -m yum_repository -a 'name="EX294_STREAM" description="EX294 stream software" baseurl="http://rhgls.domainx.example.com.com/AppStream" gpgcheck="yes" gpgkey="http://repo.domainx.example.com/RPM-GPG-KEYredhat-release"'
1
2
3
chmod +x adhoc.sh
./adhoc.sh
ansible all -a 'yum repolist'

安装软件包

创建一个名为/home/greg/ansible/packages.yml的 playbook:

  1. 将 php 和 mariadb 软件包安装到 dev、test 和 prod 主机组中的主机上
  2. RPM Development Tools 软件包组安装到 dev 主机组中的主机上
  3. dev 主机组中主机上的所有软件包更新为最新版本

packages.yml 完整文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---
- name: install packages
hosts: dev,test,prod
tasks:
- name: instal pkgs task1
yum:
name:
- php
- mariadb
state: present

- name: install pkgs task2
hosts: dev
tasks:
- name: use yum moudle
yum:
name: "@RPM Development Tools"
state: latest
- name: update all pkgs
yum:
name: "*"
state: latest

另一种使用条件判断写法(2选1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---
- name: install packages
hosts: dev,test,prod
tasks:
- name: instal pkgs task1
yum:
name:
- php
- mariadb
state: present

- name: use yum moudle
yum:
name: "@RPM Development Tools"
state: latest
when: '"dev" in group_names'
- name: update all pkgs
yum:
name: "*"
state: latest
when: '"dev" in group_names'
1
2
3
4
ansible-playbook packages.yml
# 检查安装情况
ansible dev -m "shell" -a "yum group list"
ansibel dev,test,prod -m "shell" -a "rpm -qa php mariadb"

使用RHEL系统角色

安装 RHEL系统角色软件包,并创建符合以下条件的 playbook /home/greg/ansible/timesync.yml

  1. 在所有受管节点上运行
  2. 使用 timesync 角色
  3. 配置该角色,以使用当前有效的 NTP 提供商
  4. 配置该角色,以使用时间服务器 172.25.254.254
  5. 配置该角色,以后用 iburst 参数

创建符合以下条件的 playbook /home/greg/ansible/selinux.yml

  1. 在所有受管节点上运行
  2. 使用 selinux 角色
  3. 设置所有的节点 selinux 状态为 enforcing
1
2
3
4
5
# 安装 roles
sudo yum install -y rhel-sysyem-roles
mkdir roles
# 列出当前 roles
ansible-galaxy list

timesync.yml 完整文件

1
2
3
4
5
6
7
8
9
---
- name: use system roles
hosts: all
vars:
timesync_ntp_servers:
- hostname: 172.25.254.254
iburst: yes
roles:
- rhel-system-roles.timesync
1
2
3
# 执行过程中的错误可以省略
ansible-playbook timesync.yml
ansible all -m "shell" -a "chronyc -n sources"

/home/greg/ansible/selinux.yml 完整文件

1
2
3
4
5
6
7
8
---
- name: set selinux
hosts: all
vars:
selinux_policy: targeted
selinux_state: enforcing
roles:
- rhel-system-roles.selinux
1
2
3
ansible-playbook selinux.yml
# 检查
ansible all -m shell -a "grep -v # /etc/selinux/config"

使用AnsibleGalaxy安装角色

用 Ansible Galaxy 安装角色:

  1. 新建 playbook为 /home/greg/ansible/requirements.yml。从以下URL下载并安装到 /home/greg/ansible/roles:
  2. http://rhgls.domainx.example.com/materials/haproxy.tar 此角色的名字设置为balancer
  3. http://rhgls.domainx.example.com/materials/phpinfo.tar 此角色的名字设置为phpinfo

requirements.yml 完整文件

1
2
3
4
5
- src: http://rhgls.domainx.example.com/materials/haproxy.tar
name: balancer

- src: http://rhgls.domainx.example.com/materials/phpinfo.tar
name: phpinfo
1
2
3
4
5
ansible-galaxy install -r requirements.yml
ansible-galaxy list
# 注意复制一份到以下目录
cp requirements.yml roles/
ls roles/

创建一个 web role

根据下列要求,在 /home/greg/ansible/roles 中创建名为 apache 的角色:

  1. 装 httpd 包,并启动 httpd 服务,设置服务下次开机启动
  2. 启动防火墙服务,设置防火墙服务下次开机启动,放行web流量
  3. 创建 index.html.j2文件,该模板文件用于输出如下文件 /var/www/html/index.html
    • Welcome to HOSTNAME ON IPADDRESS
    • HOSTNAME 是受管节点的 FQDN
    • IPADDRESS 则是受管节点的 IP 地址
  4. 创建 playbook /home/greg/ansible/apache.yml 在 webservers 主机组使用 apache 的角色
1
2
cd roles/
ansible-galaxy init apache

Changed roles/apache/tasks/main.yml

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
26
27
28
29
--- 
- name: install pkgs
yum:
name: httpd
state: latest

- name: enable service
service:
name: httpd
state: started
enabled: yes

- name: enable firewalld service
service:
name: firewalld
state: started
enabled: yes

- name: set firewalld
firewalld:
service: http
immediate: yes
permanent: yes
state: enabled

- name: set pages
template:
src: index.html.j2
dest: /var/www/html/index.html

Create roles/apache/templates/index.html.j2

1
Welcome to {{ ansible_fqdn }} on {{ ansible_default_ipv4['address'] }}

Changed apache.yml

1
2
3
4
5
--- 
- name: use apache roles
hosts: webservers
roles:
- apache
1
2
3
ansible-playbook apache.yml
curl node3
curl node4

从 Ansible Galaxy 使用角色

根据下列要求,创建一个名为 /home/greg/ansible/roles.yml的 playbook:

  1. playbook 中包含一个 play,该 play 在 balancers 主机组中的主机上运行并将使用 balancer 角色
  2. 此角色配置一项服务,以在 webservers 主机组中的主机之间平衡 Web 服务器请求的负载
  3. 浏览到 balancers 主机组中的主机(例如 http://node5.domainx.example.com/)将生成以下输出
  4. 新加载浏览器将从另一 Web 服务器生成输出Welcome to node4.domainx.example.com on 172.25.250.12
  5. playbook 中包含一个 play,该 play 在 webservers 主机组中的主机上运行并使用 phpinfo 角色通过 URL/hello.php 的到 webservers 主机组中的主机将生成以下输出
    • Hello PHP World from FQDN
    • 其中,FQDN 是主机的完全限定名称
  6. 浏览到 http://node3.example.com/hello.php 会生成以下输出:
  7. 浏浏览到 http://node4.example.com/hello.php 会生成以下输出:
    • Hello PHP World from node4.domainx.example.com
    • 另外还有 PHP 配置的各种详细信息,如安装的 PHP 版本等

roles.yml 完整文件

注:先执行 phpinfo role 以避免错误

1
2
3
4
5
6
7
8
9
---
- name: use phpinfo role
hosts: webservers
roles:
- phpinfo
- name: use haproxy role
hosts: balancers
roles:
- balancer
1
2
3
4
5
6
7
8
9
10
11
12
13
# 手动关闭 node5 防火墙
ssh root@node5
firewall-cmd --list-all
firewall-cmd --add-port=80/tcp
firewall-cmd --add-port=80/tcp --per
exit
ansible-playbook roles.yml
# 应产生两次不同的输出
curl node5
curl node5
# 或者在浏览器中访问
curl node3/hello.php
curl node4/hello.php

创建和使用逻辑卷

创建一个叫做 lv.yml的playbook,它将在所有被管理节点上执行下列任务:

  1. 创建逻辑卷
    • 通过 research 卷组创建逻辑卷
    • 逻辑卷的名称叫做 data
    • 逻辑卷大小是 1500MB
  2. 格式化逻辑卷为ext4文件系统
  3. 如果逻卷不能被创建(由于大小不满足),应显示误 “Could not create logical volume of that size”,然后创建 800MB 代替
  4. 如果卷组 research 不存在,应显示错误 “Volume group does not exist”
  5. 不要以任何方式挂载逻辑卷

lv.yml 完整文件

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
26
27
28
29
30
31
32
33
34
---
- name: create lv
hosts: all
tasks:
- block:
- name: create a lv use research vg
lvol:
vg: research
lv: data
size: 1500
- name: format ext4 fs
filesystem:
fstype: ext4
dev: /dev/research/data
rescue:
- name: output some info
debug:
msg: "Could not create logical volume of that size"
when: ansible_lvm.vgs.research is defined
- name: create a lv use research vg
lvol:
vg: research
lv: data
size: 800
when: ansible_lvm.vgs.research is defined
- name: format ext4 fs
filesystem:
fstype: ext4
dev: /dev/research/data
when: ansible_lvm.vgs.research is defined
- name: output some info
debug:
msg: "Volume group does not exist"
when: ansible_lvm.vgs.research is undefined
1
2
3
ansible all -a 'vgs'
ansible-playbook lv.yml
ansible all -a 'lvs'

生成主机文件

  1. 下载http://rhgls.domainx.example.com/materials/hosts.j2到 /home/greg/ansible

  2. 修改模板文件,让他能用来为每个 inventory 主机生成和 /etc/hosts 同样格式的文件

  3. 创建名为 home/greg/ansible/hosts.yml 的 playbook,它将使用此模板在 dev 主机组中的主机上生成文件 /etc/myhosts

  4. 该 playbook 运行后,dev 主机组中主机上的文件 /etc/myhosts 应针对每个受管主机包含一行内容

    1
    2
    3
    4
    5
    6
    7
    127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
    172.25.250.9 node1.domainx.example.com nodel
    172.25.250.10 node2.domainx.example.com node2
    172.25.250.11 node3.domainx.example.com node3
    172.25.250.12 node4.domainx.example.com node4
    172.25.250.13 node5.domainxexample.com node5

Download and changed /home/greg/ansible/hosts.j2

1
wget http://rhgls.domainx.example.com/materials/hosts.j2
1
2
3
4
5
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
{% for host in groups['all'] %}
{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ hostvars[host]['ansible_fqdn'] }} {{ hostvars[host]['ansible_hostname'] }}
{% endfor %}

hosts.yml

1
2
3
4
5
6
7
8
9
---
- name: deploy
hosts: all
tasks:
- name: template a host file
template:
src: hosts.j2
dest: /etc/myhosts
when: '"dev" in group_names'
1
2
ansible-playbook hosts.yml
ansible dev -a 'cat /etc/myhosts'

修改文件内容

按照下方所述,创建一个名为 /home/greg/ansible/issue.yml 的 playbook:

  1. playbook 将在所有 inventory 主机上运行
  2. 该 playbook 会将 /etc/issue 的内容替换为下方所示的一行文本:
    • 在 dev 主机组中的主机上,这文本显示 为:Development
    • 在 test 主机组中的主机上,这行文本显示为:Test
    • 在 prod 主机组中的主机上,这行文本显示 为:Production

issue.yml 完整文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---
- name: modify file content
hosts: all
tasks:
- copy:
content: Development
dest: /etc/issue
when: '"dev" in group_names'
- copy:
content: Test
dest: /etc/issue
when: '"test" in group_names'
- copy:
content: Production
dest: /etc/issue
when: '"prod" in group_names'
1
2
ansible-playbook issue.yml
ansible all -a 'cat /etc/issue'

创建web内容目录

按照下方所述,创建一个名为 /home/greg/ansible/webcontent.yml的 playbook

  1. 该 playbook 在 dev 主机组中的受管节点上运行
  2. 建符合下列要求的目录/webdev
    • 所有者为 webdev 组
    • 具有常规权限:owner=rwx, group=rwx ,other=rx
    • 有特殊权限:set gid
  3. 用符号链接将 /var/www/html/webdev 链接到 /webdev
  4. 创建文件 /webdev/index.html,其中包合如下所示的单行文件: Development
  5. 在 dev 主机组中主机上浏览此目录(例如 http://node1.domainx.example.com/webdev) 将生成以下输出:Development

webcontent.yml 完整文件

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
26
27
28
29
30
31
---
- name: set web content
hosts: dev
tasks:
- name: create a directory
file:
path: /webdev
state: directory
group: webdev
mode: "2775"
- name: create a soft link
file:
src: /webdev
dest: /var/www/html/webdev
state: link
- name: set web content
copy:
content: Development
dest: /webdev/index.html
setype: "httpd_sys_content_t"
- name: start httpd service
service:
name: httpd
state: started
enabled: yes
- name: set firewall rule to allow http traffix
firewalld:
service: http
permanent: yes
immediate: yes
state: enabled
1
2
ansible-playbook webcontent.yml
curl -L node1/webdev

生成硬件报告

  1. 新建一个名为home/greg/ansible/hwreport.yml的 playbook,它将在所有受管节点上生成含有以下信息的输出文件 root/hwreport.txt

    • 清单主机名称

    • 以 MB 表示的总内存大小

    • BIOS 版本

    • 磁盘设备 vda 的大小

    • 磁盘设备 vdb 的大小

    • 输出文件中的每一行含有一个 key=value对

  2. playbook 应当

    • http://rhgls.domainx.example.com/materials/hwreport.empty 下载文件,并将它保存为/root/hwreport.txt
    • 使用正确的值改为 /root/hwreport.txt
    • 如果硬件项不存在,相关的值应设为 NONE

hwreport.yml 完整文件

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
26
---
- name: create hardware report
hosts: all
vars:
hardware:
- hw_name: HOST
hw_info: "{{ ansible_hostname }}"
- hw_name: MEMORY
hw_info: "{{ ansible_memtotal_mb }}"
- hw_name: BIOS
hw_info: "{{ ansible_bios_version }}"
- hw_name: DISK_SIZE_VDA
hw_info: "{{ ansible_devices['vda']['size'] | default('NONE') }}"
- hw_name: DISK_SIZE_VDB
hw_info: "{{ ansible_devices['vdb']['size'] | default('NONE') }}"
tasks:
- name: get hw report from url
get_url:
url: http://rhgls.domainx.example.com/materials/hwreport.empty
dest: /root/hwreport.txt
- name: set hw report content
lineinfile:
path: /root/hwreport.txt
regexp: "^{{ item['hw_name'] }}="
line: "{{ item['hw_name'] }}={{ item['hw_info'] }}"
loop: "{{ hardware }}"
1
2
ansible-playbook hwreport.yml
ansible all -a 'cat /root/hwreport.txt'

使用Ansible Vault

  1. vault 的名字是 locker.yml
  2. vault 包含两个变量
    • pw_developer 的值是 Imadev
    • pw_manager 的值是 Imamgr
  3. 加密解密的密码为whenyouwishuponastar
  4. 密码存放在/home/greg/ansible/secret.txt
1
2
3
4
5
6
7
8
cat > locker.yml <<EOF
pw_developer: Imadev
pw_manager: Imamgr
EOF
echo "whenyouwishuponastar" > secret.txt
ansible-vault encrypt --vault-id=./secret.txt locker.yml
cat locker.yml
ansible-vault view --vault-id=./secret.txt locker.yml

创建批量添加用户role

  1. http://rhgls.domainx.example.com/materials中下载user_list.yml到/home/greg/ansible中
  2. 用 locker.yml 变量文件,创建 users.yml 来创建账号
    • 带 developer 描述的 job 的用户应该满足如下条件
      • 在 dev 和 test 主机组下创建用户
      • 设置密码为 pw_developer 变量的值
      • 附加组为 devops
    • 带 manager 描述的 job 的用户应该满足如下条件
      • 在prod主机组下创建用户
      • 设置密码为 pw_manager 变量的值
      • 附加组为 opsmgr
  3. 密码应该是 SHA512 的格式
  4. playbook 应该使用 vault 密码文件来工作

users.yml

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
26
27
28
29
30
31
32
33
34
35
---
- name: create user on dev and test
hosts: dev,test
vars_files:
- locker.yml
- user_list.yml
tasks:
- name: create group
group:
name: devops
- name: create user
user:
name: "{{ item['name'] }}"
password: "{{ pw_developer | password_hash('sha512','mysecretsalt') }}"
groups: devops
loop: "{{ users }}"
when: item.job == 'developer'

- name: create user on prod
hosts: prod
vars_files:
- locker.yml
- user_list.yml
tasks:
- name: create group
group:
name: opsmgr
- name: create user
user:
name: "{{ item['name'] }}"
password: "{{ pw_manager | password_hash('sha512','mysecretsalt') }}"
uid: 6666
groups: opsmgr
loop: "{{ users }}"
when: item.job == 'manager'
1
2
3
4
wget http://rhgls.domainx.example.com/materials/user_list.yml
ansible-playbook --vault-id=./secret.txt users.yml
ansible dev -a 'id gzy001'
ansible prod -a 'id gzy101'

重新设置 AnsibleVault 密码

  1. 下载 http://rhgls.domainx.example.com/materials/salaries.yml保存到 /home/greg/ansible/salaries.yml
  2. 当前的密码是 insecure4sure
  3. 新的密码是 bbe2de98389b
  4. vault文件使用新的密码保持为加密的状态
1
2
3
4
5
6
7
wget http://rhgls.domainx.example.com/materials/salaries.yml
ansible-vault view salaries.yml
> insecure4sure
ansible-vault rekey salaries.yml
> bbe2de98389b
ansible-vault view salaries.yml
> bbe2de98389b

创建定时任务

使用 crod 模块在所有节点给用户 natasha 创建一个任务,每两分钟运行一条命令

crond.yml 完整文件

1
2
3
4
5
6
7
8
9
10
11
12
---
- name:
hosts: all
tasks:
- name: create user
user:
name: natasha
- name: set cron
cron:
user: natasha
minute: "*/2"
job: logger "EX294 in progress"
1
2
ansible-playbook crond.yml
ansible all -m shell -a "crontab -l -u natasha"