Pull to refresh

Comments 40

Это шутка такая, да?

Когда у вас VPS даже с парой-тройкой сервисов, изоляция решает. Сегодня у вас джанга с SQLite, завтра сервис с MariaDB, а послезавтра — условный телеграм бот на PHP с Postgre. И что, все это на хост будете ставить? А когда обновите версию джанги в одном из проектов, руками пересоздавать venv на хосте будете? Или будете писать чудесный деплой-скрипт, который ходит по файловой системе, обновляет репо, создает виртуальные окружения, пересоздает все что можно? *картинка с троллейбусом*

Тот же certbot — в чем смысл ставить лишний софт на хост, который вам нужен раз в 3 месяца, запускается в одну строчку из контейнера и абсолютно независим от всего остального, что вы там наставили?

Ну да, я делаю всё именно так. В качестве "скрипта" использую Ansible-плейбук, в нём уже есть модуль для управления venv и прочие радости жизни. В чём проблема?

Да нет, если вам норм, кто ж вам запретит, нет никакой проблемы. Но мне кажется, что условные git pull && docker-compose up -d --build достигают той же цели, но куда меньшими затратами и намного проще и надежнее. Ну и docker/docker-compose нынче куда более распространены, нежели Ansible.

А затраты на написание докерфайлов и организацию взаимодействия между контейнерами вы решили тактично умолчать :) Серьёзно, я как-то пробовал автоматизировать развёртывание одного реального PHP-проекта (хоть я и питонист, ну да ладно) — Ansible-плейбук написался без проблем, а довести докер-контейнеры до пригодного для продашкена состояния у меня так и не получилось

Я говорю о проекте уровня описанного в статье — простой веб-сервис с условным nginx+uwsgi/gunicorn/php+mysql/mariadb/postgre. Бложик или пет-прожект на джанге или обычный монолит на PHP. Какая еще «организация взаимодействия»? environment: {DB_HOST: db} и depends_on: db для зависимых контейнеров? Вот и все «взаимодействие». Ну да, простите великодушно, «тактично умолчал», потому что это три строчки, которые после первых пары раз пишутся на автомате и уже не думаешь даже. Секундное дело. Но зато после этого вы получаете docker-compose файл, который кто угодно может на своей машине запустить, имея из зависимостей лишь, собственно, докер и компоуз. Один клик — и весь сервис поднят. Да, естественно, если вам захочется больше плюшек, health-чеков, определенного порядка запуска контейнеров, дальше будет сложнее, но для описанного в посте уровня простенького проекта ничего этого не нужно.

Не говоря уж про то, что написание Dockerfile это как минимум «правило хорошего тона», по которому можно понять, какие у вашего проекта зависимости. Если мы говорим про питон, то каждая вторая нетривильная зависимость из requirements.txt будет требовать какой-нибудь нативной библиотеки, про которую, естественно, автор не упомянет в ридми. А если вы напишете в докерфайле «pip install -r requirements.txt», то это банальный тест — если оно билдится, значит побилдится и у всех остальных. Не раз и не два такое было, что на хосте вроде работает, а пишешь в докерфайл — опа, оказывается, еще десяток нативных зависимостей надо доставлять (которые на хосте были поставлены неизвестно когда и уж все забыли про них).

Кэп подсказывает, что Ansible playbook у вас написался без проблем, потому что у вас есть опыт в написании плэйбуков. А у меня наоборот — я подобные простые 2-3 сервиса быстро и без проблем оберну в Dockerfile+docker-compose, а вот если надо будет Ansible юзать, то я уйду гуглить на неопределенное время.
Я говорю о проекте уровня описанного в статье

Окей, начнём с банального — раздача статических файлов. В какой контейнер их класть? В каком контейнере запустить "manage.py collectstatic" и как при этом не наплодить стопицот лишних докер-слоёв?


