JavaRush /Java блог /Random UA /Реалізуємо розгортання програми - "Java-проект від А до Я...
Roman Beekeeper
35 рівень

Реалізуємо розгортання програми - "Java-проект від А до Я"

Стаття з групи Random UA
Всім привіт. Продовжуємо цикл статей щодо написання свого проекту. "Java-проект від А до Я": Реалізуємо розгортання програми - 1

Сортуємо гілки

З важливого, щоб не загубитися у гілках та їх порядку в репозиторії, я вирішив їх перейменувати, додавши STEP_{number} приставку . Наприклад, у нас є три гілки, крім головної:
  • JRTB-0
  • JRTB-2
  • JRTB-3
Відразу не зрозумієш, яка за якою має йти. Тому я їх перейменую так:
  • STEP_1_JRTB-0 - перший крок
  • STEP_2_JRTB-2 - другий крок
  • STEP_3_JRTB-3 - третій крок
І так далі для наступних статей. Щоб перейменувати гілки, заходимо на сторінку репозиторію , знаходимо плашку branches , переходимо по ній: "Java-проект від А до Я": Реалізуємо розгортання програми - 2Під кожною гілкою натискаємо на олівець і перейменовуємо гілку: "Java-проект від А до Я": Реалізуємо розгортання програми - 3І в результаті отримаємо: "Java-проект від А до Я": Реалізуємо розгортання програми - 4До речі, всі, хто підписаний на мій телеграм-канал , дізналися відразу ж, що я перейменував гілки.

Трохи про докер

Що таке Docker? Коротко це інструмент, за допомогою якого можна швидко і безпечно розгортати (деплоїти) програми, створюючи для них закриту інфраструктуру, необхідну тільки для них. Поки що складно, я розумію. Загалом докер можна розуміти як платформу для розробки, де можна швидко і ефективно працювати. Докер можна розуміти як програму, яка працює на сервері. Ця програма може зберігати контейнери з додатками. Що таке контейнер? Це окрема інфраструктура, до якої можна додати все, що потрібно. Наприклад, для Java-програми нам потрібна JRE, щоб запустити програму, ось контейнер буде мати це, потрібно буде ще якесь програмне забезпечення — можна додати це. А можливо, нам потрібен Лінукс і Tomcat сервлет контейнер. Таке також можна буде зробити. Контейнери створюються на основі image (образу): тобто, це певний шаблон, в якому написано все необхідне для створення докеру контейнера. Як створити цей образ? У нашому випадку нам потрібно буде створити файл Dockerfile у корені проекту з описом того, що має бути у контейнері. Так як ми не хочемо десь показувати токен бота, доведеться викрутитися і передавати його щоразу, коли ми захочемо розгортати програму. Більш детально про цю тему можна почитати коли ми захочемо розгортати програму. Більш детально про цю тему можна почитати коли ми захочемо розгортати програму. Більш детально про цю тему можна почитатитут і тут .

Пишемо JRTB-13

Потрібно налаштувати швидкий та легкий процес розгортання (деплою) нашої програми на сервер. Тобто на машину, яка працює 24/7. За основу візьмемо докер. Але завдання у нашому списку, яка б відповідала за додавання цієї функціональності, немає. Якось я його пропустив під час створення. Нічого страшного зараз створимо. Заходимо на вкладку створення issue на гітхаб і вибираємо Feature Request: "Java-проект від А до Я": Реалізуємо розгортання програми - 5Додаємо опис завдання, критерії його приймання, встановлюємо, до якого проекту цей issue відноситься і можна створювати нове issue: "Java-проект від А до Я": Реалізуємо розгортання програми - 6Тепер щоб показати, що завдання взято в роботу, змінимо статус завдання з To do на In Progress:"Java-проект від А до Я": Реалізуємо розгортання програми - 7Це буде найскладніша стаття. Якщо будуть проблеми — пишіть у коментарях: я стежитиму і відповідатиму на них у міру сил. Такою буде невелика Customer Support :D

Створюємо Dockerfile

Що таке докерфайл? Для докера це скрипт (покрокова інструкція), як створювати образ для докеру контейнера. Для роботи нашої програми потрібна JDK, причому 11 версії. Тобто нам потрібно знайти докер-образ JDK 11 і додати його до нашого образу. Це щось схоже на те, як ми додаємо залежність до пам'ятника. Для цієї справи у докера є DockerHub . Щоб локально завантажувати образи, потрібно зареєструватися. Після реєстрації йдемо шукати нам JDK11. З того, що вдалося знайти - ось цей контейнер: adoptopenjdk/openjdk11 . В описі цього контейнера є те, що потрібно для докерфайлу:
FROM adoptopenjdk/openjdk11:ubi
RUN mkdir /opt/app
COPY japp.jar /opt/app
CMD ["java", "-jar", "/opt/app/japp.jar"]
Виправимо папку, з якої ми беремо jar файл. У нас він знаходиться в target папці після того, як ми запускаємо mvn package завдання мавену. Перед тим, як все це робити, на основі оновленої main гілки створюємо нову, для нашого завдання: STEP_4_JRTB-13 . Тепер можна працювати. У корені проекту створюємо файл без розширення Dockerfile і додамо всередину наступне:
FROM adoptopenjdk/openjdk11:ubi
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Перший рядок — на основі чого складатиметься образ — adoptopenjdk/openjdk11. Другий рядок - додаємо аргумент в образ з ім'ям JAR_FILE, який знаходиться в папці target. Причому поточна папка визначається за місцем Dockerfile. Третій рядок - копіюємо в докер-образ jar нашого проекту. Останній рядок містить масив, створений з команди в терміналі, яку розділабо за пробілом. Тобто, в результаті буде виконано наступне: "java -jar /app.jar" Щоб тримати в таємниці токен бота, при запуску контейнера нам потрібно буде передавати дві змінні - ім'я бота та його токен. Для цього напишемо запит, який має запустити наш проект зі змінними. А як це зробити? Потрібно загуглити: ось перше посиланняіз нормальним описом. А що хочемо зробити? У нас у application.properties файлі є дві змінні, які ми там визначаємо:
  • bot.username
  • bot.token
