[PYTHON] Erstellen Sie ein Bereitstellungsskript mit Stoff und Küche und verwenden Sie es erneut

Einführung

Dieser Artikel ist der zweite Tag des Python-Adventskalenders 2016. http://qiita.com/advent-calendar/2016/python

Erstellen Sie ein Bereitstellungsskript mit Stoff und Küche und verwenden Sie es erneut

Mein Name ist Matsunaga von LOGICA Co., Ltd. Wir möchten Ihnen das Reisen erleichtern und entwickeln einen einfacheren Cross-Search-Service für Hotels.

Derzeit entwickeln wir zwei Produkte (Crawler und Medien) intern mit Django. Da wir derzeit von einer Person entwickeln und es viele Server gibt, möchten wir die Bereitstellungsarbeiten mit einem Befehl abschließen. War dort.

Obwohl ich die Bereitstellung automatisieren wollte, berührte ich Ansible nicht und Chef verstand es nach dem Tutorial nicht gut. Daher hörte ich, dass die Lernkosten niedrig zu sein scheinen. Ich entschied mich, ein Bereitstellungsskript mit Fabric zu erstellen. Hat. Küche dient der Gewährleistung von Homosexualität (sollte nicht perfekt sein)

Was sind Stoff und Küche

Ich denke, der folgende Artikel ist leicht zu verstehen, um eine kurze Erklärung von Stoff und Küche zu erhalten. http://qiita.com/pika_shi/items/802e9de8cb1401745caa

Die Links zur Dokumentation finden Sie weiter unten. Stoffdokumentation Küchendokumentation

Verzeichnisaufbau

Die Verzeichnisse sind jedes Projekt (Projekt1, Projekt2), Rezepte und ssh_keys. Fügen Sie im Vorlagenverzeichnis jedes Projekts die Dateien ein, die Sie mischen möchten, um die Endpunkte und Einstellungen für die Produktion zu mischen, z. B. die Einstellungsdateien settings.py und Nginx von django. Verwenden Sie für jede dieser Vorlagendateien Jinja2, um die in Secrets.yml beschriebenen Variablen einzufügen und sie dann auf den Server hochzuladen. Platzieren Sie die Dateien und Binärdateien, die Sie hochladen möchten, in "project1 / files". Die Rezepte enthalten wiederverwendbare Skripte. ssh_keys dient zum Remote-Abrufen des Inhalts des Repositorys auf github.

Diese werden von Github verwaltet. Fügen Sie gitignore natürlich die Verzeichnisse secret.yml und ssh_keys hinzu.

├── project1
│   ├── fabfile.py
│   ├── files
│   │   └── phantomjs-2.1.1-linux-x86_64.tar.bz2
│   ├── secrets.yml
│   ├── secrets.yml.example
│   └── templates
│       ├── gunicorn_conf.py
│       ├── nginx.conf
│       └── settings.py
├── project2
│   ├── fabfile.py
│   ├── secrets.yml
│   ├── secrets.yml.example
│   └── templates
│       ├── gunicorn_conf.py
│       ├── nginx.conf
│       └── settings.py
├── recipes
│   ├── __init__.py
│   ├── django.py
│   ├── git.py
│   ├── gunicorn.py
│   ├── httpd_tools.py
│   ├── nginx.py
│   ├── phantomjs.py
│   ├── pyenv.py
│   ├── redis.py
│   ├── service_base.py
├── requirements.txt
└── ssh_keys
    └── github
        └── id_rsa

Beispiele für wiederverwendbare Rezepte

Da ich Amazon Linux verwende, verwende ich yum für die Paketverwaltung, aber für alles, was wie "sudo service" start "startet oder stoppt, machen Sie das folgende Skript zur übergeordneten Klasse. Die Installation selbst kann sicherlich mit dem "package_ensure" der Küche durchgeführt werden, aber ich wollte es mit einem beschreibenden Namen als Methode behalten.

recipes/service_base.py


# -*- coding: utf-8 -*-

from fabric.api import sudo
from fabric.utils import puts
from fabric.colors import green
from cuisine import package_ensure, select_package

