[PYTHON] Was machen Sie mit der Konfigurationsverwaltung eines Servers, der Ansible implementiert hat, aber bereits ausgeführt wird? Ich treffe das Problem

Diejenigen, die sich freuen würden, wenn Sie es lesen könnten

Einführung

Ich bin eine Person in der Infrastrukturabteilung, die häufig Anforderungen rund um MW gemäß den geschäftlichen Anforderungen überprüft, erstellt, testet und liefert.

Von April bis September 2016 habe ich als Mitglied des Automatisierungsteams das Playbook von Ansible erstellt und implementiert.

Ansible ist sehr praktisch, nicht wahr? Vor der Implementierung war dies ein sehr zeitaufwändiger Bauprozess, da von der Anfrage bis zur Lieferung von der Unternehmensseite aus eine große Menge an Konstruktionsmaterialien und Konstruktions- und Testnachweisen erstellt werden musste.

Nach der Implementierung von Ansible wird im Grunde genommen eine Vars-Datei erstellt, die die Anforderungen erfüllt. ~ Anfordern einer Überprüfung mit einer Pull-Anforderung auf GitHub ~ Jenkins ~ Die Ausführung und Bauarbeiten von Jobs werden sehr einfach, und es ist nicht erforderlich, Verfahrenshandbücher vorzubereiten oder Testnachweise zu erstellen. Es ist also viel einfacher.

Aber ist es nicht sehr schwierig, die Einstellungsinformationen eines Servers oder einer Instanz, die bereits ausgeführt wird, in den Ansible-Code einzufügen?

Dieser Artikel konzentriert sich darauf, wie Sie die Probleme angehen können, die aufgetreten sind, nachdem Sie Ansible geworden sind.

Das Team, dem ich angehöre, zeichnet übrigens die folgende Roadmap für die Implementierung von Ansible. Dieser Artikel ist der Inhalt von ②.

Voraussetzung: Ich möchte die Konfiguration einer manuellen Konstruktionsumgebungsgruppe mit einem bestimmten Konfigurationsgrad verwalten.

  1. Erstellt ein Playbook & eine Rolle mit derselben Konfiguration wie die manuelle Build-Umgebung → Ich erkannte, dass das Erstellen von Vars für eine manuelle Konstruktionsumgebung tödlich schmerzhaft war (Anfang dieses Artikels)
  2. Ich habe angefangen, ein Dumping-Tool zu erstellen (jetzt hier) </ font>
  3. Realisieren Sie das Konfigurationsmanagement mit derselben Konfiguration wie beim manuellen Aufbau (von nun an).
  4. Ändern Sie die Konfiguration in die für den automatischen Aufbau geeignete (von nun an)

Zusammenfassung dieses Artikels

  • Durch die Einführung von Ansible & Serverspec in die Bauarbeiten konnten wir eine Umgebung schaffen, die Personal und Fehler reduziert. ――Das Konfigurationsmanagement des Servers, der in der vorhandenen Umgebung ausgeführt wird, ist jedoch tödlich (es ist zu mühsam, die Vars-Datei manuell zu erstellen).
  • Als Antwort haben wir begonnen, ein Tool zu erstellen, das automatisch verschiedene Ansible-Einstellungsdateien (vars / host_vars / group_vars) generiert, einschließlich Vars-Dateien aus den Einstellungsdateien des Servers, der bereits unter Python 3.4 ausgeführt wird.
  • Wenn dies ausgeführt wird, wird die Vars-Datei automatisch aus der Konfigurationsdatei der vorhandenen Umgebung erstellt. Daher ist es ein Problem, den tatsächlichen Computer zu betrachten und die Vars-Datei durch Kopieren zu erstellen. Keine Notwendigkeit </ font>

So verwalten Sie die Konfiguration der vorhandenen Umgebung nach der Implementierung von Ansible

Wie oben erwähnt, sind durch die Implementierung von Ansible die Bauarbeiten selbst einfacher geworden. Auch für Geschäftsabteilungen, die die Infrastruktur für "neue" vorbereiten Der Anforderer sendet eine GitHub-Pull-Anforderung. Wenn das Infrastrukturpersonal das geänderte Teil bestätigt und genehmigt, wird die Einstellung in der Maseter-Verzweigung angezeigt und die Bauarbeiten sind abgeschlossen, die für den unten beschriebenen GitHub-Flow bereit sind. War

Veranschaulichen Sie eine einfachere GitHub Flow-Operation, die Pull-Anforderungen / Überprüfungen enthält (1/2). http://www.atmarkit.co.jp/ait/articles/1401/21/news042.html

Für Geschäftsabteilungen mit einer vorhandenen Umgebung war dies jedoch nicht so einfach.

