[PYTHON] Implementierungsbeispiel für die zentrale Verwaltung von Inventar und Variablen durch Verknüpfung von Ansible und Serverspezifikation (unterstützt mehrere Server)

Einführung

Für die mit Ansible festgelegte Linux-Servergruppe habe ich versucht, (?) Ansible und serverspec zu ändern, um die folgenden Probleme zu lösen, die ich beim Testen mit serverspec hatte.

Bitte bezeichnen Sie es als Einführungs- / Anfängerausgabe, wenn Sie Ansible und serverspec in Zusammenarbeit verwenden.

Tor

Implementieren Sie, damit Sie:

Implementierungsbeispiel

Umgebung

Ansible Control Node

Ansible Managed Node

Verzeichnisaufbau

$ tree -aF /autotools
/autotools
|-- .ssh/
|   `-- aws_key.pem    #Privater SSH-Schlüssel für verwalteten Knoten
|-- ansible/
|   |-- ansible.cfg
|   |-- group_vars/    #Variables Verzeichnis für Gruppen
|   |-- host_vars/     #Hostvariablenverzeichnis
|   |-- inventory/     #Inventarplatzierungsverzeichnis für Ansible
|   `-- centos.yml       # Playbook
`-- serverspec/
    |-- .rspec
    |-- Rakefile
    |-- spec/
    |   |-- base/      #Testen Sie das Verzeichnis für die Platzierung des Mantels auf die Grundrolle
    |   |   `-- sample_spec.rb #Testcode
    |   `-- spec_helper.rb
    `-- spec_hosts/    #Variables Platzierungsverzeichnis für Serverspezifikation

Ansible

Lassen Sie uns zunächst project_name mit einer englischen Zeichenfolge festlegen, um diese Servergruppe gemeinsam zu verwalten. Verwenden Sie hier als Beispiel "Projektname" mit dem Namen "Anken".

ansible.cfg Geben Sie hier "ansible.cfg" an, das in der Umgebungsvariablen "ANSIBLE_CONFIG" verwendet werden soll. Ich verwende denselben Hostnamen für die Codeentwicklung, daher habe ich die ssh-Argumente hier aufgelistet.

$ 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

Privater SSH-Schlüssel

Platzieren Sie den für SSH verwendeten privaten SSH-Schlüssel in den Ansible Managed Node in / autotools / .ssh. Der Schlüsselpfad ist in der Inventardatei aufgeführt.

Inventardatei

Platzieren Sie die Inventardatei "anken.ini" unter "/ autotools / ansible / inventar /".

Grundsätzlich ist es in Ordnung, wenn Sie nach der Ansible-Regel schreiben. Da es jedoch im Mechanismus für die Verknüpfung mit Serverspezifikationen verwendet wird, setzen Sie bitte die folgenden drei unverzichtbaren Punkte.

/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 wird für die IP oder den Namen von "ansible_host" ausgeführt.

Der inventar_hostname (wobei im Beispiel prod_foobar1`` dev_foobar2 angegeben ist) muss nicht mit dem tatsächlichen Hostnamen des Knotens übereinstimmen.

Playbook

Das in diesem Implementierungsbeispiel verwendete Playbook-Beispiel lautet wie folgt. name: Configure for serverspec at localhost gibt die von serverspec verwendete Variablendatei aus.

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

Variable Dateiposition

Platzieren Sie die für das Projekt gemeinsamen Variablen in / autotools / ansible / group_vars / # {project_name} .yml. Wenn Sie eine andere Variable nur für einen bestimmten Inventar-Hostnamen angeben möchten, platzieren Sie sie in / autotools / ansible / host_vars / # {Inventar-Name} .yml.

Geben Sie in der Variablendatei die Rolle an, die beim Testen mit serverspec mit serverspec_role verwendet werden soll.

/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

Hier werde ich es auf den Knoten "prod_foobar" beschränken und einige Variablen überschreiben.

/autotools/ansible/group_vars/prod_foobar.yml


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

Verzeichnis- / Dateistruktur

Nach dem Anordnen der erforderlichen Dateien sollte es so aussehen.

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

Lauf

Führen Sie das Playbook centos.yml mit der Inventardatei wie unten gezeigt aus.

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

