Skip to main content

curl 대신 gostat 어때?

gostat_main

이번에 소개해드릴 오픈소스 프로젝트는 gostat라고 명명하였으며 net/http 패키지를 사용하여 특정 URL에 HTTP GET 요청을 하여 단계별로 레이턴시를 체크하거나 요청 헤더와 응답 헤더를 간략하게 보여주는 대화형 CLI 도구입니다.

저는 주로 웹서버의 정상 동작을 확인하기 위해서 웹서버에 올라가 있는 컨텐츠 URL의 응답 코드 또는 응답 헤더를 확인하거나 레이턴시를 확인할 때 사용합니다. 또한 CDN을 사용중인 웹서버일 경우 CDN은 엣지 서버가 여러 대로 구성되어 있는데 이 중 한대의 엣지 서버의 응답을 확인하거나 각 엣지 서버의 모든 응답을 확인할 때 주로 사용합니다.

왜 gostat을 만들었을까 ?

이 도구를 만들게 된 계기는 curl 커맨드를 사용하여 HTTP GET 응답을 살펴보곤 했는데 점차 사용하는 curl 옵션이 다양해지다 보니 curl 명령어 자체가 길어졌고 개인적으로 zshrc 스크립트에 자주 사용하는 옵션과 함께 명령어를 커스텀해서 사용했었는데 이러한 방법보다 주로 사용하는 옵션과 요청 헤더에 들어가는 값들이 고정되어 하나의 도구를 만들어 저와 같은 고민을 하는 사람들이나 팀원들이 이 도구를 유용하게 사용할 수 있도록 하고 싶어 만들게 되었습니다. 단순히 저 혼자 쓰기 위해서 리소스를 사용하는 것보다 모두를 위해서 리소스를 사용하는 것이 훨씬 더 보람찬 일이고 효율적이라고 생각하며 이러한 계기가 도구를 더 잘 만들 수 있는 원동력으로 작용하니까요.

또한 도구를 만들고 성공적으로 배포하였을 때 팀원뿐만 아니라 이러한 기능들을 필요로 하는 사람들이 사용한다면 너무나 뿌듯할 것 같아요. 하지만 소프트웨어 프로그램은 잘 만들고 끝이 아니죠... 사용자가 기능을 이해하고 사용할 수 있도록 사용법을 글로 정리하는 문서화 작업이 필요합니다. 이 게시글도 문서화 작업의 일환이라고 볼 수 있습니다.

curl vs gostat

앞으로 이어질 문맥에서 저는 이 도구를 gostat이라고 부르겠습니다. 오늘 소개할 주인공은 CLI 도구이자 gostat이니까요. 먼저 동일한 기능을 수행하는 curl 명령어와 gostat 명령어를 비교한 결과를 보여드리겠습니다. 비교 명령어를 해석하자면, naver.com의 호스트 IP 주소를 사용하지 않고 DNS 서버를 우회하여 223.130.195.95으로 요청하며 Range 헤더를 추가하여 서버로부터 0번째부터 1번째 바이트까지의 데이터만 요청한 명령입니다.


curl

요청 ➡️

curl -vo /dev/null -H 'Range:bytes=0-1' --resolve 'naver.com:443:223.130.195.95' 'https://www.naver.com/include/themecast/targetAndPanels.json'

응답 ⬅️

gostat2


gostat

요청 ➡️

gostat request https://www.naver.com/include/themecast/targetAndPanels.json -t 223.130.195.95

응답 ⬅️

gostat3

여러분이 보시기에는 어떤 명령어가 더 쉽고 직관적으로 응답 코드 및 내용을 확인하기 쉬우신가요 ? 아마도 제가 생각하기에는 gostat이 아닐까 싶은데요. 이를 위해서 만든 이유이기도 하니까요.

기본 사용법

gostat의 사용법에 대해서 더 자세하게 알려드리고자 기본 명령은 아래와 같으며 아래와 같이 명령어를 입력할 경우 CDN을 사용하는 웹서버는 CDN의 모든 엣지 서버로 요청한 응답 결과를 보여주고 CDN을 사용하지 않은 오리진 서버 1대로만 구성된 경우 오리진 서버의 IP에만 요청을 하게 됩니다.

