Місяць 1 · Заняття 02

Перший бекенд, який не вимикається

Розібратися, як влаштована твоя програма на сервері, дописати до неї власну адресу відповіді й перевірити, що вона працює цілодобово — навіть коли твій комп’ютер вимкнено.

75 хвилин теорія 20 хв практика 45 хв домашнє завдання 10 хв

Що ти зробиш сьогодні

Твоя програма на Python почне відповідати на запити з інтернету: будь-хто, хто відкриє адресу /u/<логін>/api/ping, отримає відповідь із твоїм іменем. Ти впишеш дані свого проєкту у файл-опис, додаси власний ендпоінт — ще одну адресу, за якою твоя програма щось відповідає, — і перевіриш роботу з сервера й із браузера.

Далі ти перевіриш, що ця програма працює без тебе: подивишся, чи ввімкнено автозапуск, вийдеш із сервера й переконаєшся, що відповіді й далі приходять, а потім примусово зупиниш програму й подивишся, як система запускає її знову за кілька секунд.

Теорія

1. Що таке бекенд і що відбувається під час запиту

Бекенд — програма, яка постійно працює на сервері й відповідає на запити. Сервер — комп’ютер, який увімкнений цілодобово і має адресу в інтернеті; наш сервер має адресу 91.219.61.4. Те, що ти бачиш на екрані — сторінку, кнопки, текст — називають фронтендом; ним ми займемося на занятті 05.

Коли ти відкриваєш будь-яку адресу, браузер надсилає на сервер HTTP-запит. HTTP — це набір правил, за якими програми в інтернеті обмінюються повідомленнями. У запиті головні дві речі:

У відповідь сервер надсилає код відповіді — тризначне число, яке коротко каже, чим усе скінчилося. Ці числа доведеться знати напам’ять, вони будуть на кожному занятті до кінця курсу.

КодЩо означаєКоли ти це побачиш
200Запит виконаноУсе гаразд, у відповіді є дані
404Такого шляху немаєПомилка в адресі або ендпоінт ще не написано
500Програма на сервері завершилася з помилкоюЗапит дійшов до твого коду, і під час виконання сталася помилка
502Твоя програма не відповіла серверуЇї зупинено або вона саме перезапускається

Приклад із твого життя: гра на телефоні пише «немає з’єднання з сервером». Це означає, що її бекенд не відповів — або зупинився, або мережа не дійшла. Після сьогоднішнього заняття ти сам відповідаєш за те, щоб таке не сталося з твоїм сервісом.

Ти відкрив у браузері адресу свого API і бачиш сторінку з написом «500 Internal Server Error». Що сталося?

2. JSON — формат, у якому програми обмінюються даними

Звичайна сторінка — це HTML: текст разом з оформленням, зроблений для людини. Коли дані забирає інша програма, оформлення тільки заважає. Для цього є JSON — текстовий формат запису даних, який легко читає будь-яка мова програмування.

{"status": "ok", "author": "Богдан"}

Ти вже знаєш цю структуру: у Python це словник. У фігурних дужках — пари «ключ: значення», ключ у лапках, пари через кому. Значенням може бути число, рядок, список у квадратних дужках або інший такий самий набір у фігурних.

API (від application programming interface) — набір адрес, за якими дані забирає програма, а не людина. Приклад: застосунок прогнозу погоди на твоєму телефоні не відкриває сайт — він звертається до API і отримує JSON із числами, а вже сам малює з них картинку. Твій сервіс сьогодні починає працювати так само.

Окрема адреса всередині API називається ендпоінт — точка, за якою сервер щось конкретне відповідає. У тебе їх сьогодні буде три: /api/ping, /api/about і той, який ти напишеш сам.

Однокласник пише мобільний застосунок і хоче показувати в ньому кількість твоїх записів. Що зручніше віддати йому у відповіді?

3. Порт і те, як запит із інтернету потрапляє саме до твоєї програми

На сервері 91.219.61.4 одночасно працюють програми всієї групи. Щоб система розуміла, кому передати запит, кожна програма займає свій порт — число від 1 до 65535. Твій порт видано разом з акаунтом, він виглядає як 9536. Одне число — одна програма: якщо запустити другу на тому самому порту, вона одразу завершиться з повідомленням address already in use.

Твоя програма слухає порт не на всіх адресах, а тільки на 127.0.0.1. Це особлива адреса, яка означає «цей самий комп’ютер»; її ще називають localhost. З інтернету на неї зайти неможливо — вона в кожного комп’ютера своя й веде сама на себе.

