Скрипт управления django fastcgi процессами

У меня на хостинге сейчас запущено несколько сайтов на Django. Директория каждого django-проекта называется web_site и лежит в одноимённой домену директории. Например, этот блог находится в /web/web-brains.com/web_site.

Такое расположение проектов позволяет мне легко управлять демонами этих сайтов.

Система управления состоит из трёх частей.
1) скрипт /web/spawnlib.sh, который содержит базовые операции по запуску, остановке процессов
2) cкрипты /web/domain.com/web_site/spawn.sh. Каждый spawn.sh содержит информацию о том, в какой директории хранятся файлы сайта и где хранится socket-файл fastcgi-демона сайта. Управление конкретным сайтом (запуск/остановка) осуществляется через его spawn.sh
3) скрипт /web/dja.sh, который нужен для массового запуска/останова сайтов

А теперь код, из которого всё будет понятно.

Скрипт spawnlib.sh


PYTHON=/usr/local/bin/python
PIDFILE="/var/run/www/$SITE.pid"
SOCKFILE="/var/run/www/$SITE.sock"
COMMAND="$PYTHON ./manage.py runfcgi method=threaded socket=$SOCKFILE pidfile=$PIDFILE maxchildren=1000"
#COMMAND="$PYTHON ./manage.py runfcgi maxchildren=3 maxrequest=1000 minspare=1 maxspare=1 socket=$SOCKFILE pidfile=$PIDFILE"

usage() {
echo "$0 {start|stop|restart}"
exit 1
}

daemon_command() {
echo -n $COMMAND
}

daemon_pids() {
echo $(ps axwwo pid,command|grep "$(daemon_command)"|grep -v grep|cut -c 1-6)
}


start() {
PIDS=$(daemon_pids)
if [ -n "$PIDS" ]; then
echo "daemons already running"
else
echo "Loading daemons"
if [ ! -e $SITE_DIR ]; then
echo "$SITE_DIR does not exist"
else
cd $SITE_DIR
#echo $(daemon_command)
$(daemon_command)
fi
fi
}

stop() {
PIDS=$(daemon_pids)
if [ -n "$PIDS" ]; then
echo "Killing old daemons"
for pid in $PIDS; do
kill $pid
done
else
echo "No daemons running"
fi
}

pids() {
echo "pids: $(daemon_pids)"
}

restart() {
stop
sleep 2
start
}


action=$1
shift
case $action in
"start") start;;
"stop") stop;;
"pids") pids;;
"restart") restart;;
*) echo $(usage);;
esac


Пример скрипта spawn.sh для сайта dumpz.org


#!/bin/sh
SITE_DIR=/web/dumpz.org/web_site
SITE=dumpz.org

. /web/spawnlib.sh


Скрипт dja.sh


#!/bin/sh

WEB_DIR="/web"
DJANGO_DIRS="ozon adomsk.info dumpz.org pydev.ru web-brains.com pybb.org netdb.ru nashomsk.ru"

bulk_action() {
action=$1
for DIR in $DJANGO_DIRS; do
spawn_file="$WEB_DIR/$DIR/web_site/spawn.sh"
echo $spawn_file $action
if [ ! -f "$spawn_file" ]; then
echo "file $spawn_file does not exist"
else
$spawn_file $action
fi
done
}

usage() {
echo "Usage: $0 [start|stop|restart]"
exit 1
}

case $1 in
"start") bulk_action "start";;
"stop") bulk_action "stop";;
"restart") bulk_action "restart";;
*) usage;;
esac


Проблему автозапуска сайтов после перезагрузки или каких-либо злоключений с демоном я решил просто и грубо - прописал в кроне вызов dja.sh каждые пять минут )


*/5 * * * * /web/dja.sh start
Add post to:   Delicious Reddit Slashdot Digg Technorati Google
Make comment

Comments

Спасибо. Интересно.

А я для тех же нужд вот такую штуку изобрел: может быть вам или кому из ваших читателей приглянется.

Удачи!

#!/usr/bin/python
# -*- coding: utf-8 -*-
import getopt, sys, os, ihooks, imp

def import_from(filename):
    "Import module from a named file"
    loader = ihooks.BasicModuleLoader()
    path, file = os.path.split(filename)
    name, ext  = os.path.splitext(file)
    m = loader.find_module_in_dir(name, path)
    if not m:
        raise ImportError, name
    m = loader.load_module(name, m)
    return m

