Статьи

Развертывание статических файлов Django в Heroku через Hudson / Jenkins

Начиная с Django 1.3, вы можете использовать встроенную функцию статических файлов для объединения CSS, Javascript, изображений и других статических ресурсов для развертывания в CDN. Есть два популярных приложения Django, django-compress и django-pipe, которые обеспечивают дополнительную функциональность, такую ​​как минимизация и кажущаяся поддержка популярных хостов, таких как Amazon S3 .

Эти «управляющие активами» выполняют одни и те же основные вещи. Они дают вам механизм для объединения нескольких файлов CSS и JavaScript в один файл, при необходимости минимизируют содержимое и генерируют уникальные имена файлов для связанных версий. Идея, стоящая за уникальными именами файлов, заключается в том, что вы можете сказать браузерам кэшировать эти файлы навсегда; любые внесенные вами изменения будут использовать разные URL для включений. Они также генерируют уникальные имена файлов для изображений, а затем переходят в ваши CSS-файлы и заменяют пути к файлам изображений.

Обе библиотеки немного незрелые; Я столкнулся с многочисленными проблемами, пытаясь заставить развертывание на S3 работать. Сначала я попробовал django-компрессор в паре с django-хранилищами , которые можно сохранить прямо на S3. Обычно django-компрессор создает минимизированные файлы на лету, что прекрасно работает, за исключением того, что Heroku имеет эфемерную файловую систему, что означает, что файлы будут создаваться заново при каждом перезапуске процесса dyno. Что еще хуже, встроенное хранилище S3 boto очень медленно синхронизирует файлы до S3, особенно с учетом того, что большинство файлов не изменяются при любом конкретном развертывании. В общем, я смотрел примерно на 3 минуты лага каждый раз, когда хотел запустить динамо.

Я попытался использовать их автономный компрессор, и я действительно получил его развертывание файлов. Но я не мог заставить django-компрессор использовать правильные минимизированные URL; он постоянно пытался ссылаться на разные имена файлов до и после развертывания, в результате чего получалось 404. Несмотря на то, что это самый популярный фреймворк, я решил пойти дальше и попробовать django-pipe. Точно так же я не уверен, что согласен с их самым первым дизайнерским решением, а именно: «JS / CSS принадлежат шаблонам».

Для сравнения, создание django-pipel очень просто. Вот моя шпаргалка:

    pip install django-pipeline  
    apt-get install yui-compressor  

Изменить settings.py:

    INSTALLED_APPS = (  
        ...  
        'pipeline',  
    )  
      
    PIPELINE = DEBUG  
    PIPELINE_YUI_BINARY = '/usr/bin/yui-compressor'  
    STATICFILES_STORAGE = 'pipeline.storage.PipelineStorage'  
    STATIC_ROOT = '/tmp/myapp-staticfiles'   
    # using a protocol relative URL here so that resources load from http/https accordingly  
    STATIC_URL = '/static/' if DEBUG else '//s3.amazonaws.com/mys3bucket/'    
      
    # the directories stylesheets and javascript should be inside myapp/static,  
    # as per the staticfiles convention. I also put an "images" directory there.  
      
    PIPELINE_CSS = {  
        'base': {  
            'source_filenames': (  
              'stylesheets/reset.css',  
              'stylesheets/base.css',  
            ),  
            'output_filename': 'stylesheets/base.min.css',  
        },  
        'mobile': {  
            'source_filenames': (  
              'stylesheets/reset.css',  
              'stylesheets/mobile.css',  
            ),  
            'output_filename': 'stylesheets/mobile.min.css',  
        },  
    }  
      
    PIPELINE_JS = {  
        'base': {  
            'source_filenames': (  
              'javascript/jquery.min.js',  
              'javascript/jquery-ui.min.js',  
              'javascript/myapp.js',  
            ),  
            'output_filename': 'javascript/base.min.js',  
        },  
        'mobile': {  
            'source_filenames': (  
              'javascript/jquery.min.js',  
            ),  
            'output_filename': 'javascript/mobile.min.js',  
        },  
    }  

Для разработки вы можете подать статические файлы из / static со следующими строками в вашем urls.py:

    if settings.DEBUG:  
        urlpatterns += patterns('',  
            (r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}),  
        )  
    </path>  

В ваших шаблонах вы ссылаетесь на ваш CSS / JS по-другому:

    {% load compressed %}  
    <html>  
        <head>  
            {% compressed_css "base" %}  
        </head>  
        <body>  
            <!-- content here -->  
            {% compressed_js "base" %}  
        </body>  
    </html>  

Один хитрый момент заключался в том, что как с django-compress, так и с django-pipe, у меня были значительные проблемы с выяснением, как ссылаться на изображения внутри моих CSS-файлов. Я пробовал разные вещи, каждый из которых работал только в локальной среде разработки или на производстве. Наконец, я попробовал относительные URL, и это сработало. В прошлом я всегда использовал абсолютные относительные ссылки в CSS.

    body {  
        background: url(../images/bg.png); /* NOT /static/images/bg.png, or images/bg.png */  
    }  

Для развертывания я решил использовать Hudson вместо попыток заставить Heroku сделать это через взлом Procfile. Вот сценарий развертывания, который я сейчас использую:

    #!/bin/bash  
      
    set -e  
      
    # used to load different settings.py extension file  
    export ENVIRONMENT=production    
      
    # using virtualenv to separate build python class paths  
    source /var/lib/hudson/virtualenv/prod/bin/activate    
    pip install -r requirements.txt  
      
    # this is the static files bit, collect the files and copy them to s3 using the fast s3cmd utility  
    python manage.py collectstatic --noinput  
    s3cmd sync /tmp/myapp-staticfiles/ s3://mys3bucket  # note the trailing slash, critical!  
      
    heroku maintenance:on  
    heroku pgbackups:capture HEROKU_POSTGRESQL_DB --expire  
    git push -f heroku hudsonmerge:refs/heads/master  
    heroku run python manage.py migrate --noinput --merge --ignore-ghost-migrations  
    heroku maintenance:off  

Вот и все. В настоящее время мои сборки занимают около 90 секунд и включают нулевую задержку при перезапуске dyno.