[PYTHON] Exemple de mise en œuvre de la gestion centralisée de l'inventaire et des variables en liant Ansible et serverspec (prend en charge plusieurs serveurs)

introduction

Pour le groupe de serveurs Linux défini avec Ansible, j'ai essayé de modifier (?) Ansible et serverspec afin de résoudre les problèmes suivants que j'ai rencontrés lors des tests avec serverspec.

Veuillez vous y référer comme une édition d'introduction / débutant lorsque vous utilisez Ansible et serverspec en coopération.

objectif

Mettez en œuvre pour que vous puissiez:

Exemple d'implémentation

environnement

Ansible Control Node

Ansible Managed Node

Structure du répertoire

$ tree -aF /autotools
/autotools
|-- .ssh/
|   `-- aws_key.pem    #Clé privée SSH du nœud géré
|-- ansible/
|   |-- ansible.cfg
|   |-- group_vars/    #Répertoire de variables pour les groupes
|   |-- host_vars/     #Répertoire des variables hôtes
|   |-- inventory/     #Répertoire de placement d'inventaire pour Ansible
|   `-- centos.yml       # Playbook
`-- serverspec/
    |-- .rspec
    |-- Rakefile
    |-- spec/
    |   |-- base/      #Répertoire de placement des manteaux d'essai pour le rôle de base
    |   |   `-- sample_spec.rb #Code de test
    |   `-- spec_helper.rb
    `-- spec_hosts/    #Répertoire de placement variable pour serverspec

Ansible

Tout d'abord, décidons project_name avec une chaîne de caractères en anglais dans le but de gérer ce groupe de serveurs collectivement. Ici, à titre d'exemple, utilisez project_name comme ʻanken`.

ansible.cfg Ici, spécifiez ʻansible.cfg à utiliser dans la variable d'environnement ʻANSIBLE_CONFIG. J'utilise le même nom d'hôte pour le développement de code, j'ai donc répertorié les arguments ssh ici.

$ export ANSIBLE_CONFIG=/autotools/ansible/ansible.cfg

/autotools/ansible/ansible.cfg


[defaults]

[ssh_connection]
ssh_args = -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no

[privilege_escalation]
become = true

Clé privée SSH

Placez la clé privée SSH utilisée pour SSH dans le nœud géré Ansible dans / autotools / .ssh. Le chemin d'accès de la clé est répertorié dans le fichier d'inventaire.

Fichier d'inventaire

Placez le fichier d'inventaire ʻanken.inisous/ autotools / ansible / inventaire /`.

Fondamentalement, c'est OK si vous écrivez selon la règle d'Ansible, mais comme il est utilisé dans le mécanisme de liaison avec serverspec, veuillez définir les trois indispensables suivants.

/autotools/ansible/inventory/anken.ini


[anken]
prod_foobar1 ansible_host=xx.xx.xx.xx
dev_foobar1 ansible_host=yy.yy.yy.yy

[anken:vars]
ansible_user=centos
ansible_ssh_private_key_file=~/.ssh/aws_key.pem

[all:vars]
project_name=anken

Ansible est exécuté pour l'IP ou le nom de ʻansible_host`.

