Dockerfile·이미지·컨테이너·도커 — 밀린 주어 바로잡기

부제: 컨테이너는 특별한 가상머신이 아니라, 커널이 격리·제한을 걸어 준 ‘그냥 프로세스’다


쿠버네티스를 공부하려고 앉을 때마다, 나는 늘 그 아래층에서 걸렸다.
머릿속에 있던 감각은 대략 이랬다.

  • 도커는 뭔가 동일한 환경에서 연산 명령들을 모아둔 파일이고,
  • 컨테이너는 그 도커를 관리하는 프레임워크 같은 것?

방향은 얼추 맞지만, 주어가 한 칸씩 밀려 있다. 이 밀림을 바로잡지 않으면
쿠버네티스로 올라갈 때 개념이 계속 어긋난다. 그래서 이 시리즈의 첫 편은
오케스트레이션 이야기 이전에, "컨테이너란 도대체 무엇인가" 부터
한 줄씩 못박고 간다.

왜 개념을 굳이 정리하고 넘어가나

솔직히 나는 개념 설명을 길게 늘어놓는 걸 즐기는 편이 아니다. 한번 파고들면
깊이가 끝도 없이 이어져서, 정작 "그래서 이걸 어떻게 쓰나" 에서 멀어지기
쉽기 때문이다. 그런데도 이 시리즈를 여는 이유는 하나다 — 각 프레임워크가
어떤 용도로 만들어졌는지, 그 컨셉과 방향을 알아야 그 결에 맞게 쓸 수
있어서다. 도구를 만든 사람의 의도와 어긋나게 쓰면, 되는 것도 힘겹게 되고
어딘가에서 반드시 삐걱댄다.

그래서 이 시리즈는 깊게 파지 않는다. 대신 두 가지만 붙잡고 빠르게
넘어간다 — 프레임워크 한 줄 정의(이게 뭘 하는가)와 큰 그림에서의
한 줄
(전체에서 어디에 서 있는가). 딱 쓰기에 어긋나지 않을 만큼만 정리하고,
나머지 깊이는 필요할 때 그때 파는 걸로 남겨 둔다.

먼저 이 시리즈 전체를 지탱하는 두 정의를 한 줄씩 박아 두자. 자세한 풀이는
아래에서, 그리고 쿠버네티스 쪽은 다음 편부터 이어진다.

  • 도커(Docker) = 컨테이너를 빌드·실행해 주는 도구(엔진). "동일한 환경을
    레시피로 굳혀 어디서든 똑같이 실행"하게 해 준다.
  • 쿠버네티스(Kubernetes) = 그렇게 만든 컨테이너가 여러 대의 머신에 수십·수백
    개로 늘어났을 때, "이 상태로 유지하라"는 선언만으로 배치·복구·확장·교체를
    대신 맡아 주는 오케스트레이터(관리자).
    컨테이너 하나를 다루는 게 도커라면,
    컨테이너 무리를 다루는 게 쿠버네티스다.

이 글(A1)은 앞의 절반 — 도커와 컨테이너 — 를 확실히 굳히는 편이다.


한 줄 정의부터

용어 한 줄 정의
Dockerfile 이미지를 어떻게 만들지 적은 레시피(빌드 명령의 나열)
Image 레시피를 실행해 얼려둔 파일시스템 + 메타데이터 스냅샷(정지 상태)
Container 이미지를 실행한 격리된 프로세스(관리 대상)
Docker 이미지를 빌드·실행해 주는 도구/엔진(컨테이너를 만들어 주는 쪽)

내가 "명령을 모아둔 파일"이라고 감각한 것의 정체는 사실 Dockerfile이었다.
그리고 "컨테이너가 도커를 관리한다"는 완전히 뒤집혀 있었다 — 도커가
컨테이너를 만들고 관리하는 쪽
이고, 컨테이너는 관리당하는 대상이다.

Dockerfile·Image·Container·Docker의 관계와 컨테이너의 정체


네 단어를 ‘흐름’으로 다시 읽기

한 방향으로 흐른다: Dockerfile → (build) → Image → (run) → Container.

  • Dockerfile은 레시피다. FROM node:20, COPY . ., RUN npm ci 처럼
    "이 이미지를 이렇게 만들어라"를 적은 텍스트 파일. 요리로 치면 조리법.
  • Image는 그 레시피를 실제로 실행해서 얼려 둔 스냅샷이다. 파일시스템
    전체 + 실행 메타데이터(진입점·환경변수)를 담은 정지 상태의 결과물. 조리법대로
    만들어 냉동해 둔 밀키트.
  • Container는 그 밀키트를 데워서 실제로 상에 올린 한 끼다. 이미지를
    실행한 살아 있는 프로세스. 같은 이미지로 컨테이너를 100개 띄울 수 있다.
  • Docker는 이 build·run을 대신 눌러 주는 도구일 뿐이다. 그래서 도커가
    아니어도 컨테이너는 존재한다 — containerd, Podman도 같은 일을 한다.
    (쿠버네티스가 도커를 걷어내고 containerd로 옮겨 간 것도 이래서 가능했다.)

실제 콘솔로 확인 — 지금 이 블로그를 돌리는 도커에서

이 블로그(WordPress + MariaDB)도 컨테이너 위에서 돈다. 말로만 정리하면
또 밀리니, 진짜 콘솔을 그대로 캡처했다.

실제 도커 콘솔 — 이미지/컨테이너, namespace 격리, 이미지 레이어

세 장면이 각각 한 줄 정의를 눈으로 확인시켜 준다.

  1. docker images vs docker ps — 같은 wordpress:6.7-apache가 위에서는
    992MB짜리 얼려둔 이미지로, 아래에서는 *실행 중인 프로세스(healthy)*로
    나타난다. 이미지(정지) → 컨테이너(실행)의 그 한 칸이다.
  2. /proc/1/ns — 컨테이너 내부 1번 프로세스가 가진 namespace 목록.
    mnt/net/pid/ipc/uts 의 [번호]가 호스트와 달라서, 프로세스는 자기만의
    독립된 세상
    에 있다고 착각한다. 이게 ‘격리’의 실체다.
  3. docker history — 이미지는 통짜가 아니라 레이어의 적층이다.
    Dockerfile의 한 줄(RUN·COPY…)이 한 겹의 레이어가 되고, 무거운 줄
    (WordPress 다운로드 83.8MB)이 이미지 크기를 좌우한다.

그래서 컨테이너의 정체는? — namespace + cgroup

여기까지 오면 마지막 오해 하나가 남는다. "컨테이너는 가벼운 가상머신
아닌가?"
아니다. 가상머신은 게스트 OS(커널)를 통째로 하나 더 띄우지만,
컨테이너는 호스트 커널을 그대로 공유하는 그냥 프로세스다. 다만 커널이
두 가지 장치로 그 프로세스를 ‘가둔다’.

커널 기능 하는 일 한 줄로
namespace 프로세스가 뭘 볼 수 있나를 격리 PID·net·mount·ipc·uts·user 를 각자만의 것으로
cgroup 프로세스가 자원을 얼마나 쓰나를 제한·회계 CPU·메모리·IO 에 상한과 계량기를 붙임

컨테이너 = namespace(격리) + cgroup(제한). 도커는 이 커널 기능을 손으로
다루기 번거로우니 명령 한 줄로 빚어 주는 도구일 뿐이다. 위 콘솔의
/proc/1/ns 가 namespace의 증거였고, cgroup은 나중에 쿠버네티스의
requests/limits 로 다시 등장한다(A5편에서 수미상관으로 잇는다).

주의 — 같은 단어, 다른 뜻: 여기서 말한 리눅스 namespace(컨테이너 격리
커널 기능)와, 다음 편에 나올 쿠버네티스 Namespace(클러스터를 논리적으로
나누는 폴더)는 이름만 같고 완전히 다른 것이다. 이 시리즈 내내 이 함정을
계속 짚어 줄 것이다.


정리 — 밀린 주어를 제자리로

  • 명령을 모아둔 파일 = Dockerfile(레시피)
  • 얼려둔 스냅샷 = Image
  • 실행된 격리 프로세스 = Container(관리 대상)
  • 그걸 만들어 주는 도구 = Docker(관리 주체)
  • 컨테이너의 정체 = 커널이 namespace로 격리하고 cgroup으로 제한한 프로세스

한 칸씩 밀렸던 주어를 제자리에 놓고 나면, 다음 질문이 자연스럽다.
컨테이너 하나는 이렇게 이해했는데, 이 컨테이너가 수십·수백 개로 늘어나
여러 머신에 흩어지면 누가 관리하는가?
그 답이 쿠버네티스이고,
다음 편(A2)에서 Control Plane·Node·Pod·Namespace 로 클러스터 아키텍처를
같은 방식 — 개념마다 한 줄 정의 — 으로 풀어 간다.


참고

  • Docker Docs — Docker overview, Images and layers
  • Linux man-pages — namespaces(7), cgroups(7)
  • 콘솔 캡처: 이 블로그를 구동 중인 wordpress:6.7-apache / mariadb:10.11 컨테이너

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다