select_package('yum')


class ServiceBase(object):
    def __init__(self, package_name, service_name):
        self.package_name = package_name
        self.service_name = service_name

    def install(self):
        package_ensure(self.package_name)

    def start(self):
        puts(green('Starting {}'.format(self.package_name)))
        sudo('service {} start'.format(self.service_name))

    def stop(self):
        puts(green('Stopping {}'.format(self.package_name)))
        sudo('service {} stop'.format(self.service_name))

    def restart(self):
        puts(green('Restarting {}'.format(self.package_name)))
        sudo('service {} restart'.format(self.service_name))

Erstellen Sie damit das nginx-Skript zum Installieren / Starten / Stoppen wie folgt. Nginx

recipes/nginx.py


from service_base import ServiceBase


class Nginx(ServiceBase):

    def __init__(self):
        super(Nginx, self).__init__('nginx', 'nginx')
        self.remote_nginx_conf_path = '/etc/nginx/nginx.conf'

Ich werde den Upload der Nginx-Konfigurationsdatei später schreiben.

Zusätzlich zu denen, die von yum verwaltet werden, erstellen beispielsweise Pyenv, Django (da der Befehl von python manage.py ~~ ausgeführt wird) und Sellerie auch allgemeine Skripte. Ich werde nur pyenv setzen.

Pyenv

recipes/pyenv.py


class Pyenv(object):
    def __init__(self):
        pass

    def install(self):
        """Installieren Sie pyenv und verwandte Tools"""
        pyenv_dir = '~/.pyenv'
        #Bestätigung der Installation von pyenv
        if not dir_exists(pyenv_dir):
            run('curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash')
            text = """
            # settings for pyenv
            export PATH="$HOME/.pyenv/bin:$PATH"
            eval "$(pyenv init -)"
            eval "$(pyenv virtualenv-init -)"
            """
            files.append('~/.bashrc', text)
            run('source ~/.bashrc')

    def install_python(self, py_version):
        """Installieren Sie die auf Pyenv angegebene Python-Version"""

        #Wenn pyenv nicht installiert ist, installieren Sie es.
        if not dir_exists('~/.pyenv'):
            self.install()

        #Stellen Sie sicher, dass Sie die zum Erstellen von Python erforderlichen Pakete installiert haben
        packages = ['gcc', 'python-devel', 'bzip2-devel', 'zlib-devel', 'openssl-devel', 'sqlite-devel', 'readline-devel', 'patch']
        for package in packages:
            package_ensure(package)

        if not dir_exists('~/.pyenv/versions/{}'.format(py_version)):
            run('pyenv install {}'.format(py_version))
            run('pyenv rehash')

    def make_virtualenv(self, py_version, env_name):
        """Erstellen Sie eine Umgebung mit dem angegebenen Namen"""
        self.install_python(py_version)

        if not dir_exists('~/.pyenv/versions/{}'.format(env_name)):
            run('pyenv virtualenv {} {}'.format(py_version, env_name))
            run('pyenv rehash')
            run('pyenv global {}'.format(env_name))
        else:
            run('pyenv global {}'.format(env_name))

    def change_env(self, env_name):
        run('pyenv global {}'.format(env_name))

Verwenden Sie Jinja2, um eine Konfigurationsdatei hochzuladen, die Variablen enthält

Ich benutze die folgende Funktion. Da Fabric und Cuisine Python3 nicht unterstützen, wechsle ich die Remote-Python-Version vor und nach dem Hochladen mit "Rezepte / pyenv.py", die ich zuvor geschrieben habe. W (dh ich habe zwei Versionen von Python auf der Remote installiert Ich werde.) Wenn ich die Datei nicht hochgeladen hätte, könnte ich mit der Fernbedienung auf 3 gehen, aber als ich file_write gemacht habe, ist auch die Fernbedienung mit einem Fehler gefallen, es sei denn, es war 2, also mache ich diese Art von Problemen.