ʻInventory_hostname (où prod_foobar1 dev_foobar2` est spécifié dans l'exemple) ne doit pas nécessairement correspondre au nom d'hôte réel du nœud.

Playbook

L'exemple de playbook utilisé dans cet exemple d'implémentation est le suivant. name: Configure for serverspec at localhost renvoie le fichier de variables utilisé par serverspec.

centos.yml


---
- name: Playbook for centos7 managed node
  hosts: all
  gather_facts: true

  tasks:
    - name: Create group
      group:
        name: "{{ item.name }}"
        gid: "{{ item.gid }}"
      loop: "{{ group }}"
      tags: group

    - name: Create User
      user:
        name: "{{ item.name }}"
        uid: "{{ item.uid }}"
        group: "{{ item.group }}"
        groups: "{{ item.groups }}"
        home: "{{ item.home }}"
        shell: "{{ item.shell }}"
      loop: "{{ user }}"
      tags: user

    - name: System service
      systemd:
        name: "{{ item.name }}"
        enabled: "{{ item.enabled }}"
        state: "{{ item.state }}"
      loop: "{{ service }}"
      tags: service

- name: Configure for serverspec at localhost
  hosts: localhost
  connection: local
  gather_facts: false
  tasks:

    - name: Dump hostvars for serverspec
      copy:
        content: "{{ hostvars | to_nice_yaml }}"
        dest: "../serverspec/spec_hosts/{{ project_name }}.yml"
      tags: serverspec

Placement variable des fichiers

Placez les variables communes au projet dans / autotools / ansible / group_vars / # {project_name} .yml. Si vous souhaitez spécifier une variable différente uniquement pour un nom_hôte_inventaire spécifique, placez-la dans / autotools / ansible / host_vars / # {nom_inventaire} .yml.

Dans le fichier de variables, spécifiez le rôle à utiliser lors du test avec serverspec avec serverspec_role.

/autotools/ansible/group_vars/anken.yml


serverspec_role:
  - base
group:
  - name: unyo
    gid: 1101
  - name: infra
    gid: 1102
  - name: app
    gid: 1103
user:
  - name: user1
    uid: 2001
    group: customer
    groups: [ unyo, infra]
    home: /home/user1
    shell: /bin/bash
  - name: user2
    uid: 2002
    group: customer
    groups: [ app ]
    home: /home/user2
    shell: /bin/bash
  - name: user3
    uid: 2003
    group: customer
    groups: [ app, infra ]
    home: /home/user3
    shell: /bin/bash
service:
  - name: chronyd.service
    enabled: false
    state: stopped
  - name: rsyncd.service
    enabled: true
    state: started

Ici, je vais le limiter au nœud prod_foobar et écraser certaines variables.

/autotools/ansible/group_vars/prod_foobar.yml


group:
  - name: unyo
    gid: 2101
  - name: infra
    gid: 2102
  - name: app
    gid: 2103

Structure des répertoires / fichiers

Après avoir organisé les fichiers nécessaires, cela devrait ressembler à ceci.

$ tree /autotools/ansible -aF
/autotools/ansible
|-- ansible.cfg
|-- centos.yml
|-- group_vars/
|   `-- anken.yml
|-- host_vars/
|   `-- prod_foobar1
`-- inventory/
    `-- anken.ini

Courir

Exécutez le playbook centos.yml avec le fichier d'inventaire comme indiqué ci-dessous.

$ cd /autotools/ansible
$ ansible -i ./inventory/anken centos.yml

serverspec

Structure des répertoires / fichiers

Après avoir exécuté Ansible, la structure des répertoires / fichiers du côté de la spécification de serveur devrait ressembler à ceci.

# tree /autotools/serverspec -aF
/autotools/serverspec
|-- .rspec
|-- Rakefile
|-- spec/
|   |-- base/
|   |   `-- sample_spec.rb
|   `-- spec_helper.rb
`-- spec_hosts/
    `-- anken.yml        #Fichier variable généré par Ansible

Commande d'exécution

L'ordre est différent, mais la commande d'exécution de serverspec est la suivante. Il s'agit d'une image de transmission du nom de fichier variable (généré par Ansible) utilisé dans serverspec à la commande rake en tant qu'argument.

$ rake spec anken -T
rake spec               # Run spec to all hosts
rake spec:dev_foobar1   # Run spec to dev_foobar1
rake spec:prod_foobar1  # Run spec to prod_foobar1
$ rake spec anken

Rakefile

Il y a quelques changements par rapport au standard Rakefile créé par serverspec-init.

/autotools/serverspec/Rakefile


require 'rake'
require 'rspec/core/rake_task'
require 'yaml'

#Lire le fichier de variables
project_name = ARGV[1]
hosts = YAML.load_file("./spec_hosts/#{project_name}.yml")

desc "Run spec to all hosts"
task :spec => 'spec:all'

namespace :spec do
  task :all => hosts.keys.map {|key| 'spec:' + key }

  hosts.keys.each do |key|
    desc "Run spec to #{key}"
    RSpec::Core::RakeTask.new(key.to_sym) do |t|
      ENV['INVENTORY_HOST'] = key
      ENV['PROJECT_NAME'] = project_name
      # serverspec_Sous un répertoire portant le même nom que le rôle*_spec.Lire le fichier rb
      t.pattern = 'spec/{' + hosts[key]['serverspec_role'].join(',') + '}/*_spec.rb'
      t.fail_on_error = false
    end
  end
end

#Forger l'argument de la commande rake comme une tâche vide
ARGV.slice(1,ARGV.size).each{|v| task v.to_sym do; end}

Je l'ai utilisé comme référence ci-dessous. Merci beaucoup.

Référence: Écrire un traitement de type argument ordinaire dans la tâche Rake https://qiita.com/nao58/items/aa50514d97f05eb8d128

Référence: officiel Comment utiliser les propriétés spécifiques à l'hôte https://serverspec.org/advanced_tips.html

spec_helper.rb

Cela change également certaines fonctions de l'état initial spec_helper.rb.

/autotools/serverspec/spec/spec_helper.rb


require 'serverspec'
require 'pathname'
require 'net/ssh'
require 'yaml'

#Lire le fichier yml variable
key = ENV['INVENTORY_HOST']
project_name = ENV['PROJECT_NAME']
properties = YAML.load_file("./spec_hosts/#{project_name}.yml")
set_property properties["#{key}"]

set :backend, :ssh
set :path, '/sbin:/usr/sbin:$PATH'

#partie exécution ssh
RSpec.configure do |c|
  c.before :all do
    #Extraire l'hôte, l'utilisateur, le mot de passe ou la clé utilisé dans Ansible à partir du fichier de variable lu
    set :host, property['ansible_host']
    options = Net::SSH::Config.for(c.host)
    options[:user] = property['ansible_user']
    if property['ansible_password']
      options[:password] = property['ansible_password']
    else
      options[:keys] = [ property['ansible_ssh_private_key_file'] ]
    end
    options[:user_known_hosts_file] = '/dev/null'
    set :ssh_options, options
  end
end

Ce spec_helper.rb ne prend pas en charge WinRM car il est set: backend ,: ssh. Cependant, comme vous pouvez tout écrire en Ruby, il ne devrait pas être difficile de prendre en charge Windows.

Code de test

Ceci est un échantillon. Comme écrit dans spec_helper.rb,property ['xxx']peut être utilisé pour récupérer une variable d'un fichier de variable et la réutiliser.

/autotools/serverspec/spec/base/


# frozen_string_literal: true

require 'spec_helper'

puts "\nRun serverspec to #{property['inventory_hostname']}"

property['group'].each do |attr|
  describe group(attr['name']) do
    it { should exist }
    it { should have_gid attr['gid'] }
  end
end

property['user'].each do |attr|
  describe user(attr['name']) do
    it { should exist }
    it { should have_uid attr['uid'] }
    it { should belong_to_group attr['group'] }
  end
end

property['service'].each do |attr|
  describe service(attr['name']) do
    attr['enabled']            ? it { should be_enabled } : it { should_not be_enabled }
    attr['state'] == 'started' ? it { should be_running } : it { should_not be_running }
  end
end

Courir

Encore une fois, exécutez la commande rake spec avec le nom de fichier de variable généré par Ansible comme argument, comme indiqué ci-dessous. Il est également possible d'exécuter des tests pour chaque unité.

$ rake spec anken
$
$ rake spec anken -T    #Commande pour afficher la liste des tâches
rake spec               # Run spec to all hosts
rake spec:dev_foobar1   # Run spec to dev_foobar1
rake spec:prod_foobar1  # Run spec to prod_foobar1
$
$ rake spec:dev_foobar1 anken

Résumé

Avec Ansible et serverspec, nous avons pu unifier les fichiers variables et les fichiers d'événements, qui ont tendance à être gérés en double. De plus, j'ai pu gérer et exécuter le code de test rôle par rôle en me référant à la méthode décrite dans la formule serverspec.

Étant donné que serverspec est un outil assez coloré, c'est difficile pour les personnes qui ne touchent généralement pas Ruby, mais une fois que vous vous y êtes habitué, il est bon que divers processus soient faciles à écrire.

Exemple de code

Il est publié ci-dessous. https://github.com/kentarok/autotools

Recommended Posts

Exemple de mise en œuvre de la gestion centralisée de l'inventaire et des variables en liant Ansible et serverspec (prend en charge plusieurs serveurs)
Implémenter un modèle avec état et comportement (3) - Exemple d'implémentation par décorateur
Exemple d'utilisation de variables de classe et de méthodes de classe