Автор: Денис Матаков

  • Как я искал и нашел IT-работу в 2021 году

    Как я искал и нашел IT-работу в 2021 году

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

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

    Мой опыт в IT

    Я пришел в технологии 12 лет назад предпринимателем. Я не делал проекты на заказ, я делал их для себя, сам ставил задачи, сам их выполнял. Так как я не был разработчиком в прямом смысле этого слова — решения я находил такие, которые можно установить на сервер своими силами через скрипты-установщики, связать через плагины или встраиваемые скрипты. Сейчас подобная парадигма называется No Code, так вот именно этим я и занимался всю свою карьеру. И, конечно, много опыта в бизнесе — ведь это были мои магазины, а значит маркетинг и продажи тоже должны быть настроены. До 2019 года я делал это все в команде с моими соучредителями, затем — уже самостоятельно, полный цикл решений.

    Почему я решил идти в программирование?

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

    Так как я давно уже автоматизировал все в своем бизнесе — у меня было время на стороннее консультирование. Несколько лет я настраивал IT-системы в фитнес-клубе, делал их взаимосвязь, применял свой бизнес-опыт и профильное банковское образование для бизнес-консультирования. В общем, был занят. А в какой-то момент на мир спустился черный лебедь — пандемия. Естественно, фитнес-клуб закрыли одним из первых, и у меня появилось свободное время. И я решился — сейчас, пора. Python, я иду!

    Обучение на курсе Яндекс.Практикума — Python-разработчик

    У меня есть отдельная статья про Яндекс.Практикум, там есть плюсы и минусы. Рекомендую ознакомиться, здесь повторяться не буду. Здесь я сосредоточусь на тех шагах, которые я предпринял после обучения.

    Подготовка к устройству на работу

    Мне нужно было разобраться в моих знаниях. Я решил сделать это через книги. Понятное дело, что что-то могло пройти мимо. Я взялся за «Изучаем питон», «Чистый питон», «Грокаем алгоритмы», «Программист-прагматик» и другую литературу, которая гуглится по запросу «Лучшие книги по питону» и которые можно заказать на бумаге — с технической литературой я умею только так.

    Также я начал участвовать в Open Source проектах. Нашел интересного ментора, Леона Сендоя, и помогал ему писать систему резервного копирования различных баз данных Blackbox. Читал код, разбирался в фабричном методе проектирования, и что самое главное — решал поставленные задачи. Причем почти все они были для меня абсолютно новыми, я разбирался в проблеме с нуля. Серьезный буст для саморазвития и в качестве бонуса ревью от очень хорошего норвежского специалиста и владельца сообщества Python-Discord на 200 000 человек.

    Размещение резюме

    Также я разместил резюме на HH и Хабр.Карьере. Я неплохо говорю по-английски, поэтому, проконсультировавшись с сестрой, которая уже много лет живет в Англии и работает HR, разместил резюме еще на следующих сайтах:

    Также заручился рекомендациями от моих менторов — Леона Сендоя и Андрея Ли — на LinkedIn (рекомендации приложены в конце статьи для примера).

    Первое собеседование

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

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

    Пройти собеседование с HR было несложно, но нервно. У меня красивая история, я подготовился — и вот у меня уже назначено техническое собеседование. Как мог я готовился, читал все, что попадалось под руки. Вот список того, что вам поможет в этом:

    Техническое интервью тоже прошло на ура, я отвечал на вопросы, я знал как устроены некоторые сложные фичи питона и уверенно об этом говорил. Настало время технического задания, и вот тут я первый раз сделал все неправильно. Я прочитал задание, и сразу побежал выполнять. Я был слишком уверен в себе, ведь я понравился на всех этапах собеседования! И я решил задачу, получил данные по API, распечатал их в консоль, ничего сложного. Но техдиректору компании все это не понравилось от слова совсем. Потому что я не использовать паттерны проектирования, писал без классов, код нельзя было переиспользовать. Не то, что я не умел это делать — умел, но торопился, переживал, сделал тяп-ляп. Первый отказ. Я был чрезвычайно расстроен.

    Следующие 30 собеседований

    Дальше моя стратегия поменялась. Я должен был набрать опыт собеседований. Не важно, нравится ли мне компания или нет — я всегда откликался на все предложения разговоров от HR. Я внимательно изучал компанию, стек — по каждому непонятному слову сразу смотрел подробное руководство на Youtube (выбирал длинные ролики и скорость 1.75х). И с каждым разом отвечал все лучше. Попадались хитрые вопросы, попадались скучные, но почти всегда я проходил стадию HR на ура, а вот технические собеседования были успешны в 50% случаев.

    Что стоит знать:

    • напиши свой декоратор
    • напиши свой декоратор с параметрами
    • паттерны проектирования и где ты их применял
    • SOLID, ACID, принципы ООП
    • Нормализация БД и вообще вопросов на построение запроса к БД много, в основном на Postgres
    • Базовые алгоритмы и их сложность
    • Структуры данных
    • Индексы и устройство хешей в общем виде
    • Контекстный менеджер with
    • REST/OpenAPI/принципы проектирования, SOAP
    • Очереди Kafka и RabbitMQ
    • AWS будет плюсом
    • Redis
    • Celery
    • Асинхронное программирование и принцип работы GIL
    • Микросервисы
    • Рекурсия
    • MongoDB

    Два типа собеседований

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

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

    Собеседование в EPAM

    Так как компания ЕПАМ большая и ей уже 25 лет — то и процессы там гораздо лучше отлажены, в том числе собеседования. Это без преувеличения мой лучший опыт за весь период поиска работы. Ребята задавали интересные вопросы, где я что-то упускал — рассказывали, давали возможность раскрыться. Суммарно 5 часов технических собеседований, я был уставший, но довольный. Я рассказал свой опыт, я позиционировал себя как специалиста, который делает горизонтальный переход, и претендовал на должность Мидл-разработчика. Как я понял — позиционирование и вера в себя — это самые важные вещи после знаний.

    И вот я получил оффер от ЕПАМ, и сразу на достойную сумму. Не принимая его сразу, я сказал, что мне нужно две недели на принятие решения и продолжил изучать рынок. Теперь была задана планка, и я хотел перепроверить — точно ли я правильно выбрал ЕПАМ.

    Еще 10 собеседований, в том числе 3 технических, и я принял решение — я иду в ЕПАМ. Я пытался следовать советам из статей про то, как принимаются офферы и сколько надо выждать, но в итоге все равно договорился с HR о звонке и сообщил о своих намерениях. Всего процесс трудоустройства занял около трех месяцев, и это были самые насыщенные на учебу месяцы.

    Пример резюме

    Примеры моих резюме, если вам интересно как я себя позиционировал:

    Рекомендации от менторов

    Обязательно собирайте рекомендации, они важны для принятия решения!

  • No code. Создаем программные продукты в визуальных редакторах.

    No code. Создаем программные продукты в визуальных редакторах.

    В своих проектах я всегда придерживался философии «как можно меньше правок в фреймворк». Основная задача была сделать проект автообновляемым и не теряющим своей актуальности на протяжении долгого времени. Как я пришел к этой философии?

    Почему No или Less Code?

    Свои самые первые интернет-магазины я запускал в 2009-2010 годах, тогда не было еще того разнообразия готовых фреймворков и плагинов к ним. Был вордпресс, были скрипты интернет-магазинов с самоустановкой — укажи данные базы и проект начинает свое MVP-путешествие. Что происходило дальше? Если нужен новый заголовок, я лез в код и менял заголовок. Когда нужно было поставить какое-то условие в шаблоне — лез в Smarty и делал условие. Когда менялась логика бизнеса — я копал еще глубже и менял уже PHP-файлы. И вылилось это все в невозможность обновления продукта. А когда у вас успешный сайт, вам нужны новые функции для сохранения бизнеса в игре, и получить вы их можете только сделав еще больше правок в коде, или, как мы их называли всегда, костылей.

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

    Концепция самообновляемости сайта

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

    No code сервисы

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

    Рассмотрим некоторые из no code сервисов:

    Скриншот из видео сверху (оригинал недоступен, сайт не работает):

    webflow.com

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

    editorx.com

    Еще один сервис с возможностью полного проектирования как дизайна, так и базы данных. Проекты, построенные с помощью этой платформы — впечатляют.

    bravostudio.app

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

    supernova.io

    Сверхновую позиционируют уже не как no code, а как удобный код рядом с дизайном. Для тех, кто все еще хочет держать руку на пульсе, но уже хочет упростить себе жизнь.

    Какие еще сервисы?

    Обратите внимание на Notion (рич-блокнот с коллаборацией), Anima (ожившие прототипы), Play (дизайн мобильных приложений прямо на мобильных приложениях) и Airtable (прекрасная смесь экселя и баз данных).

  • Как установить psycopg2-binary на Apple Silicon M1 Big Sur (в том числе в Docker)

    Как установить psycopg2-binary на Apple Silicon M1 Big Sur (в том числе в Docker)

    Миграция на чип М1 от Apple прошла для меня практически бесшовно. Встретилась лишь одна проблема — с установкой psycopg2-binary, что является утилитой, с помощью которой Django Framework подключается к PostgreSQL.

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

    Проблема 1: Не устанавливается psycopg2-binary в Docker-контейнере через Dockerfile или docker-compose (MacOS Big Sur M1)


    Здесь самым простым способом будет отказаться от урезанных образов наподобие alpine или slim. Просто используйте самый обычный Python 3.9:

    FROM python:3.9

    После этого контейнеры поднимутся именно так, как вы их задумывали. Здесь, видимо, проблема в том, что архитектура чипа M1 пока неизвестна и не так популярна. В скором времени эту проблему наверняка исправят.

    Проблема 2: Не устанавливается psycopg2-binary в MacOS Big Sur M1

    Возможные выводы ошибки:

    $ pip install psycopg2-binary
    Collecting psycopg2-binary
      Using cached psycopg2-binary-2.8.6.tar.gz (384 kB)
        ERROR: Command errored out with exit status 1:

    И дальше длинный трейс ошибки. Как победить? Если вы только купили свой мак, скорее всего у вас не установлен пакет brew и вам нужно сделать следующие шаги:

    1. Установите brew https://brew.sh/
    2. Чтобы он запускался без указания полного пути вам нужно добавить этот путь (к многим другим путям) в переменную PATH. Не пугайтесь, это страшно лишь по началу. В unix-подобных системах терминал умеет запускать приложения только если он знает где их искать. А brew установилась в хитрую папку и наш терминал просто не знает этого пути. Пока не знает. Давайте его научим, и рекомендую запомнить этот трюк, он часто нужен (и не только на маках, но и на виндус). На вашем маке в качестве терминала по умолчанию стоит ZSH. Вам нужно создать файл в домашней директории (если там его еще нет, проверить можно командой ls -la) с названием .zshrc (именно с точкой в начале) и положить туда одну строчку:
    export PATH=/opt/homebrew/bin:$PATH

    Перезапустите терминал, теперь у вас есть brew.

    3. Дальше проще, установите сам пакет с Постгресом

    brew install postgresql

    4. Теперь установите SSL, без него не заработает

    brew link openssl

    5. Пропишите в наш созданный в пункте 2 файл еще один путь:

    echo 'export PATH="/opt/homebrew/opt/openssl@1.1/bin:$PATH"' >> ~/.zshrc

    6. И финальный аккорд! Устанавливаем psycopg2-binary с необходимыми переменными окружения (внимание, это все одна команда, просто очень длинная):

    LDFLAGS="-L/opt/homebrew/opt/openssl@1.1/lib" CPPFLAGS="-I/opt/homebrew/opt/openssl@1.1/include" PKG_CONFIG_PATH="/opt/homebrew/opt/openssl@1.1/lib/pkgconfig" pip install psycopg2-binary

    Если на каком-то этапе что-то пошло не так, попробуйте изучить эту тему.

  • Список технологий для изучения

    Список технологий для изучения

    Получил список рекомендаций от знакомого разработчика — что нужно знать, программа-минимум. Решил оставить в блоге этот перечень, чтобы не затерялся. Итак:

    1. Как минимум поверхностное понимание работы JavaScript
    2. Если учить React, то сфокусироваться на практическом применении, а также разобраться с фреймворками на его основе, например next.js, который позволяет обойтись без бэкенда вовсе
    3. Ознакомиться с технологиями публикации сайтов прямо из репозитория, например Netfly
    4. Понять JAMstack, так как он очень популярен и удобен
    5. Взять на вооружение технологию htmx, которая позволяет сделать отзывчивый интерактивный сайт без JS, только на питоне
    6. Узнать больше про безсерверную архитектуру на основе AWS и Lambdas (причем докер образы будут прямо в Лямбде). В питоне это позволяет сделать утилита Zappa

    Также общий набор рекомендаций для софт-скиллов:

    1. Писать хорошие коммиты
    2. Уметь делать CI/CD
    3. Писать тесты для своих проектов на гитхабе
    4. Проявлять активность на гитхабе в целом
    5. Писать красивый PEP8 код
    6. Писать статьи в блог
    7. Может, даже вести подкаст
    8. Помогать с опен-сорс проектами

    И это ведь только начало. Впереди столько интересных технологий!

  • Bulma для всех, кто боится Bootstrap

    Bulma для всех, кто боится Bootstrap

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

    И вот тут на помощь пришла Bulma. Это по сути своей тот же Bootstrap, только с человеческими названиями классов. Хотите кнопку? Класс называется button. Хотите колонки? Пишите columns, а внутри для каждой колонки column. Считаю, что Бульму надо включить обязательное изучение во всех курсах бэкендеров.

    Обязательно посмотрите их вводное видео:

  • Закончил курс Яндекс.Практикума Python-разработчик

    Закончил курс Яндекс.Практикума Python-разработчик

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

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

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

    Третья часть была по API. На мой взгляд, эта тема наравне с вводной частью по сложности. А вот интересности ей не занимать. Подключаться к внешним сервисам, получать информацию, обрабатывать ее, делать ботов — мечта, а не профессия.

    Дальше, к сожалению, не все так весело было. Алгоритмы и структуры данных в курсе были поданы очень вяло, а теория была совершенно не применима в практике. Объясняют рекурсию, дают задачу, ты решаешь ее рекурсией и не можешь уложиться в заданные параметры по памяти и времени. В итоге большинство задач решались лишь определенными алгоритмами и похоже это было скорее на олимпиаду, чем на обучение. После сдачи этой части я прочел самостоятельно книгу «Грокаем алгоритмы» и понял гораздо больше, чем дал Практикум. Говорят, эти главы переписывают и даже уже переписали, но факт остается фактом — часть слабая.

    Пятая глава обучения началась очень хорошо, заинтересовала серверами и докером, но в какой-то момент у авторов кончились силы и дальше пошло лишь «вставьте это в командную строку» и «смотрите, все получилось». Почему получилось? Что за параметры у команды были? Здесь у меня произошел переломный момент. Я начал докапываться до сути. Я составил список вопросов, пришел на вебинар и целый час выяснял у наставника как это все работает. Получил ответы, пошел их применять и опять начались несостыковки. Но тема докера меня всегда привлекала, и я решил разобраться. Так на этом сайте родилась серия статей про деплой, я прочитал всю документацию, придумал несколько способов сделать деплой, с каждой статьей улучшал свои познания и в какой-то момент постиг дзен. Докер стал для меня настолько родным, что я начал с удовольствием помогать однокурсникам и объяснять им, почему и как именно у них не работали проекты. Сложная глава, но по ней у меня самые лучшие знания остались.

    Шестая глава — дипломный проект. По началу все казалось достаточно простым, но потом выяснилось, что предоставленный нам фронтент был написан без DRY подхода — там постоянно и везде повторялся один и тот же код не только в HTML, но и в JavaScript и CSS. Это невероятно усложнило понимание и так не самой ясной для нас технологии. А на этапе оживления формы я вообще был готов сдаться — нас не просто учили другому, нам даже банально не объяснили как объект формы выглядит изнутри. Какое-то невероятное усилие и обратный инжиниринг все-таки помогли мне справиться с формой, дальше уже было легче. Но я твердо понял, что на факультет фронтента Практикума я не пойду, если там допустили такое.

    Сейчас я сдал дипломный проект и жду выпускного. Безусловно, я безумно рад, что не сдался, что 9 месяцев был на одной волне со своим курсом в сто с лишним человек. Яндекс.Практикум помог с пониманием маршрута по знаниям и в половине случаев эти знания дал. В другой половине — помогла документация и одногруппники.

    Формат вебинаров мне показался абсолютно бестолковым. Любую тему можно в два-четыре раза быстрее познать на Youtube, чем от наставников, да и буду честен — речь у них плохо поставлена, много слов-паразитов и в основном не очень адекватная подготовка к занятию. Вторую половину курса я вебинары не смотрел.

    Чего не хватает — так это списка литературы, а точнее ссылок на нужную документацию к заданиям, на видеоуроки, на статьи на тему, да даже на Stackoverflow. Понятно, что нужно учиться самому искать, но при объяснении темы нужно дать максимально адекватный перечень дополнительных материалов.

    Я долго думал, чем я займусь дальше. Многие уже ищут работу, но я решил познать как можно больше про технологии фронтенда, про JavaScript и фреймворки, основанные на нем. А также знакомый разработчик дал мне список технологий, которые нужно знать и понимать в современном мире. О них я расскажу в будущих статьях на этом сайте, подписывайтесь на обновления.

  • Docker-compose для разработки: видим изменения на лету

    Docker-compose для разработки: видим изменения на лету

    Docker-compose — невероятно мощный инструмент не только для деплоя в продакшине, но и для разработки. Для максимального удобства концептуально нам нужно применить в нем всего один прием — заменить тома на папки. Таким образом, наш проект будет иметь прямой доступ к хостовой машине и любые правки, которые мы будем вносить, будут в реальном времени отображаться в контейнерах.

    Давайте рассмотрим код-пример:

    version: '3.8'
    
    volumes:
      postgres_data:
    
    services:
      db:
        image: postgres:12.4
        restart: always
        volumes:
          - postgres_data:/var/lib/postgresql/data/
        env_file:
          - ./.env
      django:
        build: .
        depends_on:
          - db
        restart: always
        env_file:
          - ./.env
        volumes:
          - .:/code/
        entrypoint: /code/entrypoint.sh
    
      nginx:
        image: nginx:1.19.0-alpine
        ports:
          - "127.0.0.1:1111:80"
        volumes:
          - ./static:/code/static
          - ./media:/code/media
          - ./nginx:/etc/nginx/conf.d/
        depends_on:
          - django
        restart: always

    Мы оставили на томе только базу данных. Все остальные данные вместо этого мы прикрепили к нужным папкам в нашем проекте.

    Смотрите, весь корень проекта в папке code, статика и медиа подключена напрямую к контейнеру nginx, как и его конфиг. Вот как он выглядит:

    upstream djangodocker {
        server django:8000;
    }
    
    server {
    
        listen 80;
    
        location / {
            proxy_pass http://djangodocker;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_redirect off;
        }
        location /static/ {
            alias /code/static/;
        }
    
        location /media/ {
            alias /code/static/;
        }
    
    }

    Описание того, что здесь происходит, можно найти в статье про docker-compose. Там же вы увидите разбор entrypoint.sh:

    #!/bin/sh
    
    sleep 2
    python manage.py migrate
    python manage.py createcachetable
    
    if [ "$DJANGO_SUPERUSER_USERNAME" ]; then
      python manage.py createsuperuser \
        --noinput \
        --username "$DJANGO_SUPERUSER_USERNAME" \
        --email $DJANGO_SUPERUSER_EMAIL
    fi
    
    python manage.py collectstatic --noinput
    gunicorn foodgram.wsgi:application --bind 0.0.0.0:8000
    
    exec "$@"

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

    DB_ENGINE=django.db.backends.postgresql # указываем, что работаем с postgresql
    DB_NAME=postgres # имя базы данных
    POSTGRES_USER=postgres # логин для подключения к базе данных
    POSTGRES_PASSWORD=postgrespostgres # пароль для подключения к БД (установите свой)
    DB_HOST=db # название сервиса (контейнера)
    DB_PORT=5432 # порт для подключения к БД
    SECRET_KEY=SECRET_KEYSECRET_KEYSECRET_KEY
    DJANGO_SUPERUSER_PASSWORD=12345678
    DJANGO_SUPERUSER_EMAIL=example@example.com
    DJANGO_SUPERUSER_USERNAME=admin

    Чтобы новая установка Django подключилась к Postgres — измените настройки подключения в файле settings.py:

    DATABASES = {
        'default': {
            'ENGINE': os.environ.get('DB_ENGINE'),
            'NAME': os.environ.get('DB_NAME'),
            'USER': os.environ.get('POSTGRES_USER'),
            'PASSWORD': os.environ.get('POSTGRES_PASSWORD'),
            'HOST': os.environ.get('DB_HOST'),
            'PORT': os.environ.get('DB_PORT'),
        }
    }

    И не забудьте, что для работы базы данных вам нужна зависимость:

    pip install psycopg2-binary

    Еще один важный момент — это изменения в файле Dockerfile. Теперь нам не нужно копировать весь проект на контейнер, мы и так туда подключим всю папку, но предварительная установка зависимостей и старт контейнера зависят от двух файлов, которые уже должны быть в контейнере:

    FROM python:3.9-slim
    
    ENV PYTHONDONTWRITEBYTECODE 1
    
    ENV PYTHONUNBUFFERED 1
    
    
    WORKDIR /code
    #COPY . /code
    COPY ./requirements.txt /code
    COPY ./entrypoint.sh /code
    RUN pip install -r /code/requirements.txt
    
    
    ENTRYPOINT ["/code/entrypoint.sh"]

    После этого вы просто запускаете команду docker-compose up и получаете рабочий прототип проекта на своей машине, со статикой:

    И что самое важное, вы сможете вносить изменения на лету и тут же их видеть!

    Отдельное спасибо за идею с прямым подключением файлов Leon Sandøy в его репозитории Django-Starter. Добавил сюда nginx и автоматическое применение миграций.

  • Django, Postgres, Nginx с помощью Docker-Compose

    Django, Postgres, Nginx с помощью Docker-Compose

    В настоящем руководстве описан механизм автоматического деплоя трех контейнеров на локальный сервер. Контейнер с django проектом будет раздавать логику с помощью Gunicorn, контейнер с nginx будет раздавать статику и осуществлять так называемую reverse-proxy на Gunicorn для логики, контейнер с базой данных Postgres будет работать с настройками по умолчанию.

    Важные аспекты деплоя

    1. Контейнеры зависимы друг от друга, сначала запускаем базу данных, затем приложение, последним — nginx
    2. При создании контейнеров с помощью docker-compose они автоматически объединяются в локальную сеть.
    3. Выход из этой сети будет один и только один — это открытый порт контейнера nginx,
    4. Остальные контейнеры не будут иметь связей с портами хостовой машины, что, в целом, не мешает им открывать порты внутри той самой локальной сети, созданной docker-compose. Делают они это неявно, и конкретные порты, как правило, можно найти в документации к используемому образу
    5. Стандартный порт, где доступна база данных — 5432
    6. Стандартный порт, где доступен Gunicorn — 8000

    Volumes — Тома

    Нам понадобится 4 тома для хранения следующей информации: база данных, статика, медиа файлы, конфигурация nginx. Объявим их сразу за версией docker-compose:

    version: '3.8'
    
    volumes:
      postgres_data:
      static:
      media:
      nginx_conf:

    Контейнер 1: База данных

    Если внимательно прочитать документацию образа Postgres на Docker Hub, то можно выяснить, что контейнер этот легко запустить без каких либо дополнительных инструкций, главное, чтобы в наших переменных окружения был закрытый список необходимых для деплоя значений. Здесь мы используем одну маленькую, но очень удобную хитрость: мы используем одинаковые переменные окружения для контейнера с базой данных и контейнера с приложением django, где описано подключение к базе данных. Вот список:

    DB_ENGINE=django.db.backends.postgresql # для Django
    DB_NAME=postgres # для Django и Postgres
    POSTGRES_USER=postgres # для Django и Postgres
    POSTGRES_PASSWORD=postgrespostgres # для Django и Postgres
    DB_HOST=db # для Django
    DB_PORT=5432 # для Django

    Таким образом, код для docker-compose получается следующим:

    services:
      db:
        image: postgres:12.4
        restart: always
        volumes:
          - postgres_data:/var/lib/postgresql/data/
        env_file:
          - ./.env

    Указан образ, стоит ли перезапускать, если произойдет ошибка или сервер перезагрузится, путь к файлу базы данных внутри контейнера (это значение тоже из документации), а также откуда брать переменные окружения (в одной директории с docker-compose.yaml должен лежать файл .env).

    Наш контейнер теперь доступен только внутри сети из трех контейнеров по адресу db:5432

    Контейнер 2: Django и Gunicorn

    Так как Django раздает сам себя только в режиме debug=True, нам понадобится что-то более профессиональное, пусть этим займется Gunicorn. По умолчанию он раздает логику приложения на порту 8000, и нас это вполне устраивает, никаких дополнительных настроек здесь не требуется. Наш контейнер должен собраться только после того, как будет готова база данных. Как правило, скачивание образа, на котором будет построен ваш контейнер, занимает время, и за это время база уже построится и будет готова принимать соединения. Но при повторном деплое может произойти так, что база поднимется чуть позже, чем мы заходим применить стандартные наши команды: применить миграции и собрать статику. Поэтому в целях предотвращения этого я добавил в файл entrypoint.sh (который выполняется каждый раз при старте контейнера) инструкцию «подождать 10 секунд». Вот как выглядит код:

      django:
        image: matakov/yamdb:latest
        depends_on:
          - db
        restart: always
        env_file:
          - ./.env
        volumes:
          - static:/code/static
          - media:/code/media
          - nginx_conf:/code/nginx/
        entrypoint: /code/entrypoint.sh

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

    1. Статику в директорию со статикой
    2. Медиа файлы в директорию с медиафайлами
    3. Конфигурация Nginx берется их папки nginx. Тут есть важный момент. Наш контейнер с приложением должен первым забрать конфигурацию из этой папки и положить в свежесозданный образ. Если первым это сделает контейнер с nginx, то он подключит в образ свой конфиг по умолчанию, и он перезатрет (на самом деле не так, но для целей понимания будем говорить так) конфигурацию в нашем контейнере с приложением.

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

    entrypoint.sh

    #!/bin/sh
    
    sleep 10
    
    python manage.py migrate
    python manage.py createcachetable
    python manage.py collectstatic  --noinput
    gunicorn api_yamdb.wsgi:application --bind 0.0.0.0:8000
    
    exec "$@"
    

    Файл, который отслеживает подключение к базе данных, как только оно появляется, дает 10 секунд на то, чтобы все необходимые технические операции были завершены, затем мигрирует, создает кэш, собирает статику и, наконец, запускает Gunicorn на 8000 порту контейнера.

    Наш контейнер теперь доступен только внутри сети из трех контейнеров по адресу django:8000

    nginx.conf

    Чтобы наш сервер знал, как и с чем ему работать — нужно задать для него конфигурацию. Общая схема такая: на наш сервер на порт 80 приходит запрос, мы его проксируем на 80 порт контейнера с ngnix, он смотрит, если запрос на статику и медиа — отдает сам, а если запрос на логику — переадресовывает на контейнер с приложением на порт 8000. Приступим:

    upstream djangodocker {
        server django:8000;
    }
    
    server {
    
        listen 80;
    
        location / {
            proxy_pass http://djangodocker;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_redirect off;
        }
        location /static/ {
            alias /code/static/;
        }
    
        location /media/ {
            alias /code/media/;
        }
    
    }

    Директива upstream позволяет нам дать название адресу с портом (что довольно удобно), а также дает возможность в будущем настроить здесь балансировщик нагрузки, просто добавив еще переменных server внутри upstream.

    Дальше стандартный раздел server:

    Слушаем 80 порт, если запрос пришел на главную страницу и все остальные, кроме статики и медиа, отправляем на наш контейнер django:8000, используя красивый upsteam-ярлык для этого djangodocker. Остальные строки просто передают также дальше и заголовки запросов.

    Обратите внимание на команды root- Это означает, что /static/ приклеится к пути /code/ и станет /code/static

    Команда alias в свою очередь указывает сразу на конечное расположение. Маленькая хитрость, лучше знать.

    Контейнер 3: Nginx

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

      nginx:
        image: nginx:1.19.0-alpine
        ports:
          - "80:80"
        volumes:
          - static:/code/static
          - media:/code/media
          - nginx_conf:/etc/nginx/conf.d/
        depends_on:
          - django
        restart: always

    Обратите внимание, контейнер зависит от django, поэтому конда он получит свой том nginx_conf — в нем уже будет лежать правильных конфиг, который мы написали выше.

    Если у вас на сервере уже есть сайт?

    Вы не можете в таком случае поднять свою сеть контейнеров на 80 порту, ведь он уже занят. В таком случае можно указать связку 8080:80, тогда ваше приложение будет доступно по ip:8080 в интернете. Если же вы хотите, чтобы оба сайта работали одновременно и на разных доменах, прочитайте статью про настройку веб-сервера Caddy и обязательно про безопасность открываемых портов.

    Далее — Continues Integration

    Разворачиваем приложение с помощью GitHub Actions

  • GitHub Actions: Как решить проблему entrypoint.sh: permission denied

    GitHub Actions: Как решить проблему entrypoint.sh: permission denied

    Некоторые проблемы возникают на ровном месте. И обычные способы решения не подходят. Вчера я увидел следующее сообщение при выполнении workflow на GitHub Actions:

    Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"/entrypoint.sh\": permission denied": unknown

    Мы запускаем docker-compose, контейнеры начинают собираться и в момент, когда контейнер web решает запустить свой entrypoint (действие, совершаемое при каждом старте контейнера), мы получаем эту ошибку.

    Быстрый поиск нам дает понимание, что это проблема с разрешениями, и нужно просто выполнить классический набор команд, чтобы сделать файл исполняемым:

    chmod +x entrypoint.sh
    git add entrypoint.sh
    git commit

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

    ls -la

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

    В тот момент, когда мы делаем на Windows Git Bash команду git add, мы теряем исполнительный бит. Как до этого догадаться? Я поднял контейнеры без запуска entrypoint.sh и проверил его права, executive bit был потерян. Я начал искать его на виртуальной машине, которую создает GitHub Actions для деплоя и тоже не нашел его там. Тогда я догадался переформулировать поисковый запрос на…

    Git add lost executive bit

    Только такая формулировка дала мне правильный набор решений. Оказывается, именно под операционной системой Windows система контроля версий git может (но не всегда) терять исполнительный бит при добавлении в репозиторий. Решается это следующим набором команд:

    git add --chmod=+x -- entrypoint.sh
    git commit

    То есть мы добавляем не файлу в операционной системе исполнительный бит, а файлу в момент присоединения его к коммиту.

    Решение не помогло? Замените CRLF на LF

    Есть еще одна особенность, которая может повредить исполняемые bash-файлы — это окончания строк. В среде Windows принято в конце строки ставить перевод каретки на следующую строку и в самое начало, и называется это дело CRLF.

    Естественно, в среде Linux все иначе, там перевод на начало строки не делают, только разрыв оставляют: LF. Проблема в том, что текстовые файлы по умолчанию в Windows используют несовместимые управляющие символы, и если вы хотите сделать ваш entrypoint.sh исполняемым — зайдите в настройки вашего любимого текстового редактора или IDE и установите параметр «Окончание строк» равным LF (Unix). Я лично рекомендую Notepad++ для этого:

    Надеюсь это маленькое расследование однажды сэкономит вам несколько часов времени.

  • Удаленный деплой с помощью docker-compose на GitHub Actions

    Удаленный деплой с помощью docker-compose на GitHub Actions

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

    Допустим, как работает docker-compose? Ему необходим файл docker-compose.yaml, в котором описаны все контейнеры и их связи, а также от одного до бесконечности файлов Dockerfile, которые помогут собрать образы. Также всякие конфиги nginx, файлы с переменными окружения .env. И первое желание — перенести все эти файлы на удаленный сервер с помощью ssh или scp и начать разворачивать там руками.

    Но есть гораздо более правильный путь (This is the way). Наша задача копнуть чуть глубже и попробовать развернуть наши контейнеры на удаленном сервере без каких либо файлов в принципе. Не верите?

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

    docker context ls

    Вы увидите этот слой с настройками, его имя default *, где астериск означает, что данный контекст (слой, файл с настройками) в данный момент активен.

    Если мы хотим создать новый контекст (опять-таки, для удобства думайте о нем как о файле с настройками), то нам нужно выполнить несколько условий:

    1. У нас должен быть приватный ключ SSH
    2. На сервере должен быть соответствующий ему открытый ключ
    3. Мы должны знать IP адрес сервера. Можно настроить и по домену, но, как показала практика, IP адрес дает более надежную идентификацию хоста
    4. Наш хост должен быть знаком нашему компьютеру, чтобы избежать лишних вопросов про то, доверяем ли мы ему

    Пункты в целом несложные, последний вообще можно исполнить без SSH подключения к серверу:

    ssh-keyscan -H 192.168.1.162 >> ~/.ssh/known_hosts

    Таким вот образом указанный выше IP будет с этого момента доверенным.

    Теперь давайте создадим новый контекст настроек. Типовая команда выглядит так:

    docker context create remote --docker "host=ssh://user@$host_ip"

    В данной строке есть команда create (создать), имя нашего нового контекста remote и хост, где работает Docker. Дело в том, что докер может управлять не только докером, но и целым swarm или Kubernetes. Это все системы оркестрации, где вы можете (как дирижер оркестра) управлять деплоем на сотни машин, про них позже, сейчас мы просто прокладываем мост между двумя докерами, самое простое решение, поэтому ключ пишем --docker.

    Попробуйте обязательно эту команду на своей локальной машине, указав удаленный хост. После этого у вас в списке контекста появится новый пункт — remote! Это значит, что теперь мы можем управлять нашей машиной на расстоянии, для этого нам нужно применить настройки этого контекста командой:

    docker context use remote

    Теперь * астериск будет стоять напротив пункта remote в списке контекстов. Не забудьте потом переключиться обратно на default, когда закончите.

    Ну что, проверим магию? Давай посмотрим список контейнеров на удаленном хосте командой…

    docker ps

    Именно! Все обычные команды, которые мы выполняли на локальной машине, подходят и теперь, только общаются они с удаленным сервером!

    Если у нас крутятся какие-то контейнеры, то давайте их все остановим и удалим, чтобы развернуть наш удаленный docker-compose. Раньше я предлагал ввести следующие команды:

    docker kill $(docker ps -q)
    docker rm $(docker ps -a -q)

    Но этот способ слишком ультимативный. А если на сервере есть еще проекты? Ведь они все упадут. Поэтому новый вариант выглядит так:

              docker pull dockerhubname/image:latest
              docker-compose --context remote down
              docker volume rm foodgram-project_static

    Объясню, что здесь происходит. Мы стягиваем свежий образ нашего проекта с докерхаб, затем очень нежно выключаем наш проект с помощью встроенной в docker-compose функции down. Дальше я запускаю профилактическое удаление тома со статикой, так как она может обновиться в процессе разработки. В вашем случае нужно подставить правильное название тома, узнать его можно командой:

    docker volume ls

    Теперь самое время запустить docker-compose. Здесь важно понимать, что docker и docker-compose, несмотря на очевидное сходство в названии, по сути своей разные программы для выполнения разных задач. Раньше они вообще мало были связаны, сейчас интеграция становится все лучше и лучше. Совсем недавно docker-compose научился работать с контекстами docker, но пока это происходит не в автоматическом режиме после использования команды use, а с помощью специального флага —context. Перейдите в директорию с файлом docker-compose.yaml и выполните команду:

    docker-compose --context remote up -d --force-recreate

    Смотрите, мы используем контекст remote, поднимаем наши контейнеры в -d режиме (чтобы не видеть вывод) и на всякий случай силой их пересоздаем, если на сервере остались какие-то контейнеры с подобным названием. Все зависимости у нас под рукой — переменные окружения, yaml файл и прочие. Убедитесь, что все ваши контейнеры создаются на основе образов, а не собираются из файлов.

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

    Справка по контексту доступна на сайте докера.

    GitHub Actions

    Как нам применить эти знания к деплою проекта с помощью GitHub Actions?

    Алгоритм будет следующий:

    1. Создаем виртуальную машину
    2. Разворачиваем наш репозиторий (можно не ставить зависимости, нам нужны только файлы): actions/checkout@v2
    3. Устанавливаем Python 3.8: actions/setup-python@v2
    4. Устанавливаем наш SSH KEY в виртуальную машину и добавляем наш IP в доверенные хосты. Очень поможет вот этот action.
    5. Устанавливаем последнюю версию докера, чтобы она могла управлять всеми другими версиями докера, в том числе и младшими (используйте sudo pip install docker-compose, в текущей версии по curl есть критический баг с OpenSSL и вы точно не хотите с ним провести пару часов)
    6. Создаем переменные окружения по инструкции
    7. Создаем пустой файл .env (почему-то без него docker-compose на сервере не может перейти к этапу просмотра переменных окружения в системе, на локальной машине таких проблем нет). Поможет команда touch .env
    8. Запускаем все наши команды из статьи выше, не забыв заменить секретные данные переменными окружения.

    Вот и все. Получилось, что весь деплой уместился в 6 строчек, если не считать объявления переменных окружения и установки зависимостей.