gostat request [URL]
gostat request https://ghdwlsgur.github.io/assets/js/runtime\~main.19a37848.js

제 블로그를 예시로 들어볼게요. 제 블로그는 github pages로 호스팅되고 있고 아래 4대의 엣지 서버(185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153)로 구성되어 있으며 위에서 예시로 든 명령어를 입력시 각 엣지 서버로 요청을 한 결과를 보여주는 것을 알 수 있습니다.

gostat4

Target

타겟은 DNS를 우회하여 주어진 URL에서 타겟 IP로 요청을 하게 되는 것인데요. 제 블로그에 적용된 엣지 IP 서버 중에서 185.199.108.153으로만 요청하고 싶다면 아래와 같이 명령어를 입력할 수 있습니다.

gostat request [URL] -t [target domain/ip]
gostat request https://ghdwlsgur.github.io/assets/js/runtime\~main.19a37848.js -t 185.199.108.153

gostat5

타겟에는 아이피 또는 도메인을 입력할 수 있습니다. 그렇다면 이 타겟 플래그는 언제 유용하게 쓰일까요 ? CDN을 사용중인 기업의 경우 CDN 벤더를 다른 벤더로 이관할 수도 있고 CDN을 사용하지 않는 기업의 경우 CDN을 적용하기 전에 응답 코드를 테스트해야할 수도 있습니다. 이러한 상황에서 CDN 도메인을 생성하고 나서 실제 적용 전에 테스트를 위해서 기업의 도메인과 이관할 CDN 벤더의 엣지 IP를 로컬 파일인 /etc/hosts에 매핑하여 요청해야 하는 번거로움이 있을 수 있는데 타겟에 이관할 CDN 벤더의 엣지 IP를 입력하여 요청하면 되므로 이러한 번거로움을 줄여줍니다.

CDN을 사용하는 이유
  1. CDN을 사용하면 전 세계의 여러 지역에서 콘텐츠를 더 빠르게 로드할 수 있어 전송 속도가 향상됩니다.

  2. CDN은 콘텐츠를 여러 지역의 서버에 캐싱하여 전송 대역폭을 줄일 수 있으며 이는 웹 사이트가 많은 트래픽을 처리하는 경우 대역폭 비용을 절감하는 데 도움이 됩니다.

  3. CDN은 여러 지역에 분산된 서버들을 사용하므로 하나의 서버나 네트워크 장애가 발생하더라도 다른 서버에서 콘텐츠를 가져올 수 있습니다. 이는 웹 사이트의 가용성을 높이는 데 도움이 됩니다.

예를 들면 아래와 같습니다. 기업에서 사용하는 도메인이 example.com이라고 할 때 기업에서 신규로 적용할 CDN 벤더의 엣지 IP가 123.123.123.123일 경우 example.com 도메인에 CNAME을 설정하여 CDN 도메인을 적용하기 전에 CDN 도메인의 각 엣지 IP로 테스트가 필요하며 이 중 하나의 엣지 IP 주소가 123.123.123.123일 때 아래와 같이 /etc/hosts에 구성이 필요하지만 gostat을 사용할 경우 타겟에 아이피만 입력하면 되기에 로컬 파일(/etc/hosts) 파일을 수정하는 번거로움을 덜어줍니다.

/etc/hosts

123.123.123.123 example.com

gostat

gostat request example.com -t 123.123.123.123

요청 헤더

요청헤더에는 기본적으로 Range: bytes=0-1이 포함되어 요청되기에 웹서버에서 따로 이 요청헤더에 대한 응답코드를 설정하지 않는 이상 206 Partial Content를 반환할 것입니다. 이 요청헤더를 기본으로 들어가게끔 추가한 이유는 gostat으로 하여금 요청받는 서버에 부하를 일으키지 않도록 하기 위해서 추가하였습니다.

다른 요청 헤더로는 Host, Referer, Authorization 등이 있으며 여기서 제가 두 번째로 자주 사용하는 요청 헤더는 레퍼러이며 이는 가장 단순한 접근 제어로 레퍼러가 널리 사용되며 이를 테스트하기에 정말 유용합니다.

