Docker는 컨테이너 기술을 사용하여 애플리케이션의 실행 환경을 구축 및 운용하기 위한 플랫폼이다.
애플리케이션의 실행에 필요한 것을 하나로 모아, Docker 이미지를 관리함으로써 애플리케이션의 이식성을 높일 수 있다. 이번 글에서는 컨테이너 기술과 Docker의 개요, Docker가 작동하는 구조에 대해 정리한다.
서버 가상화 기술
1. 호스트 가상화(Host Virtualization)
• 하드웨어 위에 베이스가 되는 호스트OS를 설치하고, 그 위에 가상화 소프트웨어를 설치한 뒤, 이제 이 가상화 소프트웨어 상에서 게스트OS를 작동시키는 가상화 기술이다.
• 가상화 소프트웨어를 설치하여 간편하게 가상 환경을 구축할 수 있기 때문에 개발 환경 구축에 많이 사용.
• 하지만 이 방식은 컨테이너와 다르게 호스트 OS 상에서 다른 게스트 OS를 움직이므로 오버 헤드가 크다.
ex) Oracle VM VirtualBox, VMware Workstation Player 등
2. 하이퍼바이저 가상화(Hypervisor Virtualization)
• 하드웨어 상에 가상화를 전문으로 수행하는 소프트웨어인 '하이퍼바이저'를 배치하고, 하드웨어와 가상 환경을 제어한다.
• 호스트OS 없이 하드웨어를 직접 제어하기 때문에 자원을 효율적으로 사용 가능.
• 가상 환경마다 별도의 OS가 동작하므로 가상 환경의 시작에 걸리는 오버헤드가 클 수 있다.
• 하이퍼바이저형은 펌웨어로서 구축하는 경우가 많다.
ex) Hyper-V, XenServer 등
3. 컨테이너 가상화 (OS-level Virtualization)
• 컨테이너란 호스트 OS 상에 논리적인 구획(컨테이너)을 나누고, 애플리케이션을 작동시키기 위해 필요한 라이브러리나 애플리케이션 등을 하나로 모아, 마치 별도의 서버인 것처럼 사용할 수 있게 만든 것.
• 호스트 OS의 리소스를 논리적으로 분리시키고, 여러 개의 컨테이너가 공유하여 사용한다.
• 컨테이너는 오버헤드가 적어 가볍고 고속으로 작동.
ex) Docker, Linux containers 등
• 컨테이너 기술은 애플리케이션의 실행 환경을 모음으로써 이식성을 높이고 확장성이 좋은 환경에서 작동하는 것을 지향하고 있는 반면, 가상화 기술의 대부분은 서로 다른 환경을 어떻게 효율적으로 에뮬레이트할지라는 점을 지향하고 있다.
컨테이너의 역사
FreeBSD Jail
• 오픈 소스 UNIX인 FreeBSD의 가상화 기술로, 2000년에 출시된 FreeBSD 4.0에서 도입되었다.
• FreeBSD 시스템을 Jail이라고 부르는 독립된 작은 구획에 가둬넣어 시스템을 분할할 수 있다.
• 특징: 관리자 권한의 범위가 Jail 안으로 제한되기 때문에 시스템 관리자는 종료와 같은 시스템 전체를 조작하는 권한을 부여하지 않고도 일반 사용자에게 관리자 권한을 부여할 수 있다.
Solaris Containers
• Oracle의 상용 UNIX인 Solaris에서 사용하는 컨테이너 기술. Solaris는 상용 Unix의 대표적인 제품이다.
• Intel, AMD를 사용하는 X86 계열 마이크로프로세스뿐만 아니라 RISC 기반 마이크로프로세서로도 동작.
Solaris Containers의 두가지 기능
1) Solaris Zone 기능
- 하나의 OS 공간을 가상적으로 분할하여 여러 OS가 동작하는 것처럼 보이게 하는 소프트웨어 파티셔닝 기능이다.
- 베이스가 되는 OS 영역을 'global zone', 분할된 가상 Zone을 'non-global zone'이라고 한다. non-global zone에서 업무 애플리케이션이의 서버 기능을 동작시킨다.
- 비 글로벌 존(non-global zone)끼리는 완전히 격리되어 있어서 한 비 글로벌 존에서 작동하고 있는 프로세스에 다른 비 글로벌 존에서 엑세스할 수 없다.
2) Solaris 리소스 매니저 기능
- non-global zone에서 CPU나 메모리와 같은 하드웨어 리소스를 배분하는 리소스 관리 기능이다.
- 중요도가 높은 시스템에 우선적으로 리소스를 할당할 수 있다.
▶ Solaris Containers는 Docker와 구조가 매우 유사하다!
- 애플리케이션의 실행 환경을 Zone으로 구획화하여 각각을 제어.
Linux Container (LXC)
• Linux 상에서 사용하는 컨테이너 환경이다.
• LXC는 리눅스 커널의 컨테이너 기능을 이용하기 위한 툴이나 API를 제공한다.
• 컨테이너는 namespace와 cgroups라는 리소스 관리 장치를 사용하여 분리된 환경을 만든다.
• 데이터 영역에 대해서는 특정 디렉토리를 루트 디렉토리로 변경하는 chroot를 사용하여 분리 환경을 만든다.
⇒ 이전 버전에서 Docker는 내부에서 LXC를 사용했지만, 현재 버전에서는 사용하지 않음!
Docker
• Docker는 애플리케이션의 실행에 필요한 환경을 하나의 이미지로 모아두고, 그 이미지를 사용하여 다양한 환경에서 애플리케이션 실행 환경을 구축 및 운용하기 위한 오픈소스 플랫폼이다.
• Docker는 내부에서 컨테이너 기술을 사용하고 있는 것이 특징이다.
• 프로그래머는 Docker를 사용하여 개발한 애플리케이션의 실행에 필요한 모든 것이 포함되어 있는 Docker 이미지를 작성한다. 그리고 작성한 이미지를 바탕으로 컨테이너를 가동시킨다.
• 컨테이너의 바탕이 되는 Docker 이미지를 Docker Hub와 같은 레파지토리(repository)에서 공유 가능.
• 이미지는 Docker가 설치되어있는 환경이면 기본적으로 어디든지 작동된다.
Docker 컨테이너의 이식성(prtability)
• 한번 만들면 어디서든지 움직이는 소프트웨어의 특성을 이식성(prtability)라고 한다.
• Docker는 이식성이 높기 때문에 클라우드 시스템과의 친화력도 높은 것이 특징이다.
• 개발한 업무 애플리케이션을 온프레미스(on-premises) 환경에 대한 이전뿐만 아니라 온프레미스 환경에서 클라우드나 클라우스에서 온프레미스 환경 간에도 시스템 요건이나 예산에 따라 손쉽게 실행 환경을 선택할 수 있다.
• 또한 클라우드 서비스 사이의 이전도 손쉽게 할 수 있다. (ex. Azure에서 AWS로 이전)
Docker의 기능
1. Docker 이미지를 만드는 기능(Build)
• Docker는 애플리케이션 실행에 필요한 프로그램 본체, 라이브러리, 미들웨어, OS나 네트워크 설정 등을 하나로 모아 'Docker 이미지'를 생성한다.
• Docker 이미지는 실행 환경에서 움직이는 컨테이너의 바탕이 된다.
• Docker에서는 하나의 이미지에는 하나의 애플리케이션만 넣어두고, 여러 개의 컨테이너를 조합하여 서비스를 구축하는 방법을 권장하고 있다.
• Docker 명령을 사용하면 이미지를 tar 파일로 출력할 수 있다.
• Docker 이미지는 Docker 커맨드를 사용하여 수동으로 만들 수 있으며 Dockerfile 프로그램을 통해 자동으로 생성할 수도 있다. (Dockerfile을 사용하여 관리하는 것이 더 바람직.)
• Docker 이미지를 중첩하여 사용 가능하다.
2. Docker 이미지를 공유하는 기능(Ship)
• Docker 이미지는 Docker 레지스트리에서 공유할 수 있다.
ex) Docker의 공식 레지스트리인 Docker Hub에서는 Ubuntu나 CentOS와 같은 Linux 배포판의 기본 기능을 제공하는 베이스 이미지를 배포하고 있다.
• Ubuntu, CentOS 등의 베이스 이미지에 미들웨어나 라이브러리, 애플리케이션 등을 포함한 이미지를 중첩하여 독자적인 Docker 이미지 생성해나간다.
• Docker 커맨드를 통해 Docker Hub에 로그인하여 레지스트리 상에서 이미지를 검색하고 업로드 및 다운로드가 가능하다.
• Docker hub는 Github나 Bitbucket와 연계할 수 있다.
ex) Github 상에서 Dockerfile을 관리하고, 거기서 Docker 이미지를 자동으로 생성하여 Docker Hub에서 공개하는 것이 가능하다. ⇒ 이러한 자동 생성 기능을 Automated Build라고 함!
3. Docker 컨테이너를 작동시키는 기능(Run)
• 하나의 Docker 이미지를 가지고 여러 개의 컨테이너를 가동시킬 수 있다.
• 컨테이너의 기동, 정지, 파기는 Docker의 명령을 사용한다.
• 다른 가상화 기술로 서버 기능을 실행시키려면 OS의 실행부터 시작하기 때문에 시간이 걸리지만, Docker의 경우는 이미 움직이고 있는 OS 상에서 프로세스를 실행시키는 것과 거의 똑같은 속도로 빨리 실행시킬 수 있다.
• 하나의 Linux 커널을 여러 컨테이너가 공유하고 있다.
• 컨테이너 내에서 동작하는 프로세스를 하나의 그룹으로 관리하고 그룹별로 각각 다른 파일 시스템과 호스트명, 네트워크 등을 할당하고 있다.
• 서로 다른 그룹인 경우, 프로세스나 파일에 엑세스 불가
• 컨테이너를 독립된 공간으로 관리
Docker 컴포넌트
Docker Engine(Docker의 코어 기능)
• Docker 이미지 생성과 컴포넌트 구동 등을 위한 Docker의 코어 기능
• Docker 커맨드 실행 및 Dockerfile을 통한 이미지 생성 등을 수행
Docker Registry(이미지 공개 및 공유)
• 컨테이너의 기반이 되는 Docker 이미지를 공개 및 공유하기 위한 레지스트리 기능
Docker Compose(여러 컨테이너를 통합 관리)
• 여러 컨테이너의 구성 정보를 코드로 정의하고 커맨드를 통해 애플리케이션 실행 환경을 구성하는 컨테이너 통합 관리 툴
Docker Machine(Docker 실행 환경 구축)
• 로컬 호스트용인 VirtualBox를 시작으로 AWS EC2와 DigitalOcean, SoftLayer 등 클라우드 환경에 Docker 실행 환경을 커맨드로 자동 생성하기 위한 툴
Docker Swarm(클러스터 관리)
• 여러 Docker 호스트를 클러스터화하기 위한 툴
• Manager는 클러스터 관리와 API를 제공하며 Node는 Docker 컨테이너를 실행
Docker의 작동 구조
컨테이너를 구분하는 구조(namespace)
• 컨테이너를 구획하는 기술은 Linux 커널의 namespace라는 기능을 사용.
• namespace는 한 덩어리의 데이터에 이름을 붙여 할당함으로써 충돌 가능성을 줄이고, 쉽게 참조할 수 있게 하는 개념이다.
• 이름과 연결된 실체는 그 이름이 어떤 이름 공간에 속해 있는지 고유하게 정해진다.
• 이름공간이 다르면 동일한 이름이라도 다른 실체로 처리된다.
• Linux 커널의 namespace 기능은 Linux의 오브젝트에 이름을 붙임으로써 다음과 같은 6개의 독립된 환경을 구축할 수 있다.
PID namespace
• PID namespace는 PID와 프로세스를 분리한다.
• namespace가 서로 다른 프로세스는 서로 액세스 불가능
Network namespace
• 네트워크 디바이스, IP 주소, 포트 번호, 라우팅 테이블, 필터링 테이블 등과 같은 네트워크 리소스를 namespace별로 할당 가능
• 호스트OS 위에서 사용 중인 포트가 있어도 컨테이너 안에서 같은 번호의 포트 사용 가능
UID namespace
• UID(user ID), GID(Group ID)를 namespace별로 독립하여 가질 수 있음
• namespace 안에서 호스트OS상의 UID 및 GID와 서로 연결되어 namespace 안과 밖에서 서로 다른 UID 및 GID를 가질 수 있음
• namespace의 관리자 계정이 호스트OS에서는 관리 권한을 가질 수 없게 할 수 있으므로 보다 보안을 강화할 수 있음
MOUNT namespace
• 마운트를 조작하여 namespace 안에 격리된 파일 시스템 트리를 생성
UTS namespace
• namespace별로 호스트명과 도메인명을 독자적으로 가질 수 있음
IPC namespace
• 프로세스 간의 통신(IPC) 오브젝트를 namespace별로 가질 수 있음
⇒ Docker는 이러한 namespace 장치를 사용하여 호스트 상에서 컨테이너를 가상적으로 격리시킨다.
릴리즈 관리 장치(cgroup)
• cgroups는 프로세스와 스레드를 그룹화하여, 그 그룹 안에 존재하는 프로세스와 스레드에 대한 관리를 수행하기 위한 기능이다.
ex) 호스트 OS의 CPU, 메모리와 같은 리소스를 그룹별로 제한을 둘 수 있음.
• 한 컨테이너가 같은 호스트OS 상에서 동작하는 다른 컨테이너에 영향을 주는 일을 막을 수 있음
• cgroups는 계층 구조를 사용하여 프로세스를 그룹화해 관리할 수 있다.
ex) 사용자 애플리케이션과 서버와 같은 데몬 프로세스를 나눠 각각의 그룹에 CPU 사용량을 할당 가능
• cgroups의 부모자식 관계에서는 자식이 부모의 제한을 물려받는다.
ex) 자식이 부모의 제한을 초과하는 설정을 하더라도 부모 cgroups의 제한에 걸린다.
네트워크 구성(가상 브리지/가상 NIC)
• Linux는 Docker를 설치하면 서버의 물리 NIC가 docker0이라는 가상 브리지 네트워크로 연결된다. 이 docker0 은 Docker를 실행시킨 후에 디폴트로 만들어진다.
• Docker 컨테이너가 실행되면 컨테이너에 172.17.0.0/16이라는 서브넷 마스크를 가진 프라이빗 IP 주소가 eth0으로 자동으로 할당된다. 이 가상 NIC는 OSI 참조 모델의 레이어 2인 가상 네트워크 인터페이스로, 페어인 NIC와 터널링 통신을 한다.
• Docker 컨테이너와 외부 네트워크가 통신을 할 때는 가상 브릿지 docker0과 호스트 OS의 물리 NIC에서 패킷을 전송하는 장치가 필요하다. Docker에서는 NAPT 기능을 사용하여 연결한다.
NAPT (Network Address Port Translation) 란?
• 하나의 IP 주소를 여러 컴퓨터가 공유하는 기술로, IP 주소와 port 번호를 변환하는 기능이다. 프라이빗 IP 주소와 글로벌 IP 주소를 투과적으로 상호 변환하는 기술로, TCP/IP의 포트 번호까지 동적으로 변환하기 때문에 하나의 글로벌 IP 주소로 여러 대의 머신이 동시에 연결할 수 있다. Docker에서는 NAPT에 Linux의 iptables를 사용하고 있다.
• Docker에서 이 기능을 사용할 때는 컨테이너 시작 시에 컨테이너 안에서 사용하고 있는 포트를 가상 브릿지인 docker0에 대해 개방한다. 예를 들어 컨테이너 시작 시에 컨테이너 안의 웹 서버가 사용하는 80번 포트를 호스트 OS의 8080번 포트로 전송하도록 설정한다. 그러면 외부 네트워크에서 호스트 OS의 8080번 포트에 액세스하면 컨테이너 안의 80번 포트로 연결된다.
NAT 와 NAPT (IP mascarade) 의 차이
• 프라이빗 IP주소와 글로벌 IP주소를 변환하여 프라이빗 IP 주소가 할당된 컴퓨터에 대해 인터넷 액세스를 가능하게 할 때 사용하는 기술로는 NAT와 NAPT (IP 마스커레이드)가 있다.
NAT (Network Address Translation)
프라이빗 IP 주소가 할당된 클라이언트가 인터넷상에 있는 서버에 액세스할 때 NAT 라우터는 클라이언트의 프라이빗 IP 주소(예: 192.168.0.1)를 NAT이 갖고 있는 글로벌 IP 주소 (예: 55.xxx.xxx.xxx) 로 변환하여 요청을 송신한다. 응답은 NAT 라우터가 송신처를 클라이언트의 프라이빗 IP 주소로 변환하여 송신한다.
이러한 주소 변환에 의해 프라이빗 네트워크 상의 컴퓨터와 인터넷상의 서버 간의 통신이 성립된다. 그런데 NAT의 경우 글로벌 IP 주소와 프라이빗 IP 주소를 1:1로 변환하기 때문에 동시에 여러 클라이언트가 액세스할 수가 없다.
• NAT는 프라이빗 IP 주소와 글로벌 IP 주소를 1:1 로 변환한다.
NAPT (Netwrok Address Port Translation)
NAPT는 프라이빗 IP 주소와 함께 포트 번호도 같이 변환하는 기술이다. 프라이빗 IP 주소를 글로벌 IP 주소로 변환할 때 프라이빗 IP 주소별로 서로 다른 포트 번호로 변환한다.
예를 들어 클라이언트 A가 보낸 요청은 포트 번호가 1500이고, 클라이언트 B가 보내온 요청은 포트 번호 1600이라고 해보자. 인터넷상의 서버로부터 NAPT의 글로벌 IP 주소의 서로 다른 포트 번호 앞으로 응답이 되돌아 온다. NAPT는 포트 번호를 바탕으로 프라이빗 IP 주소로 변환할 수 있다. 이로써 하나의 글로벌 IP 주소와 여러 개의 프라이빗 IP 주소를 변환할 수 있는 것이다.
• NAPT는 프라이빗 IP 주소와 글로벌 IP 주소뿐만 아니라 포트 번호도 변환한다.
또한 NAPT는 이러한 기술의 이름으로, Linux에서 NAPT를 구축하는 것을 IP 마스커레이드라고 부른다. 마스커레이드(mascarade)란 영어로 '가면무도회' 를 의미힌다. 이것은 많은 가면을 쓴 IP 패킷이 포트 번호의 가면을 붙여 변환되는 모습을 나타낸 것이다.
* 위 게시글은 완벽한 IT 인프라 구축을 위한 Docker (정보문화사, Asa Shiho 지음, 이영란 옮김) 도서를 참고하여 작성하였습니다.
'Docker' 카테고리의 다른 글
Ubuntu에 Docker 설치 및 Portainer 구축하여 쉽게 관리하기 (0) | 2021.10.08 |
---|---|
윈도우에서 도커(Docker) 설치하기 (Windows 10 Home 포함) (0) | 2021.09.29 |
[Docker] 시스템과 인프라 기초 지식 (0) | 2021.09.14 |