JavaRush /Курсы /Docker for Spring /Smoke-check API и логи контейнера

Smoke-check API и логи контейнера

Docker for Spring
3 уровень , 4 лекция
Открыта

1. «Контейнер запущен» ≠ «сервис работает»

Контейнер в docker ps — ещё не доказательство того, что сервис действительно работает. Контейнер — это всего лишь «коробка» вокруг процесса. Он может быть запущен, а приложение внутри ещё стартует, упало при инициализации контекста, слушает не тот порт или вообще зависло на старте. Поэтому вводим простое правило: успех — это не docker run, успех — это HTTP-ответ + внятные стартовые логи.

Давайте зафиксируем минимальную модель проверки, чтобы она стала не разовой магией, а рефлексом.

На уровне первого запуска сервис считается «живым», когда выполняются два условия. Во-первых, служебный endpoint отвечает — это быстрый индикатор, что приложение поднялось и хотя бы базовая инфраструктура Spring Boot работает. Во-вторых, прикладной endpoint тоже отвечает — это подтверждение, что поднялся не только HTTP-слой, но и реально работает ваш контроллер и ваш код, а не просто «заглушка здоровья». И параллельно мы читаем логи, чтобы понимать, что происходило при старте, а не гадать по симптомам.

Вот как это выглядит в виде маленькой схемы:

flowchart TD
    %% Сначала проверяем факт HTTP-ответа, а потом уже углубляемся в детали
    A[Контейнер запущен] --> B{"HTTP отвечает?"}
    B -->|Нет| C[Смотрим docker ps + порты + docker logs]
    B -->|Да| D{"Health OK? /actuator/health"}
    D -->|Нет| E[Читаем логи: что не поднялось]
    D -->|Да| F{"Бизнес endpoint OK? /api/catalog/items"}
    F -->|Нет| G[Логи + проверка правильного URL]
    %% Если дошли сюда — для текущего шага считаем сервис живым
    F -->|Да| H[Сервис считается живым для текущего шага]

Обратите внимание на важную психологическую штуку: эта схема специально устроена так, чтобы вы не «тыкались» случайными командами. Сначала наблюдаем, потом проверяем, потом читаем симптомы.

2. Smoke-check №1: проверяем /actuator/health

Первый smoke-check должен быть быстрым, предсказуемым и не зависеть от демо-данных или сложной бизнес-логики. Поэтому начинаем с Actuator endpoint /actuator/health. В нашем учебном сервисе он уже есть как часть операционного baseline, и его задача проста: сказать «приложение живо» в понятном JSON-формате. Это не про красоту, а про диагностику.

Сначала убедимся, что контейнер вообще запущен и у него то же имя, с которым мы его поднимали: catalog-service.


# Проверяем, что контейнер действительно запущен и порт проброшен наружу
docker ps
# CONTAINER ID   NAMES           STATUS          PORTS
# 4c2a...         catalog-service Up 15 seconds  0.0.0.0:8080->8080/tcp

Теперь делаем запрос на health. Если у вас есть curl, используем его:


# Smoke-check: проверяем, что Actuator отвечает и приложение вообще поднялось
curl http://localhost:8080/actuator/health
# {"status":"UP"}

Если вы на Windows и curl ведёт себя неожиданно, потому что PowerShell иногда подсовывает свой алиас, можно использовать Invoke-RestMethod:


# Для PowerShell это удобный «родной» способ сделать HTTP-запрос
Invoke-RestMethod http://localhost:8080/actuator/health
# status
# ------
# UP

Смысл ответа на сегодня максимально конкретный. Нас не интересует «всё ли идеально» — это будет позже. Нас интересует: есть ли ответ вообще и есть ли в нём UP.

Чуть расширим проверку: иногда полезно сразу видеть HTTP-статус. Для этого можно добавить флаг -i, который покажет заголовки:


# -i показывает статус и заголовки — удобно сразу увидеть 200/401/503 и т.д.
curl -i http://localhost:8080/actuator/health
# HTTP/1.1 200
# Content-Type: application/vnd.spring-boot.actuator.v3+json
#
# {"status":"UP"}

