После того как сайт написан, его нужно выложить в интернет. Стандартный путь включает три пункта:
- Покупка домена
- Покупка хостинга и его настройка
- Деплой
В этом уроке мы рассмотрим эти пункты более подробно.
Покупка домена
Чтобы приобрести домен, необходимо обратиться в компании-регистраторы. Домен оплачивается раз в год и закрепляется за определенным человеком или компанией.
Иногда хостер и регистратор — одна и та же компания. Тогда домен и сервер связываются одновременно. Иначе нужно производить определенные настройки на стороне сервера и подтверждать свое право владения доменом.
Покупка хостинга и его настройка
Хостинг — это ресурсы для размещения и обслуживания сайта в интернете. Хостинг бывает разным. Это может быть:
- Уже полностью готовая среда, в которую заливается код сайта
- Просто железные серверы, которыми можно и нужно управлять самостоятельно
Обычно работа с хостингом выглядит так:
- Регистрируемся на сайте хостинга
- Выбираем тариф и вид хостинга (On-Prem, IaaS, PaaS, SaaS), привязываем банковскую карту
- Получаем доступ к ресурсам — к IP-адресу и паре «логин/пароль» для доступа по SSH и FTP
- При необходимости настраиваем хостинг и деплоим проект
Деплой
Деплой — это выкладка новой версии сайта на сервер. Этот процесс может быть довольно сложным и зависит от используемых технологий. Во время деплоя могут выполняться следующие задачи:
- Скачиваем код проекта на сервер (обычно через клонирование Git)
- Ставим все необходимые зависимости
- Выполняем сборку приложения
- Выполняем миграции — SQL-скрипты, которые изменяют структуру базы данных
- Запускаем новую версию кода
Обсудим самый простой способ начать деплоить. Большинство PaaS-хостеров имеют бесплатные планы, достаточные для выкладки учебных проектов. Их достоинство в том, что вам не придется покупать адрес — домен третьего уровня предоставляется бесплатно.
Для деплоя учебных проектов мы предлагаем использовать Render. Этот сервис поддерживает деплой GitHub-репозиториев
Деплой на Render.com
Чтобы наше приложение работало на Render, его необходимо упаковать в Docker-контейнер. Более подробную информацию о Docker можно найти в отдельном курсе. Здесь же достаточно просто следовать приведённым шагам.
Контейнер можно представить как коробку, в которой есть всё необходимое для работы приложения: сам код, программы для его запуска и даже настройки. Это позволяет приложению работать одинаково на любом устройстве или сервере, независимо от его окружения
Для создания такого контейнера используется специальный файл — Dockerfile. Это текстовый файл, в котором мы пишем инструкции: что установить, как настроить и как запустить приложение. Render использует Dockerfile для создания образа контейнера — это как финальная сборка коробки с приложением. После этого Render разворачивает этот образ на своих серверах, и ваше приложение начинает работать. Всё, что вам нужно сделать, — это предоставить Dockerfile и код приложения, а Render позаботится обо всём остальном
Создаем Dockerfile
Создайте в корне своего приложения файл Dockerfile. Добавьте в него следующий код:
Рассмотрим подробнее, что здесь происходит. За основу мы берем базовый образ, в котором уже установлены Gradle и JDK 21. Затем устанавливаем рабочую директорию /app внутри контейнера, теперь все последующие команды будут выполняться внутри этой директории. Далее копируем в нее из директории /app файлы нашего приложения. После этого выполняем сборку приложения с помощью команды ./gradlew clean build. При запуске контейнера приложение автоматически запускается через ./gradlew run. Таким образом, при запуске контейнер будет знать, как запустить приложение и содержать всё необходимое для его работы
В зависимости от приложения, путь к файлам приложения и команда запуска у вас могут отличаться. Путь к файлам приложения указывается относительно Dockerfile
Работаем с Render
Зарегистрируйтесь в сервисе Render, удобнее всего сделать это через GitHub. После регистрации на вкладке Dashboard вы сможете добавлять новые приложения — веб-сервисы и базы данных
Наше приложение использует базу данных, поэтому для начала нужно создать эту базу. Конечно, во время разработки мы могли использовать встроенную базу данных в памяти, чтобы быстро тестировать приложение. Но для рабочего приложения такое решение не подходит, так как при перезапуске приложения все данные исчезнут. Поэтому здесь мы будем использовать настоящую базу данных PostgreSQL, чтобы данные сохранялись постоянно, даже после перезапуска приложения
Удобство PaaS-сервисов заключается в том, что они берут на себя всю сложность настройки и управления инфраструктурой. Вам не нужно вручную устанавливать PostgreSQL или настраивать сервер — платформа сделает это за вас. Вы просто создаете базу данных в несколько кликов, а Render автоматически сделает все необходимое для её работы
Чтобы создать базу данных, нажмите на кнопку Add new и выберите Postgres
Дайте нашему сервису название (Name), оно будет отображаться в дашборде. Можно также указать имя базы данных и пользователя. Но это необязательно, если их не указать, Render сам сформирует случайные имена
Регион можно оставить по умолчанию, но обязательно запомните, какой вы выбрали, это пригодится нам дальше при выборе региона для самого приложения
Нажмите кнопку Create database в самом низу страницы, чтобы создать базу данных
Выбирайте везде бесплатный тариф, его вполне достаточно для учебных целей и так не придется вводить номер кредитной карты
После создания базы данных, Render покажет вам данные для подключения к ней (раздел Connections)
Вам нужно будет скопировать несколько важных значений: Hostname (адрес сервера), Port (порт), Database (имя базы), Username (имя пользователя) и Password (пароль). Эти данные понадобятся позже, когда вы будете настраивать переменные окружения для вашего приложения.
Теперь, когда у нас есть база данных, мы можем перейти к созданию непосредственно приложения. Вернитесь на дашборд, снова нажмите Add new и выберите Web Service
Подключите свой GitHub-репозиторий �� проектом, который хотите задеплоить, или вставьте его URL, после чего нажмите Connect
Заполните поля с настройками приложения. В поле Name задайте имя для своего приложения. В Language оставляем Docker в качестве среды выполнения проекта. Поле Root Directory оставьте пустым, так как Dockerfile находится в корне проекта
Обратите внимание на регион приложения (Region). Веб-сервис и база данных должны находиться в одном регионе, иначе приложение не сможет подключиться к базе
Сейчас наше приложение пока еще не знает, как подключиться к базе данных. Чтобы оно могло работать с базой, ему нужно передать информацию о подключении: адрес сервера, порт, имя базы, логин и пароль. Это делается с помощью переменных окружения — специальных настроек, которые передаются приложению при запуске. Переменные окружения — это способ передать приложению информацию о том, как подключиться к базе данных
Чтобы указать переменные окружения, пролистайте настройки приложения вниз, до раздела Environment variable. Нажмите на кнопку Add Environment Variable, добавьте нужные для подключения к базе данных переменные окружения и задайте им значения. Здесь нам пригодятся те сведения, которые мы скопировали в базе данных — Hostname, Port, Database, Username, Password
Переменная окружения JDBC_DATABASE_URL содержит всю информацию, необходимую для подключения к базе данных: адрес сервера, порт, имя базы, пользователь и пароль. Именно через эту переменную приложение узнает, как подключиться к базе
Формат записи URL у базы данных должен быть таким:
Например:
Чтобы всё работало правильно, ваше приложение должно быть настроено так, чтобы оно могло читать и использовать эту переменную окружения. Это стандартный способ передачи данных о подключении к базе данных в приложениях
После завершения настройки нужно будет нажать на кнопку Deploy Web Service. Ваше приложение будет клонировано из репозитория, собрано и запущено. После завершения процесса приложение станет доступно по определенному адресу, который сформируется автоматически
Перейдите по этому адресу и проверьте, что приложение работает корректно.
Если что-то пошло не так, всегда можно посмотреть логи приложения на вкладке Logs. Они помогут вам разобраться с ошибкой
Особенности сборки Gradle
Подключив базу данных к нашему приложению, мы добавили в него файл src/main/resources/schema.sql, который затем читается из кода. Когда мы попробуем собрать приложение и запустить на сервере, этот код не сработает, потому что Gradle не копирует файлы из директории src/main/resources в получившийся jar.
Это можно исправить с помощью плагина shadow, который автоматически добавляет все ресурсы в jar:
Выводы
- Процесс публикации сайта включает три ключевых этапа: покупка домена, выбор и настройка хостинга, а также деплой приложения на сервер.
- Выбор хостинга зависит от потребностей проекта: можно использовать готовые решения (PaaS) или управлять собственными серверами (IaaS).
- Деплой на PaaS упрощает процесс развертывания, предоставляя бесплатные планы и автоматизацию, что особенно полезно для учебных проектов.
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 22:29:49 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="K1H9BesXFkm_jfZrSMCwb_IxOeD0apvzLauHbvjsutzEgDYyGWm7KQnO0vNEz0AYMjgUSvxdZVGQSx06qutdsg";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Деплой | Java: Веб-технологии</title>
<meta name="description" content="Деплой / Java: Веб-технологии: Знакомимся с ключевыми понятиями и процессом деплоя">
<link rel="canonical" href="https://ru.hexlet.io/courses/java-web/lessons/deploy/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Деплой">
<meta property="og:title" content="Java: Веб-технологии">
<meta property="og:description" content="Деплой / Java: Веб-технологии: Знакомимся с ключевыми понятиями и процессом деплоя">
<meta property="og:url" content="https://ru.hexlet.io/courses/java-web/lessons/deploy/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="8TvQYKwcWkn7-HdtgizuF_gpyerfobAQ2YYSC8F86CUe6htXXmL3KU27U_WOIx5gOCDkQNeWTrJkZohfk3sPSw" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczNSwicHVyIjoiYmxvYl9pZCJ9fQ==--883f3fd4e1b571538035b5680c8d4a9eb504b1f6/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Source%20code-amico.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyMCwicHVyIjoiYmxvYl9pZCJ9fQ==--71cd9d863b21d7bfbd927cf623a7a2baaf4530ca/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-cuate.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxOSwicHVyIjoiYmxvYl9pZCJ9fQ==--e79a18f1e9f90eae650f1f4f04115cba51f4e650/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-amico.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T22:29:49.052Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"sXNV0HXJJPErqN4zt-XVhO4iUH-de79CLS0dbucp64Beop7nh7eJkZ3r-qu76iXzLit91ZVMQeCQzYc6tS4M7g","topics":[{"id":77268,"title":"Добрый день!\nПосмотрел решение учителя, после прохождения задания.\nВ решении есть срочка:\n### String queryString = request.getQueryString();\nДля чего она в решении, если её не используют в коде?","plain_title":"Добрый день! Посмотрел решение учителя, после прохождения задания. В решении есть срочка: String queryString = request.getQueryString(); Для чего она в решении, если её не используют в коде? ","creator":{"public_name":"Евгений Прохоров","id":408048,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":160366,"body":"Добрый день, Евгений! Да, вы правы, это видимо артефакт остался, убрал. Параметр строки запроса там при помощи другого метода получается","topic_id":77268}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}},{"id":71687,"title":"Добрый день!\n\nУ меня возникла проблема при сборки JAR.\nЯ ее решил, но мне не нравиться способ - прошу подсказать как сделать правильно.\n\nПри данной строчке в `build.gradle` (из видео):\n`from { (configurations.runtime).collect { it.isDirectory() ? it : zipTree(it) } }`\n\nвыскакивает ошибка:\n* What went wrong:\nCould not determine the dependencies of task ':jar'.\n> Could not get unknown property 'runtime' for configuration container of type org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer.\n\nПосле замены `runtime` на `implementation` вот такая ошибка:\n* What went wrong:\nCould not determine the dependencies of task ':jar'.\n> Resolving dependency configuration 'implementation' is not allowed as it is defined as 'canBeResolved=false'.\n Instead, a resolvable ('canBeResolved=true') dependency configuration that extends 'implementation' should be resolved.\n\nОК. добавил строчку: \n`configurations.implementation.setCanBeResolved(true)`\nВыдал ошибку:\n* What went wrong:\nExecution failed for task ':jar'.\n> Entry META-INF/LICENSE-notice.md is a duplicate but no duplicate handling strategy has been set.\n\nДобавил строчку `duplicatesStrategy = 'include'`\n\nВсе заработало!\n\nВопросы:\n\n1. самый первый вариант просто не работает на новой версии `gradle`?\n2. можно ли менять `canBeResolved `на `true `или лучше создать новый `configurations`?\n3. почему `gradle `говорит про `duplicate`? Где идет дублирование?\n4. как решить данную задачу корректно?\n\n\n\n\n\n","plain_title":"Добрый день! У меня возникла проблема при сборки JAR. Я ее решил, но мне не нравиться способ - прошу подсказать как сделать правильно. При данной строчке в build.gradle (из видео): from { (configurations.runtime).collect { it.isDirectory() ? it : zipTree(it) } } выскакивает ошибка: * What went wrong: Could not determine the dependencies of task ':jar'. Could not get unknown property 'runtime' for configuration container of type org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer. После замены runtime на implementation вот такая ошибка: * What went wrong: Could not determine the dependencies of task ':jar'. Resolving dependency configuration 'implementation' is not allowed as it is defined as 'canBeResolved=false'. Instead, a resolvable ('canBeResolved=true') dependency configuration that extends 'implementation' should be resolved. ОК. добавил строчку: configurations.implementation.setCanBeResolved(true) Выдал ошибку: * What went wrong: Execution failed for task ':jar'. Entry META-INF/LICENSE-notice.md is a duplicate but no duplicate handling strategy has been set. Добавил строчку duplicatesStrategy = 'include' Все заработало! Вопросы: самый первый вариант просто не работает на новой версии gradle? можно ли менять canBeResolvedна trueили лучше создать новый configurations? почему gradleговорит про duplicate? Где идет дублирование? как решить данную задачу корректно? ","creator":{"public_name":"Михаил","id":435107,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":150356,"body":"Да, jar файлы зависимостей тоже копируются в директорию","topic_id":71687},{"creator":{"public_name":"Михаил","id":435107,"is_tutor":false},"id":150138,"body":"Да. При запуске `stage ` jar-архив формируется и запускается. \nПравда у меня есть одна побочка: `copyToLib ` копирует много jar-файлов в папку с нашим jar-архивом. Это нормально? У вас так же или это я где-то косячу?\n\n","topic_id":71687},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":150174,"body":"Видимо это транзитивные зависимости - зависимости зависимостей","topic_id":71687},{"creator":{"public_name":"Михаил","id":435107,"is_tutor":false},"id":150013,"body":"Приветствую, Максим! если вы про этот код:\n\n```\ntask copyToLib(type: Copy) {\n into \"$buildDir/libs\"\n from(configurations.compile)\n}\nstage.dependsOn(copyToLib)\n```\nто он мне не помог. В указанном виде он у меня вообще на заработал. Я его немного поправил:\nзаменил `compile `на `implementation` \nзаменил `stage` на `jar` \nдобавил строчку: `configurations.implementation.setCanBeResolved(true)`, так как он ее просил. Получилось:\n```\ntask copyToLib(type: Copy) {\n into \"$buildDir/libs\"\n from(configurations.implementation)\n}\njar.dependsOn(copyToLib)\nconfigurations.implementation.setCanBeResolved(true)\n```\nВ таком виде он сработал: выцепил все зависимости (как будто даже лишнего прихватил) и сложил их `jar `в папку `libs`.\n\nА основной `jar `так и не запустился, говорить *\"библиотек не хватает\"*","topic_id":71687},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":149931,"body":"Приветствую, Михаил! А попробуйте сделать так, как описано в документации самого Хероку. Вот, как там предлагается копировать зависимости в проект: https://devcenter.heroku.com/articles/deploying-gradle-apps-on-heroku#verify-that-your-build-file-is-set-up-correctly. Напишите пожалуйста, помогло ли это","topic_id":71687},{"creator":{"public_name":"Михаил","id":435107,"is_tutor":false},"id":150110,"body":"У меня после первых моих манипуляций `jar `собрался и заработал. Deploy тоже получился.\nОсновной вопрос был как правильно подключать зависимости при создании `jar` в gradle.\n\nСейчас вы заменили `jar `на бинарник из `build`.\nТак тоже работает.\n>**Вопрос**: \nВы показали два варианта через `jar `и через `bin`. Так через что лучше запускать и почему? Зачем вообще gradle делает и то и то?","topic_id":71687},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":150129,"body":"Михаил, деплоить можно и так и так. Gradle может подготовить нам и jar архив и несжатый исполняемый файл приложения.\nДавайте попробуем разобраться с jar. Вы правы, первый вариант не работал, это связано с версией gradle. Начиная с версии 7 свойство `runtime ` не поддерживается.\nПопробуйте использовать свойство `compileClasspath` вместо него для копирования зависимостей:\n\n```\ntask stage(dependsOn: ['clean', 'installDist'])\ninstallDist.mustRunAfter clean\n\ntask copyToLib(type: Copy) {\n into \"$buildDir/libs\"\n from(configurations.compileClasspath)\n}\n\nstage.dependsOn(copyToLib)\n\n\njar {\n manifest {\n attributes(\n 'Class-Path': configurations.compileClasspath.collect { it.getName() }.join(' '),\n 'Main-Class': 'exercise.App'\n )\n }\n}\n```\n\nУ меня в таком виде при запуске задачи `stage` jar архив сформировался, зависимости скопировались в директорию libs. При запуске `java -jar build/libs/deploy.jar` приложение стартует","topic_id":71687},{"creator":{"public_name":"Михаил","id":435107,"is_tutor":false},"id":150175,"body":"Так они у вас тоже есть?\nЕсли использовать способ который я указал в самом первом сообщении - формируется только один jar-архив без побочных файлов.","topic_id":71687},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":150095,"body":"Михаил, доработал текст к этому уроку, добавил описание еще одного способа деплоя. Он несколько проще, попробуйте использовать его","topic_id":71687}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}},{"id":86377,"title":"Если кто-то будет пытаться задеплоить на Railway:\nУ меня получилось только после того, как я засунул все зависимости в jar и прописал манифест через грейдл:\n\n```\njar {\n manifest {\n attributes \"Main-Class\": \"exercise.App\"\n }\n duplicatesStrategy = DuplicatesStrategy.EXCLUDE\n from {\n configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }\n }\n}\n```","plain_title":"Если кто-то будет пытаться задеплоить на Railway: У меня получилось только после того, как я засунул все зависимости в jar и прописал манифест через грейдл: jar { manifest { attributes \"Main-Class\": \"exercise.App\" } duplicatesStrategy = DuplicatesStrategy.EXCLUDE from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } } ","creator":{"public_name":"Денис Брагин","id":497216,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":173705,"body":"Приветствую, Денис! Да, можно и таким путем пойти. Круто, что вы видите альтернативные варианты. Добавлю, что если есть какие-то сложности с railway, можно еще деплоить на render.com","topic_id":86377}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}},{"id":80782,"title":"В статье https://ru.hexlet.io/blog/posts/render-java при создании Docker файла есть пункт: COPY /app .\nНо это вызвало ошибку, получилось после исправления на: COPY . /app \n\nНу и CMD ./build/install/deploy/bin/deploy\n\nА на render Root Directory будет ./java-web-ru/deploy","plain_title":"В статье https://ru.hexlet.io/blog/posts/render-java при создании Docker файла есть пункт: COPY /app . Но это вызвало ошибку, получилось после исправления на: COPY . /app Ну и CMD ./build/install/deploy/bin/deploy А на render Root Directory будет ./java-web-ru/deploy ","creator":{"public_name":"Александр Кузьмин","id":490110,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":165537,"body":"Верно, содержимое докерфайла и Root Directory может немного варьироваться, в зависимости от того, по какому расположено приложение. В статье приводится деплой на прмере приложения, подготовленного в рамках проекта. Но тут важен сам принцип. Самое главное - в докерфайле мы должны указать путь к исполняемому файлу приложения, а на рендер указать, где расположен этот докерфайл.","topic_id":80782}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}},{"id":80300,"title":"в задании указано: \n\"Задачи\nЗарегистрируйте бесплатный аккаунт на heroku или Railway.\" - я сделал аккаунт на Railway.\n\nдалее в задании указано:\n\"В директории с домашним заданием выполните последовательно команды, как показано в руководстве по деплою\" - в этом руководстве указаны команды на хероку, как узнать команды для Railway?Инструкции на Railway основаны на использовании шаблонов от Railway, но на сайте Railway нет шаблона для java, то есть нигде не объясняется как деплоить java на Railway, быть может это и не предусмотрено в принципе создателями Railway.\n\nдалее в задании указано:\n\"При сдаче домашнего задания укажите ссылку на задеплоенное приложение.\" - для сдачи домашнего задания надо выполнить команду hexlet assignment submit, где подразумевается указание ссылки на задеплоенное приложение?\n\n","plain_title":"в задании указано: \"Задачи Зарегистрируйте бесплатный аккаунт на heroku или Railway.\" - я сделал аккаунт на Railway. далее в задании указано: \"В директории с домашним заданием выполните последовательно команды, как показано в руководстве по деплою\" - в этом руководстве указаны команды на хероку, как узнать команды для Railway?Инструкции на Railway основаны на использовании шаблонов от Railway, но на сайте Railway нет шаблона для java, то есть нигде не объясняется как деплоить java на Railway, быть может это и не предусмотрено в принципе создателями Railway. далее в задании указано: \"При сдаче домашнего задания укажите ссылку на задеплоенное приложение.\" - для сдачи домашнего задания надо выполнить команду hexlet assignment submit, где подразумевается указание ссылки на задеплоенное приложение? ","creator":{"public_name":"Raf Atr","id":522705,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":165027,"body":"Очень круто, что вы разобрались самостоятельно! Это отличная прокачка! Я немного дополню описание задачи, спасибо","topic_id":80300},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":165046,"body":"Дополнил описание задачи. Чтобы получить новую версию, нужно будет перекачать домашку","topic_id":80300},{"creator":{"public_name":"Raf Atr","id":522705,"is_tutor":false},"id":164943,"body":"Содержимое докерфайла:\n\nFROM gradle:7.4.0-jdk17\n\nWORKDIR /deploy\n\nCOPY /deploy .\n\nRUN gradle installDist\n\nCMD ./build/install/deploy/bin/deploy","topic_id":80300},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":164888,"body":"Там можно указать корневую директорию проекта - Root Directory. Это путь к директории, где находится докерфайл","topic_id":80300},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":164823,"body":"Добрый день! Если с railway есть какие-то сложности, можно использовать альтернативу - сервис render. У нас есть [статья](https://ru.hexlet.io/blog/posts/render-java) о том, как задеплоить приложение на java. Добавил ссылку на эту статью в задание к домашке. Ссылку указывать не нужно, убрал это требование, спасибо. Эта задача осталась с тех времен, когда домашки принимались вручную наставниками","topic_id":80300},{"creator":{"public_name":"Raf Atr","id":522705,"is_tutor":false},"id":164922,"body":"не работает, скорее всего какие то еще нужны настройки, но как узнать какие непонятно. По заданию надо в build.gradle добавить код:\n\ntask stage(dependsOn: ['clean', 'installDist'])\ninstallDist.mustRunAfter clean\n\nтакой код добавлен, также по условию еще добавлен Procfile с командой\n\n web: sh build/install/deploy/bin/deploy\n\nможет что то здесь требуется поменять с учетом, что теперь есть докерфайл и в нем тоже что то написано?\nВот что пишет render:\nDec 7 07:52:53 AM ==> Cloning from https://github.com/TheAtrAtr/hexlet-assignments...\nDec 7 07:52:54 AM ==> Checking out commit f95a7dc4304a9a85088c34e0499a2e195aab511a in branch main\nDec 7 07:53:01 AM #1 [internal] load .dockerignore\nDec 7 07:53:01 AM #1 transferring context: 2B done\nDec 7 07:53:01 AM #1 DONE 0.0s\nDec 7 07:53:01 AM \nDec 7 07:53:01 AM #2 [internal] load build definition from Dockerfile\nDec 7 07:53:01 AM #2 transferring dockerfile: 168B done\nDec 7 07:53:01 AM #2 DONE 0.0s\nDec 7 07:53:01 AM \nDec 7 07:53:01 AM #3 [internal] load metadata for docker.io/library/gradle:7.4.0-jdk17\nDec 7 07:53:01 AM #3 ...\nDec 7 07:53:01 AM \nDec 7 07:53:01 AM #4 [auth] library/gradle:pull token for registry-1.docker.io\nDec 7 07:53:01 AM #4 DONE 0.0s\nDec 7 07:53:01 AM \nDec 7 07:53:01 AM #3 [internal] load metadata for docker.io/library/gradle:7.4.0-jdk17\nDec 7 07:53:02 AM #3 DONE 1.0s\nDec 7 07:53:02 AM \nDec 7 07:53:02 AM #5 [internal] load build context\nDec 7 07:53:02 AM #5 transferring context: 2B done\nDec 7 07:53:02 AM #5 DONE 0.0s\nDec 7 07:53:02 AM \nDec 7 07:53:02 AM #6 [2/4] WORKDIR /deploy\nDec 7 07:53:02 AM #6 CACHED\nDec 7 07:53:02 AM \nDec 7 07:53:02 AM #7 [3/4] COPY /deploy .\nDec 7 07:53:02 AM #7 ERROR: failed to calculate checksum of ref yw627k4v918g1vkdavfrg28j6::a3fuc1nn5o2jh0l6run2jplv5: \"/deploy\": not found\nDec 7 07:53:02 AM \nDec 7 07:53:02 AM #8 [1/4] FROM docker.io/library/gradle:7.4.0-jdk17@sha256:5248d0e8f7f6ad2095c3a053d5461daa17b02097410f0e9f6397f8f4dedc34bf\nDec 7 07:53:02 AM #8 resolve docker.io/library/gradle:7.4.0-jdk17@sha256:5248d0e8f7f6ad2095c3a053d5461daa17b02097410f0e9f6397f8f4dedc34bf done\nDec 7 07:53:02 AM #8 DONE 0.0s\nDec 7 07:53:02 AM ------\nDec 7 07:53:02 AM > [3/4] COPY /deploy .:\nDec 7 07:53:02 AM ------\nDec 7 07:53:02 AM Dockerfile:5\nDec 7 07:53:02 AM --------------------\nDec 7 07:53:02 AM 3 | WORKDIR /deploy\nDec 7 07:53:02 AM 4 |\nDec 7 07:53:02 AM 5 | >>> COPY /deploy .\nDec 7 07:53:02 AM 6 |\nDec 7 07:53:02 AM 7 | RUN gradle installDist\nDec 7 07:53:02 AM --------------------\nDec 7 07:53:02 AM error: failed to solve: failed to compute cache key: failed to calculate checksum of ref yw627k4v918g1vkdavfrg28j6::a3fuc1nn5o2jh0l6run2jplv5: \"/deploy\": not found\n","topic_id":80300},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":164927,"body":"При деплое на рендер вам нужен только докерфайл. Скорее всего какие-то проблемы с путями в докерфайле, проверьте их. Если не получится разобраться, скиньте содержимое вашего докерфайла","topic_id":80300},{"creator":{"public_name":"Raf Atr","id":522705,"is_tutor":false},"id":165007,"body":"Спасибо, разобрался, действительно проблема была с путями в докерфайле. Хотя конечно проблема в лекции, где показан хероку, а делать надо с докером на рендере - что еще вообще не изучалось.","topic_id":80300},{"creator":{"public_name":"Raf Atr","id":522705,"is_tutor":false},"id":164867,"body":"Домашние задания находятся в репозитории hexlet-assignments, в этом репозитории много подпапок, а не только эта папка deploy, как указать сервису render что нужно деплоить не из всего hexlet-assignments а только из данной конкретной задачи?","topic_id":80300}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}},{"id":89438,"title":"Зачем для этого задания нужен метод getQueryString() возвращающий строку, если можно обойтись просто getParameter() ?","plain_title":"Зачем для этого задания нужен метод getQueryString() возвращающий строку, если можно обойтись просто getParameter() ? ","creator":{"public_name":"Андрей Борисов","id":542369,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":178090,"body":"Приветствую, Андрей! Задачи можно решать разными способами. Кто то может захотеть пойти другим путем и использовать `getQueryString()`. Я немного доработал подсказку, чтобы подчеркнуть, что не обязательно сразу два этих метода использовать","topic_id":89438}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}},{"id":96835,"title":"Где почитать нормальный гайд как задеплоить проект на render? В этой статье «Что делать, если возникли трудности с деплоем Java-приложения на Railway. Разбираем сервис Render» - непонятно примерно ничего (","plain_title":"Где почитать нормальный гайд как задеплоить проект на render? В этой статье «Что делать, если возникли трудности с деплоем Java-приложения на Railway. Разбираем сервис Render» - непонятно примерно ничего ( ","creator":{"public_name":"Александр Музалёв","id":668688,"is_tutor":false},"comments":[{"creator":{"public_name":"Elena Gromova","id":548102,"is_tutor":true},"id":187949,"body":"**Александр Музалёв**, завела тикет, поправим статью","topic_id":96835},{"creator":{"public_name":"Александр Музалёв","id":668688,"is_tutor":false},"id":187751,"body":"В самом уроке тоже никаких объяснений как настроить бд и сервис","topic_id":96835},{"creator":{"public_name":"Александр Музалёв","id":668688,"is_tutor":false},"id":187781,"body":"**Elena Gromova**, я как раз про рендер и спрашиваю. Из статьи не ясно как настроить бд, как настроить переменные окружения(есть скриншоты, не соответствующие действительности) , с докер файлом тоже непонятно что и зачем в нём написано и как адаптировать под свой проект. ","topic_id":96835},{"creator":{"public_name":"Elena Gromova","id":548102,"is_tutor":true},"id":187756,"body":"**Александр Музалёв**, добрый день! Данная статья как раз про деплой на рендер. Раньше везде предлагалось деплоить в первую очередь на Railway, а если не получилось, то на Render - отсюда и название статьи. Сейчас деплой на Railway не доступен и информацию про него убрали из всех курсов.","topic_id":96835}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}},{"id":98062,"title":"Здравствуйте! у меня выходит такая ошибка при деплое на рендер:\n\nException in thread \"main\" java.nio.file.NoSuchFileException: file:/HexletJavalin/build/install/HexletJavalin/lib/HexletJavalin-1.0-SNAPSHOT.jar!/schema.sql\n\nв build.gradle.kts подключила shadow:\nplugins {\n id(\"com.github.johnrengelman.shadow\") version \"8.1.1\"\n}\n\nчто не так не могу понять, в Докерфайл:\n\nWORKDIR /HexletJavalin\n\nCOPY ./ .\n\nRUN gradle installDist\n\nCMD ./build/install/HexletJavalin/bin/HexletJavalin","plain_title":"Здравствуйте! у меня выходит такая ошибка при деплое на рендер: Exception in thread \"main\" java.nio.file.NoSuchFileException: file:/HexletJavalin/build/install/HexletJavalin/lib/HexletJavalin-1.0-SNAPSHOT.jar!/schema.sql в build.gradle.kts подключила shadow: plugins { id(\"com.github.johnrengelman.shadow\") version \"8.1.1\" } что не так не могу понять, в Докерфайл: WORKDIR /HexletJavalin COPY ./ . RUN gradle installDist CMD ./build/install/HexletJavalin/bin/HexletJavalin ","creator":{"public_name":"Сэржэна Норбоева","id":571392,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":189173,"body":"Да, все верно. У нас в эталонном проекте так и сделано. В предыдущем уроке тоже пример поправил, чтоб при деплое работало","topic_id":98062},{"creator":{"public_name":"Сэржэна Норбоева","id":571392,"is_tutor":false},"id":189172,"body":"Мне в чате подсказали прописать путь по-другому.\nВместо этого:\n\n var url = HelloWorld.class.getClassLoader().getResource(\"schema.sql\");\n var file = new File(url.getFile());\n Collectors Collectors = null;\n var sql = Files.lines(file.toPath()).collect(Collectors.joining(\"\\n\"));\n\nзадать путь так, и тогда приставки file в пути не будет:\n\n var url = HelloWorld.class.getClassLoader().getResourceAsStream(\"schema.sql\");\n var sql = new BufferedReader(new InputStreamReader(url))\n .lines().collect(Collectors.joining(\"\\n\"));\n\nВдруг кому полезно будет","topic_id":98062}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}},{"id":103701,"title":"вот к чему такие тесты, как последний вопрос в теме Деплой? в теории описано лишь, что многие из них бесплатно предоставляются(логично же, что какие то и платно, и соответственно вариант низкая стоимость логично отметить), но ничего не сказано ни про скорость, ни про надежность, ни про легкость масштабирования, а в итоге оказывается, что именно все эти 3 галочки требуется поставить. тест решается не логикой, а лишь перебором всех доступных вариантов","plain_title":"вот к чему такие тесты, как последний вопрос в теме Деплой? в теории описано лишь, что многие из них бесплатно предоставляются(логично же, что какие то и платно, и соответственно вариант низкая стоимость логично отметить), но ничего не сказано ни про скорость, ни про надежность, ни про легкость масштабирования, а в итоге оказывается, что именно все эти 3 галочки требуется поставить. тест решается не логикой, а лишь перебором всех доступных вариантов ","creator":{"public_name":"Артур Неустроев","id":787585,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":196180,"body":"Приветствую, Артур! Да, вы правы, мы не обсуждаем эту тему в уроке. Заменил этот вопрос. В целом paas считаются самыми автоматизированными сервисами для деплоя, но при этом и самыми дорогими. За удобство нужно платить)","topic_id":103701}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}},{"id":107320,"title":"Здравствуйте! Хотелось бы сделать несколько замечаний к уроку. \n1 Плагин shadow не обязателен (по крайней мере Render.com), сервер увидел все необходимые файлы без него .\n2 Для того, чтобы Render.com смог работать с базой , пришлось немного переписать синтаксис создания таблиц ( в части определения поля id...) в schema.sql под требования postgress. Возможно так и задумано, но лучше бы от этом предупреждать.. \n ","plain_title":"Здравствуйте! Хотелось бы сделать несколько замечаний к уроку. 1 Плагин shadow не обязателен (по крайней мере Render.com), сервер увидел все необходимые файлы без него . 2 Для того, чтобы Render.com смог работать с базой , пришлось немного переписать синтаксис создания таблиц ( в части определения поля id...) в schema.sql под требования postgress. Возможно так и задумано, но лучше бы от этом предупреждать.. ","creator":{"public_name":"Александр Макевнин","id":785907,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":201001,"body":"Приветствую, Александр! Поправил немного запросы в предыдущем уроке, чтобы они были универсальными, подошли и для H2 и для PostgreSQL\n\nЧто касается плагина shadow, то тут все зависит от того, как проект запускать. Если использовать плагин application, то он сам все разрулит и даст нам уже полностью готовый исполняемый файл. А вот если jar запускать, то нужен shadow, чтобы он нам все ресурсы и зависимости тоже в этот jar упаковал. Без него внутри jar окажется только само приложение, без зависимостей и русурсов","topic_id":107320}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Деплой","entity_url":null,"active":true}}],"lesson":{"exercise":null,"units":[{"id":4790,"name":"theory","url":"/courses/java-web/lessons/deploy/theory_unit"},{"id":9519,"name":"quiz","url":"/courses/java-web/lessons/deploy/quiz_unit"}],"links":[{"id":422542,"name":"Что такое хостинг и домен сайта простыми словами","url":"https://ru.hexlet.io/blog/posts/hosting\n"}],"ordered_units":[{"id":4790,"name":"theory","url":"/courses/java-web/lessons/deploy/theory_unit"},{"id":9519,"name":"quiz","url":"/courses/java-web/lessons/deploy/quiz_unit"}],"id":2151,"slug":"deploy","state":"approved","name":"Деплой","course_order":350,"goal":"Знакомимся с ключевыми понятиями и процессом деплоя","self_study":"1. Зарегистрируйтесь на сервисе *render.com*\n2. Задеплойте на Render приложение, которое вы писали в течение этого курса\n","theory_video_provider":null,"theory_video_uid":null,"theory":"После того как сайт написан, его нужно выложить в интернет. Стандартный путь включает три пункта:\n\n1. Покупка домена\n2. Покупка хостинга и его настройка\n3. Деплой\n\nВ этом уроке мы рассмотрим эти пункты более подробно.\n\n## Покупка домена\n\nЧтобы приобрести домен, необходимо обратиться в компании-регистраторы. Домен оплачивается раз в год и закрепляется за определенным человеком или компанией.\n\nИногда хостер и регистратор — одна и та же компания. Тогда домен и сервер связываются одновременно. Иначе нужно производить определенные настройки на стороне сервера и подтверждать свое право владения доменом.\n\n## Покупка хостинга и его настройка\n\nХостинг — это ресурсы для размещения и обслуживания сайта в интернете. Хостинг бывает разным. Это может быть:\n\n* Уже полностью готовая среда, в которую заливается код сайта\n* Просто железные серверы, которыми можно и нужно управлять самостоятельно\n\nОбычно работа с хостингом выглядит так:\n\n1. Регистрируемся на сайте хостинга\n2. Выбираем тариф и вид хостинга (On-Prem, IaaS, PaaS, SaaS), привязываем банковскую карту\n3. Получаем доступ к ресурсам — к IP-адресу и паре «логин/пароль» для доступа по SSH и FTP\n4. При необходимости настраиваем хостинг и деплоим проект\n\n## Деплой\n\n**Деплой** — это выкладка новой версии сайта на сервер. Этот процесс может быть довольно сложным и зависит от используемых технологий. Во время деплоя могут выполняться следующие задачи:\n\n1. Скачиваем код проекта на сервер (обычно через клонирование Git)\n2. Ставим все необходимые зависимости\n3. Выполняем сборку приложения\n4. Выполняем **миграции** — SQL-скрипты, которые изменяют структуру базы данных\n5. Запускаем новую версию кода\n\nОбсудим самый простой способ начать деплоить. Большинство PaaS-хостеров имеют бесплатные планы, достаточные для выкладки учебных проектов. Их достоинство в том, что вам не придется покупать адрес — домен третьего уровня предоставляется бесплатно.\n\nДля деплоя учебных проектов мы предлагаем использовать [Render](https://render.com/). Этот сервис поддерживает деплой GitHub-репозиториев\n\n## Деплой на Render.com\n\nЧтобы наше приложение работало на Render, его необходимо упаковать в Docker-контейнер. Более подробную информацию о Docker можно найти в [отдельном курсе](https://ru.hexlet.io/courses/docker-basics). Здесь же достаточно просто следовать приведённым шагам.\n\nКонтейнер можно представить как коробку, в которой есть всё необходимое для работы приложения: сам код, программы для его запуска и даже настройки. Это позволяет приложению работать одинаково на любом устройстве или сервере, независимо от его окружения\n\nДля создания такого контейнера используется специальный файл — Dockerfile. Это текстовый файл, в котором мы пишем инструкции: что установить, как настроить и как запустить приложение. Render использует Dockerfile для создания образа контейнера — это как финальная сборка коробки с приложением. После этого Render разворачивает этот образ на своих серверах, и ваше приложение начинает работать. Всё, что вам нужно сделать, — это предоставить Dockerfile и код приложения, а Render позаботится обо всём остальном\n\n### Создаем Dockerfile\n\nСоздайте в корне своего приложения файл Dockerfile. Добавьте в него следующий код:\n\n```dockerfile\nFROM gradle:8.12.1-jdk21\n\nWORKDIR /app\n\nCOPY /app .\n\nRUN [\"./gradlew\", \"clean\", \"build\"]\n\nCMD [\"./gradlew\", \"run\"]\n```\n\nРассмотрим подробнее, что здесь происходит. За основу мы берем базовый образ, в котором уже установлены Gradle и JDK 21. Затем устанавливаем рабочую директорию */app* внутри контейнера, теперь все последующие команды будут выполняться внутри этой директории. Далее копируем в нее из директории */app* файлы нашего приложения. После этого выполняем сборку приложения с помощью команды `./gradlew clean build`. При запуске контейнера приложение автоматически запускается через `./gradlew run`. Таким образом, при запуске контейнер будет знать, как запустить приложение и содержать всё необходимое для его работы\n\nВ зависимости от приложения, путь к файлам приложения и команда запуска у вас могут отличаться. Путь к файлам приложения указывается относительно Dockerfile\n\n### Работаем с Render\n\nЗарегистрируйтесь в сервисе [Render](https://render.com/), удобнее всего сделать это через GitHub. После регистрации на вкладке *Dashboard* вы сможете добавлять новые приложения — веб-сервисы и базы данных\n\nНаше приложение использует базу данных, поэтому для начала нужно создать эту базу. Конечно, во время разработки мы могли использовать встроенную базу данных в памяти, чтобы быстро тестировать приложение. Но для рабочего приложения такое решение не подходит, так как при перезапуске приложения все данные исчезнут. Поэтому здесь мы будем использовать настоящую базу данных PostgreSQL, чтобы данные сохранялись постоянно, даже после перезапуска приложения\n\nУдобство PaaS-сервисов заключается в том, что они берут на себя всю сложность настройки и управления инфраструктурой. Вам не нужно вручную устанавливать PostgreSQL или настраивать сервер — платформа сделает это за вас. Вы просто создаете базу данных в несколько кликов, а Render автоматически сделает все необходимое для её работы\n\nЧтобы создать базу данных, нажмите на кнопку *Add new* и выберите *Postgres*\n\n\n\nДайте нашему сервису название (*Name*), оно будет отображаться в дашборде. Можно также указать имя базы данных и пользователя. Но это необязательно, если их не указать, Render сам сформирует случайные имена\n\n\n\nРегион можно оставить по умолчанию, но обязательно запомните, какой вы выбрали, это пригодится нам дальше при выборе региона для самого приложения\n\nНажмите кнопку Create database в самом низу страницы, чтобы создать базу данных\n\nВыбирайте везде бесплатный тариф, его вполне достаточно для учебных целей и так не придется вводить номер кредитной карты\n\nПосле создания базы данных, Render покажет вам данные для подключения к ней (раздел *Connections*)\n\n\n\nВам нужно будет скопировать несколько важных значений: *Hostname* (адрес сервера), *Port* (порт), *Database* (имя базы), *Username* (имя пользователя) и *Password* (пароль). Эти данные понадобятся позже, когда вы будете настраивать переменные окружения для вашего приложения.\n\nТеперь, когда у нас есть база данных, мы можем перейти к созданию непосредственно приложения. Вернитесь на дашборд, снова нажмите *Add new* и выберите *Web Service*\n\nПодключите свой GitHub-репозиторий с проектом, который хотите задеплоить, или вставьте его URL, после чего нажмите *Connect*\n\n\n\nЗаполните поля с настройками приложения. В поле Name задайте имя для своего приложения. В *Language* оставляем *Docker* в качестве среды выполнения проекта. Поле Root *Directory* оставьте пустым, так как *Dockerfile* находится в корне проекта\n\n\n\nОбратите внимание на регион приложения (Region). **Веб-сервис и база данных должны находиться в одном регионе, иначе приложение не сможет подключиться к базе**\n\nСейчас наше приложение пока еще не знает, как подключиться к базе данных. Чтобы оно могло работать с базой, ему нужно передать информацию о подключении: адрес сервера, порт, имя базы, логин и пароль. Это делается с помощью переменных окружения — специальных настроек, которые передаются приложению при запуске. Переменные окружения — это способ передать приложению информацию о том, как подключиться к базе данных\n\nЧтобы указать переменные окружения, пролистайте настройки приложения вниз, до раздела *Environment variable*. Нажмите на кнопку *Add Environment Variable*, добавьте нужные для подключения к базе данных переменные окружения и задайте им значения. Здесь нам пригодятся те сведения, которые мы скопировали в базе данных — *Hostname, Port, Database, Username, Password*\n\n\n\nПеременная окружения *JDBC_DATABASE_URL* содержит всю информацию, необходимую для подключения к базе данных: адрес сервера, порт, имя базы, пользователь и пароль. Именно через эту переменную приложение узнает, как подключиться к базе\n\nФормат записи URL у базы данных должен быть таким:\n\n```bash\njdbc:postgresql://<Hostname>:<Port>/<Database>?password=<Password>&user=<Username>\n```\n\nНапример:\n\n```bash\njdbc:postgresql://dpg-cuvg6tpu0jms739k7n9g-a:5432/tirion?password=qwerty&user=tirion\n```\n\nЧтобы всё работало правильно, ваше приложение должно быть настроено так, чтобы оно могло читать и использовать эту переменную окружения. Это стандартный способ передачи данных о подключении к базе данных в приложениях\n\n```java\nprivate static String getDatabaseUrl() {\n // Получаем url базы данных из переменной окружения DATABASE_URL\n // Если она не установлена, используем базу в памяти\n return System.getenv().getOrDefault(\"DATABASE_URL\", \"jdbc:h2:mem:project\");\n}\n```\n\nПосле завершения настройки нужно будет нажать на кнопку Deploy Web Service. Ваше приложение будет клонировано из репозитория, собрано и запущено. После завершения процесса приложение станет доступно по определенному адресу, который сформируется автоматически\n\n\n\nПерейдите по этому адресу и проверьте, что приложение работает корректно.\n\nЕсли что-то пошло не так, всегда можно посмотреть логи приложения на вкладке Logs. Они помогут вам разобраться с ошибкой\n\n\n\n## Особенности сборки Gradle\n\nПодключив базу данных к нашему приложению, мы добавили в него файл *src/main/resources/schema.sql*, который затем читается из кода. Когда мы попробуем собрать приложение и запустить на сервере, этот код не сработает, потому что Gradle не копирует файлы из директории *src/main/resources* в получившийся *jar*.\n\nЭто можно исправить с помощью плагина *shadow*, который автоматически добавляет все ресурсы в *jar*:\n\n```java\nplugins {\n id(\"com.github.johnrengelman.shadow\") version \"8.1.1\"\n // Остальные плагины\n}\n```\n\n### Выводы\n\n* Процесс публикации сайта включает три ключевых этапа: покупка домена, выбор и настройка хостинга, а также деплой приложения на сервер.\n* Выбор хостинга зависит от потребностей проекта: можно использовать готовые решения (PaaS) или управлять собственными серверами (IaaS).\n* Деплой на PaaS упрощает процесс развертывания, предоставляя бесплатные планы и автоматизацию, что особенно полезно для учебных проектов.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":7160,"name":"theory","url":"/courses/java-web/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":7160,"name":"theory","url":"/courses/java-web/lessons/intro/theory_unit"}],"id":3189,"slug":"intro","state":"approved","name":"Введение","course_order":100,"goal":"Знакомимся с целями и задачами курса","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Когда разработчики переходят от написания скриптов к созданию полноценных сайтов, они сталкиваются с новыми понятиями и инструментами. Эти знания выходят далеко за рамки языка, поэтому на этом этапе нужно углублять понимание операционных систем и сетей, осваивать работу с регистраторами и хостингом, отрабатывать навыки деплоя сайта.\n\nНа собеседованиях веб-программисты часто слышат такой вопрос:\n\n> _«Что происходит, когда пользователь набирает сайт google.com в адресной строке браузера и нажимает Enter?»_\n\nОчень подробный ответ на этот вопрос доступен [здесь](https://github.com/alex/what-happens-when). Через этот вопрос интервьюер хочет проверить, насколько хорошо вы разбираетесь в ключевых темах веб-разработки:\n\n1. Как DNS-запрос получает IP-адреса домена\n2. Как работает соединение с веб-сервером на порту 443 или 80 по TCP\n3. Как HTTP-запрос получает содержимого сайта по указанному домену\n4. Как происходит получение ответа и рендеринг содержимого во вкладке браузера\n\nКаждый из этих пунктов неявно подразумевает, что вы знакомы со следующими темами:\n\n* Протоколы HTTP и HTTPS\n* Виртуальные хосты\n* Принципы работы DNS\n* Порты, маски и подсети в TCP/IP\n* Модель OSI\n* Сетевые сокеты\n* Принципы работы веб-серверов\n\nИзучить эти темы можно с помощью наших [гайдов](https://guides.hexlet.io) или книг по операционным системам из нашего [списка рекомендаций](https://ru.hexlet.io/pages/recommended-books). Если говорить о самой разработке, то здесь открывается целый пласт неизведанного:\n\n* Фреймворки\n* Микрофреймворки\n* Роутинг\n* Куки и сессии\n* Безопасность\n* Шаблонизация\n* Взаимодействие с базой данных и многое другое\n\nА еще сайт находится на удаленном сервере, поэтому в разработку включается **деплой** — разворачивание сайта на хостинге. Чтобы развернуть сайт, нужно настроить удаленную машину с помощью Ansible или других похожих инструментов.\n\nКстати, сам хостинг бывает очень разный:\n\n* IaaS (AWS)\n* PaaS (Render)\n* Shared Hosting (виртуальный хостинг)\n* VPS/VDS\n\nВ этом курсе мы не успеем подробно раскрыть все эти важные темы, потому что основное внимание будет уделять веб-разработке с помощью микрофреймворков. Поэтому мы советуем выполнять все домашние задания и параллельно выкатывать код на [Render](https://render.com/) или другой подобный сервис. Так материал усвоится лучше.\n"},"id":241,"slug":"java-web","challenges_count":5,"name":"Java: Веб-технологии","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы изучите веб-технологии в языке Java. Вы узнаете больше об особенностях клиент-серверной модели, принципах роутинга HTTP-запросов и формирования ответов с помощью шаблонизаторов. В итоге вы научитесь создавать собственные веб-приложения, подключать базу данных для хранения содержимого, разделять данные приложения и управляющую логику, используя подход MVC. Вы сможете отслеживать ошибки и процессы, происходящие в рабочем приложении. Знания из этого курса помогут вам создать свой сайт и сделать его доступным в интернете.","kind":"basic","updated_at":"2026-01-20T11:37:44.200Z","language":"java","duration_cache":86340,"skills":["Выполнять HTTP-запросы","Создавать свой собственный сайт и выкладывать его в публичный доступ","Подключать базы данных","Правильно отслеживать ошибки"],"keywords":[],"lessons_count":23,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTc1OSwicHVyIjoiYmxvYl9pZCJ9fQ==--5d65b504f97342a1a3a4df6f6bb44f66fa3601b7/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJwbmciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--6067466c2912ca31a17eddee04b8cf2a38c6ad17/image.png"},"recommendedLandings":[{"stack":{"id":3,"slug":"java","title":"Java-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":30,"duration_in_months":10},"id":3,"slug":"java","title":"Java-разработчик","subtitle":"Изучите Java и фреймворк Spring Boot и REST API","subtitle_for_lists":"Изучите Java и фреймворк Spring Boot и REST API","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"java","price_text":"от 6 792 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczNSwicHVyIjoiYmxvYl9pZCJ9fQ==--883f3fd4e1b571538035b5680c8d4a9eb504b1f6/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Source%20code-amico.png"},{"stack":{"id":220,"slug":"qa-auto-engineer-python","title":"Автоматизатор тестирования на Python","audience":"for_programmers","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":100,"duration_in_months":6},"id":331,"slug":"qa-auto-engineer-python","title":"Автоматизатор тестирования на Python","subtitle":"Изучите Python, фреймворки для тестирования, автоматизация UI и API","subtitle_for_lists":"Изучите Python, фреймворки для тестирования, автоматизация UI и API","locale":"ru","current":true,"duration_in_months_text":"6 месяцев","stack_slug":"qa-auto-engineer-python","price_text":"от 4 281 ₽","duration_text":"6 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyMCwicHVyIjoiYmxvYl9pZCJ9fQ==--71cd9d863b21d7bfbd927cf623a7a2baaf4530ca/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-cuate.png"},{"stack":{"id":467,"slug":"middle-java","title":"Middle-java разработчик","audience":"for_programmers","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"not_finished","order":2002,"duration_in_months":6},"id":595,"slug":"middle-java","title":"Middle-java разработчик","subtitle":"Углубите знания Java, Spring и архитектуры серверных приложений","subtitle_for_lists":"Углубите знания Java, Spring и архитектуры серверных приложений","locale":"ru","current":true,"duration_in_months_text":"6 месяцев","stack_slug":"middle-java","price_text":"от 4 050 ₽","duration_text":"6 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxOSwicHVyIjoiYmxvYl9pZCJ9fQ==--e79a18f1e9f90eae650f1f4f04115cba51f4e650/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-amico.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/java-web/lessons/deploy/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Java: Веб-технологии</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Деплой</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Деплой","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"Java: Веб-технологии"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>После того как сайт написан, его нужно выложить в интернет. Стандартный путь включает три пункта:</p>
<ol>
<li>Покупка домена</li>
<li>Покупка хостинга и его настройка</li>
<li>Деплой</li>
</ol>
<p>В этом уроке мы рассмотрим эти пункты более подробно.</p>
<h2 id="heading-2-1">Покупка домена</h2>
<p>Чтобы приобрести домен, необходимо обратиться в компании-регистраторы. Домен оплачивается раз в год и закрепляется за определенным человеком или компанией.</p>
<p>Иногда хостер и регистратор — одна и та же компания. Тогда домен и сервер связываются одновременно. Иначе нужно производить определенные настройки на стороне сервера и подтверждать свое право владения доменом.</p>
<h2 id="heading-2-2">Покупка хостинга и его настройка</h2>
<p>Хостинг — это ресурсы для размещения и обслуживания сайта в интернете. Хостинг бывает разным. Это может быть:</p>
<ul>
<li>Уже полностью готовая среда, в которую заливается код сайта</li>
<li>Просто железные серверы, которыми можно и нужно управлять самостоятельно</li>
</ul>
<p>Обычно работа с хостингом выглядит так:</p>
<ol>
<li>Регистрируемся на сайте хостинга</li>
<li>Выбираем тариф и вид хостинга (On-Prem, IaaS, PaaS, SaaS), привязываем банковскую карту</li>
<li>Получаем доступ к ресурсам — к IP-адресу и паре «логин/пароль» для доступа по SSH и FTP</li>
<li>При необходимости настраиваем хостинг и деплоим проект</li>
</ol>
<h2 id="heading-2-3">Деплой</h2>
<p><strong>Деплой</strong> — это выкладка новой версии сайта на сервер. Этот процесс может быть довольно сложным и зависит от используемых технологий. Во время деплоя могут выполняться следующие задачи:</p>
<ol>
<li>Скачиваем код проекта на сервер (обычно через клонирование Git)</li>
<li>Ставим все необходимые зависимости</li>
<li>Выполняем сборку приложения</li>
<li>Выполняем <strong>миграции</strong> — SQL-скрипты, которые изменяют структуру базы данных</li>
<li>Запускаем новую версию кода</li>
</ol>
<p>Обсудим самый простой способ начать деплоить. Большинство PaaS-хостеров имеют бесплатные планы, достаточные для выкладки учебных проектов. Их достоинство в том, что вам не придется покупать адрес — домен третьего уровня предоставляется бесплатно.</p>
<p>Для деплоя учебных проектов мы предлагаем использовать <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://render.com/" rel="noopener noreferrer" target="_blank">Render</a>. Этот сервис поддерживает деплой GitHub-репозиториев</p>
<h2 id="heading-2-4">Деплой на Render.com</h2>
<p>Чтобы наше приложение работало на Render, его необходимо упаковать в Docker-контейнер. Более подробную информацию о Docker можно найти в <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/courses/docker-basics" rel="noopener noreferrer" target="_blank">отдельном курсе</a>. Здесь же достаточно просто следовать приведённым шагам.</p>
<p>Контейнер можно представить как коробку, в которой есть всё необходимое для работы приложения: сам код, программы для его запуска и даже настройки. Это позволяет приложению работать одинаково на любом устройстве или сервере, независимо от его окружения</p>
<p>Для создания такого контейнера используется специальный файл — Dockerfile. Это текстовый файл, в котором мы пишем инструкции: что установить, как настроить и как запустить приложение. Render использует Dockerfile для создания образа контейнера — это как финальная сборка коробки с приложением. После этого Render разворачивает этот образ на своих серверах, и ваше приложение начинает работать. Всё, что вам нужно сделать, — это предоставить Dockerfile и код приложения, а Render позаботится обо всём остальном</p>
<h3 id="heading-3-5">Создаем Dockerfile</h3>
<p>Создайте в корне своего приложения файл Dockerfile. Добавьте в него следующий код:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">FROM gradle:8.12.1-jdk21
WORKDIR /app
COPY /app .
RUN ["./gradlew", "clean", "build"]
CMD ["./gradlew", "run"]</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Рассмотрим подробнее, что здесь происходит. За основу мы берем базовый образ, в котором уже установлены Gradle и JDK 21. Затем устанавливаем рабочую директорию <em>/app</em> внутри контейнера, теперь все последующие команды будут выполняться внутри этой директории. Далее копируем в нее из директории <em>/app</em> файлы нашего приложения. После этого выполняем сборку приложения с помощью команды <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">./gradlew clean build</code>. При запуске контейнера приложение автоматически запускается через <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">./gradlew run</code>. Таким образом, при запуске контейнер будет знать, как запустить приложение и содержать всё необходимое для его работы</p>
<p>В зависимости от приложения, путь к файлам приложения и команда запуска у вас могут отличаться. Путь к файлам приложения указывается относительно Dockerfile</p>
<h3 id="heading-3-6">Работаем с Render</h3>
<p>Зарегистрируйтесь в сервисе <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://render.com/" rel="noopener noreferrer" target="_blank">Render</a>, удобнее всего сделать это через GitHub. После регистрации на вкладке <em>Dashboard</em> вы сможете добавлять новые приложения — веб-сервисы и базы данных</p>
<p>Наше приложение использует базу данных, поэтому для начала нужно создать эту базу. Конечно, во время разработки мы могли использовать встроенную базу данных в памяти, чтобы быстро тестировать приложение. Но для рабочего приложения такое решение не подходит, так как при перезапуске приложения все данные исчезнут. Поэтому здесь мы будем использовать настоящую базу данных PostgreSQL, чтобы данные сохранялись постоянно, даже после перезапуска приложения</p>
<p>Удобство PaaS-сервисов заключается в том, что они берут на себя всю сложность настройки и управления инфраструктурой. Вам не нужно вручную устанавливать PostgreSQL или настраивать сервер — платформа сделает это за вас. Вы просто создаете базу данных в несколько кликов, а Render автоматически сделает все необходимое для её работы</p>
<p>Чтобы создать базу данных, нажмите на кнопку <em>Add new</em> и выберите <em>Postgres</em></p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTc3OCwicHVyIjoiYmxvYl9pZCJ9fQ==--2d1808e17ae2287271eb9da6c4357e7c5c5ee3bb/new_db_service.png" alt="New database" loading="lazy"/></p>
<p>Дайте нашему сервису название (<em>Name</em>), оно будет отображаться в дашборде. Можно также указать имя базы данных и пользователя. Но это необязательно, если их не указать, Render сам сформирует случайные имена</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTc3OSwicHVyIjoiYmxvYl9pZCJ9fQ==--e03359206114635f0a1e6462e411cbda7a4638cc/db_options.png" alt="Database options" loading="lazy"/></p>
<p>Регион можно оставить по умолчанию, но обязательно запомните, какой вы выбрали, это пригодится нам дальше при выборе региона для самого приложения</p>
<p>Нажмите кнопку Create database в самом низу страницы, чтобы создать базу данных</p>
<p>Выбирайте везде бесплатный тариф, его вполне достаточно для учебных целей и так не придется вводить номер кредитной карты</p>
<p>После создания базы данных, Render покажет вам данные для подключения к ней (раздел <em>Connections</em>)</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTc4MCwicHVyIjoiYmxvYl9pZCJ9fQ==--cb4e3e34509e79384f90482e9a70ec59cd2fb70e/connections.png" alt="Connections" loading="lazy"/></p>
<p>Вам нужно будет скопировать несколько важных значений: <em>Hostname</em> (адрес сервера), <em>Port</em> (порт), <em>Database</em> (имя базы), <em>Username</em> (имя пользователя) и <em>Password</em> (пароль). Эти данные понадобятся позже, когда вы будете настраивать переменные окружения для вашего приложения.</p>
<p>Теперь, когда у нас есть база данных, мы можем перейти к созданию непосредственно приложения. Вернитесь на дашборд, снова нажмите <em>Add new</em> и выберите <em>Web Service</em></p>
<p>Подключите свой GitHub-репозиторий с проектом, который хотите задеплоить, или вставьте его URL, после чего нажмите <em>Connect</em></p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTc4MiwicHVyIjoiYmxvYl9pZCJ9fQ==--6fd5f2ac562dc8a3753bfc8536ea1c9f7b5c963a/add_repo.png" alt="Add repository" loading="lazy"/></p>
<p>Заполните поля с настройками приложения. В поле Name задайте имя для своего приложения. В <em>Language</em> оставляем <em>Docker</em> в качестве среды выполнения проекта. Поле Root <em>Directory</em> оставьте пустым, так как <em>Dockerfile</em> находится в корне проекта</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTc4NCwicHVyIjoiYmxvYl9pZCJ9fQ==--357809ae71fa1022d8b1f534b3b5024c1a536bd0/app_options.png" alt="App options" loading="lazy"/></p>
<p>Обратите внимание на регион приложения (Region). <strong>Веб-сервис и база данных должны находиться в одном регионе, иначе приложение не сможет подключиться к базе</strong></p>
<p>Сейчас наше приложение пока еще не знает, как подключиться к базе данных. Чтобы оно могло работать с базой, ему нужно передать информацию о подключении: адрес сервера, порт, имя базы, логин и пароль. Это делается с помощью переменных окружения — специальных настроек, которые передаются приложению при запуске. Переменные окружения — это способ передать приложению информацию о том, как подключиться к базе данных</p>
<p>Чтобы указать переменные окружения, пролистайте настройки приложения вниз, до раздела <em>Environment variable</em>. Нажмите на кнопку <em>Add Environment Variable</em>, добавьте нужные для подключения к базе данных переменные окружения и задайте им значения. Здесь нам пригодятся те сведения, которые мы скопировали в базе данных — <em>Hostname, Port, Database, Username, Password</em></p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTc4NiwicHVyIjoiYmxvYl9pZCJ9fQ==--baf63db046173b8401ab8b8c697ebec746928596/env_vars.png" alt="Environment variables" loading="lazy"/></p>
<p>Переменная окружения <em>JDBC_DATABASE_URL</em> содержит всю информацию, необходимую для подключения к базе данных: адрес сервера, порт, имя базы, пользователь и пароль. Именно через эту переменную приложение узнает, как подключиться к базе</p>
<p>Формат записи URL у базы данных должен быть таким:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">jdbc:postgresql://<Hostname>:<Port>/<Database>?password=<Password>&user=<Username></code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Например:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">jdbc:postgresql://dpg-cuvg6tpu0jms739k7n9g-a:5432/tirion?password=qwerty&user=tirion</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Чтобы всё работало правильно, ваше приложение должно быть настроено так, чтобы оно могло читать и использовать эту переменную окружения. Это стандартный способ передачи данных о подключении к базе данных в приложениях</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">private static String getDatabaseUrl() {
// Получаем url базы данных из переменной окружения DATABASE_URL
// Если она не установлена, используем базу в памяти
return System.getenv().getOrDefault("DATABASE_URL", "jdbc:h2:mem:project");
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>После завершения настройки нужно будет нажать на кнопку Deploy Web Service. Ваше приложение будет клонировано из репозитория, собрано и запущено. После завершения процесса приложение станет доступно по определенному адресу, который сформируется автоматически</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTc4NywicHVyIjoiYmxvYl9pZCJ9fQ==--a60a0f26b88a1b6dfa796cc394f35973aefed8fd/app_url.png" alt="App url" loading="lazy"/></p>
<p>Перейдите по этому адресу и проверьте, что приложение работает корректно.</p>
<p>Если что-то пошло не так, всегда можно посмотреть логи приложения на вкладке Logs. Они помогут вам разобраться с ошибкой</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTc4OCwicHVyIjoiYmxvYl9pZCJ9fQ==--229a146a0098f925e54dcc0b323da6b5005d24ac/logs.png" alt="Application logs" loading="lazy"/></p>
<h2 id="heading-2-7">Особенности сборки Gradle</h2>
<p>Подключив базу данных к нашему приложению, мы добавили в него файл <em>src/main/resources/schema.sql</em>, который затем читается из кода. Когда мы попробуем собрать приложение и запустить на сервере, этот код не сработает, потому что Gradle не копирует файлы из директории <em>src/main/resources</em> в получившийся <em>jar</em>.</p>
<p>Это можно исправить с помощью плагина <em>shadow</em>, который автоматически добавляет все ресурсы в <em>jar</em>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">plugins {
id("com.github.johnrengelman.shadow") version "8.1.1"
// Остальные плагины
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<h3 id="heading-3-8">Выводы</h3>
<ul>
<li>Процесс публикации сайта включает три ключевых этапа: покупка домена, выбор и настройка хостинга, а также деплой приложения на сервер.</li>
<li>Выбор хостинга зависит от потребностей проекта: можно использовать готовые решения (PaaS) или управлять собственными серверами (IaaS).</li>
<li>Деплой на PaaS упрощает процесс развертывания, предоставляя бесплатные планы и автоматизацию, что особенно полезно для учебных проектов.</li>
</ul></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/java?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Java-разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Java и фреймворк Spring Boot и REST API</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczNSwicHVyIjoiYmxvYl9pZCJ9fQ==--883f3fd4e1b571538035b5680c8d4a9eb504b1f6/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Source%20code-amico.png" alt="Java-разработчик" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 6 792 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/qa-auto-engineer-python?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">6 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Автоматизатор тестирования на Python</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Python, фреймворки для тестирования, автоматизация UI и API</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyMCwicHVyIjoiYmxvYl9pZCJ9fQ==--71cd9d863b21d7bfbd927cf623a7a2baaf4530ca/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-cuate.png" alt="Автоматизатор тестирования на Python" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 4 281 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/middle-java?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">6 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Middle-java разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Углубите знания Java, Spring и архитектуры серверных приложений</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxOSwicHVyIjoiYmxvYl9pZCJ9fQ==--e79a18f1e9f90eae650f1f4f04115cba51f4e650/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-amico.png" alt="Middle-java разработчик" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 4 050 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/java-web/lessons/deploy/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 23</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/java-web/lessons/deploy/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>