개발 서버 배포가 address already in use로 실패했습니다. 급해서 아무 PID나 kill했다가 같은 서버의 다른 테스트 작업까지 끊어버렸습니다.
포트를 잡은 프로세스를 정확히 찾고 안전하게 종료하는 순서가 필요합니다. 강제 종료는 마지막 선택이어야 합니다.
포트 사용 중인 프로세스 강제 종료
서버에서 새 서비스를 시작하려 할 때 address already in use 또는 port already in use 에러가 뜨는 경우가 있습니다. 이 장에서는 포트를 점유한 프로세스를 찾고, 상황에 맞게 종료하는 방법을 다룹니다.
- 1포트 충돌(address already in use) 발생 원인과 소켓 구조
- 2lsof / ss / fuser로 포트 점유 프로세스 식별
- 3kill SIGTERM·SIGKILL 시그널 차이와 올바른 종료 순서
- 4systemd Restart=always 서비스의 포트 충돌 해결
- 5Docker 컨테이너 포트 점유 해제 방법
- 6TIME_WAIT 소켓으로 인한 바인딩 실패 대응
sudo ss -tulnp | grep :포트번호python3 -m http.server 8888 &sudo lsof -i :8888systemctl list-units --type=service --state=running포트 충돌의 원인과 구조
서비스를 재시작했는데 "Address already in use: 8080"이 납니다. systemctl stop을 해서 프로세스를 내렸는데도 포트가 여전히 잡혀있습니다. 뭔가가 포트를 붙들고 있는데 무엇인지, 왜 stop을 해도 안 죽었는지 알 방법이 없습니다. 포트 충돌의 구조를 알아야 ss와 lsof로 원인을 찾고 올바르게 처리할 수 있습니다.

