서버 A가 서버 B와 통신할 수 없는 경우

22 May 2016

dev1 이라는 서버에서 web1 이라는 서버의 80 포트에 접근할 수 없는 경우의 추적 방법에 대하여.

일단 dev1과 동일한 네트워크의 또 다른 서버인 dev2에서는 web1에 접근할 수 있는지 확인해본다. dev2 또한 web1에 접근할 수 없다면 그 들 사이의 네트워크에 문제가 있을 가능성이 높다는 것을 알 수 있다.

만약 dev2는 web1에 접근할 수 있다면 dev1에 문제가 있을 가능성이 높다. 여기서는 dev2는 web1에 접근할 수 있다고 가정한다.

연결되어 있는가?

클라이언트의 네트워크 연결에 문제가 없는지 확인한다. ethtool을 이용해 dev의 네트워크 연결이 활성화 되어 있는지, 즉 이더넷 디바이스가 물리적으로 네트워크가 연결되어 있는지 확인할 수 있다. 어떤 인터페이스를 사용하는지 확실하지 않다면 ifconfig 명령어를 통해 사용 가능한 모든 네트워크 인터페이스 목록과 설정을 확인할 수 있다.

이더넷 디바이스가 eth0에 있었다면 결과는 아래와 같다.

[ec2-user@ip ~]$ sudo ethtool eth0
Settings for eth0:
    ...
    Link detected: yes

마지막 줄에서 Link detected가 yes 이므로, dev1은 물리적으로 네트워크에 연결되어 있음을 알 수 있다.

네트워크 인터페이스가 살아있는가?

다음으로 네트워크 인터페이스가 올바르게 설정되어 있는지 확인한다. ifconfig 명령어에 인터페이스 이름을 인자로 전달해 실행해본다.

[ec2-user@ip ~]$ ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:17:42:1f:18:be
          inet addr:10.1.1.7  Bcast:10.1.1.255  Mask:255.255.255.0
          inet6 addr: fe80::217:42ff:fe1f:18be/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1
          RX packets:2581082 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1505719 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1496333020 (1.3 GiB)  TX bytes:2212749845 (2.0 GiB)

여기서 중요한 부분은 두 번째 줄이다. 호스트의 IP주소와 subnet mask가 나오는데, 이 정보가 호스트에 맞는 설정값인지 확인한다. 인터페이스가 아직 설정되어 있지 않다면 sudo ifup eth0을 실행하여 활성화시켜야 한다.

설정이 잘못되었거나 인터페이스가 활성화되지 않는다면 데비안 계열에서는 /etc/network/interfaces를, 레드햇 계열에서는 /etc/sysconfig/network_scripts/ifcfg-<interface>를 살펴봐야 한다. 네트워크 설정과 관련된 오류는 이 파일에서 수정할 수 있다.

로컬 네트워크에 있는가?

다음으로 기본 게이트웨이가 설정되어 있고, 해당 게이트웨이에 접근할 수 있는지 확인한다. route 명령어는 기본 게이트웨이 정보를 포함한 현재의 라우팅 테이블 정보를 보여준다. -n 옵션은 호스트명이 아닌 IP주소로 보여준다.

[ec2-user@ip ~]$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.1.1.0        *               255.255.250.0   U     0      0        0 eth0
default         10.1.1.0        0.0.0.0         UG    0      0        0 eth0

default로 시작하는 마지막줄을 보면, 호스트가 10.1.1.1.의 게이트웨이를 가지고 있음을 확인할 수 있다. 만약 기본 게이트웨이 설정 정보를 볼 수 없고, 목적지 호스트가 다른 서브넷이라면 이것이 문제일 수 있다. 이 문제를 해결하려면 게이트웨이를 설정해야 한다.

게이트웨이가 확인되면 ping 명령어를 이용해 게이트웨이와 통신할 수 있는지 확인한다.

$ ping -c 5 10.1.1.1

ping에 성공한다면 최소한 해당 네트워크와 통신할 수 있음을 의미한다. 만약 실패한다면, 몇 가지 케이스가 있다. 게이트웨이가 ICMP 패킷을 차단하고 있을 수도 있다. 동일한 서브넷에 있는 다른 호스트에 ping을 시도해보자. 차단되지 않았다면 호스트의 스위치 포트가 VLAN으로 설정되어 있을 가능성이 높다.

