Como unificar tarefas de instalação de pacotes no ansible?

68

Estou começando com ansible e o utilizarei, entre outros, para instalar pacotes em várias distribuições Linux.

Vejo nos documentos que os comandos yume aptsão separados - qual seria a maneira mais fácil de unificá-los e usar algo como isto:

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

ao invés de

- 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"

Entendo que os dois gerenciadores de pacotes são diferentes, mas ainda têm um conjunto de usos básicos comuns. Outros orquestradores ( salt, por exemplo ) têm um único comando de instalação.

WoJ
fonte
Você pode ter três receitas: uma que itere sobre uma lista comum e, em seguida, uma para listas específicas do SO. O que estou tentando descobrir agora é como notificar um manipulador com um nome de serviço específico do SO depois que um item de configuração comum é definido. boa sorte!
Dannyman 7/10

Respostas:

66

Atualização: A partir do Ansible 2.0, agora existe um módulo genérico e abstratopackage

Exemplos de uso:

Agora, quando o nome do pacote é o mesmo em diferentes famílias de sistemas operacionais, é tão simples quanto:

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

Quando o nome do pacote difere entre as famílias de SO, você pode manipulá-lo com arquivos de distribuição ou vars específicos da família de SO:

---
# 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

Em seguida, para cada sistema operacional com o qual você deve lidar de maneira diferente ... crie um arquivo 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



EDIT: Como Michael DeHaan (criador do Ansible) optou por não abstrair os módulos do gerenciador de pacotes, como o Chef faz,

Se você ainda estiver usando uma versão mais antiga do Ansible (Ansible <2.0) , infelizmente precisará lidar com isso em todos os seus playbooks e funções. IMHO isso empurra um monte de trabalho repetitivo desnecessário para playbook e autores de papéis ... mas é do jeito que é atualmente. Observe que não estou dizendo que devemos tentar abstrair os gerenciadores de pacotes enquanto ainda tentamos oferecer suporte a todas as opções e comandos específicos, mas apenas temos uma maneira fácil de instalar um pacote que seja independente do gerenciador de pacotes. Também não estou dizendo que todos devemos entrar no Gerenciador de Pacotes Inteligentesbandwagon, mas que algum tipo de camada de abstração da instalação de pacotes em sua ferramenta de gerenciamento de configuração é muito útil para simplificar playbooks / livros de receitas entre plataformas. O projeto Smart parece interessante, mas é bastante ambicioso unificar o gerenciamento de pacotes em distribuições e plataformas sem muita adoção ainda ... será interessante ver se é bem-sucedido. O problema real é que os nomes dos pacotes às vezes tendem a ser diferentes nas distribuições, portanto ainda precisamos fazer declarações ou when:declarações de casos para lidar com as diferenças.

A maneira como tenho lidado com isso é seguir essa tasksestrutura de diretórios em um manual ou função:

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

E então tenho isso no meu main.yml:

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

- include: foo.yml tags=foo

Isto em foo.yml(para o pacote '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'

Depois, para os diferentes gerenciadores de pacotes:

Apt:

---
# 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

Yum:

---
# 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

Homebrew:

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

- homebrew: >
    name=foo
    state=latest

Observe que isso é muito repetitivo e não SECO , e embora algumas coisas possam ser diferentes nas diferentes plataformas e tenham que ser tratadas, geralmente acho que isso é detalhado e pesado quando comparado ao 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

E sim, há o argumento de que alguns nomes de pacotes são diferentes nas distribuições. E, embora atualmente haja uma falta de dados facilmente acessíveis , arrisco-me a adivinhar que os nomes de pacotes mais populares são comuns em distribuições e podem ser instalados por meio de um módulo gerenciador de pacotes abstraído. Casos especiais precisariam ser tratados de qualquer maneira e já exigiriam trabalho extra para tornar as coisas menos secas . Em caso de dúvida, consulte pkgs.org .

TrinitronX
fonte
Com o Ansible 2, você pode usar o módulo do pacote para abstrair tudo isso. Docs.ansible.com/ansible/package_module.html
Guido
@ GuidoGarcía: Muito bom! Adicionando uma observação sobre isso para Ansible 2.0
TrinitronX
Talvez também valha a pena mencionar que você pode especificar uma lista separada por vírgula ou apenas uma lista de pacotes.
Wes Turner
13

Você pode abstrair os gerenciadores de pacotes por meio de fatos

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

Tudo o que você precisa é de uma lógica que se ajuste ansible_pkg_mgra aptou yumetc.

A Ansible também está trabalhando para fazer o que você deseja em um módulo futuro .

xddsg
fonte
11
Ansible define- ansible_pkg_mgrse para qualquer empacotador que conhece. Não é necessário fazer nada. Eu uso esse construto específico em qualquer lugar.
Michael Hampton
A sintaxe ainda é bastante útil para quem deseja otimizar a execução de seus playbooks. O módulo de pacote genérico ainda não fornece otimização para with_items, portanto, é muito mais lento quando é usado para instalar vários pacotes de uma só vez.
Danila Vershinin
@DanielV. Observe que o problema do github fornece uma solução alternativa para isso.
Michael Hampton
6

Do Ansible 2.0, existe o novo Package-modul.

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

Você pode usá-lo como sua proposta:

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

Você ainda precisa considerar diferenças de nome.

Tvartom
fonte
3

Confira a documentação da Ansible sobre importações condicionais .

Uma tarefa para garantir que o apache esteja em execução, mesmo que os nomes dos serviços sejam diferentes em cada sistema operacional.

---
- hosts: all
  remote_user: root
  vars_files:
    - "vars/common.yml"
    - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
  tasks:
  - name: make sure apache is running
    service: name={{ apache }} state=running
David Vasandani
fonte
2

Você não deseja fazer isso porque certos nomes de pacotes diferem entre as distribuições. Por exemplo, nas distros relacionadas ao RHEL, o popular pacote de servidores da web é chamado httpd, e nas distros relacionadas ao Debian apache2. Da mesma forma, com uma enorme lista de outros sistemas e bibliotecas de suporte.

Pode haver um conjunto de parâmetros básicos comuns, mas também há vários parâmetros mais avançados que são diferentes entre os gerenciadores de pacotes. E você não quer estar em uma situação ambígua em que, para alguns comandos, você use uma sintaxe e para outros comandos, use outra sintaxe.

Mxx
fonte
Isso é mais ou menos o que eu estava esperando (infelizmente :)), então eu me pergunto como saltconsegue unificar os dois gerenciadores de pacotes. De qualquer forma, vou recorrer a uma configuração dupla, então.
woj
Ou não gerencie um zoológico de distribuição ;-) migre para uma infraestrutura de distribuição única e viva uma vida mais feliz.
Mxx
O zoológico é, felizmente, apenas dois animais grandes, mas este é o número mais baixo que eu posso ir para :)
woj
11
@Mxx, essa é uma boa lógica para um administrador de sistemas, mas e um fornecedor ou consultor de software que oferece suporte a várias plataformas?
David H. Bennett
@ David, isso precisa ser tratado com os distribuidores, para que eles tenham nomes de pacotes unificados e instalem ferramentas. Realmente não há como o Ansible possa ter um mapeamento unificado de TODOS os pacotes de todas as distribuições suportadas de todas as versões.
Mxx #