도커 아키텍처 개념 이해하기 + 핵심 구성요소
Docker의 주요 개념들이 서로 어떻게 관련되어 있는지 이해하는 것은 중요하다.
Docker Host
Docker Host는 Docker를 설치하고 실행하는 물리적 또는 가상 머신을 의미한다. 이것은 내 컴퓨터, EC2 인스턴스, 또는 Docker를 실행하는 모든 시스템일 수 있다. 호스트는 커널 자체가 아니라, 커널을 포함한 전체 운영 체제 환경. 즉, Docker가 설치된 전체 시스템을 가리킨다.
→ 기본적으로 호스트 OS와 같은 개념이다. (가상 머신 혹은 운영체제(Linux, Windows), Docker 엔진이 설치된 환경…)
- 로컬 컴퓨터에 Docker를 설치하면 내 컴퓨터가 Docker 호스트가 되고
- AWS EC2 인스턴스에 Docker를 설치하면 해당 EC2 인스턴스가 Docker 호스트가 된다.
- 물리적 서버에 Docker를 설치하면 역시 그 서버가 Docker 호스트가 된다.
컨테이너는 호스트의 자원을 공유하면서도 서로 격리된 상태로 작동.
Docker Daemon (dockerd)
Docker Daemon은 Docker Host 위에서 실행되는 백그라운드 서비스. 이 데몬은 Docker API 요청을 수신하고 이미지, 컨테이너, 네트워크 및 볼륨과 같은 Docker 객체를 관리한다. 데몬은 Docker의 핵심 실행 엔진으로, Docker 명령어가 실제로 처리되는 곳이다.
- Docker 명령어를 실행하면 실제로 이 데몬이 작업을 처리한다.
Docker Daemon
이 Docker 엔진의 일부라고 생각하면 된다. Docker 엔진은 Docker Daemon, REST API, CLI 등을 포함하는 더 넓은 개념이다.
eth0
eth0
는 시스템의 기본 물리적(또는 가상) 네트워크 인터페이스. EC2 인스턴스에서 이것이 VPC 네트워크와 연결된 인터페이스이다.
가상 이더넷(veth) 페어
각 컨테이너마다 생성되는 한 쌍의 가상 네트워크 인터페이스. 한쪽은 컨테이너 내부에, 다른 쪽은 호스트 측에 있으며, 이 둘은 마치 가상의 케이블로 연결된 것처럼 작동한다. ip addr 출력에서 veth로 시작하는 인터페이스로 나타남.
docker0
Docker가 생성한 가상 브릿지 네트워크 인터페이스. 이 브릿지는 Docker 컨테이너와 호스트 시스템 사이의 통신을 관리(중계)한다.
기본적으로 172.17.0.0/16
네트워크 범위를 사용한다. 내 시스템에서는 docker0 인터페이스가 172.17.0.1
IP 주소를 가지고 있다.
도커의 커널 공유 메커니즘
Docker 컨테이너는 호스트 OS의 커널을 공유한다.
- 시스템 콜 공유 : 컨테이너 내부의 애플리케이션이 시스템 콜을 요청하면, 이 요청은 호스트 OS의 커널로 직접 전달된다. 컨테이너는 자체 커널을 가지지 않음.
- 자원 격리 : Docker는 Linux의 네임스페이스(namespaces)와 컨트롤 그룹(cgroups) 기술을 사용하여 프로세스, 네트워크, 파일 시스템 등을 격리시켜 컨테이너들이 서로 독립적으로 작동하도록 한다.
- 효율성: 이러한 커널 공유 모델 덕분에 Docker 컨테이너는 가상 머신보다 훨씬 더 가볍고 빠르게 시작할 수 있다. 각 컨테이너마다 별도의 OS 커널을 실행할 필요가 없기 때문
- 네트워크 통신 : 네트워크 통신도 마찬가지로 호스트 OS의 커널을 통해 이루어진다. 컨테이너의 네트워크 요청은 호스트의 네트워크 스택을 거쳐 처리
a. 변환 과정에서 호스트 OS의 커널을 통해 IP 주소 변환(NAT)
컨테이너에서 외부로 나가는 요청(아웃바운드 통신)
- 요청 생성: 컨테이너 내부의 애플리케이션(예: 웹 서버)이 외부 서비스(예: 데이터베이스 서버)에 요청을 보냄
- 내부 네트워크 인터페이스: 이 요청은 컨테이너의 내부 네트워크 인터페이스(보통 eth0라는 이름)로 전달된다. 이 인터페이스는 컨테이너에게 172.17.0.2와 같은 내부 IP를 제공한다.
- 가상 이더넷 쌍: 요청은 가상 이더넷(veth) 쌍을 통해 컨테이너에서 호스트로 전달된다. 각 컨테이너는 한 쌍의 veth 인터페이스를 가지며, 하나는 컨테이너 내부에, 다른 하나는 호스트 측에 있다.
- docker0 브릿지: 호스트 측의 veth 인터페이스는 docker0 브릿지에 연결되어 있고, 요청은 이 브릿지를 통과한다.
- 네트워크 주소 변환(NAT): 이 시점에서 매우 중요한 변환이 일어난다. 호스트의 커널은 패킷의 출발지 IP 주소를 컨테이너의 IP(172.17.0.2)에서 호스트의 IP(10.250.3.177)로 변경한다. 이는 리눅스의 iptables를 사용하여 수행됨.
- 라우팅 결정: 호스트 OS는 변경된 패킷을 어디로 보낼지 결정한다. 일반적으로 이 패킷은 호스트의 기본 네트워크 인터페이스(eth0)를 통해 외부 네트워크로 전송된다.
- 외부 네트워크 전송: 이제 패킷은 외부 네트워크로 전송된다. 외부에서 보면, 이 요청은 마치 Docker 호스트 자체에서 온 것처럼 보인다.
외부에서 컨테이너로 들어오는 응답 (인바운드 통신)
- 응답 수신: 외부 서비스(데이터베이스 서버)는 응답을 호스트 IP(10.250.3.177)로 다시 보낸다.
- 호스트 수신: 호스트는 자신의 eth0 인터페이스를 통해 응답을 수신한다.
- 커넥션 트래킹: 호스트 OS는 리눅스 커널의 커넥션 트래킹 기능을 사용하여 이 응답이 어떤 컨테이너에서 시작된 요청에 대한 것인지 기억하고 있다.
- 역방향 NAT: 호스트는 응답 패킷의 목적지 IP를 다시 컨테이너의 IP(172.17.0.2)로 변경한다. 이는 DNAT(Destination Network Address Translation)라고도 함.
- docker0 브릿지를 통한 전달: 변환된 패킷은 docker0 브릿지를 통해 적절한 veth 인터페이스로 전달된다.
- 컨테이너 내부 전달: 패킷은 veth 쌍을 통해 컨테이너 내부의 네트워크 인터페이스로 전달된다.
- 애플리케이션 수신: 마지막으로, 컨테이너 내부의 애플리케이션이 응답을 수신합니다. 애플리케이션 입장에서는 마치 직접 외부 서비스와 통신한 것처럼 보인다.
Volume
Docker는 원하는 이미지를 바탕으로 하나의 가상화 환경을 손쉽게 구축할 수 있다. 개별적인 가상화 환경인 컨테이너에서 작업을 진행하기에 작업하는 모든 컨테이너 내부에만 존재하게 된다.
→ 여기서 데이터 유지에 대한 문제가 발생한다.
컨테이너를 삭제하게 되면 작업했던 데이터가 모두 삭제되어버린다.
이때 사용하는 것이 볼륨(Volume)이다.
즉, 도커에서 데이터 영속성을 보장하기 위한 방법 중 하나이다.
volume은 한 마디로 컨테이너 내부의 데이터를 외부로 링크를 걸어주는 기능.
볼륨을 걸어준 데이터의 경우 컨테이너 내부에서 수정되는 즉시 볼륨이 걸려있는 외부의 데이터도 같이 수정된다.
컨테이너 데이터가 삭제되어도 외부에는 데이터가 남아있다.
호스트 볼륨
호스트의 디렉토리를 컨테이너의 특정 경로에 마운트한다.
호스트 볼륨 마운팅의 작동 원리
호스트 볼륨 마운팅(-v
또는 --volume
옵션 사용)은 호스트 시스템의 디렉토리와 컨테이너 내부의 디렉토리 사이에 양방향 연결을 생성한다. 이를 통해:
- 양방향 동기화: 호스트 디렉토리(
/opt/html
)에 있는 모든 파일은 컨테이너 내부의 지정된 경로(/usr/share/nginx/html
)에서 접근할 수 있다.
반대로, 컨테이너 내부에서 해당 경로에 새 파일을 생성하거나 기존 파일을 수정하면 그 변경사항이 즉시 호스트 디렉토리에 반영된다.
- 데이터 지속성: 컨테이너가 삭제되더라도 호스트 디렉토리의 데이터는 그대로 남아있다. 이는 컨테이너의 수명주기와 관계없이 데이터를 보존할 수 있게 해준다.
- 실시간 반영: 이 연결은 실시간으로 작동한다. 파일 시스템 변경이 즉시 양쪽에 반영된다.
docker run -d --name nginx -v /opt/html:/usr/share/nginx/html nginx
<호스트의 디렉토리:(컨테이너)nginx 웹 루트 디렉토리>
위 명령어 실행한 후
- 호스트 시스템에서
/opt/html/index.html
파일을 수정하면, Nginx 웹 서버가 즉시 새로운 내용을 제공한다.
- 컨테이너 내부에서
/usr/share/nginx/html
디렉토리에 새 파일(예: new_page.html
)을 생성하면, 이 파일은 호스트의 /opt/html/new_page.html
로도 접근할 수 있다.
- 컨테이너를 삭제하고 새로운 컨테이너를 같은 볼륨 마운팅으로 생성하면, 이전 컨테이너에서 만든 모든 데이터(호스트에 저장된)를 새 컨테이너에서도 사용할 수 있다.
볼륨 마운팅의 주요 이점
- 개발 워크플로우 향상: 로컬에서 코드를 편집하면 컨테이너 내부에 즉시 반영되므로 개발 과정이 간소화됨.
- 백업 및 마이그레이션 용이: 호스트 시스템에서 직접 데이터를 백업하거나 다른 환경으로 마이그레이션할 수 있다.
- 로그 및 설정 관리: 컨테이너의 로그 파일이나 설정 파일을 호스트에서 쉽게 접근하고 관리할 수 있다.
- 성능: 데이터베이스와 같은 I/O 집약적인 애플리케이션의 경우, 볼륨 마운팅은 Docker의 기본 레이어드 파일 시스템보다 더 나은 성능을 제공할 수 있다.