Devin.KR

DNS와 TCP 포트

110분 안팎

학습 목표

이름 해석·TCP 연결·HTTP 상태를 순서대로 확인해 실패 지점을 구분합니다.

개념

한 번의 접속을 세 가지 질문으로 나눕니다

ops.lab.test로 /items를 요청했는데 응답이 없다는 신고를 받았다고 가정합니다. 이름을 주소로 바꾸지 못한 실패, 주소의 TCP 포트와 연결하지 못한 실패, HTTP 요청을 처리하지 못한 실패는 다음 조치가 다릅니다. 이번 목표는 이름 해석→TCP 연결→HTTP 상태·본문의 증거를 분리하는 것입니다. 모든 증상을 서버 재시작으로 처리하면 정상 서비스에 불필요한 변경을 주고 원래 실패 위치도 잃습니다.

이름 조회의 출처를 확인합니다

Linux의 getent hosts는 시스템 이름 해석 설정을 따르는 조회입니다. DNS 서버만 호출하는 도구와 결과가 항상 같지는 않습니다. /etc/hosts에 등록한 이름은 DNS 패킷 없이 해석될 수 있습니다. 이번 실습은 두 클라이언트의 hosts 파일에 ops.lab.test를 등록해 재현 가능한 이름 입력을 만듭니다. 이는 이름 해석 경로를 배우는 설정이며 권한 있는 DNS 영역이나 레코드 배포를 구축한 것으로 제출하지 않습니다.

이름이 해석됐으면 나온 주소가 구성도의 서버와 같은지 비교합니다. 결과가 여러 주소이면 성공 요청과 실패 요청이 같은 서버를 대상으로 했는지도 확인합니다. IPv4와 IPv6를 혼합하면 다른 경로를 시험할 수 있으므로 이 레슨의 관측은 IPv4로 제한합니다. 미션 수집기는 getaddrinfo에 AF_INET을 지정하고 해석 주소를 보고서에 보관합니다. 사소한 이름 철자 차이도 첫 실패 단계이므로 원래 입력을 생략하지 않습니다.

Could not resolve host는 이름을 주소로 바꾸는 과정에서 실패했다는 단서입니다. 아직 목적지의 HTTP 프로세스 상태를 조사했다고 말할 수 없습니다. DNS 서비스 장애뿐 아니라 오타·해석 설정·hosts 누락도 후보입니다. 이름 조회 명령의 종료 코드와 표준 출력을 함께 저장합니다. 결과가 비어 있는데 단순 파이프의 마지막 명령만 성공한 것을 전체 조회 성공으로 읽지 않습니다. 조회한 클라이언트 위치와 시각도 보고서에 포함합니다.

이름만 우회하는 비교를 만듭니다

숫자 주소 URL로 성공하고 이름 URL로 실패하면 이름 또는 이름에 연결된 대상 차이를 좁힐 수 있습니다. 다만 HTTP의 Host 필드가 달라져 서버의 응답 정책이 바뀔 수 있습니다. curl --resolve로 이름·포트에 실습 IP를 지정하면 URL의 호스트 이름을 유지한 비교를 만들 수 있습니다. 이 앱은 가상 호스트를 구분하지 않지만 이후 다른 앱을 진단할 때에도 비교 조건을 설명할 수 있도록 두 방식의 차이를 적습니다.

프록시 환경 변수가 설정된 클라이언트라면 요청이 직접 서버로 가지 않을 수 있습니다. 실습 curl에는 --noproxy '*'를 명시해 비교 대상 경로를 고정합니다. 프록시를 끄는 것이 모든 운영 요청의 해결책이라는 뜻은 아닙니다. 지정된 실습망의 직접 접속을 관찰하기 위한 조건입니다. 요청이 성공해도 실제 접속한 원격 주소를 모르면 잘못된 서버의 응답을 정상으로 받아들일 수 있습니다.

TCP 포트는 주소와 프로토콜까지 포함합니다

TCP의 18080은 이번 웹 앱 끝점이고 22는 SSH 끝점입니다. 숫자 포트가 같다고 UDP와 TCP가 같은 서비스가 되는 것은 아닙니다. 서버의 ss -H -ltn 'sport = :18080'으로 현재 리스너 주소를 읽고 클라이언트의 TCP 연결 결과와 비교합니다. ss에 리스너가 있다고 클라이언트 경로·방화벽까지 검증된 것은 아닙니다. 서버 내부의 관측과 외부 클라이언트의 관측이 함께 있어야 실패 범위를 줄일 수 있습니다.

Connection refused는 TCP 연결이 거부됐다는 단서입니다. 리스너 부재가 흔한 후보지만 방화벽의 reject 같은 응답도 가능하므로 이것만으로 앱이 종료됐다고 확정하지 않습니다. 서버 ss와 관리 상태를 같은 시각에 조회합니다. 이번 앱은 자연 종료하므로 20초 창 밖에서 시도한 거부는 정상 수명 종료일 수 있습니다. 시험 당시 active였는지와 리스너가 있었는지를 기록하지 않으면 네트워크 설정 오류로 오해하기 쉽습니다.

Connection timed out는 정해진 시간 안에 연결이 완료되지 않았음을 뜻합니다. 출발지 차단, 경로 손실, 반환 경로 문제, 상대 장비 미응답이 모두 후보입니다. timeout을 즉시 FIREWALL_BLOCK으로 반환하는 로직을 만들지 않습니다. 로컬 과제의 TCP_TIMEOUT_UNCONFIRMED는 원인 미확정 상태를 의도적으로 표현합니다. 제한 시간을 정하지 않은 진단은 오래 대기하며 다음 확인을 늦출 수 있으므로 연결과 전체 요청의 제한을 각각 둡니다.

