Dieser Artikel ist der zweite Tag des Python-Adventskalenders 2016. http://qiita.com/advent-calendar/2016/python
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)
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
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
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))
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)
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()
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