Ззовні всі заходять на порт 80 — це порт, який браузер підставляє сам, коли адреса починається з http://. На порту 80 працює nginx — програма, яка приймає всі запити на сервер і дивиться на шлях:

Налаштування nginx робить викладач; тобі його чіпати не треба. Але знати цей порядок важливо: коли API не відповідає, ти маєш розуміти, де саме шукати причину — у nginx чи у своїй програмі.

На сервері ти виконав команду curl http://127.0.0.1:9536/api/ping (curl надсилає запит за вказаною адресою і друкує відповідь текстом) — відповідь є. Друг удома вводить у себе в браузері http://127.0.0.1:9536/api/ping — нічого не відкривається. Чому?

4. Що вже лежить у твоїй теці і як воно працює

Віртуальне оточення (скорочено venv) — окрема тека з бібліотеками Python для конкретного проєкту. Вона потрібна, щоб проєкти не заважали один одному версіями бібліотек. Програма, яка встановлює бібліотеки в таке оточення, називається pip. У нас одне спільне оточення на всю групу — /opt/club/venv, і потрібні бібліотеки там уже встановлені, тому pip запускати не треба.

З того, що вже стоїть у цьому оточенні, тобі сьогодні важливі дві речі:

Ось як виглядає ендпоінт у файлі ~/app/main.py:

@app.get("/api/ping")
def ping():
    return {"status": "ok", "author": CFG["product"]["author"]}

Рядок @app.get("/api/ping") над функцією називають декоратором. Він каже FastAPI: коли надійде запит методом GET на шлях /api/ping — виклич цю функцію. Функція повертає звичайний словник Python, а FastAPI сам перетворює його на JSON. Нічого більше писати не потрібно.

Звідки береться ім’я автора? З файла ~/app/project.yaml — це маніфест проєкту, тобто короткий опис, який читає програма: назва продукту, автор, для кого сервіс і які в нього функції. Формат YAML — текст, у якому вкладеність показана відступом зліва: чим глибше рядок вкладений, тим більший у нього відступ, а всі рядки одного рівня мають однаковий відступ. У наших файлах один рівень — це два пробіли. Із цього файла читає дані і твоя програма, і скрипт автоперевірки. Якщо там написано «Ім’я» — саме це слово побачить перевірка, і критерій не зарахує.

Останнє про цей файл: uvicorn читає main.py і project.yaml один раз, коли запускається. Якщо ти змінив файл, а програму не перезапустив, сервер і далі віддаватиме стару версію. Як її перезапускати — у наступному розділі.

Ще одна команда, яка тобі знадобиться: curl — вона надсилає HTTP-запит прямо з термінала й друкує відповідь текстом. Це швидший спосіб перевірки, ніж відкривати браузер, і він працює навіть тоді, коли поруч немає екрана з браузером.

Ти вписав своє ім’я у project.yaml, зберіг файл, оновив у браузері сторінку /api/ping — а там усе ще «Ім’я». Що ти пропустив?

5. Процес, служба і чому одне вимикається, а друге ні

Процес — запущена програма. Коли ти запускаєш uvicorn руками в терміналі, процес належить твоєму сеансу SSH. Закриваєш вікно — сеанс закінчується, процес зупиняється разом із ним. Сайт після цього не відповідає.

Служба — програма, якою керує сама операційна система. Вона запускає її, коли сервер вмикається, стежить, чи процес працює, і запускає його знову, якщо той завершився. Твій сеанс SSH тут ні до чого.

У Linux службами керує systemd — частина системи, яка запускає й зупиняє все, що має працювати постійно. Служби бувають системні (їх налаштовує адміністратор) і користувацькі — твої власні. Тому в усіх командах є --user: він означає «моя служба, а не загальносерверна». Твоя служба зветься club-api.

Опис служби зберігається в текстовому unit-файлі ~/.config/systemd/user/club-api.service. У ньому написано, яку команду запускати, з якої теки і що робити, коли процес завершився. Файл уже готовий, редагувати його не треба, але два рядки варто прочитати:

ExecStart=/opt/club/venv/bin/uvicorn main:app --host 127.0.0.1 --port 9536
Restart=always
RestartSec=3

Restart=always — вказівка systemd: якщо процес завершився з будь-якої причини, запусти його знову. RestartSec=3 — зачекай перед цим три секунди. Саме ці два рядки роблять так, що після аварійного завершення програма запускається знову без твоєї участі.