Wenn ich aufgefordert wurde, die MW-Einstellungen eines bestimmten Servers zu ändern, musste ich verschiedene Ansible-Einstellungsdateien wie die Vars-Datei von Grund auf neu vorbereiten, was eine sehr problematische Situation war.

Ich hatte die Idee, ein Tool zur automatischen Erstellung von Inventardateien zu erstellen.

Aufgrund der oben genannten Probleme habe ich ein Tool entwickelt, das automatisch Ansible-Konfigurationsdateien vom Server in der vorhandenen Umgebung generiert.

イメージ図.png

Ich habe ein schäbiges Bilddiagramm gemacht. Der Fluss des Werkzeugs ist wie folgt

  1. Geben Sie den SV und MW an, die auf dem lokalen PC ausgeführt werden sollen
  2. Zum Git drücken
  3. Geben Sie in der Benutzeroberfläche von Jenkins die in ② genannte Datei an und führen Sie den Job aus.
  4. Der Jenkins-Ausführungsserver klont das Git-Master-Repository und erhält den angegebenen Zielserver und die angegebene MW-Konfigurationsdatei per SFTP.
  5. Nach der Erfassung wird das Konvertierungsskript in die Ansible-Datei ausgeführt und die Ansible-Einstellungsdatei im Ausführungsverzeichnis (vars / host_vars / group_vars) erstellt.
  6. Verschiedene erstellte Einstellungsdateien werden auf git hochgeladen

Demo (Versuchen Sie, am Beispiel von Apache automatisch eine VAR-Datei aus der Konfigurationsdatei zu erstellen.)

Hier möchte ich die Apache-Konfigurationsdatei extrahieren und automatisch die folgende Vars-Datei erstellen. Es ist ein Bild des tatsächlichen Schreibens eines Teils von ⑤ Eigentlich ist es in verschiedene Module unterteilt, damit es auf verschiedene Anforderungen reagieren kann. Da es sich jedoch um eine Demo handelt, wird es für einfache Apache-Parameter extrahiert.

Umgebung

・ Python3.4 ・ Apache 2.2.15

Zu erstellende Vars-Datei

Nach dem Ausführen des Tools werden die Informationen der Einstellungsdatei (/etc/httpd/conf/httpd.conf) extrahiert und in den folgenden Elementen gespeichert.

KeepAlive: ''
KeepAliveTimeout: ''
MaxKeepAliveRequests: '100'
PidFile: run/httpd.pid
ServerRoot: '"/etc/httpd"'
ServerTokens: OS
Timeout: '60'
load_module:[]

Vorbereitung

Installieren Sie Apache und erstellen Sie main.py direkt im Verzeichnis / etc / httpd Stellen Sie sicher, dass sich httpd.conf befindet, das das Ziel der Parameterextraktion ist.

[root@localhost httpd]$ sudo yum install httpd
[root@localhost httpd]$ pwd
/etc/httpd
[root@localhost httpd]# ls conf/httpd.conf
conf/httpd.conf
[root@localhost httpd]$vi main.py

Erstellen Sie main.py.

main.py


# coding: UTF-8
import yaml
from pathlib import Path
import re


#Beschreiben des Extrahierens von Parametern
class CommonFunc:
    def __init__(self):
        pass

    # file_Speichert den Inhalt der durch das Argument name angegebenen Zieldatei zeilenweise in einem Array
    def func_read_files(self, file_name):
        lines = []
        with file_name.open() as f:
            lines.extend(f.readlines())
        return lines

    #Speichern Sie die Zeile mit der durch den Parameter angegebenen Zeichenfolge im Array
    def func_search_parameter(self, lines, parameter):
        searched_line = []
        for line_tmp in lines:
            #Entfernen Sie den führenden Leerraum
            line = re.sub(r'^\s+', '', line_tmp)
            try:
                if line.startswith(parameter):
                    #Auskommentierte Zeile löschen
                    tmp = re.sub(r"^\s*#.*", "", line)
                    searched_line.append(tmp.rstrip('\r\n'))
            except:
                searched_line = ""

        return searched_line

    #Durchsucht die Zeile mit der durch den Parameter angegebenen Zeichenfolge und speichert sie im Array. Geben Sie den Unterschied zur Vorlagendatei mit dem Set aus
    def func_set_parameter(self, lines, compared_lines, parameter):
        src_list = []
        dst_list = []
        for line in lines:
            if line.find(parameter) >= 0:
                src_list.append(line.rstrip('\r\n'))
        for compared_line in compared_lines:
            dst_list.append(compared_line.rstrip('\r\n'))
        src_set = set(src_list)
        return src_set.difference(dst_list)

    #Suchen Sie nach Zeilen, die die durch den Parameter angegebene Zeichenfolge enthalten. Trennen Sie sich durch ein Leerzeichen und speichern Sie das zweite im Array
    def func_split_line_unique(self, lines, parameter):
        splited_line = []
        for line_tmp in lines:
            #Entfernen Sie den führenden Leerraum
            line = re.sub(r'^\s+', '', line_tmp)
            if line.startswith(parameter):
                try:
                    splited_line = line.split()[1]
                except:
                    splited_line = ""
        return splited_line