Если вы получили 200 и UP, это означает, что приложение уже подняло веб-слой и базовая инфраструктура Spring Boot внутри контейнера жива. Если вы получили Connection refused, это не «Actuator сломан», а сигнал, что ваш компьютер не смог подключиться к порту: либо контейнер не запущен, либо порт не опубликован, либо приложение ещё не поднялось, либо вы проверяете не тот порт.

И да, на старте бывает совершенно нормальная ситуация: контейнер уже запущен, а приложение ещё грузится. Тогда curl может не успеть с первого раза. Это не повод переустанавливать Docker или уходить в философию. Просто подождите несколько секунд и повторите запрос. Spring Boot — штука бодрая, но не телепатическая: ему нужно время, чтобы развернуть контекст.

3. Smoke-check №2: проверяем /api/catalog/items

После health делаем вторую проверку — прикладной endpoint. В нашем проекте это GET /api/catalog/items. Он хорош тем, что сразу проверяет несколько вещей: работает маршрутизация, поднялся контроллер, сервисный слой не падает на старте и ответ сериализуется в JSON. Для первого запуска этого более чем достаточно: мы не строим тестовую пирамиду, а оказываем первую помощь на месте.

Выполним запрос:


# Прикладной smoke-check: проверяем, что работает ваш контроллер и сериализация ответа
curl http://localhost:8080/api/catalog/items
# [{"id":1,"sku":"SKU-001","title":"Demo item","price":10.00,"status":"ACTIVE"}]

Точный JSON и количество элементов могут отличаться в зависимости от того, как подготовлен starter repo, но логика та же. Вам важно увидеть две вещи: ответ приходит и он похож на JSON — не на HTML, не на «whitelabel error page» и не на тишину.

Если хотите проверять аккуратнее и приучать себя думать статус-кодами, можно сделать так:


curl -i http://localhost:8080/api/catalog/items
# HTTP/1.1 200
# Content-Type: application/json
#
# [...]

Теперь давайте разберём типовые варианты «что пошло не так».

Если вместо JSON вы видите 404, это часто означает банальную вещь: вы ошиблись в URL или приложение стартовало, но endpoint не зарегистрирован, например если вы случайно дёргаете /api/catalog/item вместо /api/catalog/items. И здесь важно не превращаться в «мага, который читает судьбу по 404». Мы не гадаем — мы открываем логи и смотрим, поднялся ли вообще контроллер и дошло ли приложение до конца старта.

Если вы видите 500, контейнер и приложение, скорее всего, живы, раз уж ответ вообще пришёл. Но внутри произошла ошибка при обработке запроса. На этом дне мы не уходим глубоко в исправление бизнес-логики, но обязаны уметь увидеть эту ошибку в логах, иначе Docker быстро превратится для вас в «чёрный ящик».

На этом шаге уже должно появиться полезное ощущение: health — это «пульс есть», а /api/catalog/items — это «пациент уже может произнести своё имя и дату рождения». Не медицинская аналогия века, но для первого запуска работает.

4. Читаем стартовые логи

Логи — это ваша главная «камера наблюдения» за тем, что реально происходит внутри контейнера. Начинающие часто относятся к docker logs как к инструменту «только когда всё сломалось». Но инженерный подход другой: логи читаются каждый раз после старта, даже если всё вроде бы нормально. Это как посмотреть в зеркало перед выходом: можно и не смотреть, но потом будет обидно, когда выяснится, что на голове у вас «проект собрался, но не стартовал».

Самая простая команда, и самая полезная на старте, выглядит так:


docker logs catalog-service

Но на практике вы быстро заметите: логов может быть много, и чаще всего нужен именно «хвост» старта. Поэтому удобно ограничивать вывод:


# Берём «хвост» логов, чтобы быстро увидеть старт, ошибку или строку Started ...
docker logs --tail 80 catalog-service
# ... Starting CatalogApplication ...
# ... Tomcat started on port 8080 (http) ...
# ... Started CatalogApplication in 2.4 seconds ...