Далі два слова, які легко переплутати:

І окремо — linger. За звичайних налаштувань systemd зупиняє всі користувацькі служби, щойно людина виходить із сервера. Linger — це дозвіл для акаунта тримати свої служби запущеними без активного сеансу. Його вмикає викладач від імені адміністратора. Без linger твоя служба зупинялася б щоразу, коли ти виходиш із сервера, і сайт із API переставали б відповідати.

Коли щось іде не так, дивись журнал — це файл, куди systemd складає повідомлення служби: усе, що вона надрукувала, і всі помилки. Читається командою journalctl --user -u club-api -n 30: показати останні 30 рядків журналу служби club-api.

Ти налаштував службу, перевірив, що API відповідає, закрив термінал і пішов додому. Через дві години відкриваєш свою адресу з телефона — сторінка й API працюють. Завдяки чому?

Скрипт автоперевірки примусово зупиняє твій процес, чекає шість секунд і знову запитує /api/ping. У відповідь приходить 200. Що спрацювало?

Слова цього уроку

СловоЩо означає
термінал, SSHВікно, у якому команди набирають текстом, і спосіб виконувати їх на сервері зі свого комп’ютера
бекендПрограма на сервері, яка відповідає на запити
HTTPПравила, за якими браузер і сервер обмінюються повідомленнями
метод, шляхДві головні частини запиту: що зробити і з чим
код відповідіЧисло, яким сервер повідомляє результат: 200, 404, 500, 502
заголовкиСлужбові рядки на початку відповіді; у першому з них — код відповіді
JSONТекстовий формат даних, схожий на словник Python
APIНабір адрес, за якими дані забирає інша програма
ендпоінтОдна адреса всередині API, за якою сервер щось відповідає
портЧисло, за яким система розрізняє програми на одному комп’ютері
127.0.0.1Адреса «цей самий комп’ютер», ззовні недоступна
nginxПрограма на порту 80, яка передає запити твоїй програмі
venv, pipТека з бібліотеками проєкту і програма, яка їх встановлює
FastAPI, uvicornФреймворк для опису ендпоінтів і програма, що слухає порт
маніфест, YAMLФайл project.yaml з описом проєкту і формат, у якому він написаний
curlКоманда, що надсилає запит із термінала й друкує відповідь
зміннаІм’я, замість якого термінал підставляє збережений текст: $PORT
процес, службаЗапущена програма і програма під наглядом системи
systemd, unit-файлЧастина Linux, що керує службами, і текстовий опис однієї служби
enable, lingerАвтозапуск служби і дозвіл їй працювати без сеансу SSH
журналЗаписи служби, які показує journalctl

Практика

Виконуй кроки послідовно. Скрізь, де у команді є $USER, підставляти нічого не треба — на сервері ця змінна вже містить твій логін.

1 Зайти на сервер

ssh <логін>@91.219.61.4

Замість <логін> разом із дужками підстав свій, тобто набери ssh bohdan.g9@91.219.61.4. Пароль під час введення не показується взагалі: ні крапок, ні зірочок. Це нормально, набирай і натискай Enter.

2 Подивитися, у якому стані твоя служба

systemctl --user status club-api

У виводі шукай рядок Active:. Що там може бути:

Нижче видно Main PID — номер процесу в системі, і кілька останніх рядків журналу. Вийти з перегляду — клавіша q.

3 Дізнатися свій порт

grep ExecStart ~/.config/systemd/user/club-api.service

Команда grep шукає рядок у файлі й друкує його. Ти побачиш команду запуску, а в кінці — --port і твоє число. Запиши його: воно твоє на весь курс.

Щоб не набирати число щоразу, збережи його у змінну — це ім’я, замість якого термінал підставляє збережений текст. Далі досить написати $PORT, і туди підставиться твоє число. Змінна живе до кінця цього сеансу термінала: після виходу з сервера її треба задати заново.

PORT=$(grep -o -- '--port [0-9]*' ~/.config/systemd/user/club-api.service | grep -o '[0-9]*')
echo "мій порт: $PORT"

Як читати цей рядок. Запис $( ) означає «виконай команду в дужках і підстав сюди те, що вона надрукувала». Ключ -o каже grep друкувати не весь рядок, а лише знайдений шматок; два дефіси -- потрібні, щоб grep не сприйняв --port за свій власний ключ. Перший grep знаходить --port 9536, другий залишає з цього саме число. Перевір, що echo надрукував те саме число, яке ти щойно бачив.