DNS가 동작 중인가?

게이트웨이와 통신할 수 있음을 확인했다면 다음으로 DNS 기능이 작동하는지 테스트한다.

$ nslookup web1
Server: 10.1.1.3
Address: 10.1.1.3#53
Name: web1.example.net
Address: 10.1.2.5

위와 같이 출력되면 DNS는 제대로 작동하는 것이다. DNS가 실패하는 경우를 살펴보자.

설정된 네임서버가 없는 경우

$ nslookup web1
;; connection timed out; no servers could be reached

위와 같이 출력되면 호스트에 구성된 네임서버가 없거나, 네임서버에 접근할 수 없음을 의미한다. /etc/resolv.conf에서 네임서버 설정을 확인한다.

네임서버에 접근할 수 없는 경우

$ nslookup web1
search example.net
nameserver 10.1.1.3

위와 같이 나타난다면 네임서버와 접속하는 부분에 문제가 있을 수 있다. 네임서버에 ping을 날려보자.

네임서버에 ping을 보낼 수 없고, 해당 IP 주소가 동일한 서브넷에 있는 경우 해당 네임서버는 완전히 다운됐다고 할 수 있다. ping을 네임서버에 보낼 수 있지만 응답하지 않는 경우 원격 포트가 열려있는지 확인해야 한다.

누락된 검색 경로 또는 네임서버 문제

$ nslookup web1
Server: 10.1.1.3
Address: 10.1.1.3#53
** server can't find web1: NXDOMAIN

두 가지 가능성이 있는데, 하나는 web1의 도메인명이 호스트의 DNS 검색 경로에 있지 않은 경우다. 도메인까지 포함한 전체 주소로 nslookup 명령어를 실행하면 성공할 것이다. 항상 전체 주소에 해당하는 도메인명을 사용하거나, /etc/resolv.conf 파일에서 검색 경로 설정을 추가하면 된다.

만약 도메인까지 포함한 전체 주소로도 실패한다면 문제는 네임서버에 있다. 네임서버가 도메인에 대한 기록을 가지고 있는 것으로 추정된다면 해당 zone의 설정을 확인해야 한다. 재귀적 네임서버인 경우 네임서버에서 재귀적 변환이 제대로 작동하는지 확인하기 위해 다른 도메인을 찾아보면서 테스트한다. 만약 다른 도메인을 찾을 수 있다면 문제가 zone을 포함하고 있는 원격 네임서버에 있는지 확인해야 한다.

원격 호스트로 찾아갈 수 있는가?

일단 DNS는 쟁점에서 제외하고, nslookup을 통해 web1이 10.1.2.5로 정상적으로 변환되는 것을 확인한 후에는 원격 호스트로 라우팅되는지 확인해야 한다. 만약 web1에 ping을 보낼 수 있다면 해당 패킷이 그 호스트까지 라우팅된다는 것을 알 수 있다. ping을 보낼 수 없다면 web1과 같은 네트워크의 다른 호스트에 ping을 보낼 수 있는지 확인해본다. 다른 호스트에는 ping을 보낼 수 있다면 web1이 다운됐거나 요청을 막고있을 가능성이 있다. 원격 네트워크의 어떤 호스트에도 ping을 보낼 수 없다면 패킷이 정상적으로 라우팅되지 않는다고 볼 수 있다. 이런 경우 traceroute을 이용해 호스트 사이의 각 hop을 테스트해볼 수 있다.

$ traceroute 10.1.2.5
traceroute to 10.1.2.5 (10.1.2.5), 30 hops max, 40 byte packets
1 10.1.1.1 (10.1.1.1) 5.432 ms 5.206 ms 5.472 ms
2 web1 (10.1.2.5) 8.039 ms 8.348 ms 8.643 ms

여기서 패킷이 dev1에서 게이트웨이(10.1.1.1)로 가고, 그 다음 홉은 web1임을 알 수 있다. (dev1 -> gateway -> web1) 이는 10.1.1.1이 양쪽 서브넷 모두에 대한 게이트웨이임을 의미한다.