def upload_template(remote_path, local_template_path, variables={}, sudo=None):
    """
Hochladen durch Einfügen von Variablen in die jinja2-Vorlage
    """
    #Ändern Sie Remote-Python in eine 2-Systemumgebung
    pyenv = Pyenv()
    pyenv.change_env(VIRTUALENV_NAME_FOR_FABRIC)

    local_template_name = local_template_path.split('/')[-1]
    local_template_dir = local_template_path.replace(local_template_name, '')
    jinja2_env = Environment(loader=FileSystemLoader(local_template_dir))
    content = jinja2_env.get_template(local_template_name).render(variables)
    file_write(remote_path, content.encode('utf-8'), sudo=sudo)

    #Kehren Sie zur ursprünglichen Python-Umgebung zurück
    pyenv.change_env(VIRTUALENV_NAME)

Verwenden Sie diese Option, um Nginx-Konfigurationsdateien usw. hochzuladen. Variablen enthält die aus Secrets.yml gelesenen Daten.

upload_template(nginx.remote_nginx_conf_path, 'templates/nginx.conf', variables, sudo=sudo)

Schreiben Sie beispielsweise Folgendes in den Servernamen "nginx.conf".

server_name  {{ end_point }};

Es ist in Ordnung, wenn Sie den Endpunkt, den Sie in Servername angeben möchten, in "Variablen [" Endpunkt "]" einfügen. Ich denke, es ist eine vertraute Beschreibung für diejenigen, die normalerweise Jinja oder Django verwenden.

Die Datenbankeinstellungen in Djangos settings.py lauten wie folgt.

secrets.yml


django:
  settings:
    production:
      secret_key:Geheimer Schlüssel
      databases:
        default:
          engine: django.db.backends.mysql
          name:DB-Name
          user:DB-Benutzername
          password:DB-Passwort
          host:DB-Endpunkt
          port:DB-Port

project1/templates/settings.py


DATABASES = {
    'default': {
        'ENGINE': '{{ databases.default.engine }}',
        'NAME': '{{ databases.default.name }}',
        'USER': '{{ databases.default.user }}',
        'PASSWORD': '{{ databases.default.password }}',
        'HOST': '{{ databases.default.host }}',
        'PORT': '{{ databases.default.port }}',
    },
}

project1/fabfile.py


variables = secrets['django']['settings']['production']
upload_template(settings_file_path, 'templates/settings.py', variables)

Aktuelles Bereitstellungsskript

Da es gefährlich ist, das rohe Skript zu platzieren, habe ich ein Skript erstellt, das nur nginx installiert und die Python-Umgebung mit Pyenv erstellt (ich habe den Vorgang nicht bestätigt, da es sich um eine Teilkopie des tatsächlich verwendeten handelt).

project1/fabfile.py


# -*- coding: utf-8 -*-

import os
import sys
sys.path.append(os.pardir)

import yaml
from jinja2 import Environment, FileSystemLoader
from fabric.api import env, run, sudo, settings, cd
from fabric.decorators import task
from cuisine import package_ensure, select_package, file_write

from recipes.nginx import Nginx
from recipes.pyenv import Pyenv


#Python-Informationen
PYTHON_VERSION = "Die Version, die Sie in der Produktion verwenden möchten"
VIRTUALENV_NAME = "Umgebungsname, der in der Produktion verwendet wird"

#Remote-Python-Umgebung beim Hochladen von Dateien
PYTHON_VERSION_FOR_FABRIC = "In 2 System"
VIRTUALENV_NAME_FOR_FABRIC = "Umgebungsname für Remote Fabric"

#Auswahl der Paketverwaltungsmethode
select_package('yum')

#Laden Sie die Informationen, die in die Vorlage eingebettet werden sollen
secrets = yaml.load(file('secrets.yml'))

#env-Einstellungen Informationen, mit denen Sie sich beim Zielserver anmelden
env.user = "Nutzername"
env.group = "Gruppenname"
env.key_filename = "Schlüsselpfad für die Anmeldung am Server"
env.use_ssh_config = True