HTTP 성공은 상태와 데이터로 확인합니다

TCP가 연결됐다면 HTTP 요청을 보냅니다. 503은 서버나 중간 HTTP 장비가 응답했으나 요청을 정상 처리하지 못했다는 관측입니다. 이름 실패와 같은 결과로 분류하지 않습니다. 404는 그 경로를 못 찾았다는 단서이며 /items 철자를 확인합니다. 200도 원하는 데이터라는 보장은 없으므로 Content-Type과 JSON 구조·값을 읽습니다. 미션은 서버의 기존 items.json과 응답 JSON이 같은지 비교해 다른 앱에 잘못 연결한 사례를 줄입니다.

curl -sS -w로 본문 다음 줄에 HTTP 상태를 출력하면 수집 자료에서 본문과 상태를 분리할 수 있습니다. 네트워크 오류의 종료 코드와 HTTP 오류 상태를 혼동하지 않습니다. -f를 쓴 명령은 HTTP 오류를 비정상 종료로 취급하므로 본문 수집 목적과 다를 수 있습니다. 여기서는 오류 본문까지 보관하는 수집기와 정상 응답만 확인하는 명령의 목적을 구분합니다. 자동화에서 빈 본문을 숫자 0이나 성공으로 바꾸는 처리도 피합니다.

실패 주입은 한 조건씩 수행합니다

이름 오타 사례에서는 정상 이름과 없는 이름을 같은 클라이언트에서 비교합니다. 리스너 없음 사례에서는 앞 앱의 자연 종료 뒤 숫자 주소로 TCP를 관찰합니다. 다시 기동한 정상 사례를 확보한 뒤 다음 방화벽 레슨의 차단 클라이언트와 비교합니다. 여러 설정을 한꺼번에 바꾸면 이름 오류가 포트 오류를 가려 어느 변경이 결과를 만들었는지 알 수 없습니다. 실패를 재현하기 전에 정상 기준을 먼저 보관합니다.

로컬 실습의 diagnose는 제공 관측값에서 가장 먼저 실패한 단계를 반환합니다. DNS 실패면 이후 TCP·HTTP 값은 사용하지 않고, TCP 미관측이면 HTTP 성공을 추정하지 않습니다. HTTP None은 200과 구분합니다. 테스트는 이름 실패·거부·시간 초과·503·200·미관측을 포함합니다. 문자열 반환 규약을 수정해 테스트만 통과시키지 말고 각 결과가 무엇을 알고 무엇을 모르는지 README에 설명합니다.

접속 기록의 한 행에는 실행 위치, 대상 이름과 실제 IP, 목적지 포트, 원문 오류, HTTP 상태, 다음 확인이 들어갑니다. 제한 시간 안에 끝난 실패와 기능 오류 응답은 같은 장애로 합치지 않습니다. 리스너 조회의 필터와 소켓 상태 확장은 더 읽기로 보냅니다. 다음 레슨에서는 TCP가 이미 연결된 뒤에도 사용자의 증명이 실패할 수 있다는 점을 SSH 로그로 다룹니다.

따라하기

판정에 사용할 작은 모델 실행

아래 코드는 제공된 고정 입력의 계산만 실행합니다. VM 관측 출력과 구분해 읽습니다.

observations = [('ok', 'ok', 200), ('ok', 'ok', 503), ('ok', 'timeout', None)]
for dns, tcp, status in observations:
    print('TCP='+tcp, 'HTTP='+('NOT_OBSERVED' if status is None else str(status)))

실행 결과

TCP=ok HTTP=200
TCP=ok HTTP=503
TCP=timeout HTTP=NOT_OBSERVED

이름 해석과 우회 비교

클라이언트에서 정상 이름을 조회합니다. missing.lab.test의 실패도 별도 기록합니다. --resolve 비교는 URL 호스트 이름을 유지합니다.

getent ahostsv4 ops.lab.test
curl --noproxy '*' --resolve ops.lab.test:18080:192.168.56.10 --connect-timeout 2 --max-time 3 -sS http://ops.lab.test:18080/items

HTTP 상태와 서버 리스너 비교

curl은 클라이언트, ss는 서버에서 실행합니다. 기동 중 결과와 자연 종료 뒤 결과를 나누고 HTTP 오류·네트워크 오류를 구분합니다.

curl --noproxy '*' --connect-timeout 2 --max-time 3 -sS -w '\n%{http_code}\n' http://ops.lab.test:18080/items
ss -H -ltn 'sport = :18080'

경계 사례까지 로컬 검사

timeout 원인 미확정과 HTTP 미관측을 성공과 구분합니다.

bash check.sh

확인 문제

실습

task.py의 diagnose를 완성합니다. 이름 실패·거부·시간 초과·HTTP 오류·정상·미관측을 tests의 계약대로 구분합니다. 로컬 자동 검사는 고정 입력의 논리만 확인합니다. VM에서의 실제 실행과 기록은 따라하기 및 external 미션으로 확인합니다.

시작 코드·테스트 내려받기

실행 명령

bash check.sh

기대 결과

unittest의 모든 사례 OK, 종료 코드 0

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • SSH 접속이 실패할 때의 확인 순서를 설명합니다.