격리된 리눅스 프로세스. "가벼운 가상 머신" 이라는 설명은 틀렸다.
$ docker run -d --name web nginx
$ ps -ef | grep nginx # 호스트에서
root 48231 48210 nginx: master process
호스트의 ps 에 그대로 보인다. 가상 머신이라면 절대 안 보일 것이다.
컨테이너 = 리눅스 프로세스 + 세 가지 제약
| namespace | 무엇이 '보이는가' 를 제한한다 | |
|---|---|---|
| cgroup | 무엇을 '얼마나 쓰는가' 를 제한한다 | |
| 루트 변경 | 어떤 파일시스템을 루트로 보는가 |
셋 다 원래 있던 리눅스 기능이다 컨테이너 런타임은 이것을 조합해 주는 도구일 뿐이다
격리는 선택적으로 풀 수 있다
docker run --pid=host ... # 호스트의 모든 프로세스가 보인다
docker run --net=host ... # 호스트 네트워크를 그대로 쓴다
docker run --privileged ... # 방어가 통째로 꺼진다
편리하면서 동시에 위험한 지점이다. 격리가 기본이고, 푸는 것이 예외여야 한다.
커널 공유가 만드는 제약
-
호스트 커널 버전이 곧 컨테이너의 커널 버전이다
- 이미지에 든 배포판은 유저 공간(라이브러리·바이너리)일 뿐이다
- "우분투 이미지" 를 써도 커널은 호스트 것이다
-
sysctl 은 대부분 호스트와 공유된다 (일부만 네임스페이스화)
-
리눅스 컨테이너는 리눅스에서만 네이티브로 돈다
- macOS·윈도우의 Docker Desktop 은 내부에 리눅스 VM 을 띄운다
- 파일 마운트가 느린 이유가 이것이다
런타임 계층
docker CLI
→ containerd 고수준 런타임 — 이미지 관리 · 생명주기
→ runc 저수준 런타임 — 실제로 namespace·cgroup 을 만든다
→ 리눅스 커널
쿠버네티스는 CRI 로 containerd·CRI-O 를 직접 부른다 그래서 "쿠버네티스가 Docker 를 뺐다" 는 말이 나왔다 이미지는 그대로 쓴다 — OCI 표준이기 때문이다
운영 전제
- 이미지는 불변이다 → 컨테이너 안에서 고친 것은 사라진다
- "들어가서 설정을 바꿨는데 재배포하니 원복됐다" 는 당연한 결과다
- 언제든 죽고 다시 뜬다고 가정한다 → 상태를 안에 두지 않는다
- 프로세스가 하나 죽으면 컨테이너가 끝난다 → PID 1 이 중요해진다
- 제한이 없으면 한 컨테이너가 호스트를 잠식한다 → limit 은 기본이다
왜 이 사실이 실무에서 중요한가
"프로세스일 뿐" 이라는 것을 알면 다음이 자연히 따라온다
- 컨테이너 하나에 프로세스 하나를 둔다
- 여러 개를 띄우려면 그 관리자(supervisord)가 PID 1 이 되고,
- 자식이 죽어도 컨테이너는 살아 있어 장애가 감춰진다
- 호스트에서 직접 디버깅할 수 있다
- strace · perf · gdb 를 호스트에서 그 PID 에 붙이면 된다
- 리소스 지표도 결국 프로세스 지표다
- cgroup 파일을 읽으면 정확한 값이 나온다
컨테이너가 아닌 것
컨테이너는 '패키징' 이지 '실행 환경 전체' 가 아니다
| · 오케스트레이션 (여러 노드에 배치·복구) | → 쿠버네티스의 일 | |
|---|---|---|
| · 서비스 디스커버리 · 로드밸런싱 | → 그 위 계층 | |
| · 영속 스토리지 | → 볼륨·외부 스토리지 |
"도커를 쓰면 확장이 된다" 는 오해다 — 컨테이너는 '어디서든 같게 도는 실행 단위' 를 만들어 줄 뿐이고, 그것을 여러 대에 배치하는 일은 별개다
면접 함정
- ❌ "컨테이너는 가벼운 VM" → 커널 공유 여부가 근본적으로 다르다.
- ❌ "컨테이너도 자기 커널을 갖는다" → 호스트 커널을 쓴다. 그래서 리눅스 위에 윈도우를 못 돌린다.