조용진의 개발 블로그
← 글 목록

Lambda handler를 ECS 상시 실행 컨테이너로 옮긴 과정

AWSECSFargateDockerLLM

Express 기반 LLM API에서 serverless handler를 제거하고 Docker·ECR·ECS Fargate와 GitHub Actions 배포 흐름을 구성한 과정을 정리합니다.

수학 문제를 생성하는 Express API는 처음에 serverless-http로 감싸 Lambda handler로 실행했다. 이미지 참고 자료를 S3에서 찾고 LLM 응답을 기다리는 작업이 추가되면서 요청 단위 함수보다 상시 실행되는 컨테이너 서비스가 운영 구조에 더 잘 맞는지 검토하게 됐다.

Lambda handler를 일반 HTTP 서버로 되돌렸다

기존 진입점은 Express 앱을 serverless adapter로 감싸 handler를 내보냈다.

아래 코드 예시는 설명을 위해 식별자와 운영 설정값을 일반화했습니다.

import serverless from "serverless-http";
import app from "./app";

export const handler = serverless(app);

ECS 전환에서는 adapter 의존성을 제거하고 Express가 직접 포트를 열도록 바꿨다. /api/generate 라우트와 오류 미들웨어는 유지하고, 컨테이너 상태를 확인할 /health 엔드포인트를 추가했다.

app.use("/api/generate", generationRoutes);

app.get("/health", (_, res) => {
  res.status(200).send("OK");
});

const port = process.env.PORT || 3000;
app.listen(port);

이 변경의 핵심은 비즈니스 로직을 다시 만드는 것이 아니라 실행 모델의 경계를 handler 호출에서 장시간 살아 있는 프로세스로 바꾼 것이다.

멀티 스테이지 Docker 이미지로 실행 환경을 고정했다

Dockerfile은 builder 단계에서 TypeScript를 컴파일하고 실행 단계에는 dist, 의존성, package 정보만 복사했다.

FROM node:18-alpine AS builder
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build

FROM node:18-alpine
WORKDIR /usr/src/app
COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder /usr/src/app/node_modules ./node_modules
CMD ["node", "dist/index.js"]

빌드와 실행 환경을 같은 Dockerfile에 고정하면서 로컬 Node 설정과 배포 서버 설정의 차이를 줄였다. 작업 정의는 Fargate, Linux x86_64, CPU 1024, 메모리 3072MiB, CloudWatch Logs를 명시했다.

운영 브랜치 반영부터 ECS 서비스 갱신까지 자동화했다

GitHub Actions는 main push에서 다음 순서로 실행됐다.

flowchart LR
  A[운영 브랜치 반영] --> B[Docker 이미지 빌드]
  B --> C[ECR에 버전 태그 Push]
  C --> D[Task Definition에 이미지 주입]
  D --> E[ECS 서비스 배포]
  E --> F[서비스 안정화 대기]

이미지는 latest뿐 아니라 Git commit SHA로도 Push했다. 배포 단계에서는 render action이 새 이미지 주소와 환경 변수를 task definition에 넣고, deploy action이 서비스 안정화까지 기다렸다. 어떤 소스가 배포됐는지 이미지 태그로 추적할 수 있는 구조다.

LLM 호출 외의 작업도 컨테이너 책임에 포함됐다

같은 변경에는 요청 조건으로 S3의 참고 이미지를 조회하고, 이미지 URL과 텍스트를 함께 LLM 요청에 전달하는 흐름도 들어갔다. 생성 API는 단순한 짧은 계산이 아니라 외부 저장소 조회와 모델 응답을 조합하는 서비스가 됐다.

이 전환으로 Express 서버, 컨테이너 실행 환경, ECR 이미지 빌드와 ECS 서비스 갱신이 하나의 배포 흐름으로 연결됐다.

이번 전환에서 중요한 판단 기준은 서버리스와 컨테이너 중 무엇이 일반적으로 우월한지가 아니다. 요청 시간이 길고 실행 환경·메모리·배포 추적을 명시적으로 관리해야 하는 워크로드에 어떤 실행 모델이 맞는지가 기준이었다.