LabHub
배우기 러닝패스 코스

Envoyの内部構造

ログ形式を設計し、エラーだけを分ける

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

접근 로그의 형식·모양·싱크·필터 네 가지를 차례로 설계하고, 각각이 실제 로그 파일에 어떻게 나타나는지 확인한다.

왜 중요한가

접근 로그는 관측 설정 중에서 유일하게 되돌릴 수 없는 부분이다. 사고가 난 뒤에 '업스트림 주소도 남길걸' 이라고 깨달아도 그 사고의 로그에는 없다. 그래서 형식을 고르는 기준은 취향이 아니라 질문이다 — 새벽 세 시에 이 한 줄만 보고 원인을 좁힐 수 있는가. 여기에 더해 파일 싱크가 버퍼에 모았다 비운다는 사실, 값이 없는 자리에 무엇이 찍히는지, 필터를 어느 층에 적어야 하는지는 문서를 읽어서는 잘 남지 않고 한 번 겪어야 남는다.

단계

  1. 업스트림 둘을 띄우세요 — 8091ok, 8092fail. /root/envd-log/log-text.yaml/ping(direct_response pong), /bad(클러스터 bad), /(클러스터 good) 세 라우트와 파일 접근 로그 싱크를 두세요 — 경로는 /root/envd-log/access.log, 형식은 %RESPONSE_CODE%|%REQ(:PATH)%|%UPSTREAM_HOST% 입니다. --file-flush-interval-msec 200 을 붙여 띄우고 /hello 를 한 번 요청한 뒤 로그가 남을 때까지 기다려 확인하세요.
  2. /root/envd-log/log-wide.yaml 을 만드세요 — 로그 경로는 /root/envd-log/wide.log, 형식은 여덟 칸입니다: %RESPONSE_CODE%|%REQ(:METHOD)%|%REQ(:PATH)%|%PROTOCOL%|%DURATION%|%UPSTREAM_HOST%|%RESPONSE_FLAGS%|%BYTES_SENT%. 그 설정으로 띄운 뒤 /hello·/ping·/bad/x 를 한 번씩 요청해 세 줄이 남는 것을 확인하세요.
  3. /root/envd-log/log-miss.yaml 을 만드세요 — 경로는 /root/envd-log/miss.log, 형식은 %RESPONSE_CODE%|%REQ(:PATH)%|%UPSTREAM_HOST%|%REQ(X-TENANT)% 입니다(마지막 칸은 요청 헤더 x-tenant). 띄운 뒤 /ping/hello 를 헤더 없이 한 번씩 요청하고, /root/envd-log/03-missing.txtping_upstream=(/ping 줄의 세 번째 칸), ping_tenant=(네 번째 칸), hello_tenant= 세 줄을 적으세요.
  4. /root/envd-log/log-json.yaml 을 만드세요 — 경로는 /root/envd-log/access.json, 형식은 json_format 이고 열쇠는 code·path·upstream·flags·duration_ms 다섯 개입니다. 띄운 뒤 /hello/bad/x 를 한 번씩 요청하고, jq 로 읽히는지 확인하세요.
  5. /root/envd-log/log-err.yaml 을 만드세요 — 싱크 하나만 두되 filter.status_code_filter 로 500 이상만 남기고(비교 연산자 GE, 값 500), 경로는 /root/envd-log/errors.json, 열쇠는 code·path·upstream·flags 입니다. 띄운 뒤 /hello 두 번과 /bad/x 한 번을 요청하고 /root/envd-log/05-filter.txtlines=(errors.json 의 줄 수)와 codes=(그 줄들의 code 값을 공백으로) 두 줄을 적으세요.
  6. /root/envd-log/log-two.yaml 을 만드세요 — 싱크 두 개입니다. 하나는 텍스트로 모든 요청을 /root/envd-log/all.log 에(형식 %RESPONSE_CODE%|%REQ(:PATH)%|%UPSTREAM_HOST%), 다른 하나는 JSON 으로 500 이상만 /root/envd-log/only5xx.json 에 남깁니다. 띄운 뒤 /hello 세 번과 /bad/x 두 번을 요청하고 /root/envd-log/06-two.txtall=·err=·ratio_ok=(all 과 err 의 차) 세 줄을 적으세요.
  7. /root/envd-log/log-reqid.yaml 을 만드세요 — 경로는 /root/envd-log/reqid.log, 형식은 %RESPONSE_CODE%|%REQ(:PATH)%|%REQ(X-REQUEST-ID)% 입니다. 띄운 뒤 /given 은 헤더 x-request-id: envd-777 을 붙여, /none 은 아무 헤더 없이 요청하세요. /root/envd-log/07-reqid.txtgiven=(첫 요청 줄의 세 번째 칸), none_len=(둘째 요청 줄 세 번째 칸의 글자 수), none_is_given=(그 값이 envd-777 과 같으면 yes, 아니면 no) 세 줄을 적으세요.
  8. /root/envd-log/08-report.mdfields=(2단계 형식의 칸 수), missing_marker=(3단계에서 값이 없을 때 찍힌 문자), all_lines=·error_lines=(6단계의 두 값) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.