Я хочу запускати контейнер докеру і щоразу передавати туди своє значення, щоб ніхто не бачив ці значення. Я знаю, що в SpringBoot змінні оточення, які задаються в момент запуску jar проекту, будуть пріоритетнішими, ніж ті, що знаходяться у файлі application.properties. Щоб передати змінну у запиті, потрібно додати наступну конструкцію: -D{ім'я змінної}=”{значення змінної}” . Фігурні дужки не дописуємо ;) Отримаємо запит, при якому буде запущено наш додаток з визначеними значеннями - ім'я та токена бота: java -jar Тепер потрібно передати ці змінні докер контейнера. Це природна умова. Щоб у майбутньому у нас база даних працювала чітко та без проблем із нашим додатком, будемо використовувати docker-compose. Це окремий інструмент, в якому можна впорядкувати роботу, запуск та залежність між контейнерами. Іншими словами, це надбудова над докером, щоб керувати контейнерами однієї інфраструктури. Плюс перед тим, як запустити docker-compose, потрібно бути впевненим, що ми стягнули всі зміни коду з сервера, зібрали програму та зупинабо стару версію. Для цього будемо використовувати баш скрипт. Ух ... Звучить все непросто, згоден. Але робота з налаштуванням розгортання додатків — це завжди моторний та складний процес. Тому в нас вимальовується нехила схема:
  1. Запускаємо баш скрипт.
  2. Баш-скрипт запускає docker-compose.
  3. Docker-compose запускає контейнер docker з нашим додатком.
  4. Docker контейнер запускає нашу програму.
І ось потрібно зробити так, щоб дві змінні – ім'я бота та його токен – пройшли з 1 пункту в 4. Причому так, щоб ці дві змінні використовувалися при запуску нашого java-додатку. Ходімо з кінця на початок. Ми вже знаємо, яку команду потрібно виконати, щоби запустити джарник. Тому налаштовуватимемо Dockerfile, щоб він навчився приймати дві змінні і передавати їх у запит. Для цього наведемо Dockerfile до такого вигляду:
FROM adoptopenjdk/openjdk11:ubi
ARG JAR_FILE=target/*.jar
ENV BOT_NAME=test.codegym_community_bot
ENV BOT_TOKEN=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-Dbot.username=${BOT_NAME}", "-Dbot.token=${BOT_TOKEN}", "-jar", "/app.jar"]
Видно, що ми додали два рядки і оновлено ENTRYPOINT. Рядки:
ENV BOT_NAME=test.codegym_community_bot
ENV BOT_TOKEN=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
оголошують змінні всередині кодера файлу. За замовчуванням значення вказано. Якщо при створенні образу з цього докерфайлу буде передано змінні оточення з такими іменами, значення будуть інші. А в ENTRYPOINT ми додали ще кілька елементів, які зчитуватимуть ці змінні середовища.
"-Dbot.username=${BOT_NAME}", "-Dbot.token=${BOT_TOKEN}"
Тут видно, що всередині рядка за допомогою ${} конструкції буде передано значення BOT_NAME і BOT_TOKEN. Далі нам потрібно навчити отримувати та передавати ці змінні до docker-compose.

Створюємо docker-compose.yml

Добре вам про YAML формат почитати окремо, а то стаття і так вже росте, як на дріжджах. Для нас це просто ще один опис змінних на кшталт .properties. Тільки в пропертях записується через точку, а в YAML це робиться трохи красивішим. Наприклад, так. Дві змінні в .properties: codegym.telegram.bot.name=ivan codegym.telegram.bot.token=pupkin А ось у .yaml (теж саме .yml) буде це так:
codegym:
	telegram:
		bot:
		  name: ivan
		  token: pupkin
Другий варіант більш гарний та зрозумілий. Прогалини мають бути саме такі, як зазначено вище. Якось перекладемо наші application.properties і application.yml. Спершу потрібно його створити. У корені проекту створюємо файл docker-compose.yml та записуємо туди наступне:
version: '3.1'

services:
 jrtb:
   build:
     context: .
   environment:
     - BOT_NAME=${BOT_NAME}
     - BOT_TOKEN=${BOT_TOKEN}
   restart: always
Перший рядок – це версія docker-compose. services: говорить про те, що всі наступні рядки після цього (будуть зрушені) - відносяться до сервісів, які ми налаштовуємо. У нас такий поки що тільки один - java-додаток під назвою jrtb . І вже під ним будуть усі його налаштування. Наприклад, build: context: . говорить про те, що ми шукатимемо Dockerfile у тій же директорії, що й docker-compose.yml. А ось секція environment: відповідатиме за те, щоб ми передали в Dockerfile необхідні змінні середовища (environment variables). Саме те, що нам і потрібно. Тому нижче ми змінні та передаємо. Їхні docker-compose буде шукати в змінних операційного середовища сервера. Додамо їх у баш скрипті.

Створюємо баш скрипти

І останній крок – створити баш скрипт. Створюємо в корені проекту файл з ім'ям start.sh і пишемо туди:
#!/bin/bash

# Pull new changes
git pull

# Prepare Jar
mvn clean
mvn package

# Ensure, that docker-compose stopped
docker-compose stop

# Add environment variables
export BOT_NAME=$1
export BOT_TOKEN=$2

# Start new deployment
docker-compose up --build -d
Перший рядок потрібний для всіх баш скриптів: без нього працювати не буде. А далі просто набір команд у терміналі, які потрібно виконати. Я додав коментарі у кожній команді, тому має бути зрозуміло. Єдине, що хочеться пояснити, це те, що означає $1 і $2. Це дві змінні, які будуть передані у запуску баш скрипта. За допомогою команди export вони будуть додані до змінних серверів і лічені вже в docker-compose. Це працює для убунти, для віндоуса, мабуть, ні, але я не певен. Тепер потрібно додати скрипт stop.sh, який зупинятиме роботу. У ньому буде кілька рядків:
#!/bin/bash

# Ensure, that docker-compose stopped
docker-compose stop

# Ensure, that the old application won't be deployed again.
mvn clean
Тут ми зупиняємо docker-compose та зачищаємо джарник проекту, який лежить ще з минулого збирання. Робимо ми це для того, щоб наш проект точно перезбирався. Були прецеденти, тому і додаю) У результаті у на виходить 4 нових файли:
  • Dockerfile - файл для створення образу нашої програми;
  • docker-compose.yml — файл із налаштуванням того, як ми будемо запускати наші контейнери;
  • start.sh - баш скрипт для розгортання нашої програми;
  • stop.sh - баш скрипт для зупинки нашої програми.
Також оновимо версію нашої програми з 0.2.0-SNAPSHOT на 0.3.0-SNAPSHOT. Додамо до RELEASE_NOTES опис до нової версії і трохи відрефакторимо те, що було:
# Release Notes ## 0.3.0-SNAPSHOT * JRTB-13: added deployment process to the project ## 0.2.0-SNAPSHOT * JRTB-3: implemented Command pattern for handling Telegram Bot commands ## 0.1.0-SNAPSHOT * JRTB -2: added stub telegram bot * JRTB-0: added SpringBoot skeleton project
І в README додамо новий параграф з описом того, як деплоїти наш додаток:
## Deployment Deployment process as asy posible: Required software: - terminal for running bash scripts - docker - docker-compose to deploy application, switch to needed branch and run bash script: $ bash start.sh ${bot_username} ${bot_token } That's all.
Зрозуміло, все пише англійською. Вже як завжди, у нашій новоствореній гілці STEP_4_JRTB-13 створюємо новий коміт з ім'ям: JRTB-13: implement deployment process via docker і робимо пуш. Я перестаю докладно зупинятись на речах, які я вже описував у минулих статтях. Не бачу сенсу повторювати те саме. До того ж, хто розібрався і зробив у себе, у запитань не виникне. Це я про те, як створити нову гілку, як створити коміт, як запустити комміт у репозиторій.

Підсумок

За сьогодні я показав безліч нової інформації, яку потрібно добре обміркувати і розширити додатковим читанням. Найголовніше: за допомогою ОДНІЙ (!!!) команди все необхідне для розгортання нашої програми буде зроблено. Це настільки класно, що я навіть передати вам не можу. Так, довелося витратити пристойну кількість часу в документації докера, щоб зрозуміти, як правильно прокидати змінні. З цього моменту телеграм-бот завжди працюватиме на останній версії main гілки. Посилання на телеграм-бота. Сьогодні не будуть посилань на матеріали, які добре було б прочитати: відповідальність лежить на вас. Потрібно вчитися шукати інформацію. Усі, хто підписаний на мій телеграм-канал, дізналися про депло бота майже відразу. Друзі, подобається проект? Ставте йому зірку! Так він стане популярнішим і більше людей зможуть дізнатися про нього та повчитися. Традиційно пропоную зареєструватися на GitHub і підписатися на мій обліковий запис , щоб стежити за цією серією та іншими моїми проектами, які я веду там. Наразі ми готові підключати базу даних. Наступна стаття буде більшою і в ній ми зробимо все необхідне для роботи з БД. Весь опис - в JRTB-1 .

Список всіх матеріалів серії на початку цієї статті.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