1. 작업 내용
공개 API 를 받는 람다의 실행 모델을 serverless-express 에서
Lambda Web Adapter(LWA) 로 바꿨습니다. 9/6 운영 반영했고 배포 후
에러는 0건입니다.
바꾼 이유는 하나입니다. 기존 어댑터는 핸들러가 응답을 전부 모은 뒤
반환하는 구조라, AI 답변을 토큰 단위로 흘려보내는 SSE 가 구조적으로
불가능했습니다. 엔진(ECS)은 이미 흘려보낼 수 있고 게이트웨이도 흘려보낼
수 있는데, 그 사이의 람다만 못 흘리는 상태였습니다.
런타임을 바꾸니 주변 설정도 따라 움직였습니다.
- 함수 타임아웃 20초 → 900초 (사용자가 실제로 기다리는 시간을 정하는
통합 타임아웃 29초는 그대로 두었습니다)
- 전 함수에 걸려 있던 동시 실행 1 상한 해제 + 프로비저닝된 동시성 1
(스트리밍은 응답이 열려 있는 내내 슬롯을 차지해서, 1 이면 동시 사용자
상한이 1명이 됩니다)
- Nest 부팅이 첫 요청 안에서 일어나던 것이 INIT 안으로 이동
로컬에서는 검증할 방법이 없었습니다. 람다 에뮬레이터에 스트리밍 호출
경로가 아예 없고 staging 도 만들지 않는 규약이라, 같은 템플릿으로 스택
이름만 바꾼 일회성 복제 스택을 세워 확인하고 통째로 삭제했습니다.
2. Deep Dive 해본 것
왜 못 흘렸는가. 람다 실행 환경의 구조와 수명(INIT / INVOKE)부터
다시 봤습니다. 핸들러가 결과를 반환하는 함수인 이상, 반환하기 전까지는
클라이언트에게 아무것도 갈 수 없습니다. 어댑터를 아무리 잘 만들어도
이 지점은 안 열립니다.
LWA 는 무엇을 다르게 하는가. 이벤트를 번역하는 대신 함수 안에서
평범한 웹서버를 그대로 띄우고 진짜 HTTP 로 중계합니다. 그 결과 진입점,
라우트 정의(SAM 이벤트 → OpenAPI 문서를 직접 작성), 호출자 IP 를 얻는
경로가 전부 바뀌었습니다.
스위치가 두 곳에 있다. 스트리밍을 켜는 설정이 함수 단위와 통합
단위 두 곳에 있고, 단위가 달라서 라우트별로 섞을 수가 없습니다. 짝이
어긋나면 상태코드 200, 헤더 정상, 본문만 비어서 옵니다.
콜드스타트 수치를 정확히. 총량은 평균 2.8초 → 4.1초로 늘었고,
최대값만 7.8초 → 4.1초로 줄었습니다. 개선이라고 부르면 오해입니다.
실제로 달라진 것은 부팅이 INIT 으로 모여 미리 치를 수 있게 된 것이고,
평시에는 PC 가 덮어 사용자가 0초를 기다립니다.
호출자 IP. 운영 로그에서 확인해 보니 소켓 주소는 전부 루프백이었고,
게이트웨이가 채운 헤더에서 읽어야 IP 별 상한이 제대로 걸립니다.
DB 연결. 프록시가 빌려주는 연결이 사실상 전부 특정 앱에 고정돼
있었습니다. 그러면 웜 환경 수가 그대로 DB 연결 수가 됩니다. 스트리밍을
열기 전에 다시 봐야 합니다.
3. 알게된 것
에러 없이 조용히 실패하는 자리가 있다. 스위치 짝이 어긋난 조합은
로그에 아무것도 남기지 않습니다. 200 이고 헤더도 멀쩡한데 본문만
없습니다. 그래서 배포 후에 바이트가 실제로 도착하는지 눈으로 확인하는
절차가 필요했습니다. 깨져도 에러가 안 나는 호출자 IP 도 같은 종류라
회귀 테스트를 붙였습니다.
복제 스택이 결함 둘을 잡았다. 둘 다 로컬에서는 재현되지 않고, 그대로
운영에 갔으면 둘 다 사고였습니다.
- 스트리밍 통합은 흔히 쓰는 것과 다른 API 버전을 요구합니다. 모르고
갔으면 게이트웨이 갱신이 실패해 전 라우트가 영향을 받았습니다.
- 복제 스택이 워크플로 함수까지 만들면서 운영 DB·엔진·푸시에
배선됐습니다. 확인용 스택이 운영 워크플로를 1분마다 전진시키고
실사용자에게 푸시를 보낼 뻔했습니다.
기술로 안 닫히는 것이 남았다. 전 함수 동시 실행 1 은 조직 규약인데
이 함수만 예외로 두는 것을 문서로 남겨야 하고, 복제 스택 방식을 관행으로
삼을지도 기준이 필요합니다. 혼자 정할 수 있는 종류가 아니라서 스터디에서
같이 이야기하고 싶습니다.
1. 작업 내용
공개 API 를 받는 람다의 실행 모델을
serverless-express에서Lambda Web Adapter(LWA) 로 바꿨습니다. 9/6 운영 반영했고 배포 후
에러는 0건입니다.
바꾼 이유는 하나입니다. 기존 어댑터는 핸들러가 응답을 전부 모은 뒤
반환하는 구조라, AI 답변을 토큰 단위로 흘려보내는 SSE 가 구조적으로
불가능했습니다. 엔진(ECS)은 이미 흘려보낼 수 있고 게이트웨이도 흘려보낼
수 있는데, 그 사이의 람다만 못 흘리는 상태였습니다.
런타임을 바꾸니 주변 설정도 따라 움직였습니다.
통합 타임아웃 29초는 그대로 두었습니다)
(스트리밍은 응답이 열려 있는 내내 슬롯을 차지해서, 1 이면 동시 사용자
상한이 1명이 됩니다)
로컬에서는 검증할 방법이 없었습니다. 람다 에뮬레이터에 스트리밍 호출
경로가 아예 없고 staging 도 만들지 않는 규약이라, 같은 템플릿으로 스택
이름만 바꾼 일회성 복제 스택을 세워 확인하고 통째로 삭제했습니다.
2. Deep Dive 해본 것
왜 못 흘렸는가. 람다 실행 환경의 구조와 수명(INIT / INVOKE)부터
다시 봤습니다. 핸들러가 결과를 반환하는 함수인 이상, 반환하기 전까지는
클라이언트에게 아무것도 갈 수 없습니다. 어댑터를 아무리 잘 만들어도
이 지점은 안 열립니다.
LWA 는 무엇을 다르게 하는가. 이벤트를 번역하는 대신 함수 안에서
평범한 웹서버를 그대로 띄우고 진짜 HTTP 로 중계합니다. 그 결과 진입점,
라우트 정의(SAM 이벤트 → OpenAPI 문서를 직접 작성), 호출자 IP 를 얻는
경로가 전부 바뀌었습니다.
스위치가 두 곳에 있다. 스트리밍을 켜는 설정이 함수 단위와 통합
단위 두 곳에 있고, 단위가 달라서 라우트별로 섞을 수가 없습니다. 짝이
어긋나면 상태코드 200, 헤더 정상, 본문만 비어서 옵니다.
콜드스타트 수치를 정확히. 총량은 평균 2.8초 → 4.1초로 늘었고,
최대값만 7.8초 → 4.1초로 줄었습니다. 개선이라고 부르면 오해입니다.
실제로 달라진 것은 부팅이 INIT 으로 모여 미리 치를 수 있게 된 것이고,
평시에는 PC 가 덮어 사용자가 0초를 기다립니다.
호출자 IP. 운영 로그에서 확인해 보니 소켓 주소는 전부 루프백이었고,
게이트웨이가 채운 헤더에서 읽어야 IP 별 상한이 제대로 걸립니다.
DB 연결. 프록시가 빌려주는 연결이 사실상 전부 특정 앱에 고정돼
있었습니다. 그러면 웜 환경 수가 그대로 DB 연결 수가 됩니다. 스트리밍을
열기 전에 다시 봐야 합니다.
3. 알게된 것
에러 없이 조용히 실패하는 자리가 있다. 스위치 짝이 어긋난 조합은
로그에 아무것도 남기지 않습니다. 200 이고 헤더도 멀쩡한데 본문만
없습니다. 그래서 배포 후에 바이트가 실제로 도착하는지 눈으로 확인하는
절차가 필요했습니다. 깨져도 에러가 안 나는 호출자 IP 도 같은 종류라
회귀 테스트를 붙였습니다.
복제 스택이 결함 둘을 잡았다. 둘 다 로컬에서는 재현되지 않고, 그대로
운영에 갔으면 둘 다 사고였습니다.
갔으면 게이트웨이 갱신이 실패해 전 라우트가 영향을 받았습니다.
배선됐습니다. 확인용 스택이 운영 워크플로를 1분마다 전진시키고
실사용자에게 푸시를 보낼 뻔했습니다.
기술로 안 닫히는 것이 남았다. 전 함수 동시 실행 1 은 조직 규약인데
이 함수만 예외로 두는 것을 문서로 남겨야 하고, 복제 스택 방식을 관행으로
삼을지도 기준이 필요합니다. 혼자 정할 수 있는 종류가 아니라서 스터디에서
같이 이야기하고 싶습니다.