참고

로그를 파일로 남기고, 늦게 나오는 이유를 안다

업스트림 둘을 띄우세요 — 8091ok, 8092fail. /root/envd-log/log-text.yaml/ping(direct_response pong), /bad(클러스터 bad), /(클러스터 good) 세 라우트와 파일 접근 로그 싱크를 두세요 — 경로는 /root/envd-log/access.log, 형식은 %RESPONSE_CODE%|%REQ(:PATH)%|%UPSTREAM_HOST% 입니다. --file-flush-interval-msec 200 을 붙여 띄우고 /hello 를 한 번 요청한 뒤 로그가 남을 때까지 기다려 확인하세요.

파일 싱크는 요청마다 디스크에 쓰지 않습니다 — 버퍼에 모았다가 주기적으로 비웁니다. 그 주기의 기본값은 10초라서, 요청 직후에 cat 하면 아무것도 없는 것처럼 보입니다. '로그가 안 남는다' 는 신고의 상당수가 이것입니다. 실습에서는 --file-flush-interval-msec 200 으로 줄여 기다리는 시간을 없앱니다. 그래도 고정 sleep 대신 줄이 생길 때까지 도는 루프를 쓰세요.

사고가 난 뒤에 필요한 값을 미리 정한다

/root/envd-log/log-wide.yaml 을 만드세요 — 로그 경로는 /root/envd-log/wide.log, 형식은 여덟 칸입니다: %RESPONSE_CODE%|%REQ(:METHOD)%|%REQ(:PATH)%|%PROTOCOL%|%DURATION%|%UPSTREAM_HOST%|%RESPONSE_FLAGS%|%BYTES_SENT%. 그 설정으로 띄운 뒤 /hello·/ping·/bad/x 를 한 번씩 요청해 세 줄이 남는 것을 확인하세요.

형식 문자열의 %...%명령 연산자입니다. 요청 헤더는 %REQ(이름)%, 응답 헤더는 %RESP(이름)%, :METHOD:PATH 처럼 콜론으로 시작하는 것은 HTTP/2 의 의사 헤더입니다. 여기에 넣을 값을 고르는 기준은 하나입니다 — 새벽 세 시에 이 줄만 보고 원인을 좁힐 수 있는가. 상태 코드만 있으면 좁혀지지 않고, 어느 업스트림이 받았고 몇 밀리초 걸렸고 어떤 플래그가 붙었는지가 있어야 좁혀집니다.

값이 없을 때 무엇이 찍히는지 알아 둔다

/root/envd-log/log-miss.yaml 을 만드세요 — 경로는 /root/envd-log/miss.log, 형식은 %RESPONSE_CODE%|%REQ(:PATH)%|%UPSTREAM_HOST%|%REQ(X-TENANT)% 입니다(마지막 칸은 요청 헤더 x-tenant). 띄운 뒤 /ping/hello 를 헤더 없이 한 번씩 요청하고, /root/envd-log/03-missing.txtping_upstream=(/ping 줄의 세 번째 칸), ping_tenant=(네 번째 칸), hello_tenant= 세 줄을 적으세요.

/pingdirect_response 라 업스트림에 가지 않습니다 — 그러면 %UPSTREAM_HOST% 에 담을 값이 없습니다. 보내지 않은 헤더도 마찬가지입니다. Envoy 는 이런 자리를 비워 두지 않고 한 글자짜리 표시로 채웁니다. 그 표시가 무엇인지 알아 두지 않으면, 로그를 기계로 읽을 때 그 값이 진짜 값인 줄 알고 그대로 저장하게 됩니다. 칸을 세는 데는 awk -F'|' 가 편합니다.

기계가 읽을 형식으로 바꾼다

/root/envd-log/log-json.yaml 을 만드세요 — 경로는 /root/envd-log/access.json, 형식은 json_format 이고 열쇠는 code·path·upstream·flags·duration_ms 다섯 개입니다. 띄운 뒤 /hello/bad/x 를 한 번씩 요청하고, jq 로 읽히는지 확인하세요.