#Erstellen Sie ein Wörterbuch und speichern Sie den Wert in Wert. Der Prozess des Extrahierens der tatsächlichen Parameter ruft den Prozess von CommonFunc auf.
class MakeApacheDictionary(CommonFunc):

    def __init__(self):
        self.output_files = "test_vars.yml"
        self.httpd_conf_path = Path("/etc", "httpd", "conf", "httpd.conf")
        self.module_template_path = Path("/etc", "httpd", "load_module_template.txt")
        self.httpd_lines = self.func_read_files(self.httpd_conf_path)
        self.base_module_lines = self.func_search_parameter(self.httpd_lines, "LoadModule")
        self.base_module_template_lines = self.func_read_files(self.module_template_path)



    def apache_yaml_dict(self):
        self.yml_list = {
        #Einzigartig in Instanz
            'ServerTokens': "",
            'ServerRoot': "",
            'PidFile': "",
            'Timeout': "",
            'KeepAlive': "",
            'MaxKeepAliveRequests': "",
            'KeepAliveTimeout': "",
        #Gibt den Unterschied zur Vorlagendatei als Satz aus
            'load_module': []}

        d = self.yml_list
        d['ServerTokens'] = self.func_split_line_unique(self.httpd_lines, "ServerTokens")
        d['ServerRoot'] = self.func_split_line_unique(self.httpd_lines, "ServerRoot")
        d['PidFile'] = self.func_split_line_unique(self.httpd_lines, "PidFile")
        d['Timeout'] = self.func_split_line_unique(self.httpd_lines, "Timeout")
        d['MaxKeepAliveRequests'] = self.func_split_line_unique(self.httpd_lines, "MaxKeepAliveRequests")
        d['load_module'].extend(self.func_set_parameter(self.base_module_lines, self.base_module_template_lines,"LoadModule"))


    def create_yaml(self):
        with open(self.output_files, 'w') as f:
            f.write(yaml.safe_dump(self.yml_list, default_flow_style=False))


#Instanzgenerierung
a = MakeApacheDictionary()
a.apache_yaml_dict()
a.create_yaml()

Erläuterung

--Class ist in die Klasse "CommonFunc" unterteilt, die den Vorgang des tatsächlichen Extrahierens von Parametern beschreibt, und in MakeApacheDictionary, das ein Wörterbuch erstellt und als Yaml-Datei ausgibt.

  • Die letztere Klasse erbt die erstere, damit der Extraktionsprozess verwendet werden kann.
  • Einzigartige Werte in Konfigurationsdateien wie ServerRoot und TimeOut können durch Verknüpfen mit der Methode "Startswith" und Aufteilen wie in func_split_line_unique beschrieben erhalten werden. --Für Elemente wie LoadModule und RewriteCond, die mehrere Einstellungen für dasselbe Element haben, bereiten Sie Standardeinstellungen separat als Vorlagendatei vor und geben Sie die Unterschiede mithilfe eines Satzes aus.

Vorlagenbeispiel

In func_set_parameter werden die Zeilen, die mit LoadModule in httpd.conf beginnen, durchsucht und aufgelistet, verglichen mit den in der Vorlagendatei beschriebenen Inhalten, und der Unterschied wird ausgegeben.

[root@localhost collect_config]# more load_module_template.txt
LoadModule authn_alias_module modules/mod_authn_alias.so
LoadModule authn_anon_module modules/mod_authn_anon.so
LoadModule authn_dbm_module modules/mod_authn_dbm.so
etc ...

Ausführungsergebnis

Das erstellte Wörterbuch Übergeben an die Funktion create_yml (wo das Modul yaml.safe_dump verwendet wird) und an das Skriptausführungsverzeichnis ausgegeben

[root@localhost httpd]# python main.py
[root@localhost httpd]# more test_vars.yml
KeepAlive: 'Off'
KeepAliveTimeout: '15'
MaxKeepAliveRequests: '100'
PidFile: run/httpd.pid
ServerRoot: '"/etc/httpd"'
ServerTokens: OS
Timeout: '60'
load_module:
- LoadModule auth_basic_module modules/mod_auth_basic.so
- LoadModule authn_file_module modules/mod_authn_file.so
- LoadModule auth_digest_module modules/mod_auth_digest.so
[root@localhost httpd]#

Am Ende

Vielen Dank für das Lesen bis zum Ende. Ich hoffe, es ist nützlich für diejenigen, die mit dem Konfigurationsmanagement der vorhandenen Umgebung ähnlich beschäftigt sind.