def upload_template(remote_path, local_template_path, variables={}, sudo=None):
    pyenv = Pyenv()
    pyenv.change_env(VIRTUALENV_NAME_FOR_FABRIC)

    local_template_name = local_template_path.split('/')[-1]
    local_template_dir = local_template_path.replace(local_template_name, '')
    jinja2_env = Environment(loader=FileSystemLoader(local_template_dir))
    content = jinja2_env.get_template(local_template_name).render(variables)
    file_write(remote_path, content.encode('utf-8'), sudo=sudo)

    #Kehren Sie zur ursprünglichen Python-Umgebung zurück
    pyenv.change_env(VIRTUALENV_NAME)


@task
def deploy():
    #Erstellen Sie eine Python-Umgebung für das Hochladen von Vorlagen (Remote ist kein 2-System)
    pyenv = Pyenv()
    pyenv.install_python(PYTHON_VERSION_FOR_FABRIC)
    pyenv.make_virtualenv(PYTHON_VERSION_FOR_FABRIC, VIRTUALENV_NAME_FOR_FABRIC)

    #Erstellen einer Python-Umgebung für die Produktion
    pyenv.install_python(PYTHON_VERSION)
    pyenv.make_virtualenv(PYTHON_VERSION, VIRTUALENV_NAME)

    #Nginx-Umgebungskonstruktion
    nginx = Nginx()
    nginx.install()
    variables = {
        'end_point': END_POINT,
    }
    upload_template(nginx.remote_nginx_conf_path, 'templates/nginx.conf', variables, sudo=sudo)
    nginx.stop()
    nginx.start()

Zusammenfassung / Eindruck

Als Eindruck der tatsächlichen Verwendung entstehen fast keine Lernkosten (da es sich nur um einen Shell-Wrapper handelt). Wenn es jedoch darum geht, Vorlagendateien wiederzuverwenden und hochzuladen, ist Ansible meiner Meinung nach in Ordnung. (Ich weiß es nicht, weil ich es nicht berührt habe)

Da wir ein Startup sind, war ich anfangs besorgt, dass das Schreiben eines Bereitstellungsskripts einen zusätzlichen Aufwand bedeuten würde, aber aufgrund der Abwägung der Kosten und der einfachen Erstellung dieser Bereitstellungsskripts bin ich jetzt sehr zufrieden. Wir haben viele Server für die Waage, daher war es gut, ein Bereitstellungsskript zu haben. Es ist wirklich einfach.

Wenn es einen besseren Weg gibt oder wenn wir so etwas tun, lassen Sie es uns bitte im Kommentarbereich mm wissen

Recommended Posts

