4. Запускаем django приложение на сервере
18 февраля 2014 г. 9:45
Цикл статей по развёртыванию сайтов на боевом сервере с нуля по шагам с использованием python + django + uwsgi + nginx + supervisor:
Предыдущий шаг был посвящён настройке конфигураций uwsgi, nginx, supervisor, к тому же мы ещё и настроили DNS. То есть мы выполнили базовую задачу по развёртыванию обычного python приложения. А теперь мы переходим к развёртыванию сайта django nginx uwsgi, который использует базу данных и имеет картинки и другие медиа файлы! На этом шаге мы используем некоторые полезные инструменты. Будем работать почти как профессионалы. Я говорю "почти как профессионалы", потому что, научившись некоторым приёмам процесса разработки и развёртывания приложений, Вам всё равно рано или поздно придётся пополнять свой арсенал используемых инструментов и открывать новые фишечки, а пока приобретённые знания дадут Вам почувствовать себя почти профессионалами!
Настроились на работу и поехали! Сначала давайте создадим на своем компе, который стоит дома или в офисе, Django приложение "Кролики"! Мы будем выводить информацию о кроликах прямо на главной странице. Из командной строки/терминала заходим в папку, где будет лежать проект и собственного стандартной командой создаём тестовый проект. И заодно создадим некоторое приложение main.
#cd /home/vivazzi/tests/
cd d:\tests
django-admin.py startproject rabbits
cd rabbits
python manage.py startapp main
Давайте приложение main будем использовать для отображения главной страницы, в котором будет немного текста и картинка. И заодно в этом приложении реализуем обработку ошибок 404 и 500, чтобы было поинтереснее. И чтобы шибко было интересно используем библиотеку admin-tools для лучшего отображения админки. А чтобы нам было легче работать с изменением структуры БД используем классную библиотеку south. И, конечно же, используем систему контроля ревизий такую как mercurial.
Внимание! Хорошая фишечка: так как у нас будут разные настройки на нашем компе (например, настройки БД), на котором разрабатываем проект, и на боевом сервере, то нужно определить файл settings.py и local_settings.py. Тем самым settings.py будет брать нужные параметры из local_settings.py. Это ещё нужно, например, чтобы пароль к Вашей БД на локальной машине не попал в систему контроля ревизий и не пришлось изменять сам файл настроек на сервере, если пароли на разных машинах разные.
admin-tools - приложение, улучшающее административную часть django (более подробно можно почитать здесь: habrahabr.ru/post/98539/)
south - приложение, упрощающее работу с БД: создаёт и меняет структуру БД и осуществляет миграцию данных из разных полей и таблиц (тоже на хабре можно почитать: habrahabr.ru/post/47004/)
система контроля ревизий (система контроля версий файлов, VCS) - позволяет хранить несколько версий одного и того же документа и проекта в целом, при необходимости возвращаться к более ранним версиям, определять, кто и когда сделал то или иное изменение, и др. (почитать про это: habrahabr.ru/post/108658/)
Чтобы не замарачиваться сразу приведу структуру проекта и весь код, у вас должно быть также.
Структура (обратите внимание, что она изменена в отличии от созданной командой django-admin.py startproject rabbits):
rabbits:
-main:
--|static:
----|main:
------|rabbits.jpg
--|templates:
----|home.html
--|__init__.py
--|models.py
--|urls.py
--|views.py
-__init__.py
-dashboard.py
-local_settings_sample.py
-local_settings.py
-manage.py
-urls.py
# --- rabbits/local_settings_sample.py ---
DEBUG = True
DB_GENERIC = {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'user_name',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
}
DATABASES = {
'default': dict(DB_GENERIC),
}
DATABASES['default']['NAME'] = 'db'
SECRET_KEY = 'a-wuz%jy6jq28#4co_ *23fl&0rng6u5i-i+f=$o'
ALLOWED_HOSTS = ['localhost', ]
# --- rabbits/local_settings.py ---
DEBUG = True
DB_GENERIC = {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'postgres',
'PASSWORD': 'postgres',
'HOST': 'localhost',
'PORT': '',
}
DATABASES = {
'default': dict(DB_GENERIC),
}
DATABASES['default']['NAME'] = 'test_db'
SECRET_KEY = 'a-wuz%jy6jq28#4co_ *23fl&0rng6u5i-i+f=$o'
ALLOWED_HOSTS = ['localhost', ]
# --- rabbits/settings.py ---
# -*- coding: utf-8 -*-
import os
try:
import local_settings
except ImportError:
import local_settings_sample as local_settings
ROOT_DIR = os.path.dirname(__file__)
DEBUG = local_settings.DEBUG
TEMPLATE_DEBUG = DEBUG
ADMINS = (
('name', 'email'),
)
MANAGERS = ADMINS
DATABASES = local_settings.DATABASES
TIME_ZONE = 'Asia/Irkutsk'
LANGUAGE_CODE = 'ru-RU'
LANGUAGES = (
('ru', 'Russian'),
)
SITE_ID = 1
USE_I18N = True
USE_L10N = True
USE_TZ = True
MEDIA_ROOT = os.path.join(ROOT_DIR, 'media')
MEDIA_URL = "/media/"
STATIC_ROOT = os.path.join(ROOT_DIR, 'collect_static')
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(ROOT_DIR, 'static'),
)
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
SECRET_KEY = local_settings.SECRET_KEY
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
# 'django.template.loaders.eggs.Loader',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# 'middlewares.AdminSessionMiddleware'
)
ROOT_URLCONF = 'urls'
LOGIN_URL = '/login/'
LOGOUT_URL = '/logout/'
LOGIN_REDIRECT_URL = '/admin/'
WSGI_APPLICATION = 'wsgi.application'
TEMPLATE_DIRS = (
os.path.join(ROOT_DIR, 'templates'),
)
PROJECT_APPS = (
'main',
)
INSTALLED_APPS = (
'admin_tools',
'admin_tools.theming',
'admin_tools.menu',
'admin_tools.dashboard',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
'south'
) + PROJECT_APPS
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.request',
'django.core.context_processors.media', # MEDIA_URL
'django.core.context_processors.static', # STATIC_URL
'django.contrib.messages.context_processors.messages',
)
ADMIN_TOOLS_INDEX_DASHBOARD = 'dashboard.CustomIndexDashboard'
ALLOWED_HOSTS = getattr(local_settings, 'ALLOWED_HOSTS', ['localhost', ])
# --- rabbits/wsgi.py ---
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
# --- rabbits/urls.py (который в корне проекта) ---
from django.conf import settings
from django.conf.urls import patterns, include, url
import main.views as main_views
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^admin_tools/', include('admin_tools.urls')),
url(r'^', include('main.urls')),
# --- error pages ---
url(r'^404$', main_views.error_page_404),
url(r'^500$', main_views.error_page_500),
# --- error pages ---
)
handler404 = main_views.error_page_404
handler500 = main_views.error_page_500
if settings.DEBUG:
urlpatterns = patterns(
'',
url(r'^media/(?P.*)$', 'django.views.static.serve',
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
url(r'', include('django.contrib.staticfiles.urls')),
) + urlpatterns
# --- rabbits/main/urls.py (в приложении который) ---
# -*- coding: utf-8 -*-
from django.conf.urls import patterns, url
import main.views as main_views
urlpatterns = patterns('',
url(r'^$', main_views.home),
)
# --- rabbits/main/views.py ---
# -*- coding: utf-8 -*-
from django.shortcuts import render
def home(request):
rabbits = Rabbit.objects.all()
return render(request, 'index.html',
{'rabbits': rabbits})
# --- rabbits/main/models.py ---
#-*- coding: utf-8 -*-
from django.db import models
class Rabbit(models.Model):
color = models.CharField(verbose_name=u'Цвет', max_length=100)
size = models.CharField(verbose_name=u'Размер', max_length=100)
def __unicode__(self):
return u'{0} {1}'.format(self.color, self.size)
class Meta:
verbose_name = Кролик'
verbose_name_plural = u'Кролики'
# --- rabbits/main/templates/home.html ---
<!DOCTYPE html>
<html lang="en">
<head>
<title>Какие бывают разные кролики</title>
</head>
<body>
<img src="{{ STATIC_URL }}/main/img/rabbits.png" />
<h1>И всё-таки кролики бывают разные:</h1>
<div>
{% for rabbit in rabbits %}
<h3>{{ rabbit.color }} и {{ rabbit.size }}</h3>
{% endfor %}
</div>
</body>
</html>
На что нужно обратить внимание?
В файле local_settings.py:
1) значение параметра SECRET_KEY вы должны скопировать с settings.py, который автоматически генерируется при запуске django-admin.py startproject rabbits
2) рекомендую использовать настройки бд следующие:
DB_GENERIC = {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'postgres',
'PASSWORD': 'your_password',
'HOST': 'localhost',
'PORT': '',
}
DATABASES = {
'default': dict(DB_GENERIC),
}
DATABASES['default']['NAME'] = 'test_db'
Это позволит задавать несколько баз данных. Чтобы добавить ещё одну, нам нужно добавить ключ, значение в словарь DATABASES и задать имя базы данных. Например:
DATABASES = {
'default': dict(DB_GENERIC),
'other_db': dict(DB_GENERIC),
}
DATABASES['default']['NAME'] = 'test_db'
DATABASES['other_db']['NAME'] = 'other_db'
3) Этот файл не добавляем в репозиторий, а создаём его на своей локальной машине и на сервере.
4) Если вдруг Вы забыли создать local_settings.py на сервере, то будет использовано local_settings_sample.py, который храниться в репозитории.
В файле settings.py:
1) Добавляем приложение main в список PROJECT_APPS, который лежит в требуемом для джанги списка INSTALLED_APPS
2) Подключаем admin-tools вот этой строчкой: ADMIN_TOOLS_INDEX_DASHBOARD = 'dashboard.CustomIndexDashboard'
3) В значениях констант WSGI_APPLICATION, ROOT_URLCONF, а также в файле wsgi.py убрали название проекта test_app
4) Константой STATIC_ROOT определили место куда будет собираться вся статика с приложений при вызове команды collectstatic на боевом сервере
После того, как требуемые файлы созданы, давайте создадим первую миграцию приложения main и выполним её.
python manage.py schemamigration main --init
python manage.py migrate
Всё вот так просто создали миграцию. Создайте несколько записей о кроликах через админку и посмотрите, что у Вас отображаются данные на главной странице
Поздравляю, мы создали работающее приложение у себя на компьютере, и теперь осталось развернуть его на сервере! Заходим на боевой сервер через putty или терминал.
Создаём структуру папок: для нашего веб-приложения "Кролики"
mkdir -p /home/vivazzi/web/rabbits
cd /home/vivazzi/web/rabbits
mkdir /home/vivazzi/web/rabbits/src # исходный код проекта
mkdir /home/vivazzi/web/rabbits/logs # сюда собирать будем логи
mkdir /home/vivazzi/web/rabbits/docs # здесь будут храниться конфиги
Поставим postgresql для работы с БД:
apt-get install postgresql
Входим под пользователем postgres:
su - postgres
И входим в клиент, который позволяет нам работать с СУБД postgresql:
psql
Создадим владельца базы данных test_user
postgres=# CREATE ROLE test_user WITH NOSUPERUSER LOGIN PASSWORD 'test_pass';
CREATE ROLE
Создадим базу данных test_db, владельцем которой будет test_user
postgres=# CREATE DATABASE test_db OWNER test_user;
CREATE DATABASE
Выходим из редактора postgresql
\q
И разлогинимся с пользователя postgresql:
logout
Ставим mercurial
apt-get install mercurial
А теперь клонируем проект в папку src
cd /home/vivazzi/web/rabbits/src
hg clone https://vivazzi@bitbucket.org/vivazzi/rabbits # у Вас должен быть свой адрес к проекту на битбаките
И запускаем миграции
cd /home/vivazzi/web/rabbits/src/rabbits/
python manage.py migrate
Пусть у нас будет доменное имя www.happy-rabbits.ru, тогда прописываем DNS записи:
nano /etc/bind/named.conf
# --- /etc/bind/named.conf ---
zone "happy-rabitts.ru" {
type master;
file "/etc/bind/happy-rabitts.ru";
};
nano /etc/bind/happy-rabitts.ru
# --- /etc/bind/happy-rabitts.ru ---
$TTL 3600
@ IN SOA ns1.happy-rabitts.ru. root.happy-rabitts.ru. (2012000001 10800 3600 604800 86400)
@ IN NS ns1
@ IN NS ns2
ns1 IN A 62.109.10.27 # поменяйте на ip вашего сервера (ip-адрес_1)
ns2 IN A 62.109.7.235 # (ip-адрес_2)
@ IN A 62.109.10.27 # (ip-адрес_1)
www IN A 62.109.10.27 # (ip-адрес_1)
cd /home/vivazzi/web/rabbits/docs/
nano uwsgi.ini
# --- Содержимое файла uwsgi.ini ---
[uwsgi]
# Сокет для передачи данных
socket = /home/vivazzi/web/rabbits/uwsgi.sock
# Путь к виртуальному окружению (пока его не используем)
home = /home/vivazzi/web/rabbits/env
# Нам понадобится включенный python плагин
plugins = python
# Дополнительный python-путь
pythonpath = /home/vivazzi/web/test_app
# Модуль python с определением wsgi приложения
module = wsgi
# Количество процессов
processes = 2
# Максимальное количество соединений для каждого процесса
max-requests = 5000
# Максимальный размер запроса
buffer-size = 32768
# Убивать каждое соединение требующее больше 30 сек для обработки
harakiri = 30
# Включает опцию мастер-процесса uwsgi
master
# Автоматически прибивать воркеры, если умирает мастер-процесс
no-orphans
# Количество секунд, которое дается воркеру uwsgi для корректного перезапуска.
# Если воркер не успевает - по истечении этого времени его принудительно пришибает
reload-mercy = 8
Создаём файл supervisor.conf
root@viva-itstudio:/home/vivazzi/web/rabbits/docs/# nano supervisor.conf
# --- /home/vivazzi/web/rabbits/docs/supervisor.conf ---
[program:rabbits]
command=/usr/bin/uwsgi --ini /home/vivazzi/web/rabbits/docs/uwsgi.ini
user=www-data
stdout_logfile=/home/vivazzi/web/rabbits/logs/uwsgi.log
stderr_logfile=/home/vivazzi/web/rabbits/logs/uwsgi_err.log
directory=/home/vivazzi/web/rabbits/src/rabbits
autostart=true
autorestart=true
redirect_stderr=true
stopsignal=QUIT
Создаём файл nginx.conf
root@viva-itstudio:/home/vivazzi/web/rabbits/docs# nano nginx.conf
# --- /home/vivazzi/web/rabbits/docs/nginx.conf ---
server {
listen 80;
server_name happy-rabbits.ru www.happy-rabbits.ru;
access_log /home/vivazzi/web/rabbits/docs/nginx_access.log;
error_log /home/vivazzi/web/rabbits/docs/nginx_error.log;
location /static/ {
alias /home/vivazzi/web/rabbits/src/rabbits/collect_static/;
}
location / {
uwsgi_pass unix:///home/vivazzi/web/rabbits/uwsgi.sock;
include uwsgi_params;
}
}
Теперь нужно поставить необходимые библиотеки и зависимости
pip install Django==1.5 south django-admin-tools==0.5.1 psycopg2==2.4.1
Если в процессе установки ещё pip ещё что-то просит, то смотрим что он хочет и даём)
Создаём симлинки конфигураций nginx-а и supervisor-а и выдаём права на чтение и запись всей папки с проектом
ln -s /home/vivazzi/web/rabbits/docs/nginx.conf /etc/nginx/sites-enabled/test_app.conf
ln -s /home/vivazzi/web/rabbits/docs/supervisor.conf /etc/supervisor/conf.d/test_app.conf
chmod a+w -R /home/vivazzi/web/rabbits
И перечитаем конфигурацию nginx-а
# если ещё не запущен, то:
cd /usr/local/sbin
./nginx
# либо запуская init-скрипт: /etc/init.d/nginx restart
# и даём команду на перезапуск:
nginx -s reload
Также для supervisor-а нужно выполнить команду перечитывания конфигурации
supervisorctl update #добавит процесс в свой список
Позже можно управлять проектом командами: supervisorctl start|stop|restart rabbits
Всё! Теперь должно работать. Если это не так, то логи помогут (/home/vivazzi/web/rabbits/logs/)
Если всё заработало, то я Вас поздравляю! Если что-то не так, пишите мне на почту artem@vits.pro. Буду рад помочь!
И в качестве заключения хочу пожелать успехов в работе, в создании клёвых сайтов и, чтоб багов было меньше, а лучше чтоб вообще не было =). Удачи!
Предыдущая статья3. Настройка DNS, конфигураций для связки uwsgi, nginx и supervisor. Запуск проекта
Похожие статьи:
Представляю вашему вниманию книгу, написанную моим близким другом Максимом Макуриным: Секреты эффективного управления ассортиментом.
Книга предназначается для широкого круга читателей и, по мнению автора, будет полезна специалистам отдела закупок и логистики, категорийным и финансовым менеджерам, менеджерам по продажам, аналитикам, руководителям и директорам, в компетенции которых принятие решений по управлению ассортиментом.
Комментарии: 0