Перейти к содержанию

Запуск, порты и остановка контейнера

Образ API уже описан и имеет явный контекст. Теперь создадим контейнер, опубликуем порт и проследим его жизненный цикл. Результатом самостоятельного выполнения должен стать читаемый каталог по адресу компьютера и понимание различия между остановкой существующего экземпляра и созданием нового.

Приложение внутри контейнера слушает порт 4100 на 0.0.0.0. Пользователь компьютера будет обращаться к 127.0.0.1:4100. Совпадение номеров удобно для первого упражнения, но не является обязательным: между этими адресами есть настройка Docker. Все действия ниже ожидаемые; ни запуск, ни HTTP-запросы при написании материалов не выполнялись.

Создадим именованный экземпляр

Из корня professorweb-container-lab читатель использует image из предыдущего состояния. Сначала убедитесь, что имя pw-catalog-api ещё не занято другим экземпляром и выбран локальный context.

docker run --detach --name pw-catalog-api --publish 127.0.0.1:4100:4100 professorweb-container-lab-api:lab-05

Флаг detach оставляет основной процесс работать без удержания текущего терминала. Имя делает последующие операции точными: мы будем обращаться к своему экземпляру, а не копировать случайный ID. Run создаёт новый контейнер из выбранного image и запускает команду по умолчанию. Это отличается от start, который нужен для уже созданного контейнера.

Publish связывает адрес и порт Docker-хоста с внутренним портом контейнера. Здесь явно указан loopback хоста. Без такого адреса обычная публикация может сделать сервис доступным на других интерфейсах. По текущей документации loopback ограничивает внешнюю доступность; у Engine старее 28.0.0 были особые ограничения этой гарантии. Для курса используйте актуальный Engine и сверяйте его версию. Детали приведены в документации публикации портов.

Если context направлен на удалённый Engine, Docker-хостом является удалённая машина. Адрес 127.0.0.1 в параметре публикации относится к ней, а не автоматически к вашему ноутбуку. Поэтому первый запуск выполняем на понятном локальном окружении после урока о context. Подмена номера порта не исправляет ошибочно выбранный сервер.

Сопоставим два адреса

Настройка Место действия Роль
listen 0.0.0.0:4100 в Node Внутри контейнера Процесс принимает сетевой запрос
EXPOSE 4100 Метаданные image Описан предполагаемый внутренний порт
127.0.0.1:4100:4100 Docker-хост → контейнер Создано правило публикации
localhost в браузере Машина пользователя Выбран адрес обращения пользователя

Если Node слушает только собственный loopback внутри контейнера, публикация не обязана сделать процесс доступным через сетевой интерфейс контейнера. Если Node слушает правильно, но порт не опубликован, адрес компьютера тоже не начнёт работать автоматически. И наконец, EXPOSE не создаёт ни слушающий процесс, ни правило publish. Это три отдельные части маршрута запроса.

Номер слева можно изменить, например на 4188, сохранив внутренний 4100. Тогда браузер обращается к 127.0.0.1:4188, а сервер продолжает слушать 4100. Номер справа обязан соответствовать фактическому порту приложения. Для такого изменения понадобится новый контейнер с новым параметром публикации, а не редактирование произвольной строки в работающем экземпляре.

Посмотрим процесс и журнал

После самостоятельного запуска читатель выполняет отдельные чтения состояния:

docker container ls
docker logs pw-catalog-api
curl --include http://127.0.0.1:4100/api/courses

В списке должен быть запущенный pw-catalog-api с ожидаемой публикацией. Журнал должен содержать собственное сообщение catalog_api_listening:4100. Ответ каталога должен иметь статус 200 и четыре записи. Эти критерии не означают, что реальный вывод будет дословно совпадать с заранее напечатанным терминалом; ID контейнера, время и формат списка определяет Engine читателя.

Если контейнер быстро завершился, обычный список запущенных его не покажет. Используйте docker container ls --all и прочитайте logs по собственному имени. Неверный PORT или отсутствующий файл способны завершить процесс либо привести к ответу 503. При 503 сервер уже доступен: публикация порта сработала, а проблема находится в чтении каталога.

Остановим и запустим тот же экземпляр

docker stop pw-catalog-api
docker container ls --all
docker start pw-catalog-api

Stop направляет сигнал основному процессу и ожидает его завершения. Наш сервер имеет обработчик SIGTERM и закрывает HTTP-сервер. Запасной таймер приложения ограничивает ожидание собственным временем, а политика Engine также влияет на принудительную остановку. Учебный код не доказывает, что произвольные фоновые задачи будущего проекта завершатся корректно: сейчас таких задач нет.

Остановленный контейнер остаётся объектом Docker. У него сохраняются имя, выбранный image, параметры публикации и собственный слой. Start возобновляет этот экземпляр, используя его прежнюю конфигурацию. Новая сборка image с тем же тегом не подменяет автоматически содержимое уже созданного контейнера.

Повторный run с тем же именем обычно приведёт к конфликту имени, даже если старый контейнер остановлен. Это ожидаемо: остановка не освободила идентичность объекта. Не исправляйте такую ошибку общей очисткой машины. Сначала выясните, нужен ли существующий экземпляр, затем либо запустите его, либо создайте другой с новым именем и свободным host-портом.

Где искать конфликт порта

Если host-порт 4100 занят, Engine может отказать в публикации. Посмотрите, не остался ли предыдущий учебный экземпляр или собственный Node-процесс на компьютере. Порт приложения внутри разных контейнеров может совпадать; конфликт возникает при попытке занять один и тот же адрес и порт хоста.

Отказ соединения, 404 и 503 объясняются по-разному. При отказе соединения проверяем процесс, context и публикацию. При 404 сервер отвечает, но выбран неизвестный путь. При 503 маршрут /api/courses известен, однако каталог недоступен. Браузерная страница ошибки сама по себе не даёт этой классификации; статус HTTP и logs делают причину конкретнее.

В нашем упражнении нет домена, сертификата и внешнего reverse proxy. Локальная публикация — способ изучить маршрут к процессу. Её нельзя назвать завершённым размещением сайта в интернете. Добавление HTTPS и внешних правил будет отдельным проектным шагом, когда появится подходящее окружение и разрешение на его настройку.

Зафиксируем ожидаемый результат

Исходные файлы этого урока совпадают с предыдущим API: меняется понимание и сценарий выполнения, а не server.mjs. Это явно отмечено в start/after и STATE архива серии. Самостоятельно выполненный сценарий должен показать работающий каталог, остановленный объект и успешный start с прежней публикацией.

Далее изменим порядок инструкций Dockerfile, чтобы зависимость установок от исходного кода была выражена точнее. При этом контейнер жизненного цикла останется старым, пока читатель не создаст экземпляр из нового image. Такая связь поможет избежать распространённой ошибки: ждать обновлённый код после простого start прежнего контейнера.

Оглавление серии