Dev AI Worker 온디맨드 운영 - 100-hours-a-week/KTB4-2nd-Cloud GitHub Wiki

AI Worker 온디맨드 운영

Worker EC2(c7i.xlarge)는 여행을 만들 때만 켜집니다. 사진 업로드가 끝나면 Backend가 Worker를 켜고, 작업이 끝나고 10분 동안 새 작업이 없으면 Worker가 스스로 꺼집니다.

설계 단계에서 3주 동안 필요한 AI 처리 시간은 약 15시간으로 계산했고, 그래서 Worker를 계속 켜두지 않기로 했습니다. (EC2 + Docker Compose 초기 배포 설계)


전체 흐름

sequenceDiagram
  participant FE as Frontend
  participant BE as Backend
  participant EC2 as EC2 API
  participant W as Worker EC2
  participant T as 유휴 종료 타이머 (Worker Host)

  FE->>BE: 마지막 사진 Batch 업로드
  BE->>EC2: Worker 상태 조회
  alt Worker 중지 상태
    BE->>EC2: Worker 시작
    EC2->>W: 부팅, Docker와 AI 컨테이너 자동 기동
  end
  BE->>W: /health 준비 대기 (최대 180초)
  BE->>W: AI 처리 요청
  W-->>BE: 결과
  BE-->>FE: 200
  loop 1분마다
    T->>W: /health 확인
  end
  T->>W: 10분 유휴면 poweroff
Loading

켜는 쪽은 Backend, 끄는 쪽은 Worker 자신입니다. 그래서 App Role에는 Worker 시작 권한만 있고 중지 권한은 없습니다.


Backend가 Worker를 켜기

Backend는 여행 생성 요청의 마지막 Batch에서 AI를 호출하기 전에 Worker 상태를 확인하고, 꺼져 있으면 시작합니다. 이를 위해 App Role에 두 권한을 추가했습니다.

권한 범위
ec2:DescribeInstances 서울 리전 조건
ec2:StartInstances Worker 인스턴스 ARN 하나

처음 이 기능이 운영에 올라갔을 때는 권한이 없어 403이 났고, 권한을 넣은 뒤에도 확인 과정에서 한 번 헤맸습니다. IAM Policy Simulation 오진


EC2가 켜지면 컨테이너도 같이 올라오게

설계에서는 Worker 컨테이너의 재시작 정책을 on-failure:3으로 정했습니다. 작업을 마치고 정상 종료할 수 있어야 한다고 봤기 때문입니다.

그런데 구현하면서 보니 비용은 컨테이너가 아니라 EC2를 끄는 것으로 줄이고 있었습니다. Backend가 EC2를 다시 켰을 때 컨테이너가 자동으로 올라오지 않으면 /health가 응답하지 않아 여행 생성이 실패합니다.

on-failure로는 EC2가 다시 켜졌을 때 컨테이너가 올라오지 않아서 unless-stopped로 바꿨습니다.

services:
  ai-worker:
    restart: unless-stopped
    stop_grace_period: 7m

종료 대기 7분은 그대로 뒀습니다. 여행 한 건 처리에 최대 약 373초가 걸리기 때문입니다.

바꾼 뒤 Worker EC2를 수동으로 껐다 켜서 Docker가 실행되고 AI 컨테이너가 healthy로 돌아오는 것을 확인했습니다.


유휴 상태에서 스스로 종료

Worker Host에 systemd 타이머 yeodam-worker-idle-shutdown.timer를 설치했습니다.

flowchart TD
  S["1분마다 실행<br/>(부팅 2분 후부터)"] --> H{"/health 응답?"}
  H -- 실패 --> K["종료하지 않음"]
  H -- 성공 --> J{"실행 중, 대기 중<br/>작업 0건?"}
  J -- 아니오 --> K
  J -- 예 --> I{"유휴 600초 이상?"}
  I -- 아니오 --> K
  I -- 예 --> R["10초 후 다시 확인"]
  R -- 여전히 유휴 --> P["systemctl poweroff"]
  R -- 작업 생김 --> K
Loading

판단은 AI의 /health가 주는 status, active_tasks, queued, idle_seconds로 합니다. health 확인에 실패하면 상태를 알 수 없는 것으로 보고 끄지 않습니다.

마지막 확인과 실제 종료 사이에 새 작업이 들어올 수 있어서, 끄기 직전 10초 뒤에 한 번 더 확인합니다.

타이머는 배포 스크립트가 설치하는데, health와 digest 검증을 통과한 다음에만 설치하도록 순서를 잡았습니다. 실패한 릴리스 때문에 Worker가 엉뚱하게 꺼지는 일을 막기 위해서입니다.

Backend의 자동 시작과 10분 유휴 뒤 자동 종료는 실제 여행 생성으로 확인했습니다.


켜는 데 걸리는 시간

부하 테스트에서 Worker가 꺼져 있던 경우와 이미 켜져 있던 경우의 준비 시간을 비교했습니다.

실행 Worker 상태 준비 확인 시간
150장 1회차 켜져 있음 98ms
150장 2회차 꺼져 있음 40.4초
150장 3회차 꺼져 있음 36.3초

꺼져 있을 때는 여행 생성이 약 40초 늘어납니다. 150장 여행 전체가 14분 정도 걸리는 것에 비하면 5% 안쪽이라, 이 정도 지연은 비용을 줄이는 대가로 받아들였습니다.


Terraform에서 생긴 문제

Worker가 꺼졌다 켜졌다 하면서, 꺼져 있을 때 Terraform Plan을 돌리면 Worker 교체가 제안되는 문제가 있었습니다. 중지된 Worker 때문에 EC2 교체 Plan 발생


Qdrant 데이터

AI 벡터는 Worker Root 디스크 위의 Docker Volume에 저장됩니다. 별도 EBS나 백업은 두지 않았습니다.

V1의 벡터 데이터는 200MB 미만으로 예상했고, V2로 넘어갈 때 다시 만들 데이터라서 Worker를 교체하면 함께 사라지는 것을 받아들였습니다.

⚠️ **GitHub.com Fallback** ⚠️