Порты и сокеты: ss и lsof для поиска слушающего процесса
TCP-порт — число внутри сетевого namespace, а socket — объект ядра с локальным и удалённым endpoint и состоянием. Сообщение «порт занят» означает конфликт listener в той же комбинации address/port/namespace, но ещё не говорит, какое приложение правильно.
Listener и установленное соединение
Команда ss читает таблицы sockets ядра. Флаги -ltn показывают listening TCP без разрешения service names, -p добавляет процесс, если текущему пользователю разрешено его видеть.
ss -ltn
ss -ltnp
ss -tan state establishedLocal Address 127.0.0.1 доступен только через loopback данного namespace. 0.0.0.0 означает все IPv4 addresses, [::] — IPv6 wildcard и иногда также IPv4 через dual-stack policy. Проверяйте фактический вывод, а не делайте универсальное предположение.
Найти owner через lsof
port=8080
lsof -nP -iTCP:"$port" -sTCP:LISTEN
lsof -nP -a -p "$$" -d 0,1,2-nP отключает DNS и преобразование номера порта в service name, делая диагностику быстрее и однозначнее. Опция -a соединяет условия AND; без неё некоторые selections объединяются шире ожидаемого.
Namespace меняет картину
Контейнер имеет собственный network namespace. ss внутри контейнера может видеть listener на 0.0.0.0:3000, а host публикует его на 127.0.0.1:8080 через NAT/proxy. Команда на host и команда в контейнере отвечают на разные вопросы. Записывайте, где именно выполнено наблюдение.
Listener доказывает только bind/listen. Приложение может не быть ready, зависнуть при обработке или отвечать HTTP 500. После ss нужен прикладной probe через curl или клиент протокола.
Ошибка и диагностика
При Address already in use не завершайте первый найденный процесс. Зафиксируйте PID/user/command, unit или container owner и ожидаемую конфигурацию порта. Проверьте IPv4 и IPv6 listeners, namespace и socket activation systemd. Если порт принадлежит правильному старому экземпляру, причина может быть в двойном запуске deployment, а kill лишь временно скрывает orchestration defect.
Практика
Найдите на своей системе один TCP listener и опишите его address scope. Сопоставьте порт процессу через ss -p или lsof, затем проверьте команду/родителя через ps. Если подходящих listeners нет, запустите временный python3 -m http.server 18080 --bind 127.0.0.1, исследуйте его и завершите по сохранённому PID.
Что важно запомнить
- Listener, established connection и готовый application endpoint — разные состояния.
- Address, port и network namespace нужно рассматривать вместе.
- Перед остановкой процесса установите owner: unit, container, user и причину двойного запуска.