머지 충돌(Merge Conflict)로부터 되살린 나의 Linux 숙련도 게임 — Finish-Up-A-Thon 복귀기
요약
실제 기업용 Linux 관리 능력을 배양하기 위한 AI 멘토링 터미널 게임 프로젝트를 소개합니다. Docker 샌드박스 환경에서 사용자의 명령어를 실행하고, AI가 정확성, 안전성, 효율성을 기준으로 실시간 피드백을 제공합니다.
핵심 포인트
- Docker 샌드박스를 활용한 안전한 Linux 명령어 실행 환경 구축
- Ollama(Llama 3.1)를 이용한 로컬 우선 AI 멘토링 시스템 구현
- 정확성, 안전성, 효율성 기반의 AI 판정 알고리즘 적용
- FastAPI와 YAML 시나리오를 결합한 게임 아키텍처
이 글은 GitHub Finish-Up-A-Thon Challenge를 위한 제출물입니다.
내가 만든 것
Enterprise Linux Mastery Game — 실제 기업용 Linux 관리(Administration)를 연습할 수 있는 로컬 우선(Local-first), AI 멘토링 터미널 게임입니다. 멘토는 시나리오(페이지 시간(Paging-hour) 장애 상황, 권한 퍼즐, 5xx 에러 급증 등)를 설정합니다. 사용자가 Linux 명령어를 입력하면, 명령은 보안이 강화된 Docker 샌드박스(Sandbox) 내부에서 실행됩니다. AI 판정관(Judge)은 **정확성(Correctness), 안전성(Safety), 효율성(Efficiency)**을 기준으로 점수를 매기고 코칭 피드백을 제공합니다.
저는 이것을 진심으로 애정을 가진 1월의 사이드 프로젝트로 시작했습니다. 저는 기업 IT 교육 분야에서 일하고 있으며, 너무나 많은 엔지니어가 시스템을 조사하는 대신 명령어를 암기하는 잘못된 방식으로 Linux를 배우는 것을 보아왔습니다. 이 게임은 저 자신과 제가 함께 일하는 교육생들을 위해 그 문제를 해결하고자 기획되었습니다.
그러다 벽에 부딪혔습니다. 저장소(Repository)는 README 파일에 눈에 띄는 머지 충돌(Merge conflict)이 있는 상태로 4개월 동안 방치되었고, 백엔드는 제가 접근 권한을 잃어버린 유료 LLM API에 연결되어 있었습니다. Finish-Up-A-Thon은 제가 이 프로젝트를 실제로 마무리할 수 있게 해준 추진력이 되었습니다.
- 저장소(Repository): https://github.com/Bharathtrainer/Linux-game
- 아키텍처(Architecture): FastAPI + Ollama (Llama 3.1) + Docker 샌드박스(Sandbox) + YAML 시나리오 + CLI 클라이언트. 모든 것은 로컬에서 실행됩니다. 클라우드 API 키는 필요하지 않습니다.
데모
오늘 밤 캡처한 실제 플레이 세션입니다. 플레이어가 입문(Entry) 레벨을 선택하고, 샌드박스 내부에서 "/var 디스크 용량 가득 참" 시나리오를 조사한 뒤, AI 판정관으로부터 채점된 판결을 받습니다.
판정관은 이번 실행에 대해 84/100점을 부여했습니다 — 세 개의 정확한 조사 명령어, 완벽한 안전성, 거의 완벽한 효율성을 보여주었습니다. 코칭에서는 한 가지 구체적인 개선 사항을 식별했습니다: 디렉토리의 내용물 대신 디렉토리 자체에 du를 실행하는 것입니다.
================================================================
================ ENTERPRISE LINUX MASTERY GAME =================
Local-first. AI-mentored. Sandboxed.
...
심사관(Judge)은 적절할 정도로 엄격합니다. 플레이어가 대용량 파일을 식별하기는 했지만, 더 결정적인 명령어를 통해 실제로 정답을 확인하지는 않았기 때문입니다. 이러한 미묘한 차이를 반영하는 채점 방식이야말로 이 프로젝트를 단순한 게임화 (Gamification)를 넘어 훈련에 유용하게 만드는 요소입니다.
직접 시도해 보세요:
git clone https://github.com/Bharathtrainer/Linux-game
cd Linux-game
docker build -t linux-mastery-sandbox sandbox/
...
복귀 이야기
이번 주 전까지 프로젝트의 상태:
- README.md에 GitHub 랜딩 페이지에서 바로 보이는 해결되지 않은 git 머지 충돌 (Merge Conflict) 마커(
<<<<<<< HEAD,>>>>>>>및 그 사이의 모든 내용)가 남아 있었습니다. 리포지토리를 클릭하는 누구라도 가장 먼저 가공되지 않은 충돌 마크업을 보게 되는 상황이었습니다. - 백엔드가 제가 접근 권한을 잃은 유료 LLM 서비스인 NVIDIA NIM에 하드와이어링 (Hard-wired) 되어 있었습니다. 해당 환경 변수 (Env vars)가 없으면 모든 엔드포인트 (Endpoint)에서 500 에러가 발생했습니다.
- 게임의 문자 그대로의 심장부이자 사용자 명령어를 안전하게 실행하는
core/sandbox.py파일이 존재하지 않았습니다. 심사관 (Judge) 엔드포인트는 상상 속의 명령어에 점수를 매기고 있었습니다. api/mission.py는 `
- 머지 충돌(Merge Conflict) 해결 및 잔해 정리. README를 수정하고, 빈 아티팩트(artifact) 파일들을 삭제했으며, 전체 복구 로그를 담은
THE_COMEBACK.md를 추가했습니다. - NVIDIA NIM을 로컬 Ollama로 교체.
core/nim_client.py를core/llm_client.py로 이름을 변경했습니다. 기본 제공자(Default provider)는 Ollama(무료, 로컬, 키 불필요)입니다.LLM_PROVIDER=nim을 통해 NIM을 폴백(fallback) 수단으로 유지했습니다. 기존 호출부에서 오류가 발생하지 않도록 레거시 NIM 모델 이름을 Llama 3.1로 매핑했습니다. - 누락된 샌드박스 러너(Sandbox runner) 추가.
core/sandbox.py를 작성했습니다. 이는--network=none, 메모리 및 CPU 제한, 명령어별 타임아웃(timeout), 그리고 비루트(non-root) 사용자(UID 1000)를 사용하는 원샷(one-shot) Docker 러너입니다. 게임 전체의 보안 경계(security boundary) 역할을 합니다. - 실제 게임 루프 구축 및 프롬프트(Prompt) 구체화.
api/mission.py를 적절한 오케스트레이터(orchestrator,/start,/run,/finish)로 다시 작성했습니다. 심사관(Judge) 프롬프트에 3축 채점 기준(grading rubric)을 고정했습니다. 실제 도전적인 서사를 담은 세 가지 새로운 시나리오 YAML(operator_runaway_process,engineer_permissions,sre_log_triage)을 추가했습니다. 이 부분은 어떤 AI도 저 대신 작성할 수 없는 부분인데, 실제 현장에서 발생하는 엔터프라이즈 Linux 문제들이 무엇인지 실제로 알아야 하기 때문입니다. - CLI 클라이언트 및 README 다듬기. 게임을 플레이할 수 있도록
play.py를 작성했습니다. 아키텍처 다이어그램, 퀵스타트(quickstart), 스크린샷, 그리고 실제 플레이 세션의 기록(transcript)을 추가했습니다.
전체 과정은 커밋 히스토리(commit history)에서 확인할 수 있습니다. 중요한 점은, 해결되지 않은 머지 충돌(merge conflict)이 커밋 aed0242의 과거 README에 여전히 남아 있다는 것입니다.
내가 배운 것: 사이드 프로젝트는 데모가 거의 가능한(almost demoable) 상태와 실제로 플레이 가능한(actually playable) 상태 사이의 간극에서 죽습니다. 위에서 언급한 커밋들은 대부분 지루한 연결 작업(glue)이었습니다. subprocess 래퍼(wrappers), Pydantic 모델, JSON 파서, CLI 루프 같은 것들이죠. 그중 어려운 것은 하나도 없었습니다. 모두 제가 이전에 "흥미로운 부분은 끝났다"라는 이유로 중단했던 종류의 작업들이었습니다. Finish-Up-A-Thon이라는 프레임워크는 그 간극의 실체를 제대로 볼 수 있게 도와주었습니다. 그것은 능력의 부족이 아니라, 매력적이지 않은 중간 과정을 견뎌낼 인내심의 부족이었습니다.
GitHub Copilot 사용 경험
Copilot이 일반적인 자동 완성(autocomplete)이 아닌, 실제적이고 구체적인 작업을 수행한 세 가지 사례는 다음과 같습니다.
1. 머지 충돌(merge conflict) 해결. 저는 VS Code에서 망가진 README 파일을 열고 Copilot Chat에 다음과 같이 요청했습니다.
"이 README의 Git 머지 충돌을 해결해 줘. 더 풍부한 'Enterprise Linux Mastery Game' 콘텐츠는 유지하되, 실제 README의 시작 부분으로 만들어 줘. 기능 설명, 퀵스타트(quickstart), 설정(configuration) 섹션을 추가해 줘."
Copilot은 깔끔한 초안을 만들어냈고, 저는 이를 Ollama의 세부 사항에 맞춰 수정했습니다. 양쪽 변경 사항을 모두 읽고 수동으로 머지(hand-merging)하는 것보다 훨씬 빨랐습니다.
2. 기존 NIM 모델 이름을 Ollama 태그로 매핑. 저는 Copilot에게 원래의 model_router.py를 제공하며 다음과 같이 요청했습니다.
"이 세 가지 NIM 전용 모델 식별자(identifiers)를 Ollama 태그로 매핑해야 해. 정확히 일치하는 항목이 없을 때 기본값으로 llama3.1을 사용하는 폴백(fallback) 매핑을 생성하고, 레벨→모델 선택 로직은 그대로 유지해 줘."
llm_client.py에 있는 _resolve_model_alias 메서드가 이 상호작용을 통해 만들어졌습니다.
3. 샌드박스 러너(sandbox runner) 초안 작성. 이것이 가장 유용했습니다. 저는 제가 원하는 바를 다음과 같이 설명했습니다.
"subprocess를 통해 docker CLI를 사용하여 일회성 Docker 컨테이너 내부에서 셸 명령을 실행하는 Python 함수를 작성해 줘. 엄격한 타임아웃(hard timeout), 네트워크 차단, 메모리 및 CPU 제한, UID 1000으로 실행되어야 해. stdout, stderr, exit_code, 그리고
timed_out플래그를 포함하는 데이터 클래스(dataclass)를 반환해 줘."
Copilot은 제가 잊어버렸을 법한 subprocess.TimeoutExpired 처리를 포함한 구조를 초안으로 작성해 주었습니다. 저는 sandbox_image_exists() 헬스 체크(health check)와 "Docker가 설치되지 않음"을 위한 FileNotFoundError 분기(branch)를 추가했습니다.
Copilot이 구체적으로 도움이 되지 않았던 부분 — 이 점에 대해서는 솔직해지고 싶습니다:
- 시나리오 YAML을 작성하지 않았습니다. 어떤 엔터프라이즈 Linux 문제들이 교육적으로 가치가 있는지에 대한 도메인 지식(domain knowledge)이 필요했습니다. 일반적인 AI가 작성한 시나리오였다면 교과서적인 연습 문제처럼 느껴졌을 것입니다. 디스크 가득 참(disk-full), 폭주하는 프로세스(runaway-process), 권한 퍼즐(permissions-puzzle), 그리고 로그 분류(log-triage) 시나리오는 제가 교육생들을 지도하며 겪었던 실제 사고들에 기반하고 있습니다.
- 채점 루브릭(judge rubric)을 작성하지 않았습니다. 세 가지 축으로 구성된 점수 산정 방식(정확성 60% / 안전성 25% / 효율성 15%)은 LLM이 채점이 어떻게 이루어져야 한다고 생각하는 방식이 아니라, 제가 실제로 엔지니어들을 평가하는 방식을 반영합니다.
NIMClient를 완전히 제거하는 대신 하위 호환성을 유지하는 별칭(alias)으로 남겨두기로 한 아키텍처적 결정(architectural call)을 내리지 않았습니다. 이는 커밋 차이(commit diff)를 작게 유지하고 검토 가능하게 만들기 위한 판단(judgement call)이었습니다.
"Copilot이 어떻게 도움이 되었는가"에 대한 솔직한 버전은 다음과 같습니다: Copilot은 보일러플레이트(boilerplate) 작성의 마찰 — subprocess 연결 코드, Pydantic 모델, JSON 파싱 헬퍼 등 — 을 제거해 주었고, 덕분에 저는 오직 저만이 할 수 있는 부분에 한정된 주의력을 집중할 수 있었습니다. 시간 압박 속에서 부활시키고 있는 사이드 프로젝트로서, "지루한 코드는 자동화하고 판단 업무는 보존한다"는 이 비율은 정확히 적절한 비율입니다.
오늘 밤 4시간의 단일 스프린트(sprint)로 구축되었습니다. 코드는 Apache 2.0 라이선스입니다. 전체 부활 기록은 리포지토리의 THE_COMEBACK.md에 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기