41장. 왜 컨테이너인가 — VM과의 차이
이 장에서 말하고자 하는 것
지금까지 우리는 EC2 위에 애플리케이션을 띄우는 그림을 그렸다.
그런데 운영해보면 EC2만으로 부족한 지점이 보인다.
- 새 서버에 환경을 똑같이 깔기 어렵다
- 개발과 운영 환경이 미묘하게 다르다
- 배포가 느리다
- 한 서버에 여러 서비스를 띄우면 충돌이 잘 난다
이걸 한 번에 풀어주는 게
컨테이너 (Container)
이고 표준 도구가
Docker
다.
1. VM과 컨테이너의 차이
VM
[하드웨어] → [Hypervisor] → [Guest OS] → [App]
OS 자체를 통째로 만든다. 무겁고 부팅이 느리다.
컨테이너
[하드웨어] → [Host OS] → [Container = 프로세스 격리] → [App]
OS는 공유하고 프로세스만 격리한다.
수십 MB, 초 단위 부팅.
2. 컨테이너가 풀어주는 4가지
환경 재현성
"내 컴퓨터에서는 잘 됐는데..."
이미지에 OS · 런타임 · 라이브러리 · 코드를 다 담는다.
어디서든 동일하게 실행된다.
빠른 배포
docker run my-app:v2
EC2 새로 띄우는 것보다 압도적으로 빠르다.
격리
한 머신에 여러 컨테이너를 띄워도 런타임 충돌이 없다.
표준 인터페이스
빌드 → 이미지 → 실행이 표준이라
ECS · EKS · K8s 어디서든 같은 이미지가 돈다.
3. 우리 서비스에서
척추 그림이 이 단원에서 바뀐다.
이전:
ALB → EC2 (애플리케이션 직접 설치)
지금부터:
ALB → ECS (Fargate) → 컨테이너 안의 애플리케이션
같은 트래픽 분배 구조에서 “서버” 가 “컨테이너” 로 바뀐 것이다.
4. 직접 확인해보기 — CLI
docker run -p 8080:8080 nginx
8080 포트로 nginx가 즉시 뜬다. 이게 컨테이너의 첫 모습이다.
5. 코드로는 이렇게 생겼다 — Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]
이 파일이 곧 컨테이너 이미지의 설계도다. 다음 장에서 자세히 다룬다.
6. 이렇게 쓰면 망한다 — 안티패턴
안티패턴 1. 컨테이너 안에 데이터를 영구 저장한다
컨테이너 죽으면 데이터도 사라진다.
영구 데이터는 RDS · S3 · EFS 같은 외부 저장소로
안티패턴 2. 한 컨테이너에 여러 프로세스를 띄운다
- 디버깅 어렵다
- 한쪽이 죽어도 컨테이너는 살아 있는 척한다
- 스케일링이 어색하다
한 컨테이너 = 한 프로세스
안티패턴 3. 이미지를 latest로 받는다
배포마다 다른 이미지가 떨어진다. 롤백 불가.
항상 명시적 버전 태그 (커밋 해시 · 시맨틱 버전)
안티패턴 4. 운영체제를 통째로 베이스로 잡는다
FROM ubuntu:22.04 (70MB+) 대신FROM node:20-alpine (50MB) 또는 distroless 베이스를 쓴다.
7. 한 줄로 정리
컨테이너는 환경을 패키징해 어디서든 같게 돌리는 표준이며,
MSA가 자연스럽게 위에 올라타는 토대다
8. 이 장의 핵심 정리
- VM은 OS 통째, 컨테이너는 프로세스 격리.
- 컨테이너는 환경 재현성, 빠른 배포, 격리, 표준 인터페이스를 동시에 해결한다.
- 컨테이너는 일회용으로 다뤄야 한다. 영구 데이터는 외부 저장소에.
- 한 컨테이너에 한 프로세스만 띄운다.
- 이미지 태그는 항상 명시적으로, latest 의존을 피한다.