Address already in use (Bind failed) 문제 해결방법
"Address already in use" Bind 에러란 ?
서버 역할을 하는 프로그램(nginx, tomcat, java(spring), python(django), nodejs 등)이 리눅스 서버내에서 특정 IP 주소와 Port 번호를 사용하려고 할때 bind 시스템콜을 사용하게됩니다. 그런데 이미 다른 프로세스가 해당 Port 번호를 이미 사용하고 있을때 포트 충돌로 인한 Bind Failed 에러가 발생 할 수 있습니다.
아래와 같이 다양한 상황에서 Bind Failed 에러가 발생할 수 있습니다.
- Nginx 에러 메시지 예시
Nov 27 03:23:53 ubuntu nginx[13081]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) Nov 27 03:23:54 ubuntu nginx[13081]: nginx: [emerg] still could not bind() Nov 27 03:23:54 ubuntu systemd[1]: nginx.service: Control process exited, code=exited status=1 Nov 27 03:23:54 ubuntu systemd[1]: nginx.service: Failed with result 'exit-code'. Nov 27 03:23:54 ubuntu systemd[1]: Failed to start A high performance web server and a reverse proxy server.
- Java 에러 메시지 예시
java.net.BindException: Address already in use (Bind failed)
- Python 에러 메시지 예시
OSError: [Errno 98] Address already in use
- Docker 에러 메시지 예시
Error starting userland proxy: listen tcp 0.0.0.0:2224: bind: address already in use
- Nodejs 에러 메시지 예시
events.js:183 throw er; // Unhandled 'error' event ^ Error: listen EADDRINUSE 0.0.0.0:80 at Object._errnoException (util.js:1022:11) at _exceptionWithHostPort (util.js:1044:20) at Server.setupListenHandle [as _listen2] (net.js:1367:14) at listenInCluster (net.js:1408:12) at doListen (net.js:1517:7) at _combinedTickCallback (internal/process/next_tick.js:141:11) at process._tickCallback (internal/process/next_tick.js:180:9) at Function.Module.runMain (module.js:695:11) at startup (bootstrap_node.js:188:16) at bootstrap_node.js:609:3
문제 해결 방법:
많은 블로그와 게시글들에서 위 "Address already in use" Bind 문제 해결방식으로 충돌되는 포트번호를 쓰고 있는 프로세스를 찾아내서 kill 명령어 등을 통해서 종료시키는 방법을 이야기하고 있습니다.
(주의: 무턱대고 kill -9 를 사용하는것이 올바른 해결책은 아닙니다.)
우선 아래와같은 명령어(netstat, fuser, ss, lsof)들을 통해서 특정 포트번호(예: 80) 을 사용하고 있는 프로세스를 찾아 낼 수 있습니다.
$ sudo netstat -tnlp | grep 80 tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 13029/node
$ sudo fuser -v 80/tcp USER PID ACCESS COMMAND 80/tcp: root 13029 F.... node
$ sudo ss -lptn 'sport = :80' State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("node",pid=13029,fd=12))
$ sudo lsof -i :80 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 13029 root 12u IPv4 128546 0t0 TCP *:http (LISTEN)
위와 같이 찾아낸 프로세스 정보를 토대로 먼저 어떤 프로세스인지를 확인해야합니다.
꼭 80 포트가 아니더라도 다른포트번호로 동일한 이슈가 발생했을때에도 왜 그 포트를 사용중이고 누가 사용중인지 꼭 확인하고 종료 시켜야합니다.
또한 kill 은 단순히 프로세스를 종료시키는 명령어가 아닙니다. 특정 프로세스에게 kill -l 을 통해서 확인해보면 보낼 수 있는 시그널을 확인할 수 있고 9번(SIGKILL) 은 강제종료이고 15번(SIGTERM) 이 일반적인 종료의 시그널입니다. 9번(SIGKILL)으로 강제종료를 시키게 되면 시스템 자원해지를 정상적으로 하지 못하고 종료가 될 수 있고 이는 다른 부작용(side effect) 가 발생할 여지가 있습니다. 따라서 기본적으로는 15번(SIGTERM) 을 추천하고 만약에 다른방법으로 정상종료 시킬수가 있다면 (예: service nginx stop) 정상적으로 해지절차를 밟을 수있는 방법으로 종료시키는것을 추천드립니다 !
$ whatis kill kill (1) - send a signal to a process kill (2) - send signal to a process
$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
kill -9 를 무턱대고 사용하면 안되는 이유 (예시 실습)
$ htop
# 다른 터미널 통해서 htop pid 얻어내서 강제종료 vs 정상종료 비교
$ pkill -9 htop $ pkill htop
* 참고: 아래 이미지는 강제종료(비정상 종료)되었을때 예시 이미지 입니다.