Курс › СРС › SRS01
до ЛР1 рівень 2 → «4»↔ Лабораторна 1↔ Лекція 1⌨️ команди 5
Самостійна робота · ~2.5 год

Публікація портів у Docker: -p, EXPOSE і чому без них хост не бачить контейнера

🌌 Брама, крізь яку пускають — Капсула герметична навмисно — у цьому весь сенс. Але герметична капсула, з якою неможливо поговорити, теж нікому не потрібна. С.І.Д. показує, як прорубати в ній рівно один канал: з номером, під наглядом, і жодного зайвого. «Не стіна й не діра, ранере, — брама. Різниця в тому, що браму ти відчиняєш сама і знаєш, хто крізь неї пройде».

📚 ТЕОРЕТИЧНІ ВІДОМОСТІ

Що робить публікація порту

Капсула має власну мережу: свої інтерфейси, свою таблицю маршрутів і свій набір портів. Тому порт 80, відкритий усередині, — це не порт 80 хоста, і за замовчуванням із хоста до нього немає дороги. (Механізм, який це забезпечує, розберемо на Лекції 3; сьогодні достатньо знати, що так і задумано.)

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

-p <порт на ХОСТІ>:<порт у КАПСУЛІ>

Порядок не симетричний і його легко переставити. Мнемоніка одна: лівий бік — той, куди стукаєш ти; правий — той, де слухає сервіс.

Форми запису

Запис Що робить
-p 8080:80 приймає з усіх інтерфейсів хоста (0.0.0.0) — видно й іншим машинам у мережі
-p 127.0.0.1:8080:80 лише з самого хоста; ззовні капсули не існує
-p 80 хост-порт вибирає Docker випадково; дивитись через docker port
-P опублікувати усі порти, оголошені в образі через EXPOSE, на випадкові

EXPOSE — це документація, а не дія

Інструкція EXPOSE 80 у Dockerfile нічого не публікує. Вона лише записує в метадані образу: «цей сервіс, імовірно, слухає 80». Її читають люди й -P. Сама по собі вона не відчиняє нічого — і це найчастіше хибне очікування новачка.

Дві пастки, які виглядають як магія

Сервіс слухає 127.0.0.1 усередині капсули. Тоді публікація не допоможе: канал приходить у капсулу на її зовнішній інтерфейс, а сервіс слухає лише свою локальну петлю й відповідати не буде. Лікується в сервісі (--bind 0.0.0.0), не в команді запуску.

Порт на хості вже зайнятий. port is already allocated означає рівно те, що написано: хост-порт один на всю машину. Або інший хост-порт, або зупинити те, що його тримає (ss -ltnp | grep :8080 покаже, хто саме).

Таблиця варіантів

Хост-порт для завдань бери за своїм номером у списку групи N:

N % 4 Хост-порт
0 8080
1 8081
2 8082
3 8083
📖 ОПРАЦЮВАТИ

Прочитати Docker docs «Published ports» (розділ Networking) і довідку docker port --help; на своїй машині підняти контейнер із сервісом двічі — без -p і з -p — і щоразу спробувати дістатися до нього з хоста через curl. Додатково: ss -ltnp на хості, щоб побачити, хто саме слухає опублікований порт.

🖥️ ТЕРМІНАЛ — СПРОБУЙ САМ
🎯 ЗАВДАННЯ
  1. Підняти контейнер із HTTP-сервером без -p і зафіксувати точний результат curl з хоста (код 000 / Connection refused); письмово пояснити, чому це очікувана поведінка, а не збій.
  2. Перезапустити той самий контейнер із -p <свій порт>:80, отримати 200 і показати виводом docker port, який саме канал відчинено.
  3. Запустити другий контейнер із тим самим хост-портом, зафіксувати точний текст помилки port is already allocated і пояснити, який саме ресурс тут неподільний.
  4. Порівняти -p 8080:80 і -p 127.0.0.1:8080:80: перевірити доступність з іншого пристрою в тій самій мережі (або через IP хоста) і записати, у якому випадку сервіс видно, а в якому — ні.
  5. Зібрати образ із EXPOSE 80, запустити його без -p і показати, що доступу немає; потім запустити з -P, знайти призначений порт через docker port і письмово сформулювати, що насправді робить EXPOSE.
❓ КОНТРОЛЬНІ ПИТАННЯ
  1. Що означає кожен бік у записі `-p 8080:80` і як не переставити їх місцями?
  2. Чому сервіс, який слухає `127.0.0.1` усередині контейнера, недосяжний навіть з `-p`?
  3. Що робить інструкція `EXPOSE` у Dockerfile, а чого вона не робить?
  4. Чим `-p 8080:80` відрізняється від `-p 127.0.0.1:8080:80` з погляду інших машин у мережі?
  5. Чому два контейнери не можуть опублікувати один і той самий порт хоста?
  6. Як дізнатися, який хост-порт Docker призначив контейнеру, запущеному з `-P`?
✅ САМОПЕРЕВІРКА

Сервіс усередині контейнера слухає 127.0.0.1:80. Запуск зроблено з `-p 8080:80`. Чому `curl http://localhost:8080/` з хоста все одно не працює — і що треба змінити в самому сервісі, а не в команді `docker run`?

🔗 ДЖЕРЕЛА
🥚 ПАСХАЛКА
Спробуй опублікувати порт 3101 — -p 3101:80. У шістнадцятковій це 0xC1D, і С.І.Д. наполягає, що вибрав його не тому, що красиво: «На тій списаній машині в класі мій перший канал відчинявся саме на ньому. Пиксель тоді спав на клавіатурі й один раз наступив на Enter — так я і дізнався, що двері бувають відчинені».
⌨️ КОМАНДИ НА ЦІЙ СТОРІНЦІ 5▾

Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.

docker run
Створює й запускає контейнер з образу (docker run -it ubuntu:24.04 bash — інтерактивно з терміналом; -d — у фоні; --rm — видалити після виходу; -p 8080:80 — прокинути порт).
docker
Клієнт Docker: усі дії з образами й контейнерами — через його підкоманди (docker <підкоманда> --help — довідка).
curl
Консольний HTTP-клієнт: надсилає запит на URL і друкує відповідь (curl -i http://localhost:8080/ — разом із заголовками); також уміє завантажувати файли й скрипти.
docker rm
Видаляє контейнер (docker rm -f name — навіть якщо він ще працює).
grep
Фільтрує текст: виводить лише рядки, що містять шаблон (ps aux | grep bash).

💡 Прочитати README прямо в терміналі: cat README.md (виведе весь файл) або less README.md (посторінково; вихід — клавіша q).
Довідка з будь-якої команди: man curl (повний посібник, вихід — q) або коротко curl --help; для підкоманд — docker run --help, cargo build --help.

усі темиSRS02 →