Чуть менее банальное — почти любой Django-проект однажды дорастёт до необходимости использовать Celery, где и как запускать его воркеры? Если придётся запускать несколько контейнеров — как им всем обеспечить беспроблемный доступ на запись в media-каталог — возможно, делать chmod 777 и umask 000, или есть что-то более умное?


Ну ладно, если запустить два контейнера из одного образа, у них будут одинаковые uid и проблем с доступом возможно не будет. А что если однажды придётся собрать ещё один образ (из моей личной практики — syncthing, например) и дать ему доступ на запись к тому же общему media-каталогу — как это организовать, чтобы было достаточно безопасно и чтобы при этом не было проблем?


про которую, естественно, автор не упомянет в ридми.

Ага, а контейнеры тут при чём? Пусть автор просто прогонит установку на чистой системе (да хоть в том же контейнере, лол) и в ридми допишет зависимости, да и всё. В плейбуке, кстати, зависимости тоже прописаны.


написание Dockerfile это как минимум «правило хорошего тона», по которому можно понять, какие у вашего проекта зависимости.

То же самое можно сделать через CI/CD — в скрипт установки для условного Travis CI или Github Actions тоже придётся прописать все зависимости.


потому что у вас есть опыт в написании плэйбуков.

Так изначально его не было, у меня знания тоже не из воздуха появились.


а вот если надо будет Ansible юзать, то я уйду гуглить на неопределенное время.

А я до сих пор продолжаю гуглить инфу про докер, но так и не могу собрать что-то пригодное для продакшена ¯\_(ツ)_/¯

Окей, начнём с банального — раздача статических файлов. В какой контейнер их класть? В каком контейнере запустить «manage.py collectstatic» и как при этом не наплодить стопицот лишних докер-слоёв?


Вы намекаете, что мне тоже статью стоит написать? Честно говоря, я понимаю, потому что если гуглить «django docker», то во всех этих хипстерских seo-friendly бложиках ни один чел не идет дальше хелловорлда и тему статических файлов не затрагивает. Я сам довольно долго гуглил и спрашивал в чатиках насчет «правильного» способа.

На сегодняшний день я использую один из следующих вариантов:
— Shared volume лишь между контейнерами (но не с хостом, см. SO). Тогда collectstatic пишется в контейнере с джангой и nginx контейнер просто видит папку со статиками
— Если же volume не вариант (например, в Docker Swarm), то можно сделать multistage билд для nginx контейнера. Ну т.е. что-то подобное:
FROM python:3.9 as builder
RUN pip install Django
COPY . /app
WORKDIR /app
RUN manage.py collectstatic --noinput

FROM nginx
COPY config/nginx.conf /etc/nginx/config
COPY --from=builder /static /static

— Ну и «плохой» вариант, но в общем-то вполне рабочий для небольших проектиков без сотен реквестов в секунду — используйте manage.py runserver. Тогда вам даже второй контейнер с nginx не нужен. Есть несколько библиотек типа whitenoise, которые в теории несколько улучшают ситуацию с «runserver не для продакшна».

Чуть менее банальное — почти любой Django-проект однажды дорастёт до необходимости использовать Celery, где и как запускать его воркеры?

Не могу ответить, поскольку ни в одном из проектов Celery не использовал, не было нужды. Но есть подозрение, что это может выглядеть как один контейнер, в котором воркеры сами скейлятся как надо (собственно, как uwsgi для того же джанго сам делает).

Ага, а контейнеры тут при чём? Пусть автор просто прогонит установку на чистой системе (да хоть в том же контейнере, лол) и в ридми допишет зависимости, да и всё. В плейбуке, кстати, зависимости тоже прописаны.


Понятно, что хороший автор опишет все, но Dockerfile (как и ваш плейбук, видимо) — это не просто текст, это реальные инструкции, которые должны работать, чтобы проект поднялся. И само средство вынуждает автора описать environment. Лишь один коммент — в каком проценте open source проектов вы видели плейбук (или, например, Vagrantfile?) в репозитории? Сравните с процентом репозиториев, имеющих Dockerfile.