4 Перший запит — із самого сервера

curl http://127.0.0.1:$PORT/api/ping

Маєш побачити рядок виду {"status":"ok","author":"Ім’я"}. Це означає: програма працює й слухає свій порт. Ім’я поки чуже — виправиш у кроці 6.

Щоб побачити ще й код відповіді, додай ключ -i. Тоді curl надрукує ще й заголовки — службові рядки на початку відповіді, у яких сервер повідомляє код відповіді, тип даних та іншу технічну інформацію. Браузер їх не показує, але отримує так само:

curl -i http://127.0.0.1:$PORT/api/ping

Перший рядок має бути HTTP/1.1 200 OK.

5 Той самий запит — із інтернету

curl -i http://91.219.61.4/u/$USER/api/ping

Тепер запит іде довшим шляхом: спочатку до nginx на порт 80, звідти — до твоєї програми. Якщо крок 4 працює, а цей дає 502 — проблема в передачі між nginx і твоєю програмою, скажи про це викладачеві.

Відкрий ту саму адресу в браузері на своєму комп’ютері. Браузер покаже той самий JSON — просто текстом.

6 Заповнити опис проєкту

nano ~/app/project.yaml

nano — простий текстовий редактор у терміналі. Стрілками рухайся по тексту, набирай як завжди. Зберегти — Ctrl+O, потім Enter. Вийти — Ctrl+X.

Заповни розділ product даними свого проєкту — тими, що ти записав на першому занятті у файл BRIEF.md (це текстовий опис задуму: назва, для кого сервіс, що він уміє). Якщо не пам’ятаєш, де цей файл, подивись список своїх файлів командою ls ~/app і прочитай його командою cat ~/app/BRIEF.md. Ось приклад, як має вийти:

product:
  name: "Трекер домашніх завдань"
  author: "Богдан"
  target_user: "мій молодший брат, який забуває, що задали"
  features:
    - "додати завдання з датою"
    - "позначити виконане"
    - "показати список на сьогодні"

Три правила YAML, через які файл найчастіше перестає читатися:

7 Перезапустити службу й перевірити зміну

systemctl --user restart club-api
curl http://127.0.0.1:$PORT/api/ping

Тепер у полі author має бути твоє ім’я. Запам’ятай цю пару команд: після кожної зміни main.py чи project.yaml — спочатку restart, потім перевірка.

8 Прочитати код своєї програми

cat ~/app/main.py

cat друкує вміст файла на екран. Знайди в ньому обидва наявні ендпоінти й перевір, що розумієш кожен рядок: декоратор, назву функції, словник у return. Питання «поясни цей рядок» буде на початку наступного заняття.

Перевір другий ендпоінт — він віддає більше даних:

curl http://91.219.61.4/u/$USER/api/about

У полі features має бути список із трьох функцій, які ти щойно вписав у крок 6. Якщо там порожні квадратні дужки [] — повернись і заповни features.

9 Написати власний ендпоінт

nano ~/app/main.py

Опустися стрілкою вниз у самий кінець файла й з нового рядка додай цей код. Рядок @app.get має починатися від лівого краю, без відступу; відступи всередині функції — чотири пробіли, як у сусідніх функціях:

@app.get("/api/user")
def user():
    """Для кого цей сервіс. Дані беремо з project.yaml."""
    return {
        "product": CFG["product"]["name"],
        "for_whom": CFG["product"]["target_user"],
    }

Що тут відбувається: декоратор реєструє новий шлях /api/user, функція читає з уже завантаженого маніфесту два значення й повертає їх словником. FastAPI перетворить його на JSON.

Збережи, перезапусти, перевір:

systemctl --user restart club-api
curl http://91.219.61.4/u/$USER/api/user

Якщо у відповідь прийшов {"detail":"Not Found"} — так FastAPI відповідає на шлях, якого не знає. Отже, служба не перезапустилася або код додано не в той файл. Якщо служба перейшла у стан failed — у коді помилка, дивись крок 12.

10 Перевірити автозапуск і linger

systemctl --user is-active club-api
systemctl --user is-enabled club-api
loginctl show-user $USER -p Linger --value

Правильні відповіді по черзі: active, enabled, yes.

11 Перевірити, що служба запускається сама

