Закрыть

Для эффективной работы на сайте используются cookie и обработка персональных данных. Пользуясь этим сайтом, вы соглашаетесь с правилами использования сайта. Подробнее

Цитата дня

Vivazzi.ru

Личный сайт Мальцева Артема

Если человек стремится к чему-то всем своим сердцем, он обязательно сумеет добиться того, чего хочет, даже если это кажется невозможным, даже если все окружающие открыто заявляют ему, что это невозможно.

Чак Норрис

4. Запускаем django приложение на сервере

18 февраля 2014 г. 4: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. Буду рад помочь!

И в качестве заключения хочу пожелать успехов в работе, в создании клёвых сайтов и, чтоб багов было меньше, а лучше чтоб вообще не было =). Удачи!

Оцените статью

5 из 5 (всего 2 оценки)

Поля, отмеченные звёздочкой ( * ) , являются обязательными.

Спасибо за ваш отзыв!

Автор статьи

Права на использование данной статьи, расположенной на настоящей странице http://vivazzi.ru/deploy-django-site/run-django/:

Разрешается копировать статью с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор статьи: Мальцев Артём
Ссылка на статью: <a href="http://vivazzi.ru/deploy-django-site/run-django/">http://vivazzi.ru/deploy-django-site/run-django/</a>

Подробнее: Правила использования сайта

Вам нужно саморазвиваться или вы хотите зарабатывать деньги?

Или вы ищите хорошие IT сервисы или книги? Сохраните свое время и взгляните на мою подборку рекомендаций, которыми постоянно пользуюсь.
Посмотреть рекомендации

Комментариев: 0

Вы можете оставить комментарий как незарегистрированный пользователь. Но, зарегистрировавшись, вы сможете получать оповещения об ответах, а также иметь доступ к своему личному аккаунту для просмотра своих комментариев.

Чтобы оставить комментарий от своего имени войдите или зарегистрируйтесь обычным способом или через социальные сети:

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте