イメージとは何で、コンテナとは何か
한국어 원문으로 표시합니다.
한 줄 요약
이미지는 읽기 전용 레이어를 쌓아 만든 파일 트리이고, 컨테이너는 그 위에 쓰기 레이어 하나를 얹은 뒤 실행 중인 평범한 리눅스 프로세스입니다.
왜 이게 필요했나
"컨테이너는 가벼운 VM"이라는 설명이 오래 돌아다녔지만, 이 문장은 거의
모든 실무 문제에서 틀린 예측을 내놓습니다. 호스트에서 ps 를 쳐 보면
컨테이너 안의 nginx 가 그대로 보입니다.
48213 root nginx
48261 systemd+ nginx
같은 프로세스를 컨테이너 안에서 보면 1 nginx, 29 nginx 로 보입니다.
프로세스가 두 벌 있는 게 아니라 같은 프로세스를 다른 네임스페이스에서
본 것입니다. 커널 버전을 확인해 보면 더 분명해집니다. 호스트도,
alpine 컨테이너도, ubuntu 컨테이너도 전부 같은 커널을 보고합니다.
이미지가 제공하는 것은 커널이 아니라 사용자 공간의 파일 트리뿐입니다.
어떻게 동작하나
레이어는 유니온 파일시스템(overlayfs)으로 겹쳐집니다.
merged <- 컨테이너가 보는 뷰
↑ upperdir (쓰기 가능, 컨테이너 레이어)
↑ lowerdir (읽기 전용, 이미지 레이어들이 겹겹이)
- 읽기: 위에서부터 찾아 내려가고, 가장 위의 파일이 이깁니다.
- 쓰기: 아래 레이어의 파일을 고치면 위로 복사한 뒤(copy-up) 고칩니다.
- 삭제: 실제로 지우지 않고 위 레이어에 화이트아웃 표식을 남깁니다.
모든 레이어는 내용의 SHA-256 해시로 이름이 붙습니다. 내용이 같으면 이름도
같기 때문에 중복 저장이 없고, 해시만 비교하면 무결성이 검증됩니다. 이미지
ID 가 sha256:... 인 것도 같은 이유입니다.
레이어가 쌓이는 순간 무엇이 결정되나
Dockerfile 의 명령 하나가 레이어 하나입니다. 그리고 레이어는 지워지지 않습니다. 이 두 문장에서 실무의 함정 대부분이 나옵니다.
COPY secret.pem /tmp/secret.pem ← 레이어 3에 파일이 들어간다
RUN ./setup.sh && rm /tmp/secret.pem ← 레이어 4는 "지웠다" 는 기록일 뿐
최종 이미지에서 /tmp/secret.pem 은 안 보이지만, 레이어 3에는 그대로 있습니다.
docker save 로 풀면 누구나 꺼냅니다. 비밀을 이미지에 넣었다가 지우는 것은
지운 것이 아닙니다. 빌드 시크릿(RUN --mount=type=secret)이나 멀티스테이지로
해결합니다.
같은 이유로 크기도 줄지 않습니다.
RUN apt-get update && apt-get install -y build-essential # +400MB
RUN apt-get purge -y build-essential # 여전히 +400MB
한 RUN 안에서 받고 지워야 그 레이어의 최종 상태만 남습니다.
컨테이너를 지워도 남는 것
컨테이너를 지우면 쓰기 레이어가 사라집니다. 그래서 컨테이너 안에서 만든 파일은
docker rm 과 함께 없어집니다. 이것이 볼륨이 필요한 이유입니다.
| 저장 위치 | 컨테이너 삭제 후 | 쓰기 성능 | 언제 쓰나 |
|---|---|---|---|
| 쓰기 레이어 | 사라진다 | 느리다(copy-up) | 임시 파일 |
| 익명 볼륨 | 남는다(이름이 없어 찾기 어렵다) | 빠르다 | 실수로 생기는 것 |
| 이름 있는 볼륨 | 남는다 | 빠르다 | DB 데이터 |
| bind mount | 호스트 파일 그대로 | 빠르다 | 개발 중 소스 |
"copy-up" 이 중요합니다. overlayfs 에서 아래 레이어의 파일을 수정하면 전체가 위 레이어로 복사됩니다. 1GB 짜리 파일의 한 바이트를 고치면 1GB 가 복사됩니다. 데이터베이스를 볼륨 없이 컨테이너 레이어에서 돌리면 안 되는 이유가 이것입니다.
이미지 이름과 다이제스트
태그는 움직입니다. nginx:1.25 는 어제와 오늘이 다른 이미지일 수 있습니다.
latest 는 더합니다 — "최신" 이라는 뜻이 아니라 태그를 생략했을 때의 기본
이름 일 뿐입니다.
nginx:1.25 ← 움직인다
nginx@sha256:a1b2c3... ← 절대 안 움직인다
운영 배포는 다이제스트로 고정합니다. 그래야 "어제는 됐는데 오늘 안 된다" 를 "어제와 오늘이 같은 이미지인가" 로 먼저 확인할 수 있습니다.
현장에서 만나는 모습
같은 베이스를 쓰는 컨테이너 100개가 lowerdir 을 통째로 공유하기 때문에 컨테이너 생성이 빠르고 디스크가 절약됩니다. 반대로 copy-up 비용 때문에 데이터베이스 파일처럼 큰 쓰기는 반드시 볼륨으로 빼야 합니다.
또 하나. docker save 와 docker export 를 헷갈리면 하루가 날아갑니다.
save 는 레이어·태그·히스토리를 포함한 이미지를 내보내고, export 는
컨테이너의 파일시스템 한 장만 내보냅니다. export 한 것을 import 하면
ENTRYPOINT 도 ENV 도 전부 사라진 채로 살아납니다.
다음 실습에서 할 것
이미지 목록을 직접 훑고, 컨테이너를 하나 띄워 로그를 확인하고, 태그가 새 이미지를 만들지 않는다는 것을 이미지 ID 로 증명합니다.