Что именно мы ищем в стартовых логах? Не нужно читать каждую строчку как художественный роман — Spring Boot не всегда пишет сюжетно. Нам нужны несколько сигнальных моментов.

Во-первых, в логах должно быть видно, что приложение действительно стартует, а не мгновенно завершается. Обычно это строка вида Starting ....

Во-вторых, должен появиться сигнал, что веб-сервер поднялся и слушает порт. В классическом случае это что-то вроде Tomcat started on port 8080 (http).

В-третьих, мы ищем финальную строку, или близкую к ней по смыслу, о том, что приложение закончило старт. Как правило, это Started ... in ... seconds. Это одна из самых ценных строчек на этом этапе: она превращает старт из «кажется, работает» в «приложение дошло до конца инициализации».

Если вам нужно наблюдать старт в реальном времени, можно открыть второй терминал и включить «подписку» на логи:


# -f = follow, логи будут идти потоково (остановка вывода — Ctrl+C)
docker logs -f catalog-service

Теперь важный момент: логи и HTTP-проверка должны жить вместе в вашей голове. Например, ситуация: curl /actuator/health даёт Connection refused. Что вы делаете? Не переписываете Dockerfile в панике. Вы открываете docker logs и смотрите: приложение вообще стартует? Может, оно упало на старте. И если да, то в логах будет конкретная причина, а не ощущение, что «вселенная против вас».

Чтобы сделать это ещё более механическим и меньше зависеть от настроения, можно держать у себя такой мини-набор команд, буквально как чек:


docker ps
curl -i http://localhost:8080/actuator/health
curl -i http://localhost:8080/api/catalog/items
docker logs --tail 80 catalog-service

Это не «скрипт», а привычка. Когда такая привычка появляется, Docker перестаёт быть страшным: он становится предсказуемым.

5. Склеиваем картину: HTTP-симптомы + логи = быстрый диагноз

Самая частая проблема новичка в Docker звучит так: «У меня не работает». Это плохая формулировка не потому, что вы плохой человек, а потому, что она не помогает мозгу выбрать следующий шаг. Нам нужно научиться превращать «не работает» в наблюдаемую картину: что показывает curl, что показывает docker ps, что показывают логи.

У этого первого контейнера есть полезный ритуал, который лучше прогонять целиком, а не вспоминать кусками:

  1. ./gradlew bootJar — собираем артефакт.
  2. java -jar build/libs/docker-java-catalog-service-0.0.1-SNAPSHOT.jar — один раз убеждаемся, что сам jar жив без Docker.
  3. docker build -t docker-java-catalog-service:day3 . — собираем image из корня проекта.
  4. docker run --name catalog-service -p 8080:8080 docker-java-catalog-service:day3 — запускаем контейнер.
  5. curl http://localhost:8080/actuator/health — проверяем базовый признак жизни.
  6. curl http://localhost:8080/api/catalog/items — проверяем, что жив и прикладной путь.
  7. docker logs --tail 80 catalog-service — читаем стартовую картину, а не гадаем по одному симптому.

Если после docker build и docker run картина остаётся мутной, не лечите всё как «чисто Docker-проблему». Вернитесь к локальному java -jar: этот шаг быстро отделяет сломанный артефакт от сломанной контейнеризации.

Когда такой минимум повторяется без сюрпризов, Dockerfile перестаёт быть магическим файлом и превращается в обычный способ управлять запуском.

Ниже небольшая таблица-шпаргалка, которая помогает не метаться. Это не энциклопедия, а минимальный «переводчик симптомов» для первого запуска.

Что вы видите в curl Что это чаще всего означает Что проверить в первую очередь
Failed to connect / Connection refused До порта никто не слушает: контейнер не запущен, порт не опубликован, приложение не подняло web-сервер docker ps, секция PORTS, затем docker logs --tail 80
HTTP 200 на /actuator/health, но 404 на /api/catalog/items Приложение поднялось, но вы дергаете не тот URL или endpoint реально отсутствует Сверить путь, затем docker logs на наличие ошибок маппинга/старта
HTTP 500 на /api/catalog/items Endpoint найден, но обработка запроса падает внутри приложения docker logs (там будет stack trace / причина)
Долго висит без ответа (timeout) Контейнер жив, но приложение может зависнуть на старте или «подвиснуть» на обработке docker logs -f, посмотреть, дошёл ли старт до Started ...

