AI 모델을 만드는 일은 가슴 뛰는 경험입니다. 내 손으로 직접 똑똑한 아기를 키워내는 것과 같죠.
수많은 데이터를 먹여주고, 밤새워가며 학습시키고, 마침내 제법 그럴듯한 결과를 내놓는 순간의 기쁨은 무엇과도 바꾸기 어렵습니다.
그런데 이 기쁨이 좌절로 바뀌는 순간이 종종 찾아옵니다. 내가 정성껏 키운 AI를 동료의 컴퓨터에서 실행하려 할 때, 혹은 몇 달 뒤 다시 열어봤을 때입니다.
분명 내 컴퓨터에서는 완벽하게 작동했는데, 다른 곳에서는 온갖 오류를 쏟아내며 말을 듣지 않습니다. 마치 잘 자라던 아기가 낯선 환경에 가자마자 아프기 시작하는 것 같아 속상하고 막막해집니다.
이런 문제는 당신의 잘못이 아닙니다. AI라는 아기는 생각보다 무척 예민해서, 자신이 자라난 환경과 조금이라도 다르면 쉽게 탈이 나기 때문입니다.
사용하는 프로그래밍 언어의 작은 버전 차이, 설치된 수많은 라이브러리 간의 미묘한 충돌, 심지어는 운영체제의 차이까지 모든 것이 문제의 원인이 될 수 있습니다.
이 끝없는 환경 문제의 늪에서 우리를 구해줄 멋진 도구가 바로 오늘 이야기할 도커입니다.
도커는 단순히 어려운 기술이 아닙니다. 우리의 소중한 AI가 언제 어디서든, 몇 년이 지나든 똑같이 건강하게 자랄 수 있도록, 완벽한 환경을 통째로 선물해주는 마법 같은 기술입니다.
이제부터 이 마법이 어떻게 가능한지, 가장 쉬운 언어로 함께 알아보겠습니다.
컴퓨터마다 말이 다른 AI, 혹시 겪어보셨나요?
머신러닝 프로젝트를 진행하다 보면 가장 흔하게 마주하는 장벽이 바로 ‘환경 문제’입니다.
분명 내 노트북에서는 밤새 잘 돌아가던 코드가, 팀원의 데스크톱에서는 첫 줄부터 에러를 뿜어냅니다.
클라우드 서버에 올려서 본격적으로 학습을 시작하려 하니, 이번엔 또 다른 문제가 발생합니다.
이런 상황은 마치 같은 레시피를 보고 요리했는데, 사람마다 완전히 다른 맛의 음식이 나오는 것과 같습니다.
왜 이런 일이 벌어지는 걸까요?
AI 모델은 홀로 존재하는 것이 아니라, 수많은 소프트웨어 부품들이 모여 돌아가는 정교한 기계와 같습니다.
우리가 사용하는 파이썬이나 R 같은 프로그래밍 언어는 기계의 뼈대가 됩니다.
텐서플로우, 파이토치, 사이킷런 같은 라이브러리들은 엔진, 바퀴, 핸들처럼 핵심적인 역할을 하는 부품들이죠.
판다스, 넘파이 같은 도구들은 데이터를 다듬고 옮기는 컨베이어 벨트와 같습니다.
문제는 이 부품들이 저마다 다른 버전을 가지고 있다는 점입니다.
내 컴퓨터에는 텐서플로우 2.10 버전이 설치되어 있는데, 동료 컴퓨터에는 2.9 버전이 설치되어 있을 수 있습니다.
이 작은 버전 차이가 때로는 모델의 성능을 미묘하게 바꾸거나, 아예 작동 불능 상태로 만들기도 합니다. 마치 최신 스마트폰에 구형 충전기를 꽂으면 충전이 안 되는 것과 비슷한 이치입니다.
서로 호흡을 맞춰야 할 부품들 간의 궁합 문제, 즉 ‘의존성’ 문제도 심각합니다.
특정 버전의 텐서플로우는 특정 버전의 넘파이와 함께 사용해야만 최적의 성능을 냅니다. 하나를 업데이트했더니, 그와 연결된 다른 열 개의 부품이 말썽을 부리는 일이 비일비재합니다.
이런 의존성 문제를 해결하기 위해 모든 팀원이 각자 컴퓨터에 설치된 수백 개의 부품 버전을 일일이 손으로 맞추는 것은 거의 불가능에 가깝습니다. 수만 개의 나사로 조립된 기계를 분해했다가, 설명서도 없이 똑같이 재조립하려는 시도와 같습니다.
이런 혼란 속에서 개발자들은 코드 작성보다 환경 설정에 더 많은 시간을 쏟게 됩니다. 정작 중요한 AI 모델 개선이라는 본질적인 업무는 뒤로 밀려나기 일쑤입니다.
이것이 바로 우리가 일관된 개발 환경의 중요성을 이야기하는 이유입니다. 모든 팀원이, 그리고 과거의 나와 미래의 내가, 언제나 동일한 환경에서 작업할 수 있어야 합니다.
그래야만 AI라는 예민한 아기가 환경 변화에 스트레스받지 않고 무럭무럭 자랄 수 있습니다.
이 고질적인 환경 문제를 가장 우아하고 확실하게 해결해주는 해결사가 바로 도커입니다.
도커는 이 모든 복잡한 부품들과 설정들을 하나의 ‘상자’ 안에 완벽하게 담아줍니다. 그리고 이 상자만 있으면, 어느 컴퓨터에서든 똑같은 환경을 1초 만에 펼쳐낼 수 있게 해줍니다.
더 이상 “제 컴퓨터에선 잘 됐는데요?” 라는 말은 필요 없어집니다.
이제부터 그 마법의 상자를 어떻게 만드는지 차근차근 알아보겠습니다. 마치 레고 블록을 조립하듯, 하나씩 따라오시면 누구나 멋진 AI의 집을 지어줄 수 있습니다.
그래서, 마법의 도시락 ‘도커’가 필요합니다
앞서 우리는 AI가 컴퓨터 환경에 얼마나 예민한지 이야기했습니다. 이 문제를 해결해 줄 도커를 한마디로 비유하자면, ‘어디로든 배달 가능한 마법 도시락’이라고 할 수 있습니다.
우리가 AI 모델을 개발하는 것은 특별한 요리를 만드는 과정과 같습니다. 최고의 요리를 위해서는 신선한 재료뿐만 아니라, 정확한 온도의 오븐, 잘 벼려진 칼, 정밀한 계량도구 등 완벽한 주방 환경이 필요합니다.
여기서 요리 레시피는 우리의 ‘소스 코드’에 해당합니다. 각종 재료들은 텐서플로우, 파이토치 같은 ‘라이브러리’들이죠. 오븐의 온도, 조리 시간 같은 세세한 설정들은 각종 ‘환경 변수’나 ‘설정 파일’에 비유할 수 있습니다.
내 주방에서는 이 모든 것이 완벽하게 갖춰져 있어 환상적인 요리가 탄생합니다.
하지만 친구에게 이 레시피를 알려주며 “너도 한번 만들어봐”라고 했을 때 문제가 생깁니다. 친구의 집 오븐은 우리 집과 성능이 다르고, 칼은 무디며, 계량도구도 부정확할 수 있습니다. 결국 친구는 같은 레시피를 보고도 전혀 다른 맛의 요리를 만들게 될 겁니다.
이것이 바로 “내 컴퓨터에선 됐는데…” 문제의 본질입니다.
도커는 이 문제를 아주 현명한 방식으로 해결합니다. 요리 레시피만 전달하는 것이 아니라, 요리에 필요한 모든 재료, 모든 도구, 심지어 완벽하게 세팅된 오븐과 가스레인지까지 통째로 하나의 ‘도시락’에 담아버리는 겁니다.
이 도시락의 이름이 바로 ‘도커 이미지’입니다.
이 이미지 안에는 우리 AI 코드를 실행하는 데 필요한 모든 것이 담겨 있습니다. 파이썬 3.9.7 버전, 텐서플로우 2.10.0 버전, 넘파이 1.23.3 버전 등 아주 사소한 부품의 버전 정보까지 정확하게 기록되고 포장됩니다.
이렇게 만들어진 도시락(이미지)은 파일 형태로 존재하기 때문에 동료에게 쉽게 전달할 수 있습니다.
동료는 이 도시락을 받아서 자신의 컴퓨터에서 열기만 하면 됩니다. 이 도시락을 여는 행위를 ‘컨테이너를 실행한다’고 표현합니다.
컨테이너가 실행되는 순간, 동료의 컴퓨터 기종이나 운영체제와는 상관없이, 내 컴퓨터와 100% 동일한 환경이 마법처럼 펼쳐집니다. 도시락 안에 들어있던 완벽하게 세팅된 휴대용 주방이 그대로 재현되는 것과 같습니다.
이제 동료는 그 안에서 레시피(코드)를 실행하기만 하면, 나와 정확히 똑같은 맛의 요리(실행 결과)를 만들어낼 수 있습니다.
이것이 도커가 제공하는 가장 핵심적인 가치, 즉 ‘환경의 격리’와 ‘재현성 보장’입니다.
도커 컨테이너라는 독립된 공간 덕분에, 내 컴퓨터에 원래 설치되어 있던 프로그램들과 충돌할 걱정도 전혀 없습니다. 마치 내 방 안에 완벽하게 방음 처리된 작은 스튜디오를 하나 만드는 것과 같습니다. 스튜디오 안에서 어떤 음악을 연주하든 밖에는 아무런 영향을 주지 않습니다.
마찬가지로, 컨테이너 안에서 어떤 라이브러리를 설치하고 삭제하든 원래 내 컴퓨터 환경은 깨끗하게 유지됩니다. 이 덕분에 우리는 마음 놓고 다양한 실험을 할 수 있는 용기를 얻게 됩니다.
새로운 기술을 테스트해보고 싶을 때, 내 소중한 컴퓨터가 망가질까 봐 더 이상 두려워할 필요가 없습니다. 도커라는 안전한 놀이터 안에서 마음껏 시도해보고, 마음에 들지 않으면 놀이터를 통째로 없애버리면 그만입니다.
이처럼 도커는 단순한 개발 도구를 넘어, 개발자에게 심리적 안정감과 자유를 선물하는 멋진 친구와도 같습니다.
내 AI를 위한 완벽한 집, 어떻게 지어줄까요?
이제 마법 도시락, 즉 도커 이미지를 직접 만들어볼 시간입니다.
AI를 위한 완벽한 환경을 만들기 위해서는 설계도가 필요합니다. 이 설계도의 이름이 바로 ‘Dockerfile’입니다.
Dockerfile은 평범한 텍스트 파일이며, 우리가 원하는 집(환경)을 어떻게 지을지 한 줄 한 줄 명령어로 적어놓은 레시피와 같습니다. 어려운 프로그래밍 언어가 아니라, 누구나 쉽게 이해할 수 있는 몇 가지 약속된 키워드로 이루어져 있습니다.
지금부터 집을 짓는 과정을 상상하며 Dockerfile의 핵심 명령어들을 하나씩 살펴보겠습니다.
모든 집의 시작, 기초 공사
모든 건물은 튼튼한 땅 위에 지어야 합니다. Dockerfile의 첫 줄은 항상 FROM으로 시작합니다. 이는 우리 집을 어떤 땅 위에 지을지 결정하는 과정입니다.
예를 들어, FROM python:3.9-slim 이라고 적으면, “파이썬 3.9 버전이 이미 깔끔하게 설치된 땅을 기초로 사용하겠습니다”라는 의미입니다. 우리가 직접 파이썬을 설치하는 수고를 덜어주는 고마운 명령어입니다.
머신러닝에서는 보통 파이썬이나, 엔비디아에서 제공하는 CUDA(GPU 사용을 위한 도구)가 미리 설치된 땅을 많이 사용합니다.
작업 공간 정하기
집을 지을 때, 자재를 쌓아두고 실제 작업을 할 공간이 필요합니다.
WORKDIR /app 명령어는 컨테이너 안에 ‘app’이라는 이름의 작업실을 만들고, 앞으로의 모든 작업은 이 안에서 하겠다고 선언하는 것과 같습니다. 이렇게 작업 공간을 지정해두면 파일들이 여기저기 흩어지지 않고 깔끔하게 정리됩니다.
필요한 자재 옮기기
이제 집을 지을 자재들을 작업실로 옮겨야 합니다. 우리가 작성한 파이썬 코드, 모델 파일, 데이터 파일 등이 바로 그 자재들입니다.
COPY . . 명령어는 “내 컴퓨터의 현재 폴더에 있는 모든 파일과 폴더를, 컨테이너 안의 작업실(‘/app’)로 그대로 복사하라”는 뜻입니다.
여기서 첫 번째 점은 명령어을 실행하는 내 컴퓨터의 현재 위치를, 두 번째 점은 컨테이너 안의 현재 작업 위치(‘/app’)를 의미합니다.
내부 공사, 필요한 도구 설치하기
집의 뼈대를 세우고 자재를 옮겼으니, 이제 내부 공사를 할 차례입니다. 배관을 설치하고, 전기를 연결하고, 가구를 들여놓는 과정과 같습니다.
AI 개발에서는 텐서플로우, 파이토치 같은 핵심 라이브러리들을 설치하는 단계에 해당합니다.
RUN pip install -r requirements.txt 라는 명령어는 ‘requirements.txt’라는 파일에 적힌 모든 라이브러리 목록을 자동으로 설치해달라는 의미입니다.
보통 이 파일 안에는 tensorflow==2.10.0, pandas==1.5.0 처럼 필요한 라이브러리와 정확한 버전이 명시되어 있어, 우리 AI가 필요로 하는 모든 부품이 정확한 버전으로 설치됩니다.
마지막, 문패 달기
드디어 집이 완성되었습니다. 이제 이 집을 사용하는 방법을 알려줘야 합니다.
CMD ["python", "app.py"] 명령어는 “이 집에 들어오면, 자동으로 app.py라는 파이썬 파일을 실행해주세요”라고 문패를 달아두는 것과 같습니다.
이렇게 설정해두면, 나중에 다른 사람이 이 컨테이너를 실행했을 때 무엇을 해야 할지 고민할 필요 없이 자동으로 메인 프로그램이 실행됩니다.
이처럼 Dockerfile은 FROM, WORKDIR, COPY, RUN, CMD 와 같은 몇 가지 간단한 명령어들의 조합으로 이루어집니다. 마치 레고 설명서를 보며 차근차근 블록을 쌓아 올리듯, 이 명령어들을 순서대로 작성하면 누구든 자신만의 완벽한 AI 하우스를 설계할 수 있습니다.
정말 모든 재료를 하나하나 챙겨야 할까요?
앞서 Dockerfile을 이용해 AI의 집을 짓는 방법을 살펴봤습니다. 마치 요리 레시피처럼 필요한 재료(라이브러리)를 하나씩 RUN 명령어로 설치하는 과정이었습니다.
그런데 이런 생각이 들 수 있습니다. “머신러닝에 필요한 도구들은 대부분 비슷한데, 프로젝트를 시작할 때마다 매번 이 모든 걸 설치해야 하나?”
밀가루, 설탕, 계란, 버터… 케이크를 만들 때마다 이 기본 재료들을 항상 새로 계량하는 것은 번거로운 일입니다.
이런 수고를 덜어주기 위해 세상에는 ‘케이크 믹스’라는 멋진 제품이 있습니다. 케이크 믹스에는 기본적인 재료들이 최적의 비율로 미리 섞여 있어, 우리는 약간의 우유나 계란만 추가하면 손쉽게 맛있는 케이크를 완성할 수 있습니다.
도커의 세계에도 이런 ‘케이크 믹스’와 같은 개념이 존재합니다. 바로 ‘베이스 이미지’입니다.
베이스 이미지는 특정 목적에 맞게 필수적인 도구들이 미리 설치되어 있는, 일종의 잘 만들어진 기초 이미지입니다. 우리가 Dockerfile 첫 줄에 썼던 FROM python:3.9-slim 도 사실은 파이썬이 미리 설치된 베이스 이미지를 사용하겠다는 뜻이었습니다.
머신러닝 개발자들을 위해, 세상의 고마운 전문가들은 훨씬 더 강력한 베이스 이미지들을 만들어 공개해두었습니다.
예를 들어, 구글은 텐서플로우(TensorFlow) 공식 이미지를 제공합니다. FROM tensorflow/tensorflow:latest-gpu 라는 이미지를 베이스로 사용하면, 텐서플로우뿐만 아니라 GPU를 사용하는 데 필요한 복잡한 CUDA, cuDNN 라이브러리까지 완벽하게 설치된 환경에서 시작할 수 있습니다.
우리가 직접 했다면 며칠 밤을 새워도 해결하기 어려운 복잡한 설치 과정을 단 한 줄로 끝내주는 것입니다.
메타(구 페이스북)에서 만든 파이토치(PyTorch) 역시 공식 이미지를 제공하며, 데이터 분석가들에게 사랑받는 주피터 노트북이 미리 설치되고 실행되도록 설정된 이미지도 있습니다.
이런 공식 베이스 이미지들을 사용하는 것은 여러 가지 장점이 있습니다.
첫째, 시간을 절약할 수 있습니다. 가장 중요하고 복잡한 설치 과정을 건너뛸 수 있으니, 우리는 바로 본론인 모델 개발에 집중할 수 있습니다.
둘째, 안정성이 높습니다. 각 분야의 전문가들이 수많은 테스트를 거쳐 최적의 조합으로 구성해놓은 환경이므로, 라이브러리 간 충돌이나 호환성 문제가 발생할 확률이 현저히 낮습니다. 마치 유명 셰프가 검증한 케이크 믹스를 사용하는 것이, 내가 어설프게 재료를 섞는 것보다 실패 확률이 낮은 것과 같습니다.
셋째, 이미지의 크기를 줄일 수 있습니다. 전문가들은 불필요한 파일들을 제거하고 최대한 가볍게 이미지를 만드는 노하우를 가지고 있습니다. 이미지가 가벼워지면 저장 공간을 아낄 수 있고, 다른 사람과 주고받을 때도 속도가 빨라집니다.
물론, 이 베이스 이미지 위에 우리 프로젝트에만 필요한 특별한 라이브러리들을 추가로 설치할 수도 있습니다. 케이크 믹스에 나만의 개성을 더하기 위해 초콜릿 칩이나 견과류를 추가하는 것처럼 말이죠.
따라서 현명한 개발자는 모든 것을 처음부터 만들려고 애쓰기보다, 잘 만들어진 베이스 이미지를 적극적으로 활용합니다.
어떤 베이스 이미지가 있는지 궁금하다면, ‘도커 허브’라는 웹사이트에 방문해보세요. 도커 허브는 전 세계 개발자들이 자신이 만든 이미지를 공유하는 거대한 라이브러리와 같습니다. 이곳에서 ‘tensorflow’, ‘pytorch’, ‘jupyter’ 등 원하는 키워드로 검색하면, 수많은 공식 이미지와 인기 있는 커스텀 이미지들을 찾아볼 수 있습니다.
좋은 도구를 잘 활용하는 것이 실력입니다. 잘 만들어진 베이스 이미지는 우리의 개발 여정을 훨씬 더 빠르고 즐겁게 만들어 줄 것입니다.
도시락을 만들었으니, 이제 맛있게 먹어볼까요?
지금까지 우리는 Dockerfile이라는 설계도를 이용해 ‘마법 도시락’을 만드는 법을 배웠습니다.
하지만 설계도는 어디까지나 계획일 뿐, 그 자체로 음식을 먹을 수는 없습니다. 설계도를 보고 실제로 도시락을 만드는 과정, 그리고 그 도시락을 열어 음식을 맛보는 과정이 필요합니다.
도커의 세계에서는 이 두 과정을 각각 ‘이미지 빌드’와 ‘컨테이너 실행’이라고 부릅니다. 이 두 개념의 차이를 이해하는 것은 도커의 작동 원리를 파악하는 데 필수적입니다.
붕어빵을 떠올리면 이해하기 쉽습니다.
붕어빵을 만드는 ‘틀’이 바로 ‘이미지’에 해당합니다. 붕어빵을 구울 수 있는 모든 조건과 모양을 갖춘 주물 틀이죠. 이 틀 자체는 먹을 수 없지만, 이 틀만 있으면 똑같은 모양의 붕어빵을 무한정 찍어낼 수 있습니다.
그 틀에 반죽을 부어 구워낸, 김이 모락모락 나는 붕어빵 하나하나가 바로 ‘컨테이너’입니다. 컨테이너는 이미지를 기반으로 실제로 살아 움직이는 실체인 셈입니다.
우리는 붕어빵(컨테이너)을 먹고, 다 먹으면 사라집니다. 하지만 붕어빵 틀(이미지)은 그대로 남아있어 언제든 다시 붕어빵을 구울 수 있습니다.
이제 실제 명령어를 통해 이 과정을 살펴보겠습니다.
이미지 빌드: 설계도로 도시락 만들기
우리가 작성한 Dockerfile이 있는 폴더로 이동한 뒤, 터미널 창에 다음과 같은 명령어를 입력합니다.
docker build -t my-ai-app .
이 명령어는 “도커야, 현재 폴더에 있는 Dockerfile을 읽어서, ‘my-ai-app’이라는 이름표를 붙인 이미지를 만들어줘”라는 뜻입니다.
이 명령을 실행하면, 도커는 Dockerfile의 첫 줄부터 마지막 줄까지 차례대로 읽으며 작업을 수행합니다. 베이스 이미지를 다운로드하고, 파일을 복사하고, 라이브러리를 설치하는 과정이 화면에 순서대로 표시됩니다. 마치 로봇이 설계도를 보고 부지런히 집을 짓는 모습을 지켜보는 것과 같습니다.
빌드가 성공적으로 완료되면, 우리 컴퓨터에는 ‘my-ai-app’이라는 이름의 이미지가 생성됩니다. 이제 우리는 언제든 이 이미지를 사용해 AI 환경을 만들 준비가 된 것입니다.
컨테이너 실행: 도시락 열고 AI 실행하기
이제 잘 만들어진 도시락을 열어볼 차례입니다.
docker run my-ai-app
이 명령어는 “도커야, ‘my-ai-app’이라는 이미지를 사용해서 컨테이너를 하나 실행해줘”라는 뜻입니다.
이 명령이 실행되는 순간, 도커는 우리 컴퓨터 안에 완전히 독립된 작은 가상 공간을 만듭니다. 그리고 그 공간을 ‘my-ai-app’ 이미지에 기록된 환경과 똑같이 꾸며줍니다. 마지막으로, Dockerfile의 CMD 명령어에 적어두었던 python app.py가 자동으로 실행됩니다.
우리의 AI 프로그램이 마침내 격리된 완벽한 환경 속에서 작동을 시작하는 순간입니다.
때로는 프로그램만 실행하고 끝내는 것이 아니라, 컨테이너 안으로 직접 들어가 명령어를 입력하며 작업하고 싶을 때도 있습니다. 그럴 때는 옵션을 추가하여 실행할 수 있습니다.
docker run -it my-ai-app
여기서 -it 옵션은 컨테이너와 내가 서로 대화를 나눌 수 있는 통로를 열어달라는 의미입니다. 이 명령어로 컨테이너를 실행하면, 우리는 마치 그 컨테이너라는 작은 컴퓨터 안에 직접 접속한 것처럼 자유롭게 명령어를 입력하고 파일 시스템을 탐색할 수 있습니다.
작업이 끝나면 exit를 입력하여 컨테이너에서 빠져나올 수 있습니다.
이처럼 ‘이미지’와 ‘컨테이너’의 관계만 명확히 이해하면 도커의 절반을 이해한 것이나 다름없습니다. 이미지는 변하지 않는 ‘틀’, 컨테이너는 그 틀에서 찍어낸 살아있는 ‘결과물’이라는 점을 꼭 기억해주세요.
내 소중한 연구 데이터, 도시락 안에만 두긴 아깝잖아요
도커 컨테이너는 격리된 환경을 제공한다는 강력한 장점이 있습니다.
하지만 이 ‘격리’라는 특징은 때로는 단점이 되기도 합니다. 컨테이너는 기본적으로 일회용입니다. 붕어빵을 다 먹으면 사라지듯이, 컨테이너를 중지하고 삭제하면 그 안에서 작업했던 모든 내용이 함께 사라집니다.
만약 우리가 컨테이너 안에서 몇 시간 동안 AI 모델을 학습시켜서 중요한 결과 파일을 얻었다고 상상해보세요. 실수로 컨테이너를 삭제하는 순간, 우리의 소중한 연구 결과물도 허공으로 날아가 버립니다.
또한, 학습에 필요한 대용량 데이터셋을 생각해보세요. 수십 기가바이트에 달하는 데이터를 COPY 명령어로 이미지 안에 통째로 복사하는 것은 비효율적입니다. 이미지 크기가 너무 커져서 관리하기 어려워지고, 빌드하는 데에도 엄청난 시간이 걸릴 겁니다.
이런 문제를 해결하기 위해 도커는 ‘볼륨’ 또는 ‘바인드 마운트’라는 아주 똑똑한 기능을 제공합니다.
이 기능은 우리 실제 컴퓨터(호스트)의 특정 폴더와, 컨테이너 안의 특정 폴더를 연결해주는 ‘마법의 통로’를 만드는 것과 같습니다.
캠핑카(컨테이너)를 타고 여행을 떠났다고 비유해봅시다. 캠핑카 안의 냉장고는 작아서 많은 식재료를 보관하기 어렵고, 여행이 끝나면 냉장고 안의 음식물은 모두 버려야 합니다.
이때, 우리 집(호스트)의 대형 냉장고와 캠핑카의 작은 냉장고를 마법의 터널로 연결하는 겁니다. 이제 캠핑카의 냉장고 문을 열면, 우리 집 대형 냉장고 안의 식재료를 마음껏 꺼내 쓸 수 있습니다.
캠핑카 안에서 새로운 음식을 만들어 냉장고에 넣어두면, 그 음식은 마법 터널을 통해 우리 집 냉장고에 안전하게 보관됩니다. 나중에 여행이 끝나고 캠핑카를 반납하더라도, 음식들은 우리 집 냉장고에 그대로 남아있게 됩니다.
이것이 바로 볼륨의 역할입니다.
실제 명령어에서는 -v 옵션을 사용해 이 마법의 통로를 만들 수 있습니다.
docker run -v /path/on/my/computer:/app/data my-ai-app
이 명령어의 의미는 다음과 같습니다.
“-v 옵션을 사용해서 볼륨을 연결하겠습니다. 내 컴퓨터의 ‘/path/on/my/computer’ 라는 폴더를, 컨테이너 안의 ‘/app/data’ 라는 폴더와 연결해주세요.”
이렇게 컨테이너를 실행하면, 내 컴퓨터의 해당 폴더에 있는 모든 파일이 컨테이너의 /app/data 폴더 안에 즉시 나타납니다. 컨테이너 안에서 AI가 학습을 진행하며 /app/data 폴더에 결과물(학습된 모델 가중치, 로그 파일 등)을 저장하면, 그 파일들은 실시간으로 내 컴퓨터의 폴더에도 똑같이 저장됩니다.
이제 컨테이너가 멈추거나 사라져도, 우리의 소중한 결과물은 내 컴퓨터에 안전하게 보존됩니다.
이 방식을 사용하면, 코드와 환경은 컨테이너 안에 격리시키되, 데이터와 결과물은 내 컴퓨터에서 영구적으로 관리하는 이상적인 작업 흐름을 만들 수 있습니다. 코드는 언제든 수정해서 다시 이미지를 빌드하면 되고, 데이터는 그대로 유지한 채 새로운 환경의 컨테이너와 연결하여 실험을 계속 이어나갈 수 있습니다.
대용량 데이터셋을 이미지에 포함시킬 필요가 없어져 이미지 크기는 가볍게 유지되고, 빌드 시간도 단축됩니다. 데이터를 다루는 머신러닝 개발에서 볼륨 기능은 선택이 아닌 필수입니다. 이 마법의 통로를 잘 활용하여 여러분의 소중한 자산을 안전하게 지키시길 바랍니다.
GPU는 어떻게 사용하나요? AI에게 힘을 실어주는 방법
현대의 머신러닝, 특히 딥러닝 모델을 학습시키는 것은 엄청난 양의 계산을 필요로 합니다. 수백만, 수천만 개의 숫자를 반복적으로 곱하고 더하는 과정을 수없이 되풀이해야 합니다.
컴퓨터의 중앙 처리 장치, 즉 CPU는 똑똑하고 다재다능하지만, 이런 단순 반복 계산을 대량으로 처리하는 데에는 그리 효율적이지 않습니다. 마치 만능 스위스 군용 칼과 같습니다. 여러 가지 기능이 있지만, 나무 수백 그루를 베기에는 역부족입니다.
이때 필요한 것이 바로 GPU입니다.
GPU는 원래 게임이나 영상의 화려한 그래픽을 처리하기 위해 태어났습니다. 화면의 수많은 픽셀 색상을 동시에 계산해야 하는 작업에 특화되어 있죠. 이 ‘단순한 계산을 동시에 대량으로 처리하는 능력’이 딥러닝의 연산 방식과 정확히 맞아떨어진 것입니다.
GPU는 수백 그루의 나무를 베기 위해 특수 제작된 전기톱과 같습니다. 오직 한 가지 일을 하지만, 그 분야에서는 CPU와 비교할 수 없을 정도로 압도적인 속도를 보여줍니다. GPU를 사용하면 모델 학습 시간을 며칠에서 몇 시간으로, 몇 시간에서 몇 분으로 단축시킬 수 있습니다.
그런데 이 강력한 GPU를 도커 컨테이너 안에서 사용하려면 어떻게 해야 할까요?
기본적으로 도커 컨테이너는 격리된 공간이기 때문에, 우리 컴퓨터에 설치된 하드웨어, 즉 GPU에 직접 접근할 수 없습니다.
이 문제를 해결하기 위해 엔비디아(NVIDIA, 대표적인 GPU 제조사)에서는 ‘NVIDIA Container Toolkit’이라는 특별한 도구를 만들었습니다. 이 도구를 설치하면, 도커가 우리 컴퓨터의 GPU를 인식하고 컨테이너와 연결해주는 다리 역할을 해줍니다.
설치 과정은 운영체제마다 조금씩 다르지만, 공식 문서를 따라 하면 어렵지 않게 설치할 수 있습니다.
NVIDIA Container Toolkit이 설치되었다면, 컨테이너에서 GPU를 사용하는 방법은 놀라울 정도로 간단합니다. 컨테이너를 실행하는 docker run 명령어에 단 하나의 옵션만 추가해주면 됩니다.
docker run --gpus all my-ai-app
바로 --gpus all 이라는 옵션입니다. 이 옵션은 “도커야, 이 컨테이너를 실행할 때, 내 컴퓨터에 장착된 모든 GPU를 사용할 수 있도록 허락해줘”라는 의미입니다.
이렇게 실행된 컨테이너 안에서 텐서플로우나 파이토치 코드를 실행하면, 코드는 자동으로 GPU를 감지하고 계산 작업을 GPU에 맡기기 시작합니다. 컨테이너 안에서 특별히 무언가를 더 설정해줄 필요가 없습니다.
물론, 이를 위해서는 Dockerfile을 만들 때 FROM 부분에서 GPU를 지원하는 베이스 이미지를 사용해야 합니다. 앞서 잠깐 언급했던 tensorflow/tensorflow:latest-gpu 나 pytorch/pytorch:latest 같은 이미지들은 GPU 사용에 필요한 모든 소프트웨어(CUDA, cuDNN 등)가 미리 설치되어 있습니다.
만약 이런 베이스 이미지를 사용하지 않았다면, 컨테이너 안에서 GPU를 인식하지 못해 결국 CPU로만 동작하게 될 것입니다.
이처럼 도커를 사용하면, 로컬 컴퓨터에서든 클라우드 서버에서든 GPU 환경을 설정하는 복잡한 과정이 매우 단순해집니다. 하드웨어 드라이버와 CUDA 버전 간의 끔찍한 호환성 문제로 골머리를 앓을 필요가 없습니다.
잘 만들어진 공식 GPU 베이스 이미지를 선택하고, 실행 시점에 --gpus all 옵션만 추가하면 끝입니다. AI에게 강력한 힘을 실어주는 일이 이렇게 간단해지는 것, 이것 또한 도커가 우리에게 주는 커다란 선물 중 하나입니다.
우리 팀 모두가 똑같은 요리를 만들게 하려면?
지금까지 우리는 도커를 이용해 나만의 컴퓨터에서 일관된 개발 환경을 구축하는 방법에 대해 알아봤습니다. 혼자 진행하는 프로젝트라면 이것만으로도 충분히 훌륭합니다.
하지만 대부분의 AI 프로젝트는 여러 명의 팀원이 함께 협업하여 진행됩니다. 내가 정성껏 만든 ‘마법 도시락(도커 이미지)‘을 팀 동료들도 똑같이 사용할 수 있어야 진정한 협업이 가능해집니다.
어떻게 하면 내가 만든 이미지를 팀원들과 손쉽게 공유할 수 있을까요?
가장 단순한 방법은 이미지를 하나의 파일로 압축해서 USB나 이메일로 전달하는 것입니다. docker save와 docker load라는 명령어를 사용하면 이미지를 파일로 만들고, 그 파일을 다시 이미지로 불러올 수 있습니다.
하지만 이 방식은 팀원의 수가 많아지거나 이미지가 업데이트될 때마다 매번 파일을 새로 전달해야 해서 매우 번거롭습니다. 마치 중요한 문서를 수정할 때마다 매번 이메일로 ‘최종_진짜최종_정말최종.hwp’ 파일을 보내는 것과 같습니다.
이런 비효율적인 문제를 해결하기 위해 ‘컨테이너 레지스트리’라는 것이 존재합니다. 컨테이너 레지스트리는 도커 이미지를 전문적으로 저장하고 관리해주는 온라인 창고라고 생각하면 쉽습니다.
우리가 코드를 깃허브(GitHub)에 올려서 관리하고 공유하는 것처럼, 도커 이미지는 컨테이너 레지스트리에 올려서 관리하고 공유합니다.
가장 대표적이고 대중적인 컨테이너 레지스트리가 바로 ‘도커 허브’입니다. 도커 허브에 가입하면 누구나 자신만의 이미지 저장소를 가질 수 있습니다.
내가 만든 이미지를 도커 허브에 올리는 과정은 매우 간단합니다. 먼저, 이미지의 이름표를 도커 허브 규칙에 맞게 바꿔줘야 합니다.
docker tag my-ai-app your-dockerhub-id/my-ai-app
이 명령어는 ‘my-ai-app’이라는 기존 이미지에 ‘내도커허브ID/my-ai-app’이라는 새로운 이름표를 하나 더 붙여주는 것입니다.
이제, 이 이미지를 도커 허브로 밀어 올립니다. 이 과정을 ‘푸시’라고 합니다.
docker push your-dockerhub-id/my-ai-app
이 명령 한 줄이면, 내가 만든 이미지가 인터넷을 통해 도커 허브의 내 개인 창고에 안전하게 업로드됩니다.
이제 팀 동료들은 어떻게 이 이미지를 사용할 수 있을까요? 팀원들은 자신의 컴퓨터에서 ‘풀’이라는 명령어를 사용해 이미지를 내려받기만 하면 됩니다.
docker pull your-dockerhub-id/my-ai-app
이 명령 한 줄로, 팀원은 나와 100% 동일한 환경을 담고 있는 이미지를 순식간에 자신의 컴퓨터로 가져올 수 있습니다. 그 후 docker run 명령어로 컨테이너를 실행하면, 곧바로 개발에 참여할 수 있습니다.
팀에 새로운 멤버가 합류했을 때를 상상해보세요. 과거에는 새 멤버의 컴퓨터에 개발 환경을 세팅해주는 데에만 반나절, 혹은 며칠이 걸리기도 했습니다.
하지만 이제는 “도커 설치하시고, 이 docker pull 명령어 한 줄만 실행하세요” 라고 말하면 모든 준비가 끝납니다.
만약 환경에 변경이 필요하면 어떻게 할까요? 한 사람이 Dockerfile을 수정하고, 이미지를 새로 빌드한 뒤, 새로운 버전의 이미지를 다시 도커 허브에 푸시하면 됩니다.
다른 팀원들은 다시 docker pull을 실행하여 업데이트된 최신 이미지를 내려받기만 하면 됩니다. 모든 팀원이 항상 동일한, 그리고 최신의 개발 환경을 유지할 수 있게 되는 것입니다.
더 이상 “제 컴퓨터에서는 잘 되는데요?” 라는 말로 서로 시간을 낭비할 필요가 없어집니다. 모두가 똑같은 레시피와 똑같은 주방에서 요리하게 되니까요.
이처럼 컨테이너 레지스트리를 활용하는 것은 머신러닝 팀의 생산성을 극적으로 향상시키는 핵심적인 비결입니다.
실수는 괜찮아요, 언제든 과거로 돌아갈 수 있으니까요
머신러닝 프로젝트는 수많은 실험의 연속입니다.
새로운 라이브러리를 추가해보기도 하고, 기존 라이브러리의 버전을 올려보기도 합니다. 이런 시도들은 모델의 성능을 향상시키는 데 꼭 필요한 과정이지만, 때로는 예기치 못한 문제를 일으키기도 합니다.
새로 설치한 라이브러리가 기존의 다른 라이브러리와 충돌하여 모든 것이 엉망이 되어버리는 상황을 상상해보세요. 어디서부터 잘못되었는지 찾아내고 원래 상태로 되돌리는 것은 엄청난 스트레스입니다. 마치 공들여 쌓아 올린 젠가 탑이 와르르 무너져 내리는 기분일 겁니다.
도커는 이런 상황에서 우리를 구해줄 아주 강력한 ‘시간 여행’ 기능을 제공합니다. 바로 ‘이미지 태그’를 이용한 버전 관리입니다.
태그는 이미지에 붙이는 이름표와 같은 것인데, 보통 이름 뒤에 콜론(:)을 붙이고 버전 정보를 적는 방식으로 사용합니다.
우리가 이미지를 빌드할 때, 다음과 같이 태그를 붙일 수 있습니다.
docker build -t my-ai-app:v1.0 .
이렇게 하면 ‘my-ai-app’이라는 이름에 ‘v1.0’이라는 버전표가 붙은 이미지가 생성됩니다. 이 이미지는 우리 프로젝트의 안정적인 첫 번째 버전입니다. 우리는 이 버전을 도커 허브에 푸시하여 안전하게 보관할 수 있습니다.
이제 새로운 실험을 위해 Dockerfile을 수정했다고 가정해봅시다. 예를 들어, 텐서플로우 버전을 올리고 새로운 라이브러리를 추가했습니다. 그리고 새로운 버전의 이미지를 빌드합니다. 이때, 이전 버전을 덮어쓰지 않고 새로운 버전 태그를 붙이는 것이 중요합니다.
docker build -t my-ai-app:v1.1 .
이제 우리 컴퓨터에는 ‘v1.0’과 ‘v1.1’ 두 개의 버전이 공존하게 됩니다.
우리는 ‘v1.1’ 이미지를 기반으로 컨테이너를 실행하여 새로운 환경에서 테스트를 진행합니다. 만약 테스트 결과가 좋고 모든 것이 잘 작동한다면, 우리는 계속해서 ‘v1.1’ 버전을 사용하면 됩니다.
하지만 만약 ‘v1.1’ 환경에서 예상치 못한 심각한 오류가 발생한다면 어떻게 할까요?
전혀 걱정할 필요가 없습니다. 우리는 언제든지 과거의 안정적인 버전으로 돌아갈 수 있습니다.
그저 ‘v1.0’ 태그가 붙은 이미지로 컨테이너를 다시 실행하기만 하면 됩니다.
docker run my-ai-app:v1.0
이 명령 한 줄로, 우리는 완벽하게 작동했던 과거의 환경으로 즉시 돌아갈 수 있습니다. 마치 게임에서 저장된 지점으로 되돌아가듯이 말이죠.
잘못된 변경 사항으로 인해 스트레스받으며 밤새 원인을 찾을 필요가 없습니다. 그저 이전 버전으로 돌아가서 다시 시작하면 됩니다.
이러한 버전 관리 방식은 개인의 실수를 되돌리는 데에도 유용하지만, 팀 협업에서는 더욱 빛을 발합니다.
팀 전체가 특정 안정화 버전(예: my-ai-app:v2.0-stable)을 기준으로 작업하다가, 누군가 새로운 기능을 개발할 때는 자신만의 실험용 버전(예: my-ai-app:v2.1-beta-feature-x)을 만들어 테스트할 수 있습니다. 테스트가 충분히 이루어지고 안정성이 검증되면, 그때 팀 전체가 새로운 안정화 버전으로 함께 이동하면 됩니다.
이처럼 이미지 태그를 이용한 체계적인 버전 관리는 개발 과정에서 발생하는 수많은 위험을 줄여주고, 개발자에게 마음 편히 새로운 시도를 해볼 수 있는 용기를 줍니다.
실수는 성장의 과정일 뿐, 결코 실패가 아닙니다. 도커와 함께라면, 우리는 언제든 안전하게 과거로 돌아가 다시 도전할 수 있습니다.
새로운 기술을 마주하는 것은 언제나 조금은 두렵고 막막하게 느껴질 수 있습니다. 오늘 우리가 함께 살펴본 도커라는 이름도 처음에는 낯설고 어렵게만 들렸을지 모릅니다.
하지만 복잡해 보이는 겉모습 속에 담긴 핵심 아이디어는 사실 아주 따뜻합니다. 바로 기술을 사용하는 사람들을 끝없는 반복과 좌절감에서 해방시켜, 더 중요하고 창의적인 일에 집중할 수 있도록 돕겠다는 것입니다.
도커는 우리에게 완벽하게 재현 가능한 환경을 선물함으로써 “왜 안 되지?”라는 소모적인 고민 대신 “무엇을 만들어볼까?”라는 즐거운 상상을 할 수 있게 해줍니다.
팀 동료들과의 불필요한 마찰을 줄여주고, 오롯이 같은 목표를 향해 함께 나아갈 수 있는 튼튼한 다리를 놓아줍니다.
실수를 두려워하지 않고 언제든 다시 시작할 수 있다는 심리적 안정감까지 제공하는 든든한 친구이기도 합니다.
오늘 배운 모든 것을 당장 완벽하게 이해하지 못해도 괜찮습니다. 그저 ‘AI에게 언제 어디서든 똑같은 환경을 만들어주는 마법 도시락’이라는 비유 하나만 기억하셔도 충분합니다.
가장 중요한 것은 기술에 대한 막연한 불안감을 떨치고, 작은 것부터 하나씩 직접 시도해보는 용기입니다.
여러분의 멋진 아이디어가 환경 문제라는 허들에 걸려 넘어지지 않도록, 도커라는 훌륭한 도구가 곁에서 든든하게 여러분을 지켜줄 것입니다. 이제 두려움 없이, 여러분만의 똑똑한 AI 아기를 마음껏 키워나가시길 응원합니다.
토론
댓글