Containers Overview
Problem
서비스와 라이브러리 간의 호환성 및 OS에 대한 종속성을 확인해야 함. 구성 요소를 최신 버전으로 업그레이드하거나 데이터베이스를 변경하는 등의 작업을 할 때마다 다양한 구성 요소와 기본 인프라 간의 호환성을 확인하는 동일할 프로세스를 거쳐야 했음
새로운 개발자가 합류할 때마다 새로운 환경을 설정해야 함
개발, 테스트, 프로덕션 환경의 설정이 다름
→ 호환성 문제를 해결하고 다른 구성 요소에 영향을 주지 않으면서 이러한 구성 요소를 수정하거나 변경할 수 있고 필요에 따라 기본 운영 체제도 수정할 수 있는 방법
→ Docker
Docker
각 구성 요소를 별도의 컨테이너에서 자체 종속성과 자체 라이브러리를 사용하여 모두 동일한 VM과 OS에서 실행할 수 있으며, 별도의 환경 또는 컨테이너 내에서 실행 가능함
기본 운영 체제에 관계없이 도커 실행 명령어로 환경 실행 가능
Container
완전히 격리된 환경
가상 머신과 마찬가지로 자체 프로세스나 서비스, 자체 네트워크 인터페이스, 자체 마운트를 가질 수 있지만 모두 동일한 OS 커널을 공유한다는 점만 다름
Docker는 LXC 컨테이너를 사용함
Ubuntu, CentOS와 같은 운영 체제는 모두 OS 커널과 소프트웨어 집합이라는 두 가지로 구성되어 있음
- OS 커널
- 하드웨어와 직접 상호작용하며, 시스템 자원을 관리함
- Ubuntu, CentOS 등 모든 리눅스 배포판은 공통적으로 Linux 커널을 사용
- 사용자 공간 소프트웨어
- 셸, 패키지 관리자(apt, yum), 개발 도구, GUI 등
- 배포판을 서로 다르게 만드는 소프트웨어
Docker는 전체 OS를 가상화하는 것이 아니라, 호스트 OS의 커널을 공유하여 컨테이너를 실행한다. 따라서 동일한 커널(Linux 커널)을 기반으로 한 OS는 호환된다.
Windows는 Linux와 다른 커널을 가지고 있으므로 커널 공유가 불가능함.
Windows에서 Docker를 설치하면, WSL2(Windows Subsystem for Linux 2) 또는 Hyper-V 기반 가상 머신을 사용해 내부적으로 Linux 환경을 먼저 띄웁니다.
이 경우 Windows 안에서 실행된 Linux VM 위에서 실행된 것이고, 실제 Windows 커널이 직접 Linux 컨테이너를 실행하는 것은 아님
Docker의 주요 목적은 다양한 애플리케이션을 패키징하고 컨테이너화하여 언제 어디서나원하는 만큼 실행할 수 있도록 하는 것이다.
VM vs Container
항목 | 가상 머신 (VM) | Docker 컨테이너 |
---|---|---|
구조 | 하드웨어 → 호스트 OS → 하이퍼바이저 → 게스트 OS → 앱 | 하드웨어 → 호스트 OS → Docker → 컨테이너 → 앱 |
내부 구성 | 각 VM마다 OS + 라이브러리 + 앱 | 컨테이너마다 라이브러리 + 앱 (OS 없음) |
커널 공유 | 공유하지 않음 (각 VM마다 커널 존재) | 호스트 OS의 커널 공유 |
크기 | GB 단위 (무거움) | MB 단위 (가벼움) |
부팅 속도 | 수 분 소요 (OS 부팅 포함) | 수 초 이내 (빠름) |
리소스 사용률 | 상대적으로 높음 | 효율적 |
격리 수준 | 강력한 격리 (완전 독립된 OS) | 약한 격리 (커널 공유로 인한 영향 가능성 있음) |
다양한 OS 실행 | 가능 (예: 동일 하이퍼바이저에서 Linux와 Windows 혼합 가능) | 커널이 같은 OS만 직접 실행 가능→ Windows에서는 VM을 통해 Linux 컨테이너 실행 |
사용 예시 | 보안이 중요한 완전 분리 환경, 다양한 OS 실행이 필요한 경우 | 빠른 배포, 경량 마이크로서비스 실행 |
VM + Container
요즘은 가상 머신 안에서 Docker 컨테이너를 실행하는 방식을 자주 사용한다.
VM은 물리 서버를 효율적으로 나누는 용도, Docker는 애플리케이션을 빠르고 효율적으로 운영하는 용도로 사용된다.
대규모 시스템에서는 컨테이너 수가 많아지고, 그걸 실행하는 Docker 호스트도 많아지므로 VM을 기반으로 한 Docker 호스트를 활용하면 유연하게 확장·축소할 수 있다.
Docker Hub와 같은 곳에서 애플리케이션 컨테이너 이미지를 구할 수 있다.
Image vs Container
이미지: 가상화 환경에서 작업해 본 적이 있는 VM 템플릿과 같은 패키지 또는 템플릿, 하나 이상의 컨테이너를 만드는 데 사용됨
컨테이너: 격리된 이미지 인스턴스를 실행하며 자체 환경과 프로세스 집합을 가지고 있음
Docker를 사용하면 운영 환경을 Dockerfile로 정의하고, 이를 기반으로 애플리케이션용 이미지를 생성할 수 있다.
이렇게 생성된 이미지는 Docker가 설치된 모든 호스트에서 동일한 방식으로 실행되며,
어디서든 일관된 실행 환경을 보장한다.
따라서 이 이미지를 활용하여 애플리케이션을 손쉽게 배포할 수 있다.
Container Orchestration
컨테이너 오케스트레이션: 컨테이너를 자동으로 배포하고 관리하는 전체 프로세스
대표적으로는 Kubernetes가 있다.
컨테이너 오케스트레이션의 이점
- 높은 가용성 (High Availability)
- 애플리케이션 인스턴스들이 여러 노드에 분산되어 실행되므로, 특정 하드웨어에 장애가 발생해도 전체 서비스는 중단되지 않는다.
- 로드 밸런싱 (Load Balancing)
- 사용자 트래픽이 여러 컨테이너 인스턴스에 자동으로 분산되어 처리됩니다.
- 수평 확장 (Horizontal Scaling)
- 수요가 증가하면 애플리케이션 인스턴스를 빠르게 추가할 수 있다.
- 반대로 리소스가 부족할 때는 우선순위 기반으로 자원을 재배분할 수도 있습니다.
- 유연한 인프라 조정
- 애플리케이션을 중단하지 않고도 클러스터 노드 수를 늘리거나 줄일 수 있습니다.
- 선언적 구성 (Declarative Configuration)
- YAML 등의 구성 파일을 통해 어떤 상태로 유지할지 명시적으로 선언할 수 있습니다.
Kubernetes Architecture
- 노드
- Kubernetes가 설치된 실제 또는 가상 머신
- 컨테이너(예: Docker)가 실행되는 장소
- 일반적으로 다수의 노드로 구성된 클러스터 형태로 운용
- Kubernetes 클러스터
- 여러 대의 노드(Node)로 구성된 시스템
- 각 노드는 물리 서버 또는 가상 머신일 수 있음
- 클러스터는 다음 두 종류의 노드로 구성:
- 마스터 노드(Master Node): 클러스터 제어 및 관리 담당
- 워커 노드(Worker Node): 컨테이너 실행 담당
- 마스터 노드 구성 요소
| 구성 요소 | 역할 |
| --- | --- |
| **API Server** | Kubernetes의 프론트엔드. 모든 명령은 API 서버를 통해 전달됨 |
| **etcd** | 클러스터 구성 정보를 저장하는 **분산 키-값 저장소** |
| **Controller Manager** | 시스템 상태를 감시하고 필요한 조치를 취함 (ex. Pod 재시작 등) |
| **Scheduler** | 새로 생성된 Pod를 적절한 워커 노드에 배치 |
| **Container Runtime** | 실제 컨테이너를 실행 (예: Docker, containerd 등) |
- 워커 노드 구성 요소
| 구성 요소 | 역할 |
| --- | --- |
| **Kubelet** | 워커 노드에서 실행되는 에이전트. 컨테이너 상태 보고 및 명령 수행 |
| **Container Runtime** | Docker, containerd 등 컨테이너 실행 도구 |
| **Pod** | Kubernetes에서 실행되는 최소 단위. 하나 이상의 컨테이너로 구성 |
- kubectl 명령어
| 명령어 | 설명 |
| --- | --- |
| `kubectl run <application>` | 애플리케이션 실행 |
| `kubectl get nodes` | 클러스터 내 노드 목록 조회 |
| `kubectl cluster-info` | 클러스터 상태 및 구성 정보 조회 |
Docker vs ContainerD
Docker와 Kubernetes의 역사
- 초기에는 Docker가 컨테이너 실행 도구로 널리 사용되었고, Kubernetes도 Docker를 런타임으로 사용하도록 설계됨
- Kubernetes는 컨테이너 런타임 인터페이스(CRI)라는 표준 인터페이스를 만들어 이를 준수하는 다양한 런타임(예: containerd, CRI-O)에서의 컨테이너 실행을 지원함
- Docker는 CRI 호환이 아니기 때문에 Kubernetes는 별도로
Dockershim
이라는 호환 계층을 유지해 왔음
Docker vs Containerd
항목 | Docker | Containerd |
---|---|---|
정체 | 올인원 컨테이너 플랫폼 | 경량화된 컨테이너 런타임 |
구성 | CLI, 빌더, 데몬, API 등 포함 | 순수 런타임만 제공 |
Kubernetes 지원 | v1.24부터 지원 종료 (Dockershim 제거) | 기본 컨테이너 런타임 |
목적 | 개발 및 CI/CD 중심 | 프로덕션 클러스터 실행 중심 |
빌드 기능 | docker build 등 제공 |
자체적으로 제공 X (빌더 연동 필요) |
Docker는 내부적으로 containerd를 사용하지만, containerd는 Docker 없이도 사용 가능함
주요 CLI 도구 비교
도구 | 설명 | 사용 목적 | 사용 추천 상황 |
---|---|---|---|
docker CLI | Docker 명령어 도구 | 컨테이너 실행, 빌드, 관리 | 로컬 개발, CI/CD 파이프라인 |
ctr | containerd 기본 CLI | 디버깅 및 테스트 | containerd 내부 디버깅 전용 |
nerdctl | containerd용 고급 CLI | Docker와 유사한 명령어 지원 | Docker 없이 containerd 기반 환경에서 사용 |
crictl | Kubernetes CRI 디버깅 도구 | CRI 런타임 상태 점검 | Kubernetes에서 런타임 상태를 점검하거나 로그 확인 시 |
명령어 비교
작업 | Docker | nerdctl | crictl |
---|---|---|---|
컨테이너 실행 | docker run redis |
nerdctl run redis |
❌ |
이미지 목록 | docker images |
nerdctl images |
crictl images |
컨테이너 목록 | docker ps |
nerdctl ps |
crictl ps |
로그 보기 | docker logs <id> |
nerdctl logs <id> |
crictl logs <id> |
crictl은 쿠버네티스에 의해 관리되는 컨테이너만 확인 가능
Kubelet이 모르는 컨테이너는 자동으로 제거될 수 있음 (비정상 생성 시)
Kubernetes에서 Docker 제거 이후
- Kubernetes v1.24부터 Dockershim이 제거되며 Docker 지원 중단
- containerd가 기본 컨테이너 런타임으로 채택됨
- 하지만 Docker로 빌드한 이미지(OCI 이미지)는 계속 사용 가능
- Docker는 여전히 개발과 빌드 도구로 유효
- Kubernetes에서는 더 이상 Docker를 기본 런타임으로 사용하지 않으며, containerd + nerdctl 조합이 대세
A note on Docker deprecation
- Docker는 여전히 가장 널리 사용되는 개발 및 테스트용 컨테이너 도구
- 실제 운영에선 containerd 등이 사용되지만, 로컬 개발 환경이나 학습 목적에서는 Docker가 여전히 강력하고 편리함
- Docker로 만든 이미지는 OCI(Open Container Initiative) 표준을 따르므로 Kubernetes 등 다른 환경에서도 그대로 사용할 수 있음
- 컨테이너의 기본 개념을 이해하기 위해 Docker를 예제로 사용함
- Docker가 설치되지 않은 환경에서는
nerdctl
같은 대체 도구로 동일한 예제를 실행할 수 있음