Зараз ти навмисно зупиниш свій процес найрізкішим способом. Сигнал SIGKILL завершує процес негайно: програма навіть не отримує цього сигналу, тому не встигає нічого зберегти й не може скасувати зупинку. Це саме те, що робить скрипт автоперевірки.

systemctl --user kill -s SIGKILL club-api
systemctl --user is-active club-api

Друга команда одразу після першої, найімовірніше, покаже activating, inactive або failed — це очікувано. Порахуй уголос до п’яти й виконай перевірку знову:

systemctl --user is-active club-api
curl http://127.0.0.1:$PORT/api/ping

Має бути active і звична відповідь із твоїм іменем. Пауза між зупинкою і новим запуском — три секунди, її задає рядок RestartSec=3.

Тепер друга перевірка — вихід із сервера:

exit

Термінал повернувся на твій комп’ютер. Онови в браузері сторінку http://91.219.61.4/u/<логін>/api/ping — вона відповідає, хоча твого сеансу на сервері вже немає. Це працює linger.

Кроки 12 і 13 знову виконуються на сервері, тому зайди туди ще раз:

ssh <логін>@91.219.61.4

Змінна PORT після виходу зникла разом із попереднім сеансом. Коли зайдеш, задай її тією самою командою, що в кроці 3 — інакше в наступних командах замість числа підставиться порожнє місце:

PORT=$(grep -o -- '--port [0-9]*' ~/.config/systemd/user/club-api.service | grep -o '[0-9]*')

12 Якщо служба у стані failed — прочитати журнал

journalctl --user -u club-api -n 30

Останні 30 рядків журналу. Шукай знизу вгору перший рядок зі словом Error — там буде назва помилки й номер рядка у файлі. Найчастіші випадки:

Виправ помилку, збережи файл, виконай systemctl --user restart club-api і перевір стан ще раз.

13 Перевірити сторінку й подивитися кабінет

cat ~/www/index.html

Перевірка L02.6 шукає на цій сторінці назву продукту — точно ту саму, що в project.yaml. Якщо назви немає або вона інша, відредагуй сторінку: nano ~/www/index.html.

Потім відкрий у браузері свій кабінет http://91.219.61.4/k/<логін>/ і подивись, які критерії вже зараховано. Перевірка виконується не миттєво — почекай кілька хвилин і онови сторінку.

Що перевіряє автоматика

Шість пунктів, кожен зараховується окремо. Починай з L02.1: поки він не проходить, не пройдуть і L02.2 та L02.5 — вони теж вимагають, щоб служба працювала. L02.3, L02.4 і L02.6 перевіряються незалежно від нього.

КодКритерійЩо робить скриптЯкщо не зараховано
L02.1 /api/ping відповідає 200 і віддає JSON Відкриває ззовні /u/<логін>/api/ping, чекає код 200 і JSON із ключами status та author. Перевір по черзі: systemctl --user status club-api — чи active; curl http://127.0.0.1:$PORT/api/ping — чи працює локально (якщо ти щойно зайшов на сервер заново, спочатку задай PORT командою з кроку 3, інакше замість числа підставиться порожнє місце й curl видасть незрозумілу помилку). Якщо локально працює, а ззовні ні — скажи викладачеві, це налаштування nginx.
L02.2 автор у відповіді збігається з project.yaml Читає product.author з ~/app/project.yaml і звіряє з полем author у відповіді. Поки там заготовка «Ім’я» — не зараховує. Впиши своє ім’я без прізвища у project.yaml і перезапусти службу. Значення мають збігатися дослівно, разом із великими літерами.
L02.3 служба ввімкнена в автозапуск Виконує systemctl --user is-enabled club-api і чекає відповіді enabled. Це означає, що після перезавантаження сервера служба запуститься сама. Виконай systemctl --user enable --now club-api. Якщо у відповідь помилка — скопіюй її текст і покажи викладачеві.
L02.4 служба працює далі після виходу із SSH (linger) Виконує loginctl show-user <логін> -p Linger --value і чекає yes. Це налаштовує викладач від імені адміністратора: loginctl enable-linger <логін>. Сам ти виправити не можеш — просто скажи про це.
L02.5 служба сама запускається після примусової зупинки Примусово зупиняє процес сигналом SIGKILL, чекає шість секунд і перевіряє, чи служба знову active. Працює завдяки Restart=always. Перевір, що в ~/.config/systemd/user/club-api.service є рядок Restart=always, і виконай systemctl --user daemon-reload. Якщо служба щоразу завершується з помилкою одразу після запуску, systemd після кількох спроб припиняє її запускати: виправ помилку в коді, потім systemctl --user reset-failed club-api і restart.
L02.6 сторінка учня відкривається Відкриває http://91.219.61.4/u/<логін>/, чекає код 200 і шукає в тексті сторінки назву продукту з project.yaml. Файл має лежати саме в ~/www/index.html. Назва на сторінці має збігатися з product.name дослівно. Якщо там ще текст заготовки — заміни його.

