如何在ansible中统一包安装任务?

如何在ansible中统一包安装任务?

我开始ansible并将使用它在多个 Linux 发行版上安装软件包。

我在文档中看到yumapt命令是分开的 - 将它们统一起来并使用类似这样的最简单方法是什么:

- name: install the latest version of Apache
  unified_install: name=httpd state=latest

代替

- name: install the latest version of Apache on CentOS
  yum: name=httpd state=latest
  when: ansible_os_family == "RedHat"

- name: install the latest version of Apache on Debian
  apt: pkg=httpd state=latest 
  when: ansible_os_family == "Debian"

我知道这两个包管理器是不同的,但它们仍然有一套共同的基本用法。其他编排器(例如盐) 有一个安装命令。

答案1

更新:从 Ansible 2.0 开始,现在有一个通用且抽象的package模块

使用示例:

现在,当不同操作系统系列中的软件包名称相同时,就很简单了:

---
- name: Install foo
  package: name=foo state=latest

当软件包名称在不同的操作系统系列中有所不同时,您可以使用发行版或操作系统系列特定的 vars 文件来处理它:

---
# roles/apache/apache.yml: Tasks entry point for 'apache' role. Called by main.yml
# Load a variable file based on the OS type, or a default if not found.
- include_vars: "{{ item }}"
  with_first_found:
    - "../vars/{{ ansible_distribution }}-{{ ansible_distribution_major_version | int}}.yml"
    - "../vars/{{ ansible_distribution }}.yml"
    - "../vars/{{ ansible_os_family }}.yml"
    - "../vars/default.yml"
  when: apache_package_name is not defined or apache_service_name is not defined

- name: Install Apache
  package: >
    name={{ apache_package_name }}
    state=latest

- name: Enable apache service
  service: >
    name={{ apache_service_name }}
    state=started
    enabled=yes
  tags: packages

然后,对于您必须以不同方式处理的每个操作系统...创建一个 vars 文件:

---
# roles/apache/vars/default.yml
apache_package_name: apache2
apache_service_name: apache2

---
# roles/apache/vars/RedHat.yml
apache_package_name: httpd
apache_service_name: httpd

---
# roles/apache/vars/SLES.yml
apache_package_name: apache2
apache_service_name: apache2

---
# roles/apache/vars/Debian.yml
apache_package_name: apache2
apache_service_name: apache2

---
# roles/apache/vars/Archlinux.yml
apache_package_name: apache
apache_service_name: httpd



编辑: 自 Michael DeHaan(Ansible 的创建者)以来选择不抽象包管理器模块喜欢厨师做,

如果你仍在使用旧版本的 Ansible(Ansible <2.0),不幸的是你需要处理这个全部你的剧本和角色。 恕我直言这会将许多不必要的重复工作推到剧本和角色作者身上……但目前情况就是这样。请注意,我并不是说我们应该尝试抽象包管理器,同时仍然尝试支持其所有特定选项和命令,而只是提供一种简单的方法来安装与包管理器无关的包。我也不是说我们都应该跳上智能包裹管理器潮流,但配置管理工具中的某种软件包安装抽象层对于简化跨平台剧本/手册非常有用。Smart 项目看起来很有趣,但它在尚未被广泛采用的情况下,在发行版和平台上统一软件包管理是一项雄心勃勃的任务……看看它是否成功将会很有趣。真正的问题是,软件包名称有时在发行版之间会有所不同,因此我们仍然必须使用 case 语句或when:语句来处理差异。

我处理这个问题的方式是tasks在剧本或角色中遵循这个目录结构:

roles/foo
└── tasks
    ├── apt_package.yml
    ├── foo.yml
    ├── homebrew_package.yml
    ├── main.yml
    └── yum_package.yml

然后在我的main.yml

---
# foo: entry point for tasks
#                 Generally only include other file(s) and add tags here.

- include: foo.yml tags=foo

以下为foo.yml(针对包‘foo’):

---
# foo: Tasks entry point. Called by main.yml
- include: apt_package.yml
  when: ansible_pkg_mgr == 'apt'
- include: yum_package.yml
  when: ansible_pkg_mgr == 'yum'
- include: homebrew_package.yml
  when: ansible_os_family == 'Darwin'

- name: Enable foo service
  service: >
    name=foo
    state=started
    enabled=yes
  tags: packages
  when: ansible_os_family != 'Darwin'

然后对于不同的包管理器:

易于:

---
# tasks file for installing foo on apt based distros

- name: Install foo package via apt
  apt: >
    name=foo{% if foo_version is defined %}={{ foo_version }}{% endif %}
    state={% if foo_install_latest is defined and foo_version is not defined %}latest{% else %}present{% endif %}
  tags: packages

好吃:

---
# tasks file for installing foo on yum based distros
- name: Install EPEL 6.8 repos (...because it's RedHat and foo is in EPEL for example purposes...)
  yum: >
    name={{ docker_yum_repo_url }}
    state=present
  tags: packages
  when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 6

- name: Install foo package via yum
  yum: >
    name=foo{% if foo_version is defined %}-{{ foo_version }}{% endif %}
    state={% if foo_install_latest is defined and foo_version is not defined %}latest{% else %}present{% endif %}
  tags: packages

- name: Install RedHat/yum-based distro specific stuff...
  yum: >
    name=some-other-custom-dependency-on-redhat
    state=latest
  when: ansible_os_family == "RedHat"
  tags: packages

自制:

---
- name: Tap homebrew foobar/foo
  homebrew_tap: >
    name=foobar/foo
    state=present

- homebrew: >
    name=foo
    state=latest

请注意,这是非常重复的,并且不是干燥,尽管有些事情可能在不同的平台上会有所不同,并且必须进行处理,一般来说,我认为这与 Chef 相比是冗长和笨拙的:

package 'foo' do
  version node['foo']['version']
end

case node["platform"]
when "debian", "ubuntu"
  # do debian/ubuntu things
when "redhat", "centos", "fedora"
  # do redhat/centos/fedora things
end

是的,有一种观点认为一些不同发行版的软件包名称有所不同。尽管目前有一个缺乏易于获取的数据,我大胆猜测最多流行的软件包名称在各个发行版中很常见,可以通过抽象的软件包管理器模块安装。无论如何,特殊情况都需要处理,而且已经需要额外的工作,使事情变得不那么 DRY 如果有疑问,请检查pkgs.org

答案2

你可以通过事实抽象出包管理器

- name: Install packages
  with_items: package_list
  action: "{{ ansible_pkg_mgr }} state=installed name={{ item }}"

ansible_pkg_mgr您所需要的只是一些设置为aptyum等的逻辑。

Ansible也正在努力在未来的模块中做你想做的事情

答案3

从 Ansible 2.0 开始有了新的Package-modul。

http://docs.ansible.com/ansible/package_module.html

然后您可以像您的提议那样使用它:

- name: install the latest version of Apache
  package: name=httpd state=latest

您仍然需要考虑名称的差异。

答案4

您不想这样做,因为某些软件包名称在不同的发行版之间有所不同。例如,在与 RHEL 相关的发行版中,流行的 Web 服务器软件包名为httpd,而在与 Debian 相关的发行版中,它名为apache2。其他系统和支持库的庞大列表也是如此。

可能会有一组通用的基本参数,但也有一些更高级的参数在不同的包管理器之间有所不同。你肯定不希望出现这样一种模棱两可的情况:对于某些命令,你使用一种语法;而对于其他命令,你使用另一种语法。

相关内容