ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • GPU 하나로 AI 작업 두 개 돌리기 — 우선순위 스케줄러 만들기
    IT 2026. 3. 21. 21:00

    GPU 하나, 할 일은 둘 — 무엇이 문제인가?

    로컬 AI 서버를 운영하다 보면 이런 상황이 생깁니다. 가족사진 수천 장을 VLM(Vision Language Model, 이미지를 이해하는 AI)으로 분석하는 작업이 몇 시간째 돌아가고 있는데, 갑자기 녹음 파일을 전사해야 할 일이 생긴 겁니다.

    GPU는 하나뿐입니다. 이미지 분석이 GPU 메모리를 꽉 잡고 있으니, 음성 전사 프로그램은 실행조차 안 됩니다. 그렇다고 이미지 분석을 매번 수동으로 죽이고, 음성 처리 끝나면 다시 켜는 건 너무 번거롭습니다.

    diagram

    이 문제를 해결하기 위해 우선순위 기반 GPU 스케줄러를 만들었습니다. 운영체제가 CPU를 여러 프로세스에 나눠주는 것처럼, GPU를 여러 AI 작업에 효율적으로 배분하는 도구입니다.

    실생활 활용 사례

    사례 1: 녹음 파일이 들어오면 바로 전사

    녹음 파일이 서버에 업로드됩니다. 이때 이미지 분석이 돌고 있더라도, 음성 전사가 더 급하기 때문에 스케줄러가 알아서 이미지 분석을 잠시 멈추고 GPU를 음성 전사에 넘겨줍니다. 전사가 끝나면? 이미지 분석이 자동으로 다시 시작됩니다.

    사례 2: 서버 부팅하면 알아서 시작

    서버가 재부팅되면 이전에 돌던 작업 상태가 날아갑니다. 스케줄러는 부팅 시 자동으로 "급한 건 없으니까 느긋한 작업부터 시작하자"라고 판단하고, background 타입의 이미지 분석을 자동 실행합니다.

    설계 원칙 — OS 스케줄링에서 빌려온 아이디어

    이 스케줄러는 운영체제(OS)의 프로세스 스케줄링 개념을 GPU 자원 관리에 적용한 것입니다. 핵심은 세 가지입니다.

    diagram

    1. 우선순위 선점 (Priority Preemption)

    OS에서 실시간 프로세스가 일반 프로세스를 밀어내는 것처럼, 우선순위가 높은 GPU 작업이 낮은 작업을 선점(preemption)합니다.

    우선순위는 숫자로 표현하며, 숫자가 낮을수록 더 급한 작업입니다 (Linux의 nice 값과 비슷한 개념).

    작업 우선순위 타입 설명
    음성 전사 (voice-pipeline) 10 (높음) on-demand 사용자가 요청할 때만 실행
    이미지 분석 (vlm-analysis) 90 (낮음) background GPU 비어있으면 자동 실행

    2. 상호 배제 (Mutual Exclusion)

    데이터베이스의 락(Lock)처럼, GPU를 사용하려면 반드시 잠금(lock)을 획득해야 합니다. gpu.lock 파일이 이 역할을 합니다 — 파일 안에 현재 GPU를 쓰고 있는 작업 이름이 적혀 있습니다.

    diagram

    3. 자동 복구 (Auto Recovery)

    작업이 GPU를 반환하면 스케줄러는 자동으로 다음 할 일을 찾습니다. 등록된 background 타입 작업 중 가장 우선순위가 높은 것을 골라 시작합니다.

    전체 아키텍처

    스케줄러는 별도 데몬(daemon, 백그라운드에서 계속 돌아가는 프로그램)이 아닙니다. 대신 각 작업이 시작/종료할 때 스케줄러 스크립트를 호출하는 이벤트 기반(event-driven) 구조입니다. 이렇게 하면 메모리를 차지하는 상주 프로세스가 없어서 가볍습니다.

    diagram

    구성 요소를 정리하면:

    구성 요소 역할 비유
    gpu-scheduler.sh 핵심 스케줄러 스크립트 교통 경찰
    jobs.conf 작업 등록부 (이름, 우선순위, 타입) 신호등 설정표
    gpu.lock 현재 GPU 점유자 기록 사용 중 표지판
    scheduler.log 모든 동작 기록 교통 CCTV 녹화
    gpu-scheduler-boot.sh 부팅 시 초기화 + background 작업 자동 시작 아침에 신호등 켜기

    핵심 흐름 1: GPU 확보 (acquire)

    "이 작업이 GPU를 쓰겠다"고 요청하는 과정입니다. 이미 다른 작업이 쓰고 있다면 우선순위를 비교해서 밀어낼지 대기할지 결정합니다.

    diagram

    코드로 보면 핵심은 이 한 줄입니다:

    if [ "$owner_priority" -le "$my_priority" ]; then
        # 상대가 나보다 높거나 같은 우선순위 → 대기
        return 1
    else
        # 내가 더 높은 우선순위 → 상대를 선점
        _stop_job "$owner"
    fi

    왜 "같은 우선순위"일 때 대기하는가? 같은 우선순위 작업끼리 서로 선점하면 무한 핑퐁이 발생합니다. 먼저 시작한 쪽이 끝까지 쓰도록 해서 이 문제를 방지합니다.

    핵심 흐름 2: GPU 반환 (release)

    작업이 끝나면 GPU를 돌려주고, 대기 중인 background 작업을 자동으로 깨웁니다.

    diagram

    실전 시나리오 — 타임라인으로 보기

    이 모든 것이 실제로 어떻게 동작하는지 시간 순서대로 보겠습니다.

    diagram

    이 전체 과정에서 사용자가 개입하는 시점은 없습니다. 음성 전사 요청이 들어오면 스케줄러가 알아서 이미지 분석을 멈추고, 전사가 끝나면 자동으로 이미지 분석을 재시작합니다.

    작업 타입이라는 설계 결정

    스케줄러에서 가장 중요한 설계 결정 중 하나는 작업을 두 가지 타입으로 나눈 것입니다.

    diagram

    왜 이렇게 나눴을까?

    음성 전사처럼 "사용자가 결과를 기다리는" 작업은 빨리 끝내야 합니다. 이미지 분석처럼 "언제 끝나도 상관없는" 작업은 GPU가 놀 때만 돌리면 됩니다. 이 구분을 타입으로 명시하면 스케줄러가 "GPU가 비었다. 뭘 돌리지?" 판단할 때 background 타입만 자동 시작하게 됩니다. on-demand 작업을 마구 시작하면 사용자가 의도하지 않은 시점에 GPU가 점유되는 부작용이 생기기 때문입니다.

    상태 관리 — 파일 기반 락의 단순함

    고급 동시성(concurrency) 도구 — 뮤텍스, 세마포어, 메시지 큐 — 대신 파일 하나로 상태를 관리합니다.

    diagram

    왜 데이터베이스나 Redis 대신 파일을 쓰는가? 이 스케줄러는 단일 머신에서 동작하고, GPU 작업은 동시에 시작되지 않습니다 (한 번에 하나만 acquire를 호출). 이런 환경에서는 파일 기반 락으로 충분하며, 외부 의존성이 없어서 서버 부팅 직후에도 바로 동작합니다.

    부팅 시 자동 초기화

    서버 재시작 후에도 스케줄러가 자동으로 동작하도록 systemd 서비스를 등록해두었습니다.

    # gpu-scheduler-boot.sh가 하는 일
    1. 이전 세션의 gpu.lock, preempted.txt 삭제 (stale 락 정리)
    2. GPU 드라이버 로드 대기 (10초)
    3. gpu-scheduler.sh release _boot 호출 → background 작업 자동 시작

    여기서 release _boot라는 가짜 release를 호출하는 게 핵심입니다. release의 부수 효과(side effect)인 "background 작업 자동 시작" 로직을 재활용하는 것입니다.

    결과 — 무엇이 달라졌는가?

    Before (수동) After (스케줄러)
    음성 전사 전에 이미지 분석 수동 종료 자동 선점 — 개입 불필요
    전사 끝나면 이미지 분석 수동 재시작 자동 재시작 — 잊어버릴 일 없음
    서버 재부팅 후 작업 수동 시작 systemd가 자동 초기화
    GPU가 놀고 있는 시간 많음 background 작업이 빈틈 없이 활용
    어떤 작업이 GPU를 쓰는지 모름 status 명령으로 한눈에 파악

    새 GPU 작업 추가하기

    나중에 새 AI 작업을 추가하고 싶다면? 두 단계면 됩니다.

    # 1. 작업 등록 (이름, 우선순위, 타입, 시작명령, 중지패턴)
    gpu-scheduler.sh register my-new-job 50 background "python3 my_job.py &" "my_job.py"
    
    # 2. 코드에 acquire/release 추가
    gpu-scheduler.sh acquire my-new-job
    # ... GPU 작업 수행 ...
    gpu-scheduler.sh release my-new-job

    우선순위 50이면 음성 전사(10)보다 낮지만 이미지 분석(90)보다 높으므로, 이미지 분석은 밀어내지만 음성 전사에는 양보합니다.

    마무리 — 250줄로 만든 OS

    이 GPU 스케줄러의 전체 코드는 셸 스크립트 252줄입니다. 데이터베이스도, 메시지 큐도, 별도 런타임도 필요 없습니다. OS의 프로세스 스케줄링에서 핵심 개념 세 가지 — 우선순위, 선점, 자동 복구 — 만 빌려와서, 파일 시스템이라는 가장 원시적인 도구 위에 구현했습니다.

    복잡한 인프라 없이도, 적절한 추상화와 단순한 설계로 실용적인 문제를 해결할 수 있다는 것. 그게 이 작은 스케줄러가 전하는 메시지입니다.


    이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.

Designed by Tistory.