Erstellen Sie ein Bereitstellungsskript mit Stoff und Küche und verwenden Sie es erneut
Erstellen Sie mit Streamlit schnell ein Python-Datenanalyse-Dashboard und stellen Sie es in AWS bereit
Erstellen Sie eine temporäre Datei mit Django als Zip und geben Sie sie zurück
Erstellen Sie ein Sternensystem mit Blender 2.80-Skript
Erstellen Sie mit Python einen Entscheidungsbaum von 0 und verstehen Sie ihn (5. Information Entropy)
Erstellen Sie mit Py2app und Tkinter eine native GUI-App
Erstellen Sie einen Stapel von Bildern und blasen Sie sie mit ImageDataGenerator auf
Erstellen Sie mit PyQt5 und PyQtGraph einen 3D-Modell-Viewer
[Linux] Erstellen Sie ein Selbstzertifikat mit Docker und Apache
Bis Sie mit Python unter Windows 7 eine maschinelle Lernumgebung erstellen und ausführen
Holen Sie sich die passende Zeichenfolge in den regulären Ausdruck und verwenden Sie sie beim Ersetzen unter Python3 erneut
Erstellen Sie eine WEB-Überwachungskamera mit Raspberry Pi und OpenCV
Verknüpfen Sie Python Enum mit einer Funktion, um es aufrufbar zu machen
[Azure] Erstellen, Bereitstellen und erneutes Lernen eines Modells [ML Studio classic]
Erstellen wir ein Skript, das sich bei Ideone.com in Python registriert.
Erstellen Sie eine Homepage mit Django
Erstellen Sie Anwendungen, registrieren Sie Daten und teilen Sie sie mit einer einzigen E-Mail
Ein Skript, mit dem Sie mit der LINE Messaging-API auf einfache Weise umfangreiche Menüs erstellen können
Wenn ich eine Django-App auf Apache2 bereitstelle und keine statischen Dateien mehr liest
Schritte zum Erstellen eines Jobs, der ein Docker-Image abruft und mit Github-Aktionen testet
Schritte zum Einrichten von Pipenv, Erstellen einer CRUD-App mit Flask und Containerisieren mit Docker
Erstellen Sie ein Verzeichnis mit Python
Lassen Sie uns mit Pylearn2 eine Drei-Wege-KI erstellen - Modell speichern und laden -
Erstellen Sie eine gestreifte Illusion mit Gammakorrektur für Python3 und openCV3
Machen Sie ein Thermometer mit Raspberry Pi und machen Sie es im Browser Teil 4 sichtbar
Erstellen Sie mit BigQuery ein privates DMP ohne Anfangskosten und ohne Entwicklung
Ich habe versucht, Bulls and Cows mit einem Shell-Programm zu erstellen
Ich möchte eine Pipfile erstellen und im Docker wiedergeben
Erstellen Sie eine CP932-CSV-Datei für Excel mit Chalice und geben Sie sie zurück
Ich habe einen Chat-Chat-Bot mit Tensor2Tensor erstellt und diesmal hat es funktioniert
Stellen Sie das Skript mithilfe von Fabric für jboss bereit
Stellen Sie die Django-Anwendung mit Docker bereit
Erstellen Sie eine virtuelle Umgebung mit Python!
Geben Sie Stoff gleich mit Küche
Erstellen Sie einen Poisson-Stepper mit numpy.random
Schreiben Sie ein Batch-Skript mit Python3.5 ~
Erstellen Sie mit Django einen Datei-Uploader
Ich habe ein POST-Skript erstellt, um ein Problem in Github zu erstellen und es im Projekt zu registrieren
[AWS lambda] Stellen Sie mit lambda verschiedene Bibliotheken bereit (generieren Sie eine Zip-Datei mit einem Kennwort und laden Sie sie auf s3 hoch) @ Python
2. Erstellen Sie mit Python einen Entscheidungsbaum von 0 und verstehen Sie ihn (2. Grundlagen des Python-Programms)
[AWS] Erstellen Sie mit CodeStar eine Python Lambda-Umgebung und führen Sie Hello World aus
Stellen Sie die Lambda-Funktion ein und lassen Sie sie mit S3-Ereignissen arbeiten!
Erstellen Sie einen Stapel mit einer Warteschlange und eine Warteschlange mit einem Stapel (von LetCode / Implement Stack using Queues, Implement Queue using Stacks)
Todo-App mit Django erstellen ④ Ordner- und Aufgabenerstellungsfunktion implementieren
Erstellen Sie eine Python3-Umgebung mit pyenv auf einem Mac und zeigen Sie NetworkX-Diagramme an
Machen Sie mit Python einen Entscheidungsbaum von 0 und verstehen Sie ihn (4. Datenstruktur)
Kann in 5 Minuten erledigt werden! Erstellen Sie eine Gesichtserkennungs-API mit Fast API und OpenCV und veröffentlichen Sie sie auf Heroku
Die Geschichte eines virtuellen Planetariums [bis es einem Anfänger gelingt, ein Modell mit einem Skript zu erstellen]
Erstellen Sie mit Class einen Python-Funktionsdekorator
Erstellen Sie mit Python + PIL ein Dummy-Image.
[Python] Erstellen Sie mit Anaconda eine virtuelle Umgebung
Erstellen wir mit Python eine kostenlose Gruppe
Erstellen Sie eine GUI-App mit Tkinter of Python
Ein Memo mit Python2.7 und Python3 in CentOS
Erstellen Sie eine große Textdatei mit Shellscript