Домашнє завдання

  1. Ендпоінт /api/plan. Додай у main.py ще один ендпоінт, який повертає список із трьох рядків — що ти плануєш додати до сервісу найближчим часом. Список пиши прямо в коді: return {"next": ["...", "...", "..."]}. Не забудь перезапустити службу й перевірити відповідь через curl.
  2. Перевірка з телефона по мобільному інтернету. Вимкни Wi-Fi на телефоні й відкрий http://91.219.61.4/u/<логін>/api/about. Так ти перевіряєш саме те, що бачить сторонній відвідувач, а не те, що видно з домашньої мережі. Зроби скриншот.
  3. Самостійний тест на відновлення. Один раз повтори без підказки першу частину кроку 11: зупини процес і заміряй по годиннику, за скільки секунд is-active знову покаже active. Запиши це число. Виходити з сервера цього разу не треба.
  4. Запис у README. Відкрий файл командою nano ~/app/README.md і додай два рядки: свій порт і час відновлення з пункту 3. Якщо в файлі є розділ «Як запустити» — пиши в нього; якщо такого розділу немає, додай рядки в кінець файла. Якщо файла ще немає, nano відкриє порожній — напиши рядки й збережи.

На початку наступного заняття буде питання «поясни рядок»: викладач покаже будь-який рядок із твого main.py і попросить сказати, що він робить.

Якщо щось не працює

API віддає старе ім’я або старі дані

Найчастіша ситуація за сьогодні. uvicorn читає main.py і project.yaml один раз під час запуску. Поки не буде systemctl --user restart club-api, у пам’яті лишається попередня версія. Якщо після перезапуску дані все одно старі — перевір, що редагував файл у теці ~/app, а не копію в іншому місці: ls -l ~/app/project.yaml покаже час останньої зміни.

Стан служби — failed, сайт не відповідає

Виконай journalctl --user -u club-api -n 30 і читай знизу вгору. Перший рядок з Error назве файл і номер рядка. Помилка майже завжди у тому, що ти щойно міняв. Якщо служба багато разів поспіль завершувалася з помилкою, systemd припиняє спроби її запускати — тоді після виправлення потрібні дві команди: systemctl --user reset-failed club-api, потім systemctl --user restart club-api.

Локально працює, а публічна адреса дає 502

Код 502 означає: nginx звернувся до твоєї програми й не отримав відповіді. Причини за частотою: служба саме в цю секунду перезапускається — почекай три секунди й повтори; служба у стані failed — дивись попередній пункт; порт у unit-файлі не збігається з тим, який знає nginx. Останнє сам не виправляй: порт видано разом з акаунтом, міняти його не можна, скажи викладачеві.

Служба завершується з помилкою yaml.scanner.ScannerError

Це помилка формату у project.yaml. Дві причини покривають майже всі випадки: у файл потрапила табуляція замість пробілів, або після двокрапки немає пробілу. Перевірити файл окремо від служби можна так:

cd ~/app && /opt/club/venv/bin/python -c "import yaml; print(yaml.safe_load(open('project.yaml'))['product'])"

Якщо надрукувався словник із твоїми даними — файл цілий, причина в іншому. Якщо помилка — у її тексті буде номер рядка, з якого починати.

SSH не пускає на сервер

Спочатку перевір розкладку клавіатури й Caps Lock: пароль не показується під час введення, тому помилку не видно. Після кількох невдалих спроб підряд адресу твоєї мережі тимчасово блокує fail2ban — програма на сервері, яка стежить за невдалими входами й закриває доступ тим, хто підбирає пароль. Тоді підключитися не вийде навіть із правильним паролем: блокування знімається саме, зазвичай за 10–15 хвилин. Якщо чекати ніколи, скажи викладачеві — він зніме блокування одразу. Якщо пароль ти вже змінював командою passwd на першому занятті, діє новий, а не той, що видали на папірці.