$ traceroute 10.1.2.5
traceroute to 10.1.2.5 (10.1.2.5), 30 hops max, 40 byte packets
1 10.1.1.1 (10.1.1.1) 5.432 ms 5.206 ms 5.472 ms
2 * * *
3 * * *

만약 dev1에서 web1에 ping을 보낼 수 없다면 이런 형태로 출력될 것이다. 게이트웨이에서 web1으로 패킷을 보낼 수 없는 상황이므로 그 사이의 라우터를 확인할 필요가 있다.

$ traceroute 10.1.2.5
traceroute to 10.1.2.5 (10.1.2.5), 30 hops max, 40 byte packets
1 10.1.1.1 (10.1.1.1) 5.432 ms 5.206 ms 5.472 ms
2 10.1.1.1 (10.1.1.1) 3006.477 ms !H 3006.779 ms !H 3007.072 ms

이런 경우는 게이트웨이에서 ping이 timeout 된 것이다. 호스트가 다운되었거나 동일한 서브넷에서도 web1에 접근할 수 없는 경우다.

원격 포트가 열려있는가?

traceroute를 통해 라우팅이 되는 것은 확인했으나 여전히 80번 포트에는 접근할 수 없을 때, 포트가 열려있는지 확인하는 법을 살펴보자.

$ telnet 10.1.2.5 80
Trying 10.1.2.5...
telnet: Unable to connect to remote host: Connection refused

간단하게 telnet 명령어를 시도해볼 수 있다. Connection refused가 나타난다면 원격 호스트에 apache 등의 웹서버가 떠있지 않거나, 해당 포트에 대해 리스닝하지 않고 있을 가능성이 높다. 혹은 방화벽에 의해 차단되었을 수 있다. 텔넷 연결에 성공한다면 네트워킹에 전혀 문제가 없는 것이다.

telnet 대신 nmap을 사용하면 포트가 열려있지 않은 것인지, 방화벽에 의해 차단된 것인지를 구분할 수 있다.

$ nmap -p 80 10.1.2.5
Starting Nmap 4.62 (http://nmap.org) at 2009-02-05 18>49 PST
Interesting ports on web1 (10.1.2.5):
PORT STATE SERVICE
80/tcp filtered http

포트가 열려있으나 필터링되었음을 알 수 있다. 즉 방화벽에 의해 차단된 것이다.

로컬에서 원격 호스트 테스트하기

문제가 호스트 자체에 있다고 생각된다면, 80번 포트를 사용할 수 있는지를 확인해보자.

포트 리스닝 확인

web1에서 80번 포트가 리스닝 상태인지 여부를 확인한다. netstat -lnp 명령어는 포트를 열고있는 프로세스와 함께 모든 리스팅 포트의 목록을 보여준다.

[ec2-user@ip ~]$ netstat -lnp | grep :80
tcp 0   0   0.0.0.0:80  0.0.0.0:*   LISTEN 919/apache

모든 IP(0.0.0.0)에 대해 80번 포트로 들어오는 트래픽을 리스닝하고 있음을 알 수 있다. 919/apache는 포트를 열고있는 프로세스를 나타낸다.

방화벽 규칙 확인

apache 등 웹서버가 떠있고, 80번 포트를 리스닝 중이라면 방화벽 문제일 수 있다. iptables 명령으로 모든 방화벽 규칙을 확인할 수 있다.

[ec2-user@ip ~]$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

아무런 규칙이 추가되어있지 않고, 기본 정책이 ACCEPT로 되어있다. 방화벽이 비활성화 되어있는 경우이다. 만약 policy DROP으로 출력된다면 기본적으로 모든 패킷을 차단하는 것이다.

[ec2-user@ip ~]$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source      destination
REJECT     tct  --  0.0.0.0/0   0.0.0.0/0   tcp dpt:80 reject-with-icmp-port-unreachable

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

80번 포트를 차단하는 방화벽 규칙이 있다면 위와 같이 출력될 수 있다.


Reference: 데브옵스 - 카일 랜킨, 5장

켄트백의 구현 패턴 - 1. 클래스 Node.js login with passport (Express 4)