• Несколько советов по организации Python-приложения на сервере
    0

    Принципиально, наверное, ничем. Не считая того, что local_settings.yaml каждый пишет так, как удобно ему, а .env — признанный индустрией формат, который принимают такие монстры, как docker, systemd, heroku и куча сошек поменьше.


    Хотя нет, есть принципиальное отличие. local_settings.yaml обязан быть, а .env — нет. Это лишь один из удобных девелоперу способов указать переменные окружения. На продакшн так стараются не делать, конечно.


    У меня такого файла вообще нет, например.

  • Несколько советов по организации Python-приложения на сервере
    0

    Ну вопрос-то как был задан: как в окружение попадают данные. Как спросили, так и ответил. Ну, точнее, как понял вопрос, так и ответил.


    Да и, серьезно, в командную строку передавать over 20 параметров?

    Нет никаких шеллов. Не нужны :)


    В итоге все равно пишем в файл, просто по другому оформленный.

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

  • Несколько советов по организации Python-приложения на сервере
    0

    12 факторов — это не конкретная имплементация, это подход. И уж тем более не про контейнеры. Её можно реализовать и на одной машине, хоть и сложнее изоляцию организовать, наверное.


    Статья Ваша, безусловно, полезна. Хотя бы вот этим холиваром :) к тому же, я вот узнал, что systemd умеет в EnvironmentFile. Вот реально этого не знал


    Понятно, что у каждого опыт свой, но у меня в голове уже вряд ли уложится, как это так можно — вручную заходить по ssh на сервер, чего-то там править. Как можно не использовать контейнеры. Как можно деплоить приложения, не имея гарантии, что они запустятся в том окружении, которое я указал.


    Так что давайте и вправду закроем эту тему :)

  • Несколько советов по организации Python-приложения на сервере
    0

    Вот прям нутром чую, что подвох. Но отвечу как есть


    Самое простое — перечислить их в командной строке:


    $ DEBUG=1 SECRET_KEY=123 ./manage.py migrate
    $ DEBUG=1 SECRET_KEY=123 ./manage.py shell_plus
    

    Или выставить их в текущий сеанс. Тогда они будут работать для всех процессов, порождённых текущей сессией шелла


    export DEBUG=1
    export SECRET_KEY=123
    $ ./manage.py migrate
    $ ./manage.py shell_plus

    Если речь о докере, то там при запуске можно передать ключ типа --env-fle=/path/to/env. Обычный текстовый файл с парами KEY=VALUE по одной на строку.


    Для systemd можно такой же файл указать. Для питона существует куча пускалок, которая смотрит, нет ли файла .env в текущей директории. И если есть, считывает переменные и помещает их собственно в окружение. Такая штука, правда, больше про разработку, а не сервер.

  • Несколько советов по организации Python-приложения на сервере
    0
    Их выставляет пользователь. Или сама система. Или их передаёт процесс-родитель. Возможны варианты, короче. Я суть вопроса, признаться, не уловил
  • Несколько советов по организации Python-приложения на сервере
    0
    Куда бы я засунул переменые окружения? Сложный вопрос. Наверное, я бы их и оставил в переменных окружения :)
  • Несколько советов по организации Python-приложения на сервере
    0

    И на этот вопрос тоже отвечают 12 факторов.


    Вкратце, идея такая: вводится понятие "ревизия приложения". Ревизия приложения состоит из ревизии кода (натуральной ревизии, из гита которая), списка зависимостей и набора переменных окружения.


    Когда нужно задеплоиться: в случае, если появился новый код или поменялось окружение (появилась \ изменилась \ удалилась переменная), создаётся новая ревизия приложения. Технически — это создаётся новый образ, в который помещается кодовая ветка, устанавливаются с нуля все зависимости. И переменные.


    Далее на основе этого образа запускается контейнер. И неважно что там: gunicorn, ./manage.py shell_plus, ./manage.py migrate — образ один, набор переменных один. После завершения процесса контейнер умрёт.

  • Несколько советов по организации Python-приложения на сервере
    0

    На мой взгляд, пример с энвами более красивый. Даже не потому что там энвы, он был бы красивее даже с хардкодом. Из тех хотя бы соображений, что мы определили какие-то переменные сами и знаем, что ничего неожиданного там нет. и никто случайно или по злому умыслу не втыкнул DEBUG = True на продакшн.


    И вопрос — какое конкретно значение сейчас на запущенном сервисе на сервере?

    На такие вопросы отвечают 12 факторов. И, что немаловажно, отвечают и за свои слова :)


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

  • Несколько советов по организации Python-приложения на сервере
    0
    Ну да, засунуть эти оверрайды в отдельный файл — гораздо более продуманная тактика :)
  • Несколько советов по организации Python-приложения на сервере
    0
    Запустили вы в subprocess какой-нибудь внешний процесс, а он получил все ваши пароли, а потом в крэшдампе отправил своим разработчикам.

    Вроде бы в subprocess можно создавать чистое окружение, но аргумент всё же
    принимается, спасибо.


    Я реально не вижу удобства в переменных окружения. Мне лично удобнее прочитать переменную DATABASES как dict из yaml/json файла чем составлять ее из нескольких переменных окружения.

    Хе-хе. Если б одна запись составлялась из нескольких переменных, это не было так круто, как есть. Жизнь — она горазда элегантнее.


    Уже стало хорошей традицией записывать все кренделя в виде DSN


    DATABASE_URL=postgres://user:supersecretpassword@hostname.com:5432/db?timeout=20
    ANOTHER_DATABASE_URL=mysql://user:passwrd@mysqlserver.com/mysqldb

    а настройки выглядят так:


    import dj_database_url as db_config  # да, это внешняя зависимость. dj-database-url
    
    DATABASES = {
        'default': db_config.config(),
        'another': db_config.config(var='ANOTHER_DATABASE_URL'),
    }

    Однажды видел, правда, не помню где, как особо одарённые люди делали так:


    DATABASES = parse_env()

    В результате этой надстройки в DATABASES под ключом default попадало распарсенное значение DATABASE_URL, а все переменные вида MYPREFIX_DATABASE_URLпопадали туда же как значение ключа myprefix.


    Совершенно аналогично задаются кренделя к e-mail, кешам, очередям типа rabbitmq и прочая, и прочая, и прочая.


    С примитивами тоже наглядно. Понятно, что переменная окружения всегда строка (если есть) и None, если не определена. Но многочисленные обвязки позволяют писать очень, на мой взгляд, довольно прикольно. Например, так:


    SECRET_KEY = env('SECRET_KEY', cast=str, default='')
    DEBUG = env('DEBUG', default=False, cast=bool)
    ALLOWED_HOSTS = env('ALLOWED_HOSTS', cast=list, default=['*']) # разделяет строку по запятой

    или так:


    SECRET_KEY = env.string('SECRET_KEY')
    DEBUG = env.boolean('DEBUG')

    кто во что горазд, короче.


    Разумеется, это тоже внешняя зависимость, как и в Вашем случае. Но всё же, по чесноку, что более выразительно и явно:


    import_settings("/usr/local/etc/myservice.yaml")

    или


    DEBUG = env('DEBUG', default=False)
    SECRET_KEY = env('SECRET_KEY_FROM_MY_ENV_VAR')

    ? Мы же помним, что явное лучше неявного. Кто его знает, какие переменные понаинжектили злобные русские хакеры в конфиг, который Вы инклюдите? :)

  • Несколько советов по организации Python-приложения на сервере
    0
    Окей, про gunicorn принимается, убедили, хотя случай довольно странный.

    Других аргументов, извините, не увидел. И по старинному правилу «бремя доказательства лежит на обвиняющем», прошу всё же показать аргументы :)
  • Несколько советов по организации Python-приложения на сервере
    0

    Если руки кривые (а это бывает… ох бывает), то контейнер кокрастыке поможет.


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

    Из-за какого мнения? Программирование — это не искусство, это прикладная область, за неё кушать дают. А хочется прекрасного — участвуйте в олимпиадах. Денег не заплатят, ну так художник и должен быть голодным :)

  • Несколько советов по организации Python-приложения на сервере
    +1
    Если выломают приложение, то пострадает только приложение, если руки не кривы нужные юниксовые права правильно настроить

    А вдруг кривые. Зачем оставлять потенциальную возможность?


    Идентичное окружение — зло.

    Голословное утверждение. И в корне неверное. Хорошее приложение должно делать только одно: выполнять поставленную бизнес-задачу с разумными затратами русурсов (времени, денег, мощностей). Всё остальное — пожелания, причём не всегда критичные.


    Даже если в контейнере говнокод — да ради бога, если он работает, не просит каши и его можно поддерживать. Действительно, контейнер всё стерпит. Хорошее правило.


    Требование же чтобы работало везде… А зачем?

  • Несколько советов по организации Python-приложения на сервере
    0
    Виноват, а воркер целери не может быть мастер-процессом?
  • Несколько советов по организации Python-приложения на сервере
    0

    Я буквально на днях переносил старую кодовую базу на новое железо и старался использовать тот же софт, что и раньше. Так вот, как supervisord не умел 4 года назад убивать воркеры celery при перезапуске задачи, так и не умеет до сих пор. В принципе, этого одного уже достаточно, чтобы им не пользоваться :-)

  • Несколько советов по организации Python-приложения на сервере
    +2

    Про хранение настроек


    На мой взгляд, среди всех перечисленных способов Вы порекомендовали самый неудачный. По сути, он имеет все те же самые минусы, что и local_settings.py, кроме одного: его случайно не поместить в репозиторий. Да и то плюс этот сомнительный, с учётом .gitignore


    Зато привносит дополнительные минусы:


    • Магия. Сами упомянули.
    • Лишняя зависимость. Не столько даже yaml, сколько необходимость размещать этот "магический" код в каком-то файле, чтобы его импортировать
    • Захардкоженные пути. По идее, они должны быть одинаковы, но где, к примеру, искать /usr/local/etc/ в Windows? :) Или логику городить? Тогда почему бы просто не остановиться на local_settings?

    Переменные окружения таких проблем не продуцируют.


    • Они специально придуманы для конфигурации окружения, в котором запускается процесс. Конфигурация от народа!
    • Их поддержка есть в каждой операционной системе
    • Их использование всегда одинаково и на windows, и в macos, и в linux, и в докер-контейнерах.
    • Они случайно не попадут в версионный контроль
    • Они прекрасно интегрируются с тем же systemd

    Про какие неудобства идёт речь — я так и не понял. И почему они менее защищены чем файл, лежащий снаружи репозитрия.


    И вот этот Ваш коммментарий:


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

    тоже не понял. Особенно в контексте джанги выглядит странно, где конфиг вычисляется при старте приложения.

  • Несколько советов по организации Python-приложения на сервере
    +1

    Почему лучше использовать контейнеры.


    • Потому что если выломают приложение, пострадает только контейнер, а на хост-система
    • Потому что можно добиться идентичного окружения в на бою и на машине разработчика, даже если на сервере Linux, а у разработчика — macos (или windows, ни к ночи упомянута будет).
    • Разработка в контернере с файловой системой, примонтированной в ReadOnly дисциплинирует: приходится писать так, чтобы не привязываться к собственно файловой системе. Поэтому приложения получаются такими, что их легче масштабировать
    • Это, чёрт возьми, модно!
  • Несколько советов по организации Python-приложения на сервере
    +2

    Почему не нужно использовать supervisord


    • Потому что внешняя зависимость, без которой можно обойтись
    • Потому что глючный
    • Потому что python2

    Ещё, помнится, был проект circus. Что-то типа supervisord, но для Python3. В бою не испытывал, да и смысла особого нет, поскольку, согласно бритве Оккама, лучше пользоваться тем, что есть.

  • Несколько советов по организации Python-приложения на сервере
    +1

    Почему не надо писать логи в файл


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

    Почему надо писать логи в STDOUT


    • Потому что это стандартный вывод. Он есть везде и везде более-менее одинаково работает
    • Потому что стандартным выводом процесса может управлять файловая система (или пользователь). Этот стандартный вывод можно грепать в реальном времени; его можно писать в journald, его можно отправлять в fluentd, его даже можно писать в файл и логротейтить :)
      Но заниматься этим должно не наше приложение.
  • Несколько советов по организации Python-приложения на сервере
    +1

    Вот Вы утверждаете, что supervisord — лишняя сущность и надо её избегать. Но при этом рекомендуете файлы с логами ротейтить, а конфиги хранить не в переменных окружения, а в файлах. Да ещё и велосипед какой-то для этого придумали. На мой взгляд, это двойные стандарты :)


    Если уж избавляться от лишних сущностей, то по-большому. Тезисно:


    1. Логи писать исключительно в stdout;
    2. Если есть systemd, использовать его. Если нет, обновлять сервер, чтобы был. Или менять на другой. Ни в коему случае не использовать supervisord сотоварищи.
    3. Контейнеры по возможности использовать, причём с readonly-файловой системой.
    4. Хранить настройки в переменных окружения

    Здесь, напоминаю, только тезисы. Давайте более подробно обсудим в комментариях?


    P.S. Хабраюзер ahmpro уже упоминал про 12 факторов. Рекомендую обратить внимание и почитать. Соглашаться со всем не обязательно, но крайне полезно для знакомства.

  • Мониторинг событий git clone и git push на локальном GitLab сервере
    0

    Немного критики (возможно даже конструктивной).


    • Хардкодить константы — не очень изящное решение. На мой взгляд, куда интереснее брать их из переменных окружения. Всё равно же через systemd запускаете.
    • По psycopg2 Вы не очень внимательно документацию почитали. В частности, вот тут (таких мест несколько, взял первое попавшееся):


      sql_string_find_username="""SELECT extern_uid FROM identities WHERE user_id = (SELECT user_id FROM keys WHERE id = %s);""" %user_key

      Вы наступаете на те самое грабли, про который документация не говорит, а кричит: Never, never, NEVER use Python string concatenation (+) or string parameters interpolation (%) to pass variables to a SQL query string. Not even at gunpoint.


    • Опять же, про psycopg2: задавать соединение можно очень красиво:


      conn = psycopg2.connect(dsn='postgres://user:password@host:5432/database')

      и да, зачем устанавливать соединение в обработчике строки? Мне кажется, что лучше создать одно соединение (или пул, не принципиально) при старте скрипта.
      К слову, и сам объект подключения psycopg2, и его курсор можно использовать как менеджер контекста. Код получается чище, плюс не надо париться о закрытии.


    • Запись в лог — вообще что-то странное :) комментировать не берусь.

    А если субъективно, то такие штуки круче на баше писать. Получается куда выразительнее :)

  • Несколько полезных полей Django
    0
    И не надо пользователей призывать. Нехай вводят так, как им удобно. А нормализовывать — это наше дело, программерское.
    Кроме того, вроде бы существуют инпуты соответствующего типа, специально для телефонов.
  • Несколько полезных полей Django
    +1
    Мне почему-то кажется, что пользователи мобильных устройств будут поминать разработчиков таких композитных полей по матушке.
  • Удобная работа с картинками в Django
    0
    Не могу уяснить постановку задачи, виноват. Что-то типа такого?

    {% thumbnail "x100" crop="100,100" as im%}
        <img src="{{ im.url }}">
    {% endthumbnail %}
    
  • Удобная работа с картинками в Django
    0
    Пример приведите, что ли. Мне, каюсь, вообще сложно представить, что тумбам нужно кроме ресайза и кропа (и неважно, с сохранением пропорций или без).
  • Удобная работа с картинками в Django
    0
    Не очень представляю, как это — квадрат, у которого одна сторона ровно 100 пикселей, а другая — 100 или больше.
  • Удобная работа с картинками в Django
    0
    Как-то не очень я понимаю подход с дополнительным полем для каждого тумбнейла в модели.
  • Удобная работа с картинками в Django
    –1
    По ссылке много букв сплошные хочуны на тему «было бы здорово», которые в принципе не решить без человеческого вмешательства. Ну, разве что реализовывать детекторы лиц.

    А с простыми задачами — кропнуть, вписать целиком, вписать по ширине, вписать по высоте, отбить поля — с этим любые решения справляются, солр в том числе.
  • Удобная работа с картинками в Django
    0
    Прям чрезвычайно?
  • Удобная работа с картинками в Django
    +1
    Если использовать кеширование, он ни разу медлительным не будет :-)

    (старый комментарий стёр, т.к. ошибся веткой, извините)
  • Inline редактирование в Django
    0
    Машинерия — это, грубо говоря, набор механизмов. В нашем случае — это набор классов джанги, ответственных за работу с формами (от генерации хтмл до собственно валидации)
  • Inline редактирование в Django
    0
    А, ясно. На огонёк зашли, или так, потроллить? :-)
  • Inline редактирование в Django
    0
    Кажется, разобрались. Мы говорим о разных вещах.

    Для меня, как и, надеюсь, для большинства джанго-разработчиков, форма — это то, с чем работаем мы на сервер-сайде. То есть часть джанги. А не какой-то там html-код, за который должны отвечать верстальщики :-)
  • Inline редактирование в Django
    0
    Именно что должна.

    Но на клиентскую валидацию действительно полагаться нельзя, поэтому она (валидация) должна происходить на сервере. А JS, который всего лишь упрощает жизнь пользователю, должен всего лишь гонять от клиента серверу введённые данные и обратно, от сервера клиенту, решение сервера о том, верно ли всё заполнено.

    Но как бы то ни было, задача валидации лежит целиком на машинерии формы.

    P.S. На самом деле, в моделях тоже есть валидация, но это другой вопрос.
  • Inline редактирование в Django
    0
    Что ж, у Вас есть право на личное мнение :-) Но оно не совпадает со мнением разработчиков джанги, которые считают, что

    • Check submitted data against a set of validation rules.
    • Convert submitted form data to the relevant Python data types.

    вполне себе задачи форм (в самом начале, пп 2 и 4).

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

    data = {
        'name': raw_input("Enter your name: "),
        'age': raw_input("Enter your age: ")
    }
    
    form = Form(data)
    
    if not form.is_valid():
        print "Something goes wrong"
    


    и кстати, разве форма не является частью сервера приложений?

  • Inline редактирование в Django
    0
    Именно этим форма и занимается.
  • Inline редактирование в Django
    0
    Я правильно понимаю, что Вы предлагаете сделать что-то типа

    if form.is_valid():
        if form.cleaned_data['start_date'] >= form.cleaned_data['end_date']:
            message.error('Неверный интервал') 
        else:
            form.save()
    
    

    и считаете, что это не размазывание логики и не нарушение DRY?
  • Inline редактирование в Django
    0
    А форма не этим занимается?
  • Inline редактирование в Django
    0
    Вы уточните, пожалуйста, какой валидатор?
  • Inline редактирование в Django
    0
    Я не понимаю, о чём Вы, Простите. О какой валидации данных речь?