有时我们需要部署无法访问网络的服务器,这时候使用Ansible的command模块执行rpm命令。而使用rpm命令的最大缺点是第2次执行时会报错,因为该模块已被安装。介绍在Ansible上使用rpm命令安装软件包时,多次执行时不重复安装软件包的方法。
目录结构
编写Ansible代码时,基本使用Role进行开发。创建标准Role的目录结构的方法,参照 自动化工具Ansible入门
Role的目录结构如下。
oracle_client/
|-- README.md
|-- defaults
| `-- main.yml
|-- files
| |-- libaio-0.3.112-1.el8.x86_64.rpm
| |-- libnsl-2.28-189.1.el8.x86_64.rpm
| |-- oracle-instantclient19.18-basic-19.18.0.0.0-1.x86_64.rpm
| |-- oracle-instantclient19.18-sqlplus-19.18.0.0.0-1.x86_64.rpm
| `-- oracle-instantclient19.18-tools-19.18.0.0.0-1.x86_64.rpm
|-- handlers
| `-- main.yml
|-- meta
| `-- main.yml
|-- tasks
| `-- main.yml
|-- templates
|-- tests
| |-- inventory
| `-- test.yml
`-- vars
`-- main.ym
实际使用的目录为下面3个。
– defaults/main.yml 文件定义,需要安装的rpm信息
– files目录下保存将要安装的rpm文件
– tasks/main.yml 编写Ansible代码
defaults/main.yml文件
在这里定义需要安装的rpm package一览及oracle_client Role使用的变量。
安装的Oracle Client的版本为19.18(19c),需要安装的rpm package有5个,在这里将oracle_client_packages定义为字典类型。
---
# defaults file for oracle_client
oracle_client_version: "19.18"
oracle_client_packages:
- package: libaio-0.3.112-1.el8.x86_64
- package: libnsl-2.28-189.1.el8.x86_64
- package: oracle-instantclient19.18-basic-19.18.0.0.0-1.x86_64
- package: oracle-instantclient19.18-sqlplus-19.18.0.0.0-1.x86_64
- package: oracle-instantclient19.18-tools-19.18.0.0.0-1.x86_64
tasks/main.yml文件
第1步使用 rpm -qa 命令查询服务器上安装rpm package一览,并将结果保存到installed_packages变量。
第2步可使用debug模块为了查看installed_packages的内容,实际运行时可注释掉。
第3步,使用 with_items 读取在 defaults/main.yml 上定义的oracle_client_packages变量。
使用 when 条件判定查看该模块是否已安装。installed_packages.stdout_lines保存着已安装的所有package名,当将要安装的package名不存在(not in)于该一览时,执行rpm命令。
- name: Gather all installed package facts
command: rpm -qa
register: installed_packages
- name: debug
debug:
var: installed_packages
- name: Install oracle client packages
command: "rpm -ivh /tmp/{{ item['package'] }}.rpm"
when: 'item["package"] not in installed_packages.stdout_lines'
with_items: "{{ oracle_client_packages }}"
执行Ansible playbook(第1次)
未安装Oracle Client的rpm package时执行Ansible Playbook的结果如下。
# ansible-playbook -i hosts/test playbook.yml
PLAY [Setup oracle client server] *******************************************************************************
TASK [oracle_client : Gather all installed package facts] ****************************************************
changed: [XXX.XXX.XXX.XXX]
TASK [oracle_client : debug] *********************************************************************************
ok: [XXX.XXX.XXX.XXX] => {
"installed_packages": {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"cmd": [
"rpm",
"-qa"
],
"delta": "0:00:00.701150",
"end": "2023-01-28 12:53:44.500712",
"failed": false,
"msg": "",
"rc": 0,
"start": "2023-01-28 12:53:43.799562",
"stderr": "",
"stderr_lines": [],
"stdout": "python3-syspurpose-1.28.29-3.el8.x86_64\ngeolite2-country-20180605-1.el8.noarch\npython3-unbound-1.7.3-17.el8.x86_64~省略~",
"stdout_lines": [
"python3-syspurpose-1.28.29-3.el8.x86_64",
~省略~
"polkit-pkla-compat-0.1-12.el8.x86_64"
]
}
}
TASK [oracle_client : Install oracle client packages] ********************************************************
changed: [XXX.XXX.XXX.XXX] => (item={'package': 'libaio-0.3.112-1.el8.x86_64'})
changed: [XXX.XXX.XXX.XXX] => (item={'package': 'libnsl-2.28-189.1.el8.x86_64'})
changed: [XXX.XXX.XXX.XXX] => (item={'package': 'oracle-instantclient19.18-basic-19.18.0.0.0-1.x86_64'})
changed: [XXX.XXX.XXX.XXX] => (item={'package': 'oracle-instantclient19.18-sqlplus-19.18.0.0.0-1.x86_64'})
changed: [XXX.XXX.XXX.XXX] => (item={'package': 'oracle-instantclient19.18-tools-19.18.0.0.0-1.x86_64'})
PLAY RECAP ***************************************************************************************************
XXX.XXX.XXX.XXX : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
执行结果里需要关注的内容如下。
- stdout:debug输出的installed_packages内容,已被安装的rpm package一览(没有换行)
- stdout_lines:debug输出的installed_packages内容,已被安装的rpm package一览(列表),可使用installed_packages.stdout_lines进行访问
- PLAY RECAP:执行结果概要,
- ok=3 3个步骤的执行结果为
- changed=2 rpm -qa命令和rpm -ivh命令成功执行
- unreachable=0 无法访问的服务器的数量
- failed=0 执行失败的task
- skipped=0 未满足条件而被skipped的task数量
- rescued=0 block模块里可使用rescued和always,一般为0
- ignored=0 在task里不写ignore_erros=True的话,一般为0
执行Ansible playbook(第2次)
rpm package已安装的情况下,再次执行该Ansible Playbook(debug模块除外)。
# ansible-playbook -i hosts/test playbook.yml
PLAY [Setup oracle client server] *******************************************************************************
TASK [oracle_client : Gather all installed package facts] ****************************************************
changed: [XXX.XXX.XXX.XXX]
TASK [oracle_client : Install oracle client packages] ********************************************************
skipping: [XXX.XXX.XXX.XXX] => (item={'package': 'libaio-0.3.112-1.el8.x86_64'})
skipping: [XXX.XXX.XXX.XXX] => (item={'package': 'libnsl-2.28-189.1.el8.x86_64'})
skipping: [XXX.XXX.XXX.XXX] => (item={'package': 'oracle-instantclient19.18-basic-19.18.0.0.0-1.x86_64'})
skipping: [XXX.XXX.XXX.XXX] => (item={'package': 'oracle-instantclient19.18-sqlplus-19.18.0.0.0-1.x86_64'})
skipping: [XXX.XXX.XXX.XXX] => (item={'package': 'oracle-instantclient19.18-tools-19.18.0.0.0-1.x86_64'})
PLAY RECAP ***************************************************************************************************
XXX.XXX.XXX.XXX : ok=1 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
可确认到Oracle Client需要的5个package已安装,因此第2次执行Ansible Playbook时rpm package的安装task被skip了。
这么做的好处是,可保持Ansible的幂等性,同样task无论执行多少次都可以得到想要的结果。