Так изначально его не было, у меня знания тоже не из воздуха появились.

А я до сих пор продолжаю гуглить инфу про докер, но так и не могу собрать что-то пригодное для продакшена ¯\_(ツ)_/¯


Ну видите, мы в одинаковой ситуации, только с разными тулами :) Только в самом начале я пошел гуглить про довер и теперь знаю о докере и применяю его, а вы пошли гуглить про Ansible и применяете его. 1-1 :)
Нет, это не шутка, я просто недостаточно развил свою мысль. Она действительно не для всех очевидна, так что исправляюсь.

В определённый исторический период сложился такой консенсус, что лучше иметь универсальный механизм изоляции для всего, то есть контейнеризацию через Docker. Предполагалось, что девопс-инженерам не придётся изучать тонкости языков и платформ, они просто будут воспроизводить одинаковую среду на серверах и машинах разработчиков, и все будут счастливы.

Но сегодня стало очевидно, что этот универсальный механизм себя не оправдал, не реализовал обещаний. Для разработчиков затраты на поддержание той же системы контейнеров, что и на серверах, оказались неподъёмны: сложно, требует изучения и поддержки, нужно мощное железо, намного усложнились задачи, которые раньше были простыми, типа отладки. Девопсы так и не смогли обойтись без изучения целевых платформ, просто вместо прямого подхода (прочитать немного про CPython, pip и virtualenv) им приходится начинать погружение в чудесный мир Python с отлаживания необъяснимых багов (почему cryptography не собирается в контейнере с Alpine).

В результате поддержка простых и понятных деплой-скриптов на Fabric оказывается намного практичней, чем монструозная система изоляции всего и вся. Особенно если основа продакшена − Python, который уже имел специфические, весьма развитые и взрослые средства изоляции с отличным уровнем переносимости, когда докера ещё не было в проекте.
Контейнеры — ладно, но хоть virtualenv-то надо, там же не один проект будет крутиться наверняка.
Вы правы, virtualenv и pyenv − обязательно. Даже если проект будет один, зависимости надо фиксировать.

Прошу прощения — Вы правда считаете что установкой certbot начинается и заканчивается настройка ssl в nginx ?

Здравствуйте, в данной статье я рассмотрел полный порядок от начала и до конца, как быстро и просто, в том числе заполучить SSL-сертификат силами let's encrypt — файлы сертификаты автоматически создаются для домена и последующей командой в файл конфигурации nginx вносятся изменения для работы домена с https:

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    server_name example.com;
    return 404; # managed by Certbot
Наверно, был намёк на то, что certbot может автоматически обновлять сертификат.

Не нужно использовать if для ретрая на https. Отдельный блок с 80 портом.

Плохо в смысле безопасности.

Сейчас в systemd есть пользовательские юниты. Использовать их очень просто: нужно вместо /etc/systemd/system использовать ~/.config/systemd/user, а к вызову systemctl добавлять ключ --user.

Когда пользовательских юнитов не было, принято было подменять пользователя для запуска gunicorn/uwsgi/et c. вот таким образом:

[Service]
User=www
Group=www


Пускать Django из-под рута − это варварство.
Слабовато. Не безопасно. Устаревше. Пора к ASGI привыкать.
я конечно не спец в 3й джанге (давно сменил стек), но небольшой эксперимент все же проводил. и производительность джанго под asgi у меня проседала на 30% относительно wsgi варианта. а где обещанные бонусы ?))
На ASGI придётся переходить, если понадобится вебсокет.
мой опыт показывает, что вебсокеты делаются на чем угодно aio* за 5 минут без джанги. да и зачем джанго для пуш уведомлений? или вы апи фигачите через сокеты…?
  1. «Что угодно aio*» − это плюс один фреймворк, то есть для собственного резюме − здорово, а для предприятия − не то чтобы.
  2. Лично я, скажу честно, не горю желанием перейти на асинхронное программирование, и не встречал ещё таких питонистов, которые бы мечтали об этом. Я видел, как самые простые вещи с async/await превращаются в инкубатор неотлаживаемых багов, и не хочу быть крайним в разгребании этих авгиевых конюшень.
  3. Что плохого в API через вебсокеты?
