Docker
실행 환경째로 포장해 어디서든 똑같이 굴린다.
한눈에
- Docker = 앱 + 실행 환경째로 상자에 포장 ('내 컴에선 되는데'가 사라짐)
- 컨테이너 = 규격 화물 컨테이너 (바깥 규격이 같아 어느 서버·클라우드든 그대로 얹어 돌림)
- 이미지 = 붕어빵 틀(읽기 전용 원본) / 컨테이너 = 그 틀로 구운 붕어빵(실제로 돌아가는 실체)
- 이미지엔 런타임까지 포함 (받는 서버에 파이썬·JDK 미리 안 깔아도 됨)
- Dockerfile → build → 이미지 → run → 컨테이너 (설계도로 상자 찍어 어느 서버든 열어 실행)
협업하다 한 번쯤 겪는 문장이 있다. "어? 제 컴퓨터에선 되는데요." 같은 코드인데 누구 컴에선 돌고 누구 컴에선 죽는다. 파이썬 버전이 다르거나, 깔린 라이브러리가 다르거나, OS가 달라서다. Docker는 앱만 달랑 옮기는 대신, 그 앱이 돌던 실행 환경(OS 라이브러리·런타임·의존성)까지 통째로 상자에 넣어 옮긴다. 상자 안이 어디서나 똑같으니, 환경 차이로 생기던 그 문장이 사라진다.
규격 화물 컨테이너 비유
이름 그대로 '컨테이너'다. 항구의 그 쇠상자를 떠올리면 된다.
컨테이너가 생기기 전엔, 짐마다 모양이 제각각이라 배·트럭·항구마다 다시 싣고 푸는 게 지옥이었다. 규격 컨테이너가 나오고선, 안에 뭐가 들었든 상자 규격이 같으니 어느 배든 트럭이든 크레인이든 그대로 옮겨 실린다. Docker도 똑같다. 안에 든 게 파이썬 서버든 웹 프론트든, 상자의 바깥 규격이 같아서 내 노트북이든 남의 서버든 클라우드든 그대로 얹어 돌린다.
이미지와 컨테이너는 다르다
여기서 딱 하나 헷갈리는 지점이 이미지와 컨테이너의 구분이다. 둘은 다른 단계다.
- 이미지(image) = 설계도이자 포장된 규격 상자 원본. 앱 + 실행 환경을 한 덩어리로 찍어낸, 읽기 전용 결과물이다. 그 자체로는 가만히 있다.
- 컨테이너(container) = 그 이미지를 실제로 띄워 돌아가는 실체. 하나의 이미지에서 컨테이너를 여러 개 찍어낼 수 있다.
붕어빵으로 치면 이미지가 붕어빵 틀, 컨테이너가 그 틀로 구워낸 붕어빵이다. 틀 하나로 붕어빵 열 개를 굽듯, 이미지 하나로 똑같은 컨테이너를 여러 서버에 띄운다.
Java 하던 사람 기준 다리
이 감각은 이미 겪어봤다. Java의 슬로건 "한 번 짜서 어디서나 돈다(Write Once, Run Anywhere)"가 바로 이 문제를 언어 차원에서 풀었던 것이다. Docker는 같은 걸 한 층 더 아래, OS·의존성 차원에서 한다.
차이가 하나 있다. JAR는 실행하려면 그 서버에 JDK가 깔려 있어야 한다. Docker 이미지는 런타임까지 상자 안에 넣으니, 받는 쪽에 아무것도 미리 깔 필요가 없다. "이 서버에 파이썬 3.11이 없는데" 같은 걱정 자체가 없어진다.
우리 프로젝트에선
새김 AI 레포에는 Dockerfile이 실제로 들어 있다. 백엔드 Dockerfile은 멀티스테이지 빌드로 python:3.11-slim 위에 FastAPI + Uvicorn 서버를 얹고, 프론트엔드에도 별도 Dockerfile이 있다. 로컬에선 docker-compose.yml이 이 상자들을 한 번에 묶어 띄운다.
그래서 파이썬 버전이나 의존성 때문에 "내 컴에선 되는데"를 겪을 일이 없었다. 상자 규격이 정해져 있으니, 로컬에서 돌던 그대로 배포(Railway)가 이미지를 받아 컨테이너로 굴린다. 규격 컨테이너 비유가 실제로 어떻게 짐을 옮기는지, 이 레포가 그 예시인 셈이다.
더 읽기
상자 비유를 넘어 직접 docker run 한 번 해보면 감이 확 붙는다.
- 도커(Docker) 입문편 — 44BITS — 컨테이너 기초부터 서버 배포까지, 한국어 입문 글의 정석
- 도커 컨테이너와 이미지란? — lainyzine — 이미지와 컨테이너의 차이를 그림과 함께 또박또박
- 도커 이미지와 컨테이너 — 오웬의 개발 이야기 — 둘의 관계를 짧고 깔끔하게 정리