serverspec

Verzeichnis- / Dateistruktur

Nach dem Ausführen von Ansible sollte die Verzeichnis- / Dateistruktur auf der Serverspezifikationsseite folgendermaßen aussehen.

# tree /autotools/serverspec -aF
/autotools/serverspec
|-- .rspec
|-- Rakefile
|-- spec/
|   |-- base/
|   |   `-- sample_spec.rb
|   `-- spec_helper.rb
`-- spec_hosts/
    `-- anken.yml        #Von Ansible generierte variable Datei

Ausführungsbefehl

Die Reihenfolge ist unterschiedlich, aber der Ausführungsbefehl von serverspec lautet wie folgt. Dies ist ein Bild der Übergabe des variablen Dateinamens (von Ansible generiert), der in serverspec verwendet wird, an den Befehl rake als 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

Es gibt einige Änderungen gegenüber dem von serverspec-init erstellten Standard-Rakefile.

/autotools/serverspec/Rakefile


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

#Variablendatei lesen
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_Unter einem Verzeichnis mit dem gleichen Namen wie die Rolle*_spec.Rb-Datei lesen
      t.pattern = 'spec/{' + hosts[key]['serverspec_role'].join(',') + '}/*_spec.rb'
      t.fail_on_error = false
    end
  end
end

#Fälschen Sie das Argument des Rechenbefehls als leere Aufgabe
ARGV.slice(1,ARGV.size).each{|v| task v.to_sym do; end}

Ich habe es unten als Referenz verwendet. Vielen Dank.

Referenz: Schreiben Sie eine normale argumentähnliche Verarbeitung in die Rake-Task https://qiita.com/nao58/items/aa50514d97f05eb8d128

Referenz: Offizielle Verwendung hostspezifischer Eigenschaften https://serverspec.org/advanced_tips.html

spec_helper.rb

Dies ändert auch einige Funktionen gegenüber dem Ausgangszustand "spec_helper.rb".

/autotools/serverspec/spec/spec_helper.rb


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

#Lesen Sie die variable yml-Datei
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'

#SSH-Ausführungsteil
RSpec.configure do |c|
  c.before :all do
    #Extrahieren Sie den in Ansible verwendeten Host, Benutzer, Kennwort oder Schlüssel aus der Datei mit den gelesenen Variablen
    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

Diese spec_helper.rb unterstützt WinRM nicht, da sie set: backend,: ssh ist. Da Sie jedoch alles in Ruby schreiben können, sollte es nicht schwierig sein, Windows zu unterstützen.

Testcode

Dies ist ein Beispiel. Wie in "spec_helper.rb" geschrieben, kann "property ['xxx']" verwendet werden, um eine Variable aus einer Variablendatei abzurufen und wiederzuverwenden.

/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

Lauf

Führen Sie den Befehl rake spec erneut mit dem von Ansible generierten Variablendateinamen als Argument aus, wie unten gezeigt. Es ist auch möglich, Tests für jede Einheit durchzuführen.

$ rake spec anken
$
$ rake spec anken -T    #Befehl zum Anzeigen der Aufgabenliste
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

Zusammenfassung

Mit Ansible und serverspec konnten wir variable Dateien und Ereignis-Refiles vereinheitlichen, die in der Regel doppelt verwaltet werden. Außerdem konnte ich den Testcode rollenweise verwalten und ausführen, indem ich mich auf die in der Serverspezifikationsformel beschriebene Methode bezog.

Da serverspec ein ziemlich rubinrotes Tool ist, ist es für Leute, die nicht mit Ruby vertraut sind, schwierig, aber wenn Sie sich erst einmal daran gewöhnt haben, ist es gut, dass verschiedene Prozesse einfach zu schreiben sind.

Beispielcode

Es wird unten veröffentlicht. https://github.com/kentarok/autotools

Recommended Posts

Implementierungsbeispiel für die zentrale Verwaltung von Inventar und Variablen durch Verknüpfung von Ansible und Serverspezifikation (unterstützt mehrere Server)
Implementieren Sie ein Modell mit Status und Verhalten (3) - Beispiel für die Implementierung durch den Dekorateur
Beispiel für die Verwendung von Klassenvariablen und Klassenmethoden