한 가지 예시로 저는 블로그에 사용되는 이미지 파일을 S3 버킷에 저장하여 사용하며 가장 기본적인 접근 제어로 레퍼러를 사용하고 있습니다. 이는 예시처럼 S3에서 레퍼러의 유무에 따라 다른 응답 코드를 반환합니다.

403 Forbidden

아래는 요청헤더에 레퍼러가 없을 때 거부되는 응답코드입니다.

gostat6

206 Partial Content

아래는 요청헤더에 레퍼러를 추가하였을 때 접근되는 응답코드입니다.

gostat7


Host

gostat request [URL] -H [Host]

Referer

gostat request [URL] -r [Referer]

Authorization

gostat request [URL] -A [Authorization]

응답코드 색깔

응답 코드는 2xx번대 응답은 초록색, 4xx번대 응답은 노란색, 5xx번대 응답은 빨간색으로 표시하여 응답코드를 직관적으로 구분할 수 있도록 했으며 HTTP, HTTPS 프로토콜 모두에서 레이턴시를 학인할 수 있습니다.

프린트 화면 소개

gostat8

위 이미지와 같이 노란색에는 호스트와 타겟 아이피(리졸브 아이피/도메인)이 표시되며 Latency Status에는 각 단계별로 레이턴시를 확인할 수 있고 Request Headers 아래에는 클라이언트가 HTTP GET 요청시에 기입한 요청헤더를 보여주고 Response Headers 아래에는 클라이언트가 서버로부터 전달받은 응답 헤더를 살펴볼 수 있습니다. 응답 헤더 키 값이 너무 길면 데이터 포맷이 달라지기에 대문자 및 대쉬로 축약하여 보여줍니다.

gostat은 golang으로 제작되었으며 HTTP 요청 로직에서 net/http 패키지를 사용하기에 서버에서 확인되는 클라이언트 에이전트 값은 Go-http-client/1.1입니다.

Attack Mode

최근에 추가한 플래그 -a의 경우 attack 모드로 HTTP 스트레스 테스트 도구로 사용할 수 있도록 기능을 추가 및 개선중에 있으며 이 플래그를 사용할 경우 Range 헤더는 추가되지 않으며 반복적으로 동일한 URL HTTP GET 요청을 하게 됩니다.

DashBoard

실시간으로 요청을 확인하기 위해서는 gostat request 명령어를 배쉬 스크립트 내에서 반복문을 돌리거나 어택 모드를 실행하여 응답 코드를 확인하는 방법이 있었지만 현재 어택모드에서는 응답코드와 요청횟수만 보여주기에 어떤 엣지 서버에서 어떤 응답코드를 반환하는지는 알 수 없었습니다. 사실 이 기능을 만들게 된 계기가 된 에피소드가 있습니다.

지난 6월 22일 NHN Cloud Make IT 행사 참석 도중 고객사의 요청으로 CDN 설정을 변경했었는데 이 설정으로 인해 엣지 서버별로 응답이 상이하였고 현재 어택 모드에서는 엣지 서버별로 응답코드를 확인하는 것에 한계가 있다는 것을 깨달아 다음날인 금요일날 신규 기능을 추가하였습니다.

아래 영상은 대쉬보드 모드를 실행하는 모습을 담은 영상입니다.

asciicast

영상에서 보시다시피 색깔별로 구분하였으며 200번대 응답은 초록색, 300번대 응답은 파란색, 400번대 응답은 노란색, 500번대 응답은 빨간색으로 표시하였습니다.

실행 방법은 대쉬보드를 연상케 하도록 -d 플래그를 추가하면 위 기능을 사용할 수 있도록 하였습니다.

# example1) naver.com 요청
gostat request https://naver.com -d

# example2) ghdwlsgur.github.io 요청
gostat request https://ghdwlsgur.github.io -d

설치 및 사용방법

# 설치
brew tap ghdwlsgur/gostat
brew install gostat

# 업그레이드
brew upgrade gostat

gostat을 사용하시면서 부족한 기능이나 개선할 기능을 아래 깃허브의 Pull Request에 남겨주시면 최대한 빨리 반영할 수 있도록 하겠습니다!!

긴 글 읽어주셔서 감사합니다:)