Обратите внимание: почти в каждой строке таблицы всплывают логи. Это нормально. В контейнерном мире логи — это «чёрный ящик», который вдруг становится прозрачным, потому что вы умеете пользоваться docker logs.

Ещё один практический приём: когда вы видите проблему, попробуйте сформулировать её через один из двух вопросов. Либо «процесс жив?» — это видно по docker ps и логам, — либо «HTTP отвечает?» — это видно по curl. Когда вы научитесь честно отвечать на эти вопросы, половина типовых проблем перестанет быть загадкой.

Когда image уже собран и контейнер запущен, помогает ещё более короткий runtime-чек в виде текста — его даже можно сохранить себе в заметки:


1) Есть контейнер в docker ps?
2) Есть ли опубликованный порт 8080?
3) Отвечает ли /actuator/health?
4) Отвечает ли /api/catalog/items?
5) Если что-то нет — открыть docker logs и искать момент, где всё пошло не так

Да, это почти инструкция по сборке шкафа. Но шкаф хотя бы можно собирать молча. А Spring Boot, если его не слушать, начинает «мстить» stack trace’ами.

6. Типичные ошибки при smoke-check и логах

Ошибка №1: остановиться на docker ps и объявить победу.
Очень хочется: контейнер в списке — значит всё ок. Но контейнер может быть “Up”, а приложение внутри всё ещё стартует или уже упало и перезапустилось — в более сложных сценариях. Даже на этом раннем шаге дисциплина простая: после запуска контейнера вы делаете хотя бы один HTTP-запрос и смотрите стартовые логи. Это не паранойя, а гигиена.

Ошибка №2: проверять только /api/catalog/items и игнорировать /actuator/health.
Если прикладной endpoint не отвечает, вы сразу проваливаетесь в мир «слишком много причин». Health endpoint нужен как быстрый фильтр: приложение вообще поднялось или вы стучитесь в пустоту? Это экономит минуты, а иногда и часы, и помогает не ругаться на контроллеры, когда проблема на самом деле в старте приложения.

Ошибка №3: путать Connection refused и 404, называя это одинаково «не работает».
Connection refused означает, что соединение не установилось: на этом порту никто не слушает или вы подключаетесь не туда. 404 означает, что вы подключились к приложению, но оно не нашло маршрут. Это две разные вселенные. В первой вы смотрите на контейнер, порты и старт. Во второй — на правильность URL, маппинги и, конечно, логи.

Ошибка №4: читать одну последнюю строчку логов и пытаться по ней понять всё.
Spring Boot умеет красиво падать, но причина почти всегда лежит выше по логу. Если приложение не стартовало, в логах обычно есть момент «вот здесь всё пошло не туда»: исключение, сообщение о порте, отсутствие конфигурации. Поэтому на старте лучше смотреть хотя бы последние 50–100 строк. --tail 80 — хороший рабочий минимум. Ищите связный кусок: от Starting до Started или до явной ошибки.

Ошибка №5: искать логи по случайному container ID, потому что контейнер запускали без имени.
Технически так жить можно, но это боль. Когда у контейнера есть имя catalog-service, вы просто пишете docker logs catalog-service и не тратите ресурсы мозга на копипаст ID. В реальной разработке это мелочь, которая экономит десятки микрострессов в день. А микрострессы, как известно, отлично складываются в состояние «почему я устал, я же ничего не делал».

1
Задача
Docker for Spring, 3 уровень, 4 лекция
Недоступна
Smoke-check health и прикладного endpoint
Smoke-check health и прикладного endpoint
1
Задача
Docker for Spring, 3 уровень, 4 лекция
Недоступна
Сохранение логов и результатов smoke-check в файлы
Сохранение логов и результатов smoke-check в файлы
1
Опрос
Spring Docker, 3 уровень, 4 лекция
Недоступен
Spring Docker
Сборка и запуск сервиса
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