settings = import_from('/etc/django/fcgi-config.py')

name = 'Django application control script'
version = 0.1

projects_dir = settings.projects_dir
run_dir = settings.run_dir

def start(app, force = False):
    """Запускает приложение app"""
    app_path = '%s/%s' % (projects_dir,app)
    pidfile_path = '%s/%s.pid' % (run_dir,app)
    if not force:
        try:
            os.stat(pidfile_path)
            sys.stderr.write('Application is already running\n')
            sys.exit(1)
        except OSError:
            pass
    os.chdir(app_path)
    if os.system('python manage.py runfcgi socket=/tmp/%s.sock pidfile=%s' % (app, pidfile_path)) == 0:
        sys.stdout.write('OK\n')
    else:
        sys.stdout.write('Fail!')
        sys.stderr.write('Unknown error: Can\'t launch %s' % app)

def stop(app):
    """Останавливает приложение app"""
    try:
        pidfile_path = '%s/%s.pid' % (run_dir,app)
        pidfile=open(pidfile_path,'r')
        pid = pidfile.readline()
        pidfile.close()
        os.remove(pidfile_path)
        if os.system('kill -9 %s' % pid) != 1:
            sys.stdout.write('OK\n')
        else:
            sys.stdout.write('Fail!')
            sys.stderr.write('Unknown error: Can\'t stop %s' % app)

    except IOError:
        sys.stderr.write('Application is not running\n')
        sys.exit(1)

def restart(app):
    """Перезапускает приложение app"""
    stop(app)
    start(app)

def usage():
    """docstring for usage"""
    sys.stderr.write('Usage: django-control { -V | -h | [{--start | --stop | --restart | --reload } app] }\n')

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'Vh', ('start','stop','reload','restart'))
        try:
            app = args[0]
        except:
            if opts == [('-V', '')] or opts == [('-h', '')]:
                pass
            else:
                raise KeyError
    except:
        usage()
        sys.exit(2)

    for opt, arg in opts:
        if opt == '-V':
            sys.stderr.write("%s (version %0.1f)\n" % (name, version))
            sys.exit(0)
        if opt == '-h':
            usage()
            sys.exit(0)
        if opt == '--start':
            start(app)
        elif opt == '--stop':
            stop(app)
        elif opt == '--reload':
            restart(app)
        elif opt == '--restart':
            restart(app)
        else:
            usage()
        sys.exit(0)

if __name__=='__main__':
    main()

А еще у вас markdown в превью не работает :)

Я вообще удивляюсь, что тут что-то работает :-) Всё руки не дойдут обновить версию Byteflow — она у меня старая.

автозапуска сайтов после перезагрузки…каждые пять минут” А если просто после перезагрузки один раз запустить, или есть как-то скрытые моменты с manage.py runfcgi?

Да, моменты в следующем:

  • а вдруг демон сайта возьмёт да помрёт, не знаю, насколько такое вероятно, но я боюсь )
  • сайт надо периодически перезапускать: убивать старый демон, запускать новый. Вот тут как раз очень удобно пользоваться скриптом.

Кстати, недавно написал аналогичную версию, работащую с pid файлами, а не с выводом ps. Дымс: http://dumpz.org/3169/

Я часто использую такой скрипт для рестарта после вливания новых изменений:

#!/bin/sh
hg push
ssh user@host 'cd ~/website; hg up; ./spawn.sh restart'

Spin this text to add variation to your comments and check this for http://www.google.com search access. New comment/post.org bot browser bulk replace byteflow cache captcha chromium commands conference css curl dd debian debug debugging decorator design djaba django djangodash.org excel exception export fastcgi favicon feedzilla filemanager firebug firefox foreignkey forum google grep html import iphone jabber javascript jquery linkexchange linkfeed linkfeed.ru linux logrotate mainlink memoryleak mercurial middleware mongodb newforms nginx omsk opensource orm oscon pagination partitions php pip plugin production punbb pybb pycurl pyjob.ru python registration ruby rubyonrails rupy russia sape sape.

Required. 30 chars of fewer.

Required.

captcha image Please, enter symbols, which you see on the image