구분자로 자른 텍스트 형식은 사람이 읽기 좋지만, 값 안에 구분자가 들어가는 순간(경로에 물음표와 값이 붙는 경우) 칸이 밀립니다. 로그를 수집기에 보내 질의할 계획이라면 처음부터 json_format 으로 두는 편이 낫습니다. 열쇠 이름은 마음대로 정할 수 있고, 값 자리에 같은 명령 연산자를 씁니다. 확인은 jq -c . 파일 로 합니다 — 한 줄이라도 깨지면 jq 가 그 자리에서 실패합니다.

오류만 따로 모은다

/root/envd-log/log-err.yaml 을 만드세요 — 싱크 하나만 두되 filter.status_code_filter 로 500 이상만 남기고(비교 연산자 GE, 값 500), 경로는 /root/envd-log/errors.json, 열쇠는 code·path·upstream·flags 입니다. 띄운 뒤 /hello 두 번과 /bad/x 한 번을 요청하고 /root/envd-log/05-filter.txtlines=(errors.json 의 줄 수)와 codes=(그 줄들의 code 값을 공백으로) 두 줄을 적으세요.

싱크마다 filter 를 달 수 있습니다. filtertyped_config 안이 아니라 name 과 같은 층에 적습니다 — 안쪽에 적으면 '그런 필드가 없다' 며 설정이 거절됩니다. 필터에는 상태 코드 말고도 소요 시간(duration_filter), 응답 플래그, 헤더 조건이 있습니다. 오류만 따로 모으면 보관 기간을 길게 잡아도 용량이 감당됩니다.

싱크 두 개를 함께 쓴다

/root/envd-log/log-two.yaml 을 만드세요 — 싱크 두 개입니다. 하나는 텍스트로 모든 요청을 /root/envd-log/all.log 에(형식 %RESPONSE_CODE%|%REQ(:PATH)%|%UPSTREAM_HOST%), 다른 하나는 JSON 으로 500 이상만 /root/envd-log/only5xx.json 에 남깁니다. 띄운 뒤 /hello 세 번과 /bad/x 두 번을 요청하고 /root/envd-log/06-two.txtall=·err=·ratio_ok=(all 과 err 의 차) 세 줄을 적으세요.

싱크는 목록이라 여러 개를 둘 수 있고 각자 다른 형식과 다른 필터를 가집니다. 실무에서 흔한 구성이 바로 이것입니다 — 전체 로그는 짧은 형식으로 짧게 보관하고, 오류 로그는 자세한 형식으로 길게 보관합니다. 둘을 한 파일에 섞으면 보관 기간을 따로 정할 수 없습니다.

요청 아이디는 누가 만드나

/root/envd-log/log-reqid.yaml 을 만드세요 — 경로는 /root/envd-log/reqid.log, 형식은 %RESPONSE_CODE%|%REQ(:PATH)%|%REQ(X-REQUEST-ID)% 입니다. 띄운 뒤 /given 은 헤더 x-request-id: envd-777 을 붙여, /none 은 아무 헤더 없이 요청하세요. /root/envd-log/07-reqid.txtgiven=(첫 요청 줄의 세 번째 칸), none_len=(둘째 요청 줄 세 번째 칸의 글자 수), none_is_given=(그 값이 envd-777 과 같으면 yes, 아니면 no) 세 줄을 적으세요.

요청 아이디는 여러 서비스를 지나는 한 요청을 이어 붙이는 실이고, 그래서 맨 앞의 프록시가 없으면 만들어 주고 있으면 그대로 물려줍니다. 클라이언트가 준 값을 그대로 쓰는 것이 기본 동작이라, 밖에서 아무 값이나 넣어 보낼 수 있다는 점도 함께 기억해 두어야 합니다(신뢰 경계 밖에서 온 값은 지우는 설정이 따로 있습니다). 글자 수는 awk '{print length($0)}'wc -c 로 셉니다.

로그 설계 메모를 남긴다

/root/envd-log/08-report.mdfields=(2단계 형식의 칸 수), missing_marker=(3단계에서 값이 없을 때 찍힌 문자), all_lines=·error_lines=(6단계의 두 값) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.

이 메모는 다음 서비스의 로그 형식을 정할 때 자기가 읽을 글입니다. '무엇을 넣었다' 보다 '왜 그것을 넣기로 했는가' 를 적으세요. 값은 앞 단계에서 만든 파일에서 가져옵니다.