AI 두뇌 넣기: 내 책상 위의 ChatGPT: Docker로 첫 컨테이너 띄우기
— 내 책상 위의 ChatGPT: Docker로 첫 컨테이너 띄우기
지난 편에서 사무실에서도 본체에 접속할 수 있게 되었습니다. 그런데 거기까지 하고 나니 새로운 질문이 떠올랐어요.
“그래서 이 서버 위에 뭘 올릴 거지?”
원격 접속은 어디까지나 길을 낸 것일 뿐, 그 길 끝에 뭐가 있어야 의미가 있어요. 오늘 글은 그 첫 번째 무언가, “내 책상 위의 ChatGPT” 를 만든 이야기입니다.
시작하며: 무엇부터 시작할까
후보가 정말 많았어요. 며칠간 어디서부터 시작할지 고민이 깊었습니다.
처음 가장 끌렸던 건 NemoClaw 였어요. GTC 2026 발표를 보고 알게 된 도구였습니다. 컨셉이 정말 강렬했어요. 본인 컴퓨터에 항상 켜져 있는 AI 에이전트를 두고, 텔레그램으로 어디서나 그 에이전트와 대화하면서 일을 시키는 시스템이거든요. 사무실 가는 길에 폰으로 “오늘 정리해야 할 자료 뽑아둬” 라고 시키면 집의 본체가 알아서 처리하는 그림. 변호사로서는 이게 진짜 환상적인 그림이었어요. 외출 중에도 사무실 책상이 옆에 있는 느낌이랄까요.
그리고 또 하나, GIGABYTE에서 제공하는 AI TOP UTILITY 도 정말 써보고 싶었어요. 광고 문구를 보니 “클릭 몇 번으로 파인튜닝, RAG, 인퍼런스가 다 된다” 고 적혀 있었거든요. 본인이 가진 PDF로 사적인 검색엔진을 만드는 RAG 같은 게 GUI 몇 번이면 된다니, 어떻게 안 궁금하겠어요. 보자마자 손이 근질근질했어요.
그런데 며칠 고민하다가 결국 가장 평범한 걸 골랐어요. Open WebUI.
이유는 단순합니다. 기본기가 필요하다고 느꼈거든요. NemoClaw도, AI TOP UTILITY도 멋있지만, 사실 그건 다 “매직 박스” 예요. 클릭 몇 번에 결과가 나오는 도구는 잘 돌 땐 좋지만, 안 돌 때가 진짜 문제예요. 안에서 뭐가 도는지 모른 채로 쓰다가 막히면, 그때부터는 손도 못 댑니다. 변호사로서는 그게 더 무서워요. 안 보이는 게 더 위험합니다.
Open WebUI는 그 사이 어딘가에 있어요. “본인 컴퓨터 위에 ChatGPT 비슷한 채팅 화면을 띄워주는 도구”. 한 줄 설명이 깔끔하고, 그러면서도 그 과정에서 Docker 라는 기본기를 손으로 배울 수 있어요. NemoClaw 만들 때 깔리는 그 Docker, AI TOP UTILITY가 안에서 쓰는 그 Docker가 다 같은 거거든요. 여기서 한 번 손에 익혀두면 다음 어디로 가도 발판이 됩니다.
Docker라는 새로운 등장인물
Open WebUI를 깔려면 Docker 라는 도구가 필요합니다. 이게 이번 편의 진짜 주제예요.
Docker는 본인 컴퓨터 안에 격리된 작은 미니 컴퓨터(컨테이너)들을 만들어주는 도구입니다.
ATOM이 큰 아파트 건물이라면, 컨테이너는 그 안의 호실이에요. 각 호실은 독립적이라 한 호실이 망가져도 다른 호실에 영향이 없습니다. 다만 건물의 공용 시설인 GPU, CPU, 메모리, 네트워크는 다 같이 씁니다.
┌─────────────────────────────────────┐
│ ATOM (아파트 건물) │
│ ┌──────────┐ ┌──────────┐ │
│ │ 101호 │ │ 102호 │ │
│ │ Open WebUI │ │ ComfyUI │ │
│ │ (LLM 채팅) │ │ (이미지) │ │
│ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 103호 │ │ 104호 │ │ gpt-oss와 폰에서 부르는 ChatGPT
│ │ RAG 도구 │ │ 무엇이든 │ │
│ └──────────┘ └──────────┘ │
│ 공유: GPU, 메모리, 네트워크 │
└─────────────────────────────────────┘
왜 굳이 이렇게 하냐? 깔끔함 때문이에요. Open WebUI 관련된 모든 파일이 그 호실 안에만 있어서, 나중에 “이거 별로네” 싶으면 호실 하나 통째로 버리면 끝입니다. 시스템 다른 곳에 흔적이 안 남아요.
생각해보면 변호사 사무실도 비슷해요. 각 의뢰인 폴더가 따로 있고, 한 사건이 끝나면 그 폴더만 정리하면 되잖아요. 사무실 전체를 헤집을 필요가 없죠. Docker가 컴퓨터 안에서 그런 격리를 해주는 도구입니다.
첫 명령어부터 벽 - 권한이라는 개념
설치는 자동으로 됐어요. NVIDIA가 친절하게 미리 깔아두는 게 있으니까요. 첫 명령어를 쳐봤습니다.
docker ps
이건 “지금 도커 안에 뭐 돌아가는지 보여줘” 라는 뜻이에요. 컨테이너 목록을 확인하는 가장 기본적인 명령. 그런데 결과는,
permission denied while trying to connect to the Docker daemon socket
거절. “권한이 없습니다.”
“권한이 없다고? 왜? 그리고 어떻게 받아야 하지?” 풀어보니 이게 리눅스의 핵심 개념 중 하나더라고요. gpt-oss와 폰에서 부르는 ChatGPT 리눅스에서는 사용자들을 그룹(Group) 으로 묶을 수 있어요. 회사 안의 부서랑 비슷해요. 부서마다 권한이 달라요.
🏢 회사 (= ATOM)
├── 👤 본인 계정
│
├── 👥 sudo 그룹 (관리자 부서)
├── 👥 audio 그룹 (오디오 만질 권한)
├── 👥 video 그룹 (그래픽 만질 권한)
└── 👥 docker 그룹 (도커 만질 권한) ← 여기 들어가야 함
Docker는 시스템에 컨테이너 만들고, 네트워크 바꾸고, 파일시스템 마운트하고… 사실상 관리자급 권한이 필요한 도구예요. 그래서 “아무나 쓰면 안 됨” 으로 잠가둔 거예요. 본인이 docker 그룹에 들어가야 풀립니다.
해결은 두 줄이었어요.
sudo usermod -aG docker $USER
newgrp docker
근데 여기서 의문이 들죠. 아니 내가 관리자 부서에 들어가면 되지. 왜 굳이 docker로 들어가야하나?
“잠깐, 본인 이미 sudo 그룹에 들어가 있잖아? 그럼 그냥 매번
sudo *****하면 되는 거 아닌가?”
sudo vs 그룹 - 최소 권한이라는 철학
이게 사실 보안의 핵심 원리예요. “최소 권한 원칙(Principle of Least Privilege)” 이라고 부릅니다.
“필요한 권한만 받고, 그 이상은 받지 마라.”
본인이 회사 헬스장 가고 싶다고 칩시다. 두 가지 방법이 있어요.
방법 A: 회장 자리를 받아서 모든 시설 자유롭게 이용 → 이게 sudo
방법 B: 헬스장 출입증만 받기 → 이게 docker 그룹 가입
방법 A가 편할 것 같지만 사실 위험해요. 회장 자리에 있으면 실수로 회사 금고를 만지거나, 인사 시스템을 망가뜨릴 위험이 있거든요. 단순히 운동만 하고 싶었는데 말이죠.
리눅스도 똑같습니다. sudo docker run ... 하다가 명령어 한 글자만 잘못 치면 시스템 전체에 영향을 줄 수 있어요. 근데 docker 그룹 권한으로 같은 명령을 치면 docker 영역 안에서만 실수가 일어나요. 피해 반경이 작아져요.
그래서 일상적으로 쓸 거면 sudo로 매번 처리하지 말고, 그 도구 전용 그룹에 가입하는 게 정답이에요.
명령어는 무슨 뜻인가
sudo usermod -aG docker $USER → 회사 HR 시스템에 "본인 docker팀 발령" 등록
newgrp docker → 본인 사원증을 새로 발급받기
usermod는 시스템의 사용자 데이터베이스(/etc/group 파일)를 영구적으로 수정해요. 본인이 이제 docker 그룹 멤버라고 공식 등록된 거예요.
그런데! 본인이 지금 쓰고 있는 이 터미널 세션은 그걸 모르고 있어요. 리눅스는 로그인할 때 한 번만 그룹 정보를 읽어서 들고 있거든요. 그래서,
- HR 시스템: “본인은 docker팀으로 이동” → ✅ 등록됨
- 본인 사원증: 아직 이전 부서가 적혀 있음 → ❌ 갱신 안 됨
- 그래서 docker 영역에 못 들어감 → “권한 없음”
- dfs 이걸 해결하는 방법은 두 가지,
- 방법 A: 새 사원증 발급 =
newgrp docker(그 자리에서 즉시 적용) - 방법 B: 회사 출근 다시 = 로그아웃 후 재로그인
newgrp는 그 자리에서 빨리 적용하려고 쓰는 편의 명령이에요.
이미지를 받자: :ollama vs :cuda라는 함정
권한 통과. 이제 Open WebUI를 받을 차례입니다. 명령어는 이거였어요.
docker pull ghcr.io/open-webui/open-webui:ollama
비유로 풀면 “앱스토어에서 앱 다운로드” 같은 거예요. 각 부분의 뜻은:
| 부분 | 뜻 |
|---|---|
docker pull | “도커야, 이미지 받아와” |
ghcr.io | 받아올 곳 (GitHub Container Registry, 일종의 앱스토어) |
open-webui/open-webui | 받아올 이미지 이름 |
:ollama | 그 중에서도 “ollama 포함된 버전” |
여기서 또 멈췄어요. 이미지 페이지를 보니까 :ollama 말고도 :cuda, :main 같은 다른 태그들이 있더라고요. 변호사 직감이 발동했어요.
“잠깐. 본인 머신엔 NVIDIA GPU 있잖아. 그럼
:cuda받는 게 더 빠르지 않나? CUDA 가속의 ‘CUDA’ 인 거고.”
너무 당연한 추론 같았어요. NVIDIA GPU 가진 사람이 NVIDIA 가속 버전을 받는 게 합리적이잖아요. 그런데 답은 또 정반대였습니다. 3편의 float16 사건과 비슷한 패턴이에요. 직관과 다르더라.
진실은 이래요. :cuda와 :ollama는 서로 경쟁하는 옵션이 아닙니다. 다른 일을 해요.
| Tag | 안에 뭐가 들어있나? | LLM이 GPU 쓰나? |
|---|---|---|
:ollama ⭐ | Open WebUI + Ollama 포함 | ✅ Ollama가 자동으로 GPU 사용 |
:cuda | Open WebUI만 (Ollama 없음) | ❌ Ollama 없어서 LLM 못 돌림 |
:main | Open WebUI만, CPU 모드 | ❌ Ollama 없어서 LLM 못 돌림 |
:ollama 안에 들어있는 Ollama가 자체적으로 GPU를 자동 사용해요. 별도 설정 필요 없습니다. 그래서 :ollama + GPU 옵션 한 줄이 본인이 원하는 GPU 가속된 LLM 채팅이에요.
그럼 :cuda는 뭐냐? 다른 문제를 푸는 거예요. Open WebUI에는 LLM 채팅 외에 부속 기능들(RAG용 임베딩, 음성 인식, 이미지 처리)이 있는데, :cuda 태그는 그 부속 기능들의 모델을 GPU로 돌려주는 변형이에요. 정작 메인 LLM은 안 들어있어요.
실행 명령어 - 마법의 한 줄
이미지 받고 나서 실행 명령어를 쳤어요. 이게 오늘 가장 긴 명령어였습니다.
docker run -d -p 8080:8080 --gpus=all \
-v open-webui:/app/backend/data \
-v open-webui-ollama:/root/.ollama \
--name open-webui ghcr.io/open-webui/open-webui:ollama
| 부분 | 뜻 |
|---|---|
docker run | “새 컨테이너 만들어서 실행해” |
-d | “백그라운드(detached)로 돌려” |
-p 8080:8080 | “내 컴퓨터 8080 포트를 컨테이너 8080에 연결” |
--gpus=all | “GPU 다 써도 돼” ⭐ 이게 핵심! |
-v open-webui:/app/backend/data | “데이터 저장 공간 마련” (대화 기록 등) |
-v open-webui-ollama:/root/.ollama | “Ollama 모델 저장 공간 마련” |
--name open-webui | 컨테이너 이름 지정 |
ghcr.io/...:ollama | 방금 받은 이미지 사용 |
여기서 두 가지가 중요해요.
--gpus=all 이 한 줄이 진짜 마법이에요. 이게 컨테이너에 GPU 사용 권한을 주는 명령어입니다. 컨테이너는 기본적으로 격리돼 있어서, 명시적으로 “GPU 줘” 하지 않으면 GPU에 접근 못 해요. 이 한 줄 빼면 LLM이 CPU로 돌아가서 응답이 100배 느려져요. 이 한 줄이 ATOM을 산 의미를 살려주는 거예요.
-v (볼륨)이 두 개나 있는 게 또 신경 쓰였어요. 알고 보니 이게 Docker의 진짜 중요한 개념이에요.
볼륨 - 컨테이너가 죽어도 살아남는 데이터
-v 옵션이 만드는 게 볼륨(Volume) 이에요. 처음 이름만 들으면 추상적인데, 풀어보면 단순합니다.
문제 상황: 컨테이너는 격리돼서 좋긴 한데, 컨테이너 지우면 안에 있던 모든 게 다 날아가요. 모델 12GB짜리 받느라 30분 기다렸는데 컨테이너 지우면 그것도 사라짐. 끔찍합니다.
해결: 볼륨은 컨테이너 밖에 따로 저장 공간을 만들어서, 컨테이너랑 연결해두는 거예요.
비유하면 “컨테이너에 외장하드를 끼우는 것” 같습니다.
[컨테이너] —(연결)— [볼륨: 외장하드]
↓
컨테이너가 폭발해도 외장하드는 멀쩡
볼륨이 두 개인 이유는 분리 보관 때문이에요.
open-webui볼륨 → 앱 데이터 (대화 기록, 사용자 설정)open-webui-ollama볼륨 → Ollama 모델 파일들 (수십 GB까지 갈 수 있는 무거운 친구)
이렇게 분리해두면 나중에 앱은 업데이트해도 모델은 보존할 수 있어요.
첫 접속 - localhost:8080
명령어 실행 후 결과는 의외로 시시했어요. 긴 영문/숫자 문자열 한 줄이 떨어졌어요.
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z
이게 컨테이너의 고유 ID라고 합니다. 에러 메시지 없이 이것만 뜨면 성공이에요.
그리고 브라우저 열고 주소창에 입력합니다.
http://localhost:8080
localhost는 “내 컴퓨터 자신” 이라는 뜻이에요. 즉 “내 컴퓨터의 8080 포트로 접속” 이라는 거. 엔터 누르자마자… 정말로 ChatGPT 비슷한 화면이 떠버렸어요.
처음 회원가입 화면이 뜨더라고요. 이름, 이메일, 비밀번호 입력. 이건 클라우드가 아니라 본인 컴퓨터 안에 저장되는 로컬 계정이에요. 외부로 안 나갑니다. 그래도 비밀번호는 기억해두세요. 잊으면 컨테이너 다시 만들어야 해요.
가입 후 메인 화면. 익숙한 채팅 인터페이스가 떴어요. 다만 한 가지가 빠져 있었습니다.
두뇌가 없어요.
마치며
오늘 글이 좀 길었네요. 솔직히 정리하면 한 가지 일이었어요.
“본인 컴퓨터 위에 ChatGPT 비슷한 화면을 띄웠다. 다만 아직 안에 두뇌(=AI 모델)는 없다.”
빈 ChatGPT 인터페이스만 있는 상태예요. 두뇌는 다음 편에 넣을 거고요. 그 정도 일에 글이 이렇게 길어진 건, 결국 그 과정에서 만난 개념들이 다 처음 보는 것들이었기 때문이에요.
- ✅ Docker - 격리된 작은 컴퓨터를 만드는 도구. 아파트 호실 비유
- ✅ 최소 권한 원칙 - sudo는 응급용, 그룹은 일상용. 변호사의 위임장 같은 거
- ✅ 이미지 vs 컨테이너 vs 볼륨 - Docker의 3층 구조. 안전 장치
- ✅
:ollamavs:cuda- 직관과 다른 함정. 이름만 보면 안 됨 - ✅
--gpus=all- ATOM을 산 의미를 살리는 마법의 한 줄 - ✅ 볼륨 - 컨테이너가 죽어도 데이터는 살아남는 장치
가장 강조하고 싶은 한 줄을 고른다면 이거예요.
“Docker 한 번 익혀두면 다음 어디로 가도 발판이 됩니다. NemoClaw도, AI TOP UTILITY도, 결국 안에서는 같은 도구가 돌아가니까요.”
지금 단계에서 가장 무서웠던 건 NemoClaw 같은 매력적인 도구를 먼저 손대는 거였어요. 잘 돌면 좋지만, 안 돌면 막막하니까요. Open WebUI를 거쳐가는 길은 멀어 보여도, 밑에 있는 Docker가 손에 잡히면 그 다음은 다 응용이에요.
다음 편에서는 이 빈 ChatGPT에 진짜 두뇌를 넣을 거예요. OpenAI가 직접 풀어놓은 gpt-oss 라는 모델을 받고, 사무실에서도 폰으로 본인 AI를 부르는 이야기를 알려드리겠습니다.
읽어주셔서 감사합니다. 🙂
다음 편: “AI 두뇌 넣기 - gpt-oss와 폰에서 부르는 ChatGPT”