Как правильно отправить код в продакшн
Большинство веб-разработчиков, с которыми я общаюсь сейчас, любят писать JavaScript со всеми новейшими функциями языка — async/await, классами, стрелочными функциями и т.д.
Однако, несмотря на то, что все современные браузеры могут исполнять код ES2015+ и изначально поддерживают упомянутый мной функционал, большинство разработчиков по-прежнему транспилируют свой код на ES5 и связывают его с полифиллами, чтобы удовлетворить небольшой процент пользователей, все еще работающих в старых браузерах.
При работе с новыми API-интерфейсами JavaScript и DOM мы можем условно загружать полифиллы, т.к. мы можем выявить поддержку этих интерфейсов во время выполнения программы. Но с новым синтаксисом JavaScript сделать это намного сложнее, поскольку любой неизвестный синтаксис вызовет ошибку синтаксического анализа (parse error), и тогда наш код вообще не будет запущен.
- Хотя в настоящее время у нас нет подходящего решения для создания функционала выявления нового синтаксиса, у нас есть способ установить базовую поддержку синтаксиса ES2015 уже сегодня.
- Решим это с помощью тега script type=»module».
- Большинство разработчиков думают о script type=»module» как о способе загрузки модулей ES (и, конечно же, это так), но script type=»module» также имеет более быстрый и практичный вариант использования — загружает обычные файлы JavaScript с функциями ES2015+, зная, что браузер может справиться с ними!
- Другими словами, каждый браузер, поддерживающий script type=»module» также поддерживает большинство функций ES2015+, которые вы знаете и любите. Например:
- Каждый браузер, поддерживающий script type=»module», также поддерживает async/await
- Каждый браузер, поддерживающий script type=»module», также поддерживает классы.
- Каждый браузер, поддерживающий script type=»module», также поддерживает стрелочные функции.
- Каждый браузер, поддерживающий script type=»module», также поддерживает fetch, Promises, Map, Set, и многое другое!
Осталось только предоставить резервную копию для браузеров, которые не поддерживают script type=»module». К счастью, если вы в настоящее время генерируете ES5-версию своего кода, вы уже сделали эту работу. Все, что вам теперь нужно — создать версию ES2015+!
В остальной части этой статьи объясняется, как реализовать эту технику, и обсуждается, как возможность развертывания кода ES2015+ изменит способ создания модулей в будущем.
Реализация
Если вы уже используете сборщик модулей (module bundler), например webpack или rollup для генерации своего кода на JavaScript, продолжайте по-прежнему это делать.
Затем, в дополнение к вашему текущему набору (bundle), вы создадите второй набор, как и первый; единственное различие будет заключается в том, что вы не будете транспилировать код в ES5, и вам не нужно будет подключать устаревшие полифиллы (legacy polyfills).
Если вы уже используете babel-preset-env (что должны), то второй шаг будет очень прост. Все, что вам нужно сделать, это изменить список браузеров только на те, которые поддерживают script type=»module», и Babel автоматически не будет делать ненужные преобразования.
Иными словами, это будет вывод кода ES2015+ вместо ES5.
Например, если вы используете webpack, и вашей основной точкой входа является скрипт ./path/to/main.js, тогда конфигурация вашей текущей версии ES5 может иметь следующий вид (обратите внимание, так как это ES5, я называю набор (bundle) main-legacy):
module.exports = {
entry: {
'main-legacy': './path/to/main.js',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'public'),
},
module: {
rules: [{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
['env', {
modules: false,
useBuiltIns: true,
targets: {
browsers: [
'> 1%',
'last 2 versions',
'Firefox ESR',
],
},
}],
],
},
},
}],
},
};
Для того, чтобы сделать современную версию для ES2015+, все, что вам нужно — это создать вторую конфигурацию и настроить целевую среду только для браузеров, поддерживающих script type=»module». Вот как это может выглядеть:
module.exports = {
entry: {
'main': './path/to/main.js',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'public'),
},
module: {
rules: [{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
['env', {
modules: false,
useBuiltIns: true,
targets: {
browsers: [
'Chrome >= 60',
'Safari >= 10.1',
'iOS >= 10.3',
'Firefox >= 54',
'Edge >= 15',
],
},
}],
],
},
},
}],
},
};
При запуске эти две конфигурации выведут на продакшн два JavaScript-файла:
- main.js (ES2015+ синтаксис)
- main-legacy.js (ES5 синтаксис)
Следующим шагом будет обновление вашего HTML для условной загрузки ES2015+ пакета (bundle) в браузерах, поддерживающих модули. Вы можете сделать это, используя script type=»module» и script nomodule:
Внимание! Единственная засада (gotcha) здесь — браузер Safari 10, который не поддерживает атрибут nomodule, но вы можете решить это, встроив JavaScript-сниппет в ваш HTML до использования любых тегов script nomodule. (Примечание: это было исправлено в Safari 11).
Важные моменты
По большей части описанная выше техника «просто работает», но перед ее реализацией важно знать несколько деталей о том, как загружаются модули:
- Модули загружаются как script defer. Это означает, что они не выполняются до тех пор, пока документ не будет распарсен. Если какую-то часть вашего кода нужно запустить раньше, лучше разбить этот код и загрузить его отдельно.
- Модули всегда запускают код в строгом режиме (strict mode), поэтому, если по какой-либо причине часть вашего кода должна быть запущена за пределами строгого режима, ее придется загружать отдельно.
- Модули обрабатывают объявления верхнего уровня переменных (var) и функций (function) отлично от обычных сценариев. Например, к var foo = 'bar' и function foo() {…} в скрипте можно получить доступ через window.foo, но в модуле это не будет работать. Убедитесь, что в своем коде вы не зависите от такого поведения.
Рабочий пример
Я создал webpack-esnext-boilerplate, чтобы разработчики могли увидеть реальное применение описанной здесь техники.
В этот пример я намеренно включил несколько продвинутых фич webpack, поскольку хотел показать, что описанная мною техника готова к применению и работает в реальных сценариях. К ним относятся хорошо известные передовые практики, такие как:
- Разделение кода (Code splitting)
- Динамический импорт (Dynamic imports, условная загрузка дополнительного кода во время выполнения программы)
- Asset fingerprinting (для эффективного длительного кэширования)
И так как я никогда не буду рекомендовать то, что не использую сам, я обновил свой блог, чтобы использовать эту технику. Вы можете проверить исходный код, если хотите увидеть больше.
Если для создания пакетов (bundles) продакшн вы используете инструмент, отличный от webpack, этот процесс более или менее одинаковый.
Я решил продемонстрировать описанную мной технику с помощью webpack, поскольку в настоящее время он является самым популярным инструментом для сборки, а также и самым сложным.
Я полагаю, что если описанная мной техника может работать с webpack, она может работать с чем угодно.
Игра стоит свеч?
По-моему, определенно! Экономия может быть значительной. Например, ниже приведено сравнение общих размеров файлов для двух версий кода из моего блога:
ES2015+ (main.js) | 80K | 21K |
ES5 (main-legacy.js) | 175K | 43K |
Устаревшая ES5-версия кода более чем в два раза превышает размер (даже gzipped) версии ES2015+.
Большие файлы занимают больше времени для загрузки, но они также занимают больше времени для анализа и оценки. При сравнении двух версий моего блога, время, затраченное на parse/eval, также было стабильно вдвое дольше для устаревшей ES5-версии (эти тесты выполнялись на Moto G4 с использованием webpagetest.org):
ES2015+ (main.js) | 184ms, 164ms, 166ms | 172ms |
ES5 (main-legacy.js) | 389ms, 351ms, 360ms | 367ms |
Хотя эти абсолютные размеры файлов и время parse/eval не особенно большие, поймите, что это блог, и я не загружаю много скриптов. Но для большинства сайтов это не так. Чем больше у вас скриптов, тем больше будет выигрыш, который вы получите, развернув код на ES2015+ в своем проекте.
Если вы все еще настроены скептически, и считаете, что размер файла и различия во времени выполнения в первую очередь связаны с тем, что для поддержки устаревших сред требуется много полифиллов, вы не совсем ошибаетесь. Но, к лучшему или худшему, сегодня в Интернете это очень распространенная практика.
Быстрый запрос данных HTTPArchive показывает, что из лучших сайтов по рейтингу Alexa, 85 181 включают в себя babel-polyfill, core-js или regenerator-runtime в своих пакетах (bundles) продакшн. Шесть месяцев назад их число было 34 588!
Реальность транспилируется, и в том числе полифиллы быстро становятся новой нормой. К сожалению, это означает, что миллиарды пользователей получают триллионы байтов, без необходимости отправленные через сеть в браузеры, которые изначально были бы вполне способны запускать нетранспилированный код.
Пришло время собирать наши модули как ES2015
Главная засада (gotcha) для описанной здесь техники сейчас состоит в том, что большинство авторов модулей не публикуют ES2015+ версии исходного кода, а публикуют сразу транспилированную ES5-версию.
Теперь, когда развертывание кода на ES2015+ возможно, пришло время изменить это.
Я полностью осознаю, что такой шаг сопряжен с множеством проблем в ближайшем будущем.
Сегодня большинство инструментов сборки публикуют документацию, рекомендующую конфигурацию, которая предполагает, что все модули написаны на ES5.
Это означает, что если авторы модулей начнут публиковать исходный код на ES2015+ в npm, то они, вероятно, сломают некоторые сборки пользователей и просто вызовут путаницу.
Проблема заключается в том, что большинство использующих Babel разработчиков настраивают его так, чтобы код в node_modules не транспилировался. Однако, если модули опубликованы с исходным кодом ES2015+, возникает проблема. К счастью, она легко исправима. Вам просто нужно удалить исключение node_modules из конфигурации сборки:
rules: [
{
test: /.js$/,
exclude: /node_modules/, // удалите эту строку
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}
]
Недостаток заключается в том, что если такие инструменты как Babel должны начать транспилировать зависимости (dependencies) из node_modules, в дополнение к локальным зависимостям, это замедлит скорость сборки. К счастью, эту проблему можно отчасти решить на уровне инструментария с постоянным локальным кэшированием.
Несмотря на удары, мы, скорее всего, пройдем путь к тому, что ES2015+ станет новым стандартом публикации модулей. Я думаю, что борьба стоит своей цели. Если мы, как авторы модулей, публикуем в npm только ES5-версии нашего кода, мы навязываем пользователям раздутый (bloated) и медленный код.
Публикуя код в ES2015, мы даем разработчикам выбор, что в конечном счете приносит пользу всем.
Заключение
- Хотя script type=»module» предназначен для загрузки модулей ES (и их зависимостей) в браузере, его не нужно использовать только для этой цели.
- script type=»module» будет успешно загружать единственный файл Javascript, и это даст разработчикам столь необходимое средство для условной загрузки современного функционала в тех браузерах, которые могут его поддерживать.
- Это, наряду с атрибутом nomodule, дает нам возможность использовать код ES2015+ в продакшн, и наконец-то мы можем прекратить отправку транспилированного кода в браузеры, которые в нем не нуждаются.
- Написание кода ES2015 — это победа для разработчиков, а внедрение кода ES2015 — победа для пользователей.
- © Habrahabr.ru
Простой способ разворачивать (деплоить) проекты на боевом (production) сервере через git
Иногда встречаются заказчики использующие git для хранения своих проектов и, вместе с этим, выгружающие их на боевые серверы через FTP (sFTP). Эта простая инструкция покажет способ разворачивать и обновлять проекты через git.
Для тестирования данного рецепта необходимы базовые навыки работы с git.
Для начала нам нужен основной репозиторий проекта с веткой master. Скорее всего, он у вас уже есть. Если же нет, то создайте его и отправьте в него свой проект. В master-ветке будет происходить разработка.
Эта версия проекта (master) будет работать на компьютере разработчика со всеми необходимыми настройками (данными для подключению к БД, логами и т.д.). Чтобы другие разработчики могли участвовать в проекте, выложите master-ветку на один из git-сервисов, например, на github.
com, bitbucket.org, свой gitlab-сервер и так далее.
Для боевой версии сайта создадим отдельную ветку – “production”.
Её мы подготовим для работы с боевым сервером, то есть берем нашу master-ветку и выставляем все необходимые настройки: подключение к БД production-сервера, отключение вывода ошибок, отключения лишнего логирования, деактивация модулей тестирования и так далее.
Проще говоря, ветка “production” будет представлять из себя полностью подготовленный проект для работы на боевом сервере. После подготовки выгружаем нашу новую ветку на git-сервер, где уже хранится master-ветка. Именно с этого git-сервера мы будем получать production-ветку для боевого сервера.
Создаем репозиторий на боевом сервере и закачиваем в него нашу production-ветку. После этого проводим дополнительную настройку, например, обновляем библиотеки composer’a, активируем миграции и так далее. Вот пример того как это можно сделать:
# Закачиваем на production-сервер production-ветку проекта.
git init
git remote add origin https://bitbucket.org/ваш_логин/наименование_проекта.git
git pull origin production
git checkout production
git branch -D master # на боевом сервере master-ветке делать нечего
# Осуществляем дополнительную настройку проекта для работы. Примеры:
composer update
php init_magrate_command
other_initial_command…
Сливаем master-ветку в production-ветку для обновления кода (переходим на ветку production и делаем merge с master). Проверяем все выставленные настройки, все ли нормально соединилось. Отправляем все изменения на git-сервер. Обновляем production-ветку на сервере с git-сервера через pull, вот так:
git pull origin production
После обновления кода может потребоваться повторно обновить библиотеки composer’a, миграции и так далее.
Желательно подключить веб-хук в используемом git-хранилище. Он будет запускать скрипт развертывания проекта. Плюс к этому, все локальные настройки окружения, как для локальной машины, так и для боевого сервера, стоит хранить в .env-файле.
Чтобы не допустить утечек исходников проекта, вам необходимо закрыть доступ к .git папке, так как в ней находятся исходники. Также желательно, чтобы корневая директория проекта (DocumentRoot имеется ввиду) вела на какую-нибудь публичную директорию без git. Например:
— .git
— project_folder_1
— project_folder_2
— project_folder_3
— …
— project_folder_N
— project_public_directory (DocumentRoot должен вести на неё)
Таким образом, ваш репозиторий окажется в большей безопасности. В разных проектах/cms/фреймворках “project_public_directory” именуется по-разному, например, “public”, “web”, “www” и так далее.
Эта директория нужна для размещения в ней публичных файлов (эти файлы предназначены для обычных пользователей и доступны всем), таких как фронт-контроллеры (index.
php), css-стили, js-скрипты, изображения, шрифты и другие.
В сети вы можете найти большое количество различных инструкций по разворачиванию проектов не только через git, но и через специальные инструменты наподобие Capistrano и Deployer. Эти инструменты лучше использовать для крупных проектов, где процесс разворачивания и обновления требует большого количества действий, а для малых и небольших или личных проектов вполне подойдет эта инструкция.
Как быстро загрузить код на продакшн в Laravel
Небольшой пост о загрузке кода на продакшн сервер. Этот вопрос уже поднимался много раз, а в сети полно информации об этом. Как же в Laravel организовать деплой в продакшн, чтобы было легко и просто? Речь пойдет о инструменте deployer. Весь функционал запакован в phar файл и очень легко переносится от проекта к проекту.
Есть несколько путей установки, включая и composer.
В общем, скачиваем дистрибутив deployer.phar и кидаем его в корень вашего проекта. И переименовываем в dep. Просто файл без расширения. Далее нужно создать файл настроек для определенного фреймворка. В нашем случае — это Laravel.
Находясь в корне проекта, вводим следующую команду в терминал:
php dep init
После этого появится список фреймворков. Ставим цифру, которая стоит возле Laravel. В корне проекта появится новый файл: deploy.php Именно в нем мы и будем прописывать наши настройки. Перед тем, как разобрать файл настроек, пару слов о том, как это все работает и как нужно настроить apache и nginx.
Смысл автоматического деплоя в том, чтобы создать ряд папок для хранения релизов и постоянных директорий и файлов. Например, на сервере будут созданы три каталога: current, releases и shared.
Каталог current на самом деле пустой. Он по сути является ссылкой на текущий релиз.
Когда произойдёт удачное развертывание проекта в папку releases, то для current будет создана новая ссылка на последний релиз.
Все релизы идут под номерами, которые генерируются исходя из даты деплоя на production. Именно внутри папки с цифрами и лежат все ваши файлы проекта. Вернее, почти все файлы. Так как общие файлы и папки, которые используются в любом релизе, хранятся в директории shared. А в релизах хранятся ссылки на эти общие файлы и папки.
С папкой current и releases разобрались. Что же должно быть в папке shared. В неё однозначно выносится файл настроек проекта — .env. Именно в этом файле нужно прописать все настройки проекта, включая базу данных, работу с почтой и т.д. И он ни в коем случае не должен хранится в репозитории!
Также нужно в shared поместить npm, который применяется для всех релизов, папку storage, где хранятся кэш и логи. И те папки из public, которые одинаковы для всех релизов. Например, папка upload_images, куда загружаются изображения с сайта.
Теперь нужно подправить конфиги apache и nginx. В них нужно указать новую рабочую директорию, что-то типа:
/var/www/site/current/public
Единственное что нужно сделать на сервере, это только подправить конфиги. Создавать структуру папок не нужно. Это сделает сам deployer.
Для того, чтобы можно было делать выгрузку на production, вы должны хранить ваш проект в репозитории, например на github или bitbucket.
Помимо этого потребуется настроить ssh ключи доступа, чтобы ваш production сервер мог получать изменения из репозитория не по запросу пароля, а по приватному ключу. Как это делается можно легко найти в интернете. Переходим к настройке файла deploy.php:
Code review. Всё, что нужно знать
Vlad Khambir May 25, 2021 2021-05-25T23:00:00+03:00
Mar 26, 2022 2022-03-26T15:03:27+02:00 4 min
Два инженера, которые смотрят код, ищут пути его улучшения, делятся своим опытом — этот замечательный процесс называется Code review. Именно эту неотъемлемую часть жизни разработчика я хочу разложить по полочкам. В этой статье я опишу как провести Code review, на что обратить внимание, как сделать этот процесс более эффективным.
https://www.youtube.com/watch?v=xi2ItRYZgAE\u0026pp=ygVH0JrQsNC6INC_0YDQsNCy0LjQu9GM0L3QviDQvtGC0L_RgNCw0LLQuNGC0Ywg0LrQvtC0INCyINC_0YDQvtC00LDQutGI0L0%3D
А начнем мы с главного — обсудим зачем вообще нужно Code review.
Какой профит? ????
Code review создано не только для улучшения кода, это социальный процесс, в котором принимают участие коллеги с двумя ролями — автор и ревьювер.
Вы можете делать друг друга лучше, так как каждый делится своим опытом и мнением. Это позволяет быстро обучаться, находить пробелы в знаниях и стремительно их заполнить.
Ловушка
Code review стоит воспринимать исключительно в положительном ключе. Все плюсы могут быть утеряны, если автор воспринимает обсуждение как критику.
Какой процесс проведения Code review?
Перед началом ревью я рекомендую создать документ в котором будут записываться список рекомендаций от ревьювера.
???? Code-review — процесс итерационный и на каждой итерации происходит следующее:
- Автор делится кодом
- Ревьювер смотрит код, формирует фидбек и отправляет его автору
- Автор реагирует на фидбек уточняющими ми или изменениями в коде и снова отдает код на ревью.
⚽️ Такая игра в пас приведет к чистому коду и шарингу знаний, все в плюсе.
Не забываем есть слона по кусочкам, так как если мы имеем дело с большим куском кода — лучше провести ревью небольшого участка кода, разобраться со всем что нашли и переходить к следующему куску слона кода.
Как писать фидбек?
Фидбек должен быть изложен как рекомендации, а не команды к выполнению.
Рекомендации лучше команд, потому что это дает право последнего слова. И вполне возможно, что если аргументы автора будут сильны — в продакшн пойдет именно его реализация поставленной задачи.
Круто, если ревьювер предоставляет наброски кода, возможно даже proof of concept того как бы он решил поставленную задачу.
Критикуем код, а не кодера
Цель Code review — сделать код лучше, а не найти виновных. Чтобы повысить командный дух и понизить напряжение — используй слово Мы, вместо Ты. Нужно размыть конкретику о том кто написал код, гораздо важнее сосредоточится на решении проблем, которые были найдены в коде.
Кстати, в коде нужно искать не только проблемы, а и положительные моменты. За элегантные решения, грамотное программирование ревьювер может похвалить автора ????
One more thing совет ☝️
Настоятельно рекомендую время от времени менять ревьюверов. Это позволит получить новый взгляд и опыт для всех сторон.
На что обращать внимание? ????
Развертывание приложений на продакшн серв. Вопрос по архитектуре
?
?
Categories: Всем привет. В ru_sysadmins посоветовали обратиться сюда.Как правильно организовать взаимодействие программистов с продакшн серверами?На текущий момент рассматривается такой вариант: develop виртуалка с gitlab на борту. Там же будут висеть все приложения для разработки и тестирования. Алгоритм заливки на продакш мне видится таким: у одного чувака ssh доступ со скриптом вместо шела, который при введении, скажем, «commit mysite» отправляет все коммиты на продакшн.По крону на той же виртуалке git репы разворачиваются в полноценные приложения.Беда в том, что приложения взаимосвязаны, работают с несколькими базами данных. Что с этим делать? Разворачивать копии всех бд с некоторым тестовым набором данных? А как тогда быть, если делается какой-нибудь Alter table?
Блин, уверен, всякие EA games и прочие уже давно порешали эти вопросы и выработали правильный подход к организации работы. Кто-то может поделиться опытом внедрения?
- Раньше, в стародавние времена, вроде с этим не было затруднений. Вставляешь диск и переустанавливаешь. Слетевшая копия чинилась, почти все настройки…
- Теперь соединение проходит, но при попытке получить контент приходит эксепшен — код 403 от страницы. Вот код: try { HttpsURLConnection con =…
- Как известно, не так давно из Яндекса украли репозиторий. Его можно найти на торрентах, я вот похлопотал. А кто-нибудь пробовал разобраться в этой…
- Раньше, в стародавние времена, вроде с этим не было затруднений. Вставляешь диск и переустанавливаешь. Слетевшая копия чинилась, почти все настройки…
- Теперь соединение проходит, но при попытке получить контент приходит эксепшен — код 403 от страницы. Вот код: try { HttpsURLConnection con =…
- Как известно, не так давно из Яндекса украли репозиторий. Его можно найти на торрентах, я вот похлопотал. А кто-нибудь пробовал разобраться в этой…
Деплой JavaScript-приложений
В работа над любым проектом рано или поздно наступает момент, когда его нужно запустить на продакшн-сервере. Очевидно, чтобы запустить, нужно выгрузить.
https://www.youtube.com/watch?v=xi2ItRYZgAE\u0026pp=YAHIAQE%3D
Всегда есть варианты и свобода выбора, и деплой не исключение. Само слово deployment означает “развёртывание, размещение”, что, в целом, отражает конечную цель этого процесса.
Любой специалист, которому выпала возможность поработать в более-менее серьёзной организации с необходимостью регулярного обновления кода на сервере подтвердит, что хорошо настроенный процесс деплоя сделает вас немного счастливее.
Я расскажу вам историю о том, как я пришёл к хорошему деплою, каким способом я пользуюсь и почему он мне нравится. А заодно мы рассмотрим несколько возможных вариантов.
Стадия 1: “Зачем мне что-то? Хостер уже дал мне FTP!”
Итак, за окном 2015 год, и я только начинаю осваивать искусство вебдева. Мой сайт – PHP/jQuery/CSS, никаких сборщиков, гита и прочего, просто файлы, которые достаточно положить в правильную директорию на сервере. Чем я, собственно, и занимаюсь.
- Сейчас уже трудно вспомнить, каким хостинг-провайдером я пользовался, но это было что-то в стиле “заплати 5 рублей и получи соответствующую услугу”, но я, как человек, у которого нет денег и есть самый первый заказ по фрилансу, счастлив этому безмерно.
- Первый вариант – самый простой – доступ к серверу по FTP и ручная загрузка файлов.
- Это может быть хорошо, потому что:
- Не требуется абсолютно ничего, кроме FileZilla или аналогов;
- Сэкономит вам время, если сайт статический, и кодовая база в обозримом будущем не будет обновляться;
- Есть на подавляющем большинстве платных и бесплатных хостингов.
Однако, эта статья называется не “Деплой статических сайтов”, так что вы вполне законно можете спросить: при чём тут статика? И будете правы. Плох данный способ тем, что он лишает вас очень много полезных штук:
- Версионирования релизов;
- Возможности быстро “откатить” релиз, если в нём ошибка;
- В случае использования сборщиков (Webpack и т.д.), помечающих собранные файлы хэшем, этот способ приведёт к файловой путанице.
Естественно, есть и самый очевидный минус – это неудобно, долго, да ещё и руками надо что-то делать. Мне, как чертовски ленивому человеку, это не подходит. Едем дальше!
Стадия 2: “Окей, я буду как большие дяденьки! Где мой Git?..”
По своему опыту (а также по опыту других разработчиков, подсмотренному мной) скажу, что когда в жизни молодого и горячего разработчика появляется Git, а особенно тогда, когда он понимает, как им пользоваться хотя бы на начальном уровне, он видится просветом в полном туч небе, способным решить любую проблему.
Хранить код? Git. Библиотека фоток? Git. Список контактов? Git. Деплой? Конечно же, тоже Git. И, вроде бы, это уже лучше, чем просто заливать файлы вручную. Особенно если на этой стадии наш герой, как и я в то время, уже более-менее мог в какую-нибудь автоматизацию, типа шелл-скриптов.
В голове у меня сидела мысль: если Git — система контроля версий, то кому, как ни ей, заниматься контролем версий на продакшне? Таким образом процесс деплоя представляет собой клонирование репозитория в нужную папку и периодические git pull.
Плюсы такого способа:
- Вы всегда знаете, что на продакшне тот же код, что и на вашем компьютере
- Решение более-менее автоматизированное: достаточно лишь вызвать команду.
Однако, есть и минусы, и довольно серьёзные.
Прежде всего, давайте признаем: как бы мы с вами ни отыгрывали знатоков, рано или поздно возникает момент, когда ты правишь что-то прямо на продакшне, к примеру, какую-нибудь критическую ошибку.
Это вынуждает нас внимательно следить за состоянием кода в репозитории и на бою, убеждаться в том, что нет конфликтов, делать пуши в мастер прямо с продакшна, или делать пулл-реквесты. Кроме того, минусы такие:
- Конфликты могут сломать ВСЁ. Поверьте, я знаю, о чём говорю. Если случается конфликт при обновлении кодовой базы, Git пометит мх прямо в файлах, ломая синтаксис. Если познания Git не очень большие, быстро устранить проблему может быть сложно.
- Если доступ к серверу имеют несколько человек с разными логинами и правами, нужно обладать неплохими познаниями Linux, чтобы все они могли выполнять git pull
- Раз уж речь про JavaScript, то нужно сказать, что этот способ вынуждает нас хранить собранные файлы в репозитории. А это как-то не круто. Тем более, если проект серверный: нужно либо дёргать npm install после обновления, либо хранить в репозитории папку node_modules. Чувствуете мурашки на коже?
Последний минус — банальная лень. Мне лично через какое-то время стало лень следить за синхроном репозитория и продакшна, и через какое-то время продакшн и гитхаб разошлись далеко друг от друга.
https://www.youtube.com/watch?v=cc0gpWr_kR8\u0026pp=ygVH0JrQsNC6INC_0YDQsNCy0LjQu9GM0L3QviDQvtGC0L_RgNCw0LLQuNGC0Ywg0LrQvtC0INCyINC_0YDQvtC00LDQutGI0L0%3D
Мой вердикт: это тоже не подходит.
Стадия 3: Shipit, потому что каждый инструмент должен делать своё дело
Через какое-то время я увидел инструмент PHP Deployer, который сразу мне понравился. Я начал поиски аналога для JavaScript, и вскоре нашёл Shipit. По заверению их репозитория, это универсальный инструмент для автоматизации и деплоя.
Вкратце, Shipit делает следующее:
- Создаёт на вашем компьютере временную папку, куда клонирует ваш репозиторий для получения актуальной кодовой базы
- Даёт возможность делать с этими файлами что угодно, к примеру, устанавливать зависимости и запускать сборку
- Подключается к вашему серверу по SSH и загружает файлы в удалённую папку
Выглядит негусто, но это ещё и не всё. Главное, что даёт Shipit — систему событий, на которые можно подписаться и выполнить какую-либо команду на локальном либо удалённом сервере.
Начать использовать его очень просто. Проверьте, вы соблюли эти условия:
- У вас есть доступ к Git-репозиторию
- У вас есть SSH-доступ к продакшн серверу
Далее, вам нужно установить два пакета:
npm install —save-dev shipit-cli
npm install —save-dev shipit-deploy
Первый пакет — инструменты командной строки, второй — набор тасков (от оригинального “tasks”), которые предназначены для деплоя.
Далее, в корне вашего проекта нужно создать конфиг-файл shipitfile.js. В репозитории проекта есть пример этого файла, но я покажу вам конфигурацию, которая мне показалось наиболее удачной и которой я сам пользуюсь.
I. Деплой компилируемого фронтенда
Это может быть приложение на каком-нибудь фреймворке или библиотеке, в моём случае — на Vue.js. Будем рассматривать конфиг-файл на примере выдуманного проекта MyFrontend
// shipitfile.js
const path = require('path')
const WORKSPACE_DIR = '/tmp/myfrontend'
Нам понадобится path из стандартной библиотеки Node. Переменная WORKSPACE_DIR – это временная папка, в которую Shipit будет клонировать репозиторий и производить манипуляции с ним.
// shipitfile.js
const path = require('path')
const WORKSPACE_DIR = '/tmp/myfrontend'
module.exports = (shipit) => {
require ('shipit-deploy').(shipit)
shipit.initConfig({
default: {
workspace: WORKSPACE_DIR,
dirToCopy: path.join(WORKSPACE_DIR, 'dist'),
deployTo: '/var/www/myapp/myfrontend',
repositoryUrl: 'https://gitgit.git/myapp/myfrontend.git',
ignores: ['.git', 'node_modules'],
keepReleases: 5,
keepWorkspace: false,
shallowClone: false,
deploy: {
remoteCopy: {
copyAsDir: false
}
}
},
staging: {
servers: 'myuser@123.45.678.90'
}
})
}
Далее нужно написать конфиг для Shipit. Вообще, у него очень много разных ключей на все случаи жизни, и я очень рекомендую ознакомиться с ними в документации. Мы же рассмотрим только те, которые важны для нас сейчас.
- workspace – передаем путь до локальной папки
- dirToCopy – какую папку из склонированного репозитория мы будем загружать. Если не прописать этот ключ, Shipit загрузит всю папку workspace
- deployTo – куда на удалённом сервере нужно положить наши файлы из предыдущей папки
- repositoryUrl – адрес вашего Git-репозитория
- ignores – какие файлы и папки точно не нужно загружать. Вообще, указание dirToCopy решает эту проблему, но лучше перестраховаться, как мне кажется.
- keepReleases — сколько релизов будет храниться на продакш сервере, и, соответственно, на сколько релизов назад вы сможете откатиться
- copyAsDir – как копировать папку dirToCopy на сервер: создавать на сервере эту же папку (true) или же просто загрузить её содержимое в deployTo (false)
- staging – это всего лишь название конфигурации деплоя, оно может быть любым
- servers — один или несколько IP-адресов сервера с указанием имени пользователя.
Если мы прямо сейчас запустим процесс деплоя, мы вряд ли добьёмся успеха: склонированный из Git проект не собран, а папка dist не существует. Значит, нам нужно “вклиниться” в процесс деплоя и вовремя собрать наш проект. В этом нам помогут уже упомянутые мной события.
https://www.youtube.com/watch?v=cc0gpWr_kR8\u0026pp=YAHIAQE%3D
Добавим в наш файл следующее:
// shipitfile.js
const path = require('path')
const WORKSPACE_DIR = '/tmp/myfrontend'
module.exports = (shipit) => {
require ('shipit-deploy').(shipit)
shipit.initConfig({
default: {
workspace: WORKSPACE_DIR,
dirToCopy: path.join(WORKSPACE_DIR, 'dist'),
deployTo: '/var/www/myapp/myfrontend',
repositoryUrl: 'https://gitgit.git/myapp/myfrontend.git',
ignores: ['.git', 'node_modules'],
keepReleases: 5,
keepWorkspace: false,
shallowClone: false,
deploy: {
remoteCopy: {
copyAsDir: false
}
}
},
staging: {
servers: 'myuser@123.45.678.90'
}
})
shipit.on('fetched', async () => {
await shipit.start('app:build')
})
shipit.blTask('app:build', async () => {
await shipit.local(
`cd ${WORKSPACE_DIR} && npm install && npm run build`
)
})
}
У пакета shipit-deploy есть набор событий на каждое действие (ознакомиться можно тут). Нам интересно событие fetched, которое происходит сразу после клонирования репозитория. В нём вызываем наш собственный таск app:build.
app:build объявлен как blTask: в Shipit это значит, что задача блокирующая, и начинать следующую задачу нельзя, пока не завершится эта. В ней мы при помощи метода shipit.local запускам локальную команду: переходим в WORKSPACE_DIR, устанавливаем зависимости и собираем проект.
Если в процессе сборки что-то пойдёт не так, деплой прекратится: “сломанный” код не окажется на продакшне.
Всё готово к деплою: в командной строке набираем ./node_modules/.bin/shipit staging deploy. Когда процесс завершится, в папке на сервере вы увидите два элемента: директорию releases и симлинк current. Ваш веб-сервер нужно направить именно на симлинк, чтобы в браузер отдавались только актуальные файлы.
Если что-то всё же пошло не так, и нужно срочно вернуть предыдущий релиз, вы можете вызвать команду ./node_modules/.bin/shipit staging rollback. Она переключит симлинк current на предыдущий релиз.
II. Деплой бэкенда на Node.js
Здесь принципы те же, за исключением пары деталей: бэк на ноде не надо собирать, но нужно установить зависимости на продакшн-сервере. В итоге конфиг будет выглядеть как-то так:
// shipitfile.js
const path = require('path')
const WORKSPACE_DIR = '/tmp/mybackend'
module.exports = (shipit) => {
require ('shipit-deploy').(shipit)
shipit.initConfig({
default: {
workspace: WORKSPACE_DIR,
deployTo: '/var/www/myapp/mybackend',
repositoryUrl: 'https://gitgit.git/myapp/mybackend.git',
ignores: ['.git', 'node_modules'],
keepReleases: 5,
keepWorkspace: false,
shallowClone: false,
deploy: {
remoteCopy: {
copyAsDir: false
}
}
},
staging: {
servers: 'myuser@123.45.678.90'
}
})
shipit.on('updated', async () => {
await shipit.start('app:install-deps')
})
shipit.blTask('app:install-deps', async () => {
await shipit.remote(
`cd ${shipit.releasePath} && npm install`
)
})
}
Ключевые изменения:
- Убрали dirToCopy, так как обычно бэкенд-проекты начинаются от корня репозитория
- Теперь мы завязываемся на событие updated, которые происходит после создания новой папки внутри releases на сервере и копирования файлов туда.
- В блокирующей задаче app:install-deps мы при помощи shipit.remote запускаем установку зависимостей на сервере, перемещаясь в папку релиза. Заранее мы не знаем, какую папку создаст Shipit, но её можно легко получить на этом этапе при помощи shipit.releasePath
Также может понадобиться перезапустить приложение Node. Для этого можно использовать событие deployed, и вызвать, к примеру, pm2 reload, если для запуска ноды в бою вы используете PM2.
IV. Деплой компилируемого бэкенда на Node.js
Да-да, я говорю про TypeScript. Однако, я думаю, что сложив вышеописанные шаги, вы получите как раз то, что вам нужно 🙂
Итоги
Хороший деплой – это, прежде всего, сэкономленные нервы. Вы знаете, что можете быстро развернуть новую версию, или вернуть предыдущую, если что-то пошло не так. Вы знаете, что у вас никогда не возникнет путаницы на продакшне, и вы всегда будете видеть, сколько релизов было сделано.
Расширив shipitfile.js, вы можете логировать деплои, собирать статистику, да и вообще делать всё, что вам нужно. Потратив на настройку минут 20 один раз, вы сохраните кучу времени в будущем.
Не в последнюю очередь – это красиво. Аккуратная структура директорий, полный контроль над продакшном, это эстетика, которая важна для нас, как для разработчиков, ведь видеть её – значит развиваться во всём.