Я видел, как самые простые вещи с async/await превращаются в инкубатор неотлаживаемых багов

Вот все вокруг почему-то об этом говорят, а я сколько ни программировал асинхронщину — почему-то ни разу не сталкивался с какими-то специфическими проблемами

А что вы делали с неспецифическими проблемами, когда отладчики в 3.4 и 3.5 ещё не умели останавливаться в соседнем треде?

А я отладчиками не пользуюсь) Как-то ни разу не возникало необходимости

не горю желанием перейти на асинхронное программирование, и не встречал ещё таких питонистов, которые бы мечтали об этом

Так я первый буду, который мечтает. Хотя, верится с трудом.
я конечно не спец в 3й джанге (давно сменил стек), но небольшой эксперимент все же проводил. и производительность джанго под asgi у меня проседала на 30% относительно wsgi варианта. а где обещанные бонусы ?))


А вы уверены, что ваши тесты верны и в них использовалась реальная работа веб приложения, а не простое измерение пропускной способности? Бонусы от асинхронности вы получите в любом современном веб приложении, за исключением простых лендингов и отдельных статичных страницах или даже динамичных но простых. Нужно понимать где применять асинхронность, а где нет. Я уже не буду говорить что при длительном синхронном запросе вы его можете просто потерять и при высокой нагрузке на сервер он у вас просто упадет при синхронных запросах. Плюс к этому, чем вам не бонус еще и сокеты. При этом ASGI обратно совместим с синхронными запросами.
я что-то не понял. а что вам дает gunicorn в systemd с таким конфигом, кроме автостарта при рестарте виртуалки? supervizord не осилили ?)

А зачем ставить лишнюю сущность, если systemd и так есть по умолчанию почти в любом линуксе? А ещё supervisord всё равно будет запускаться через тот же самый systemd)

вы все верно написали, но не по делу.
супервизор решает одну оч простую задачу — рестарт приложения при краше. именно про это и был мой вопрос тк конфиг автора для системд этого не делает. и если приложение ляжет — само не поднимется
А systemd тоже умеет в рестарт при краше.
Достаточно добавить что-то вроде
StartLimitIntervalSec=500
StartLimitBurst=5

[Service]
Restart=on-failure
RestartSec=5s
а кто говорит, что он этого не умеет? я написал, что конфиг автора этого не делает

Не делает.
Неясно, правда, зачем нужно ставить аж целый supervisord, если можно добавить одну строчку в конфиг systemd.

Символическую ссылку не нужно удалять при изменениях оригинального файла. Текстовым редактором можно открывать сразу ссылку, редактироваться будет оригинальный файл. Ведь на то она и ссылка.
https://ru.m.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B0

Ну и restart делать тоже необязательно. Как правило, достаточно reload.
Как я указал в Предисловии, инструкция с Digital Ocean содержит множество ошибок и является недосказанной, потому что, если следовать ей, то у вас ничего не получится, если вы делаете это в первый раз, как я (возможно профессионал на ту статью посмотрит иначе — просто как на подсказку к действию, но не будет читать каждое ее предложение, чтобы получить результат).
Я написал данную статью для новичков, для тех, кто впервые сталкивается с подобной задачей, когда они осилили написать django-проект и умеют только в runserver. Данная статья описывают процесс от самого начала и до конца, чтобы сайт можно было вывести в прод. Также, я собирал информацию со множества источников, форумов и даже личных переписок со специалистами в соц. сетях.
apt get update
apt get-install nginx

Исправьте, пожалуйста:
apt-get update или apt update
apt-get install nginx или apt install nginx
Поправил, большое спасибо за внимательность!
Sign up to leave a comment.

Articles