"port already in use" 에러란
하나의 포트(IP:포트 조합)는 동시에 하나의 프로세스만 바인딩할 수 있습니다. 이미 사용 중인 포트에 새 프로세스가 바인딩하려 하면 OS가 오류를 반환합니다.
Node.js: Error: listen EADDRINUSE: address already in use :::80
Python: OSError: [Errno 98] Address already in use
Java: java.net.BindException: Address already in use
nginx: bind() to 0.0.0.0:80 failed (98: Address already in use)
포트 충돌이 발생하는 주요 원인
| 원인 | 설명 |
|---|---|
| 이전 프로세스가 살아있음 | 서비스 재시작 시 기존 인스턴스가 종료되지 않음 |
| 좀비 소켓 (TIME_WAIT) | TCP 연결 종료 후 소켓이 일정 시간 유지됨 |
| 다른 서비스 충돌 | 예: nginx와 Apache 둘 다 80 포트 사용 설정 |
| Docker 컨테이너 | 컨테이너가 호스트 포트를 점유 중 |
| systemd 재시작 정책 | Restart=always로 인해 프로세스가 계속 살아남 |
포트와 소켓의 관계
프로세스 A (nginx)
└── 소켓 생성: SOCK_STREAM
└── bind(0.0.0.0:80) ← 포트 점유
└── listen() 상태로 대기
프로세스 B (새 nginx)
└── 소켓 생성: SOCK_STREAM
└── bind(0.0.0.0:80) → EADDRINUSE (에러!)
0.0.0.0:80은 "모든 인터페이스의 80번 포트"를 의미합니다. 반면 127.0.0.1:80으로 바인딩된 프로세스가 있어도, 0.0.0.0:80에 바인딩하려 하면 충돌이 발생합니다.
포트 상태 종류
LISTEN — 포트를 열고 새 연결 대기 중 (점유 중)
ESTABLISHED — 현재 활성 연결 (통신 중)
TIME_WAIT — 연결 종료 후 일정 시간 유지 (보통 60~120초)
CLOSE_WAIT — 원격 측이 연결 종료 (로컬 애플리케이션이 close 안 함)
TIME_WAIT 상태 소켓은 프로세스가 없어도 잠시 포트를 점유합니다.
포트 점유 프로세스 확인 도구
포트 충돌을 확인하려 합니다. 어떤 프로세스가 포트를 쓰는지, PID는 무엇인지, 시스템에 따라 lsof가 있는 서버도 있고 ss밖에 없는 서버도 있습니다. 두 도구의 사용법을 알아야 어떤 환경에서든 포트 점유 프로세스를 찾고 처리할 수 있습니다.
1. lsof: 열린 파일/소켓 목록
lsof(list open files)는 프로세스가 열고 있는 파일, 소켓, 포트 정보를 보여줍니다.
# 실습 디렉토리 준비
mkdir -p /tmp/networking/part3/exam_13 && cd /tmp/networking/part3/exam_13
# 80번 포트를 사용 중인 프로세스 확인
$ sudo lsof -i :80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1234 root 6u IPv4 12345 0t0 TCP *:http (LISTEN)
nginx 1235 nobody 6u IPv4 12345 0t0 TCP *:http (LISTEN)
# 특정 포트 범위 확인
$ sudo lsof -i :8080-8090
# TCP만 확인
$ sudo lsof -i TCP:80
# 특정 프로세스가 열고 있는 포트 확인
$ sudo lsof -p 1234 -i
- PID—포트를 점유한 프로세스 ID를 정확히 확인합니다
- COMMAND—종료 대상이 의도한 프로세스인지 이름을 확인합니다
- LISTEN 해제—종료 후 같은 포트가 더 이상 LISTEN 상태가 아닌지 봅니다
출력 필드 의미:
COMMAND: 프로세스 이름PID: 프로세스 IDUSER: 실행 사용자FD: 파일 디스크립터NODE NAME: 소켓 주소 (IP:포트)
2. ss: 소켓 상태 조회 (권장)
ss는 netstat의 현대적 대체 도구로 더 빠르고 정확합니다.
# 80번 포트 상태 확인
$ ss -tulnp | grep :80
tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))
# 옵션 설명:
# -t: TCP 소켓
# -u: UDP 소켓
# -l: LISTEN 상태 (대기 중인 포트)
# -n: 이름 해석 없이 숫자로 표시
# -p: 프로세스 정보 포함
# 특정 포트 연결 상태 포함 조회
$ ss -tnp | grep :80
3. fuser: 파일/소켓 사용 프로세스
# 80번 TCP 포트를 사용하는 PID 확인
$ fuser 80/tcp
80/tcp: 1234 1235
# 프로세스 정보 포함
$ fuser -v 80/tcp
USER PID ACCESS COMMAND
80/tcp: root 1234 F.... nginx
nobody 1235 F.... nginx
# 확인 없이 즉시 종료
$ sudo fuser -k 80/tcp
# 확인하면서 종료
$ sudo fuser -ki 80/tcp
4. netstat (구형, 참고용)
# 80번 포트 확인 (일부 구형 환경)
$ netstat -tlnp | grep :80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1234/nginx
도구 비교
| 도구 | 장점 | 단점 |
|---|---|---|
ss | 빠름, 현대적 | 일부 구형 OS 미설치 |
lsof | 상세 정보 | 느릴 수 있음 |
fuser | 직접 kill 가능 | 정보 제한적 |
netstat | 익숙함 | 느림, deprecated |
목표
포트를 점유한 프로세스를 세 가지 방법으로 확인하고 결과를 비교합니다.
단계
1단계: 테스트용 서버 프로세스 실행 — nc로 간단한 리스닝 서버를 띄웁니다.
# Python으로 간단한 HTTP 서버 실행 (8888번 포트)
$ python3 -m http.server 8888 &
[1] 12345
Serving HTTP on 0.0.0.0 port 8888 ...
2단계: lsof로 확인 — 포트를 점유한 프로세스의 PID와 경로를 확인합니다.
$ sudo lsof -i :8888
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 12345 user 3u IPv4 98765 0t0 TCP *:8888 (LISTEN)
3단계: ss로 확인 — 소켓 상태와 PID를 함께 출력합니다.
$ ss -tulnp | grep :8888
tcp LISTEN 0 5 0.0.0.0:8888 0.0.0.0:* users:(("python3",pid=12345,fd=3))
4단계: fuser로 확인 — 포트를 직접 지정해 사용 중인 PID를 찾습니다.
$ fuser 8888/tcp
8888/tcp: 12345
5단계: PID로 프로세스 상세 정보 확인 — PID로 어떤 프로세스인지 상세 확인합니다.
# PID로 프로세스 정보 조회
$ ps aux | grep 12345
user 12345 0.0 0.1 15000 8000 pts/0 S 10:30 0:00 python3 -m http.server 8888
# 프로세스 실행 경로 확인
$ ls -la /proc/12345/exe
lrwxrwxrwx 1 user user 0 Mar 27 10:30 /proc/12345/exe -> /usr/bin/python3
6단계: 테스트 서버 종료 — 실습 후 nc 프로세스를 정리합니다.
이 명령은 프로세스를 종료해 연결 중인 사용자나 배치 작업을 중단시킬 수 있습니다. PID와 프로세스 이름이 목표 서비스인지 확인한 뒤 실행하세요.
$ kill %1 # 백그라운드 작업 번호로 종료
# 또는
$ kill 12345
정리: 포트 확인 원라이너
# 가장 간결한 방법
$ sudo ss -tulnp | grep :포트번호
# PID만 빠르게 확인
$ fuser 포트번호/tcp
목표
포트를 점유한 프로세스를 상황별로 올바르게 종료합니다.
단계
1단계: 테스트 환경 준비 — 종료 연습용 프로세스를 두 개 띄웁니다.
# 두 개의 서버 프로세스를 다른 포트로 실행
$ python3 -m http.server 9001 &
$ python3 -m http.server 9002 &
$ ss -tulnp | grep '900[12]'
tcp LISTEN 0 5 0.0.0.0:9001 0.0.0.0:* users:(("python3",pid=11111,fd=3))
tcp LISTEN 0 5 0.0.0.0:9002 0.0.0.0:* users:(("python3",pid=11112,fd=3))
2단계: kill 명령어로 PID 종료 — SIGTERM(15)으로 정상 종료를 먼저 시도합니다.
이 명령은 프로세스를 종료해 연결 중인 사용자나 배치 작업을 중단시킬 수 있습니다. PID와 프로세스 이름이 목표 서비스인지 확인한 뒤 실행하세요.
# PID 확인 후 종료
$ PID=$(ss -tulnp | grep :9001 | grep -oP 'pid=\K[0-9]+')
$ echo "종료할 PID: $PID"
$ kill $PID # SIGTERM (정상 종료 요청)
# 응답 없을 때 강제 종료
$ kill -9 $PID # SIGKILL (즉시 강제 종료)
3단계: fuser -k로 포트 직접 종료 — PID를 몰라도 포트 번호만으로 종료할 수 있습니다.
# 포트 번호로 직접 종료 (PID 확인 불필요)
$ sudo fuser -k 9002/tcp
# 확인
$ ss -tulnp | grep :9002
# 아무것도 출력되지 않으면 성공
4단계: kill -9 시그널 차이 이해 — SIGKILL은 프로세스가 무시할 수 없는 강제 종료입니다.
이 명령은 프로세스를 종료해 연결 중인 사용자나 배치 작업을 중단시킬 수 있습니다. PID와 프로세스 이름이 목표 서비스인지 확인한 뒤 실행하세요.
# SIGTERM (15): 정상 종료 요청, 프로세스가 정리 작업 수행 가능
$ kill -15 [PID] # 또는 kill [PID]
# SIGKILL (9): 즉시 강제 종료, OS가 직접 처리
$ kill -9 [PID]
# 실무 권장 순서
$ kill [PID] # 먼저 SIGTERM 시도
$ sleep 5 # 5초 대기
$ kill -9 [PID] # 응답 없으면 SIGKILL
5단계: 종료 후 포트 확인 — 포트가 해제됐는지 ss로 확인합니다.
$ ss -tulnp | grep '900[12]'
# 아무것도 출력되지 않으면 포트 해제 완료
목표
Restart=always 설정으로 인해 kill해도 살아나는 서비스를 올바르게 중지합니다.
systemd Restart 정책 이해
# 서비스 설정 파일 예시
$ cat /etc/systemd/system/myapp.service
[Unit]
Description=My Application
[Service]
ExecStart=/usr/bin/node /opt/myapp/server.js
Restart=always # 어떤 이유로든 종료되면 재시작
RestartSec=3 # 3초 후 재시작
[Install]
WantedBy=multi-user.target
단계
1단계: 서비스 상태 확인 — systemctl로 nginx가 실제로 실행 중인지 확인합니다.
# 서비스가 포트를 점유 중인지 확인
$ sudo ss -tulnp | grep :3000
tcp LISTEN 0 128 0.0.0.0:3000 0.0.0.0:* users:(("node",pid=5678,fd=15))
# 해당 PID의 서비스 확인
$ systemctl status $(ps -o unit= -p 5678)
● myapp.service - My Application
Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
Active: active (running)
2단계: kill -9로 시도 (실패 케이스) — systemd 관리 서비스는 kill로 종료해도 즉시 재시작됩니다.
이 명령은 프로세스를 종료해 연결 중인 사용자나 배치 작업을 중단시킬 수 있습니다. PID와 프로세스 이름이 목표 서비스인지 확인한 뒤 실행하세요.
$ sudo kill -9 5678
# 3초 후 확인
$ sleep 4 && sudo ss -tulnp | grep :3000
tcp LISTEN 0 128 0.0.0.0:3000 0.0.0.0:* users:(("node",pid=5901,fd=15))
# PID가 바뀐 채로 다시 살아남!
3단계: systemctl stop으로 올바르게 중지 — systemd가 관리하는 서비스는 반드시 systemctl로 중지합니다.
이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.
# systemd를 통해 서비스 중지 (재시작 정책 무시)
$ sudo systemctl stop myapp.service
# 확인
$ sudo ss -tulnp | grep :3000
# 출력 없음 — 포트 해제됨
# 서비스 상태 확인
$ systemctl status myapp.service
● myapp.service - My Application
Active: inactive (dead)
4단계: 서비스 비활성화 (부팅 시 자동 시작 제거) — disable하면 재부팅 후 자동 시작을 막습니다.
이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.
# 중지 + 부팅 자동 시작 해제
$ sudo systemctl stop myapp.service
$ sudo systemctl disable myapp.service
Docker 컨테이너 포트 점유 해결
Docker 포트 포워딩 충돌과 대화형 모니터링 연계
SRE는 프로세스가 점유한 포트를 강제 해제할 때, 일반 OS 데몬뿐 아니라 커널 레벨에서 Docker 엔진이 기동시킨 포트 바인딩도 함께 통제해야 합니다.
- Docker 컨테이너 포트 점유 해소:
Docker는 컨테이너 실행 시 호스트 커널의 iptables NAT 테이블 체인을 직접 제어하여 포트를 통제합니다. 따라서 일반적인
kill -9로 프로세스가 죽지 않는다면,docker stop <컨테이너명>을 실행하거나 필요시iptables -D명령어를 통해 커널에 주입된 해당 포워딩 규칙을 명시적으로 제거해야 할 수 있습니다. - 대화형 리소스 모니터링 도구 활용:
단순히 포트를 점유한 단일 PID만 죽이기 어려울 경우,
htop과 같은 대화형 프로세스 뷰어를 실행하여 트리 구조로 정렬된 부모-자식 프로세스 계보를 직관적으로 추적해 연관 데몬들을 안전하게 원터치 일괄 종료할 수 있습니다.
Docker 포트 포워딩 충돌과 대화형 모니터링 연계
SRE는 프로세스가 점유한 포트를 강제 해제할 때, 일반 OS 데몬뿐 아니라 커널 레벨에서 Docker 엔진이 기동시킨 포트 바인딩도 함께 통제해야 합니다.
- Docker 컨테이너 포트 점유 해소:
Docker는 컨테이너 실행 시 호스트 커널의 iptables NAT 테이블 체인을 직접 제어하여 포트를 통제합니다. 따라서 일반적인
kill -9로 프로세스가 죽지 않는다면,docker stop <컨테이너명>을 실행하거나 필요시iptables -D명령어를 통해 커널에 주입된 해당 포워딩 규칙을 명시적으로 제거해야 할 수 있습니다. - 대화형 리소스 모니터링 도구 활용:
단순히 포트를 점유한 단일 PID만 죽이기 어려울 경우,
htop과 같은 대화형 프로세스 뷰어를 실행하여 트리 구조로 정렬된 부모-자식 프로세스 계보를 직관적으로 추적해 연관 데몬들을 안전하게 원터치 일괄 종료할 수 있습니다.
Docker 포트 포워딩 충돌과 대화형 모니터링 연계
SRE는 프로세스가 점유한 포트를 강제 해제할 때, 일반 OS 데몬뿐 아니라 커널 레벨에서 Docker 엔진이 기동시킨 포트 바인딩도 함께 통제해야 합니다.
- Docker 컨테이너 포트 점유 해소:
Docker는 컨테이너 실행 시 호스트 커널의 iptables NAT 테이블 체인을 직접 제어하여 포트를 통제합니다. 따라서 일반적인
kill -9로 프로세스가 죽지 않는다면,docker stop <컨테이너명>을 실행하거나 필요시iptables -D명령어를 통해 커널에 주입된 해당 포워딩 규칙을 명시적으로 제거해야 할 수 있습니다. - 대화형 리소스 모니터링 도구 활용:
단순히 포트를 점유한 단일 PID만 죽이기 어려울 경우,
htop과 같은 대화형 프로세스 뷰어를 실행하여 트리 구조로 정렬된 부모-자식 프로세스 계보를 직관적으로 추적해 연관 데몬들을 안전하게 원터치 일괄 종료할 수 있습니다.
# Docker가 포트를 점유 중인 경우
$ sudo ss -tulnp | grep :80
tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("docker-proxy",pid=7890,fd=4))
# 어떤 컨테이너인지 확인
$ docker ps | grep "0.0.0.0:80"
a1b2c3d4e5f6 nginx:latest "/docker-entrypoint…" Up 2 hours 0.0.0.0:80->80/tcp
# 컨테이너 중지
$ docker stop a1b2c3d4e5f6
# 확인
$ sudo ss -tulnp | grep :80
# 출력 없음
현상
포트를 점유한 프로세스를 kill -9로 종료했는데 수 초 후 다시 살아납니다.
이 명령은 프로세스를 종료해 연결 중인 사용자나 배치 작업을 중단시킬 수 있습니다. PID와 프로세스 이름이 목표 서비스인지 확인한 뒤 실행하세요.
$ sudo kill -9 $(fuser 8080/tcp)
$ sleep 3
$ fuser 8080/tcp
8080/tcp: 9999 # 다른 PID로 다시 살아남!
원인 분석
- systemd Restart=always: 가장 흔한 원인. systemd가 프로세스를 자동 재시작
- supervisor/pm2 관리: 프로세스 관리 도구가 재시작
- crontab: 주기적으로 프로세스를 실행하는 크론 작업
- init.d 스크립트: 구형 SysV init 방식의 모니터링
원인 파악 및 해결
이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.
# 1. systemd 서비스인지 확인
$ systemctl list-units --type=service --state=running | grep -i 해당서비스명
# systemd 서비스면
$ sudo systemctl stop 서비스명
# 2. supervisor로 관리되는지 확인
$ sudo supervisorctl status
# supervisor 프로세스면
$ sudo supervisorctl stop 프로그램명
# 3. pm2로 관리되는지 확인 (Node.js)
$ pm2 list
# pm2 프로세스면
$ pm2 stop 앱이름
# 4. crontab 확인
$ sudo crontab -l
$ crontab -l # 현재 사용자
완전 종료 확인 방법
이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.
# 종료 후 5초 대기하며 재확인
$ sudo systemctl stop myapp
$ for i in {1..5}; do
echo "[$i초] 포트 상태:"
ss -tulnp | grep :8080 || echo "포트 해제됨"
sleep 1
done
현상
기존 프로세스를 종료했는데도 새 서비스 시작 시 여전히 포트 충돌이 발생합니다.
이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.
$ sudo systemctl stop nginx
$ sudo systemctl start myapp
Job for myapp.service failed. See 'journalctl -xe'
# Error: bind EADDRINUSE: address already in use :::80
원인 분석
TIME_WAIT 소켓
TCP 연결 종료 후 소켓은 TIME_WAIT 상태로 일정 시간(기본 60초) 유지됩니다.
$ ss -tn | grep TIME-WAIT | grep :80
TIME-WAIT 0 0 192.168.1.50:80 10.0.0.1:54321
SO_REUSEADDR 미설정
애플리케이션이 SO_REUSEADDR 소켓 옵션을 설정하지 않으면 TIME_WAIT 상태의 소켓과 충돌할 수 있습니다.
해결 방법
이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.
# 방법 1: 잠시 대기 (TIME_WAIT 소멸 기다림)
$ sleep 60 && sudo systemctl start myapp
# 방법 2: 커널 파라미터로 TIME_WAIT 재사용 허용
$ sudo sysctl -w net.ipv4.tcp_tw_reuse=1
# 방법 3: ss로 TIME_WAIT 상태 소켓 모니터링
$ watch -n 1 'ss -tn | grep TIME-WAIT | grep :80'
# 방법 4: 남아있는 프로세스 최종 확인
$ sudo lsof -i :80
$ sudo ss -tulnp | grep :80
$ fuser 80/tcp
예방 방법
- 애플리케이션 코드에
SO_REUSEADDR옵션 설정 (개발자에게 요청) - 서비스 중지 후 최소 30초 대기 후 재시작
- systemd 서비스에
TimeoutStopSec설정으로 충분한 종료 시간 확보
배포 시 포트 충돌 시나리오
새 버전 배포 과정에서 포트 충돌은 흔히 발생합니다. 현업에서는 다음과 같은 패턴으로 처리합니다.
표준 대응 절차
이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.
# 1. 포트 점유 상태 확인
$ sudo ss -tulnp | grep :8080
# 2. 점유 프로세스가 서비스인지 확인
$ systemctl status $(sudo ss -tulnp | grep :8080 | grep -oP '"[^"]+",pid=\K[0-9]+' | xargs -I{} ps -o unit= -p {})
# 3. 서비스면 systemctl stop, 아니면 kill
# (서비스)
$ sudo systemctl stop old-service
# (일반 프로세스)
$ sudo kill -TERM [PID]
$ sleep 5
$ sudo kill -9 [PID] 2>/dev/null || true
# 4. 포트 해제 확인
$ sudo ss -tulnp | grep :8080 || echo "포트 해제됨"
# 5. 새 서비스 시작
$ sudo systemctl start new-service
무중단 배포와의 관계
포트 충돌 없이 서비스를 교체하려면:
방법 1: Blue-Green 배포
- 기존: 8080 포트에서 실행
- 새 버전: 8081 포트에서 먼저 시작
- 로드밸런서에서 트래픽을 8081로 전환
- 기존 8080 서비스 중지
방법 2: systemd socket activation
- systemd가 소켓을 관리
- 서비스 재시작 시 소켓은 유지
- 포트 충돌 없이 교체 가능
포트 현황 모니터링
# 시스템 전체 포트 현황 스냅샷
$ sudo ss -tulnp | awk 'NR>1 {print $5, $7}' | sort -k1
# 특정 사용자가 열고 있는 포트
$ sudo lsof -u www-data -i | grep LISTEN
# 포트별 프로세스 매핑 저장
$ sudo ss -tulnp > /var/log/port-snapshot-$(date +%Y%m%d-%H%M%S).txt
알아두어야 할 포트 범위
| 범위 | 이름 | 설명 |
|---|---|---|
| 1-1023 | Well-known | root 권한 필요 (HTTP:80, HTTPS:443) |
| 1024-49151 | Registered | 일반 사용자 가능 (8080, 3000 등) |
| 49152-65535 | Dynamic/Ephemeral | OS가 클라이언트 연결에 자동 할당 |
팁: 애플리케이션은 가능하면 1024 이상 포트를 사용하세요. root 권한 없이 실행 가능하고, 보안상으로도 더 안전합니다.
핵심 명령어 치트시트
이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.
# ===== 포트 점유 확인 =====
sudo ss -tulnp | grep :포트번호 # 권장
sudo lsof -i :포트번호 # 상세 정보
fuser 포트번호/tcp # PID만 빠르게
# ===== 프로세스 종료 =====
kill [PID] # SIGTERM (정상 종료)
kill -9 [PID] # SIGKILL (강제 종료)
sudo fuser -k 포트번호/tcp # 포트로 직접 종료
# ===== systemd 서비스 =====
sudo systemctl stop 서비스명 # 서비스 중지
sudo systemctl disable 서비스명 # 자동 시작 해제
# ===== Docker =====
docker ps | grep 포트번호 # 어떤 컨테이너인지 확인
docker stop 컨테이너ID # 컨테이너 중지
정리
포트 충돌 해결의 핵심은 무엇이 포트를 점유하고 있는지 정확히 파악한 뒤, 그 프로세스의 관리 방식에 맞게 종료하는 것입니다. 단순히 kill -9를 남발하면 systemd나 supervisor에 의해 즉시 재시작될 수 있습니다. 서비스 관리 도구를 통해 올바르게 중지하는 습관을 들이세요.