도커와 컨테이너 오케스트레이션 시스템에 대한 공부를 시작했다.
도커 스웜/쿠버네티스와 같은 오케스트레이션 시스템에 관련된 내용, 혹은 여러 호스트 위에서 오버레이 네트워크에 대한 자세한 내용은 다음 포스트로 미뤄두고, 이번 포스트에서는 도커와 컨테이너, 가상화에 대한 구조를 정리한다.
도커와 컨테이너, 가상화 개요
도커
“도커는 리눅스 컨테이너 시스템인 runC를 이용해 하나의 운영체제 위에서 격리된 컴퓨팅 환경을 운영할 수 있도록 도와주는 애플리케이션 레벨 가상화 소프트웨어”
-
였지만 위에서 설명했듯이 Windows또한 NT Kernel 상에서 isolation을 통해 windows(Host)→windows(Base) 컨테이너를 생성하므로 위는 outdated된 definition이다.
-
컨테이너 가상화는 게스트 운영체제 없이 호스트 운영체제 위에 격리된 환경에서 가상화를 구현한다. 게스트 운영체제가 따로 존재하지 않으니 호스트 운영체제의 요소를 공유하며 그만큼 중복되는 요소가 줄어 성능적 이점을 누릴 수 있다.
-
컨테이너는 운영 체제의 동작을 완전히 재현하지는 못하기 때문에, 좀 더 엄밀한 운영 체제의 동작이 요구되는 가상 환경을 구축해야 한다면 VMWare나 VirtualBox가 낫다.
Docker가 기존 LXC(아래 나옴)에 비해 가진 장점
-
호스트 운영 체제의 영향을 받지 않는 실행 환경(Docker Engine을 이용한 실행 환경 표준화, LXC를 사용할 땐 LXC의 설정 차이로 LXC에서 만든 application이 다른 LXC에서 안 돌아가는 문제가 발생하곤 했음)
-
DSL(Domain Specific Language)로 이용되는 Dockerfile
-
이미지 버전 관리
-
레이어 구조를 갖는 이미지 포맷(차분 빌드가 가능함)
-
도커 레지스트리(이미지 저장 서버 역할)
컨테이너
도커가 만들어내는 게스트 운영체제 수준 가상화 공간(operating-system-level virtualization)
virtual machine은 some level of hardware와 kernel virtualization.을 통해 guest os를 실행시킨다.
hypervisor 라는 소프트웨어가 존재하여 virtualized hw, virtual network interface, virtual cpu, 등등을 제공하여 virtual machine을 구현시켰고, vm은 guest kernel을 가져서 만들어진 virtual hw에 접근했다.
(hypervisor는 Host OS에서 실행되는 소프트웨어다.)
하지만 이 방법은 가상화해야할 파트가 너무 많아 cost가 크게 취급 받음. 위 나열한 컴포넌트들처럼 한 머신에 isolated group이 많은데, 이를 각각 또 나눠서 VM을 만드는 HW virtualization이 필요하고 좋은 방법이 아님. (그렇다곤 하는데 장단점이 있다.)
cgroups (linux control groups)
- 리눅스 커널의 기능으로 구글이 구현했으며, user process들이 사용하는 resource가 분리되도록 했다.
- 위만 보면 당연한 이야기지만, cgroups는 namespace isolation을 통해 각 프로세스를 그룹으로 나누어 통제, 이전보다 higher level에서 isolation을 해냈음.
- 정리하자면 각 namespace가 그에 속한 process들에게는 하나의 machine 그 자체처럼 작용했다는 것.
이후 cgroup을 이용해 LXC (linux containers) 가 나타남.
process와 networking space를 분리해 user spaces를 분리했다. 초기 버전의 도커는 LXC를 그대로 사용했음 → 이후 cgroups, namespace api를 직접 실행하는 libcontainer 라이브러리를 개발하고 LXC 업싱 동작할 수 있게 됐고, runC(libcontainer 리팩토링)을 이용한다..
“Container Host”와 “Container OS”의 차이
Container Host (Host OS)
-
Docker client와 Docker daemon이 실행되고 있는 곳의 OS
-
linux와 non-Hyper-V container들은 Host OS가 Docker container에게 kernel을 나누어주지만, Hyper-V container는 각자의 Hyper-V kernel을 가진다.
Container OS (Base OS)
-
Ubuntu, CentOS, windowsservercore와 같이 컨테이너의 OS
-
Base OS를 담은 이미지 위로 쌓이는 이미지는 Base OS를 utilize함.
Windows container는 Base OS를 꼭 필요로하지만, Linux container는 그렇지 않다. (여기서 windows, linux는 Host OS를 말하는 듯)
쭉 설명했듯이 도커는 기본적으로 Linux kernel을 이용하여 개발되었다. Docker for Windows/Mac이 linux container를 만들 때는 LinuxKit(linux vm)을 이용한다. → Linux containers are running on Linux, and Windows containers are running on Windows. → 그랬는데 이게 또 windows에선 LCOW가 기본이 되면서 바뀌었다.
-
The setup for running Linux containers with LCOW is a lot simpler than the previous architecture where a Hyper-V Linux VM runs a Linux Docker daemon, along with all your containers. With LCOW, the Docker daemon runs as a Windows process (same as when running Docker Windows containers), and every time you start a Linux container Docker launches a minimal Hyper-V hypervisor running a VM with a Linux kernel, runc and the container processes running on top.
-
이렇게하면 Docker Daemon이 windows(Host OS) 위에서 돌아도 Linux Container를 만들 수 있어서 Windows와 Linux 컨테이너를 동시에 실행해줄 수 있다.
-
windows container가 windows에서 생성되는 과정이나, docker 자체 기술이나, 현재 진행형으로 새로 개발되는 것들이 있는 것 같다.
Hyper-v
개요
- https://docs.microsoft.com/ko-kr/windows-server/virtualization/hyper-v/hyper-v-technology-overview
VMware와의 차이
기본적으로 monolithic hypervisors와 microkernelized hypervisor의 차이라고 보면된다.
VMware가 사용하는 VMware ESX는 monolithic hypervisor
- host OS를 필요로 하지 않고, hypervisor가 직접 OS처럼 physical machine에 대한 control을 가져간다.
- 그러므로 Hypervisor가 Hypercall 처리에 필요한 driver들을 포함하게 됨.
Hyper-V는 microkernelized hypervisor,
- CPU와 memory management를 제외한 다른 접근은 parent partition을 통해서 이루어진다.
- child partition이 hw에 접근하려 할 때, Hyper-V가 HW로 바로 보낼 지, Parent의 VMBus로 보낼 지 판단함.
- 이렇게 해서 memory management와 process scheduling을 제외한 작업은 hypervisor를 통하지 않고 Parent partition의 자원을 이용해 실행된다. hypervisor가 더 얇은 레이어가 되는 것.
- 그리고, Hyper-V는 memory management와 process scheduling 둘 다 hw에 포함된 드라이버를 이용한다고 한다, 그래서 Hyper-V를 돌릴 수 있는 device 조건이 좀 까다롭다.
Docker for Windows
설명이 좀 두서 없을 수 있는데, Docker for Windows는 계속 다른 형식의 variants가 있었고 지금도 변화하고 있어서 그렇다, 종류별로 요약하자면 아래와 같음
Docker Toolbox
which includes Docker Machine that will spin up a boot2docker image inside of VirtualBox. These are Linux containers running with a Linux kernel inside the VM. This was originally the only option for Windows users.
Docker for Windows
using Hyper-V to run the Moby VM, based on LinuxKit, to run Linux images. LinuxKit provides a container based Linux OS, and there’s some integration to make it appear less like a VM to the end user, e.g. you can use 127.0.0.1 instead of the IP of the VirutalBox VM. If you have Hyper-V available and want to run Linux containers on Windows, this is the preferred option.
Windows Server Containers
which run Windows binaries on the same host OS, similar to how Linux containers on a Linux OS do not need a VM.
Hyper-V Containers
which run Windows binaries inside of a separate VM for additional isolation.
LCOW
this Docker daemon runs as a Windows process when launching linux containers, every time you start a linux container Docker launches a minimal Hyper-V hypervisor running a VM with a Linux kernel, runc and the container processes on top.
원래 Moby VM을 사용하는 구조가 아래와 같았다면,
LCOW를 사용하면 아래와 같은 구조를 띔.
가상화의 단점
완전한 운영체제가 올라가기 때문에, 대량의 메모리가 필요하다. 프로세스의 경우 전환이 매우 빠르고, 애플리케이션들이 대부분의 시간을 sleep상태로 있기 때문에, 여러 개의 VM들이 CPU를 경쟁해도 크게 문제가 되지 않는다. 하지만 메모리는 CPU 만큼 빠르게 전환 할 수가 없다. 따라서 CPU를 아끼는 만큼 메모리를 아낄 수는 없고, 상대적으로 대량의 메모리를 가지고 있어야 한다. 가상화를 할 때, 적정 메모리를 산정하는 것은 상당히 까다로운 문제가 될 수 있다.
Container의 장단점
장점
- 가상 머신의 경우 부팅 준비하는 것만 해도 상당한 시간이 걸리지만 컨테이너는 ms 단위로 실행된다. 일반 프로세스를 실행하는 것과 차이가 없다. 가상머신 방식과 비교해서 가장 큰 장점이다.
- 가상머신은 운영체제가 실행된다. 따라서 실행 자체만으로도 수백메가의 메모리를 소비한다. 가상환경에서 CPU와는 달리 메모리는 자원회수가 느리다. 상당히 많은 메모리를 소모할 수 밖에 없다. 컨테이너는 실행하기 전에는 자원을 소비하지 않는다.
- 가상머신은 완전한 운영체제를 포함한다. 우분투 서버리눅스의 경우 설치만으로도 1G가 넘는 디스크 공간을 사용한다. 컨테이너는 이미지로 부터 Copy on write 방식으로 만들어진다. 그 자체로 디스크 공간을 점유하지 않는다. 1000개의 가상머신을 만든다고 가정해 보자. 가상머신을 띄우는데만 1테라의 공간이 필요하다. 반면 컨테이너는 디스크 공간을 거의 쓰지 않는다.컨테이너의 장점이다.
단점
- 자원의 격리와 제한이 어렵다. cgroup와 namespace와 같은 기술들이 발전 하고 있기는 하지만, 가상머신에 비해서는 부족한 점이 있다.
- 스토리지 성능이 좋지 않다. 가상머신은 블럭 디바이스 위에 ext3, ext4를 이용해서 스토리지를 사용한다. 컨테이너는 AUFS, Device mapper, Overlay Filesystem등과 같은 유니온 파일 시스템(Union filesystem)을 사용하는데, ext4와 같은 파일 시스템 위에 올라가는 데다가 변경된 내용을 기록하고, 기록된 내용으로 부터 원본 데이터를 복원해야 하기 때문에 느릴 수 밖에 없다. 데이터의 읽기와 쓰기를 일반 파일 시스템으로 분리하거나 Btrfs나 ZFS 같은 COW파일 시스템을 이용하는 등으로 문제를 해결 할 수 있기는 하지만, 가상머신에 비해서 신경써야 할게 많다.
- 네트워크 구성이 어렵다. 가상머신은 물리적인 컴퓨터 시스템과 차이가 없어서 네트워크 구성이 특별히 어렵지 않다. 그냥 가상머신 마다 IP 주소 붙여서 연결하면 된다. 하지만 컨테이너는 그렇게 하기 힘들다. 그런 방식을 그냥 사용하면 프로세스에 IP를 주는 것이 되며, 게다가 컨테이너는 대량으로 만들 수 있기 때문에 IP 주소를 붙이는 것 자체가 낭비다. 호스트 운영체제 레벨에서 네트워크를 한번 더 추상화 해야 해서 가상머신 보다 네트워크 구성에 신경을 써야 할게 많다.
(현재)도커의 한계: Kernel 및 Architecture의 차이
-
호스트형 가상화 기술처럼 하드웨어를 연산으로 에뮬레이션하는 기술과 달리, 도커에서 사용되는 컨테이너형 가상화 기술은 호스트 운영 체제와 커널 리소스를 공유한다. 이는 사실상 도커 컨테이너를 실행하려면 호스트가 특정 CPU 아키텍처 혹은 운영 체제를 사용해야 한다는 의미. 라즈베리 파이와 같은 ARM 계열의 arm7I 아키텍처를 채용한 플랫폼에서는 인텔의 x86_64 아키텍처에서 빌드한 도커 컨테이너를 실행할 수 없다.
-
microsoft의 windowsservercore 이미지는 리눅스나 macOS등의 플랫폼에서는 실행할 수 없다.
도커 파일 시스템
Image/Container의 Layer Structure
Image (위 컨테이너 레이어는 컨테이너 실행 시 생기는 영역, 아래의 Container 이미지처럼.)
Container
위 구조(Union File System)는 storage driver들이 저마다 다른 방식으로 핸들링한다.
Union Mount
하나의 디렉터리에 여러 개의 디렉터리를 마운트함으로써 마치 하나의 통합된 디렉터리처럼 보이게 하는 것.
Layers multiple directories on a single Linux host and presents them as a single directory.
Union File System
Union Mount가 가능한 파일시스템
1993년에 Inheriting File System 이라는 이름으로 구현되었는데, 그 복잡도 때문에 개발자들에 외면받고, UnionFS, 2006년에는 AUFS, 2009년에 OverlayFS가 나왔음. AUFS는 mainline Linux Kernel에 못 들어갔고, OverlayFS는 머지에 성공함. kernel 4.0에서는 향상된 버전인 Overlay2를 이용할 수 있게 됨.
COW
Union File System 중 aufs의 경우, 파일에 접근할 때
- 맨 위 레이어부터 내려가면서 해당 파일이 존재하는 레이어를 찾는다. modification이 없는 경우 그 위치에서 읽는다.
- modification이 필요한 경우 copy_up operation을 통해 찾은 파일을 container layer로 복사하고 그 위에서 수정한다.
각 storage driver마다 Union Mount 방식은 제각각.
Docker Storage Drivers
AUFS (Advanced Multi Layered Unification Filesystem)
기본적으로 위에 언급한 COW 설명과 동일하게 작동하며 아래와 같은 특징이 있다.
copy_up을 통해 container layer로 복사한 디렉터리를 지울 경우, “opaque file”이 만들어져서 image layer의 디렉터리에 접근하지 못하게 한다.
renaming directory의 경우, 순수하게 rename의 기능은 지원하지 않으며, EXDEV(“cross-device link not permitted”)를 반환한다. 오퍼레이션은 “copy and unlink”를 통한 유사 renaming으로 fall back.
OverlayFS
Image layer를 lowerdir, Container layer를 upperdir, 둘을 합쳐서 보여주는 Container mount를 merged directory라고 부른다.
이미지처럼, 여러 개의 layer가 스택을 이루는 AUFS와는 달리 단 2개의 layer만 가지고 있다.
- overlay
- Multi-Layer Image 구성이 불가하지만, 각 image layer마다 /var/lib/docker/overlay 디렉터리 밑에 자신만의 디렉터리를 가지고, 하드링크를 통해 해결한다. 즉, 각 이미지 레이어마다 해당 레이어 이전의 파일을 모두 포함하며 lowerdir는 최상위 이미지 레이어를 가르킨다.
- 단점
- overlay가 사용한 하드링크 자체가 inode를 만들어내진 않지만, 이미지가 심볼릭 링크나 character device를 포함한다면 더 많은 inode를 생성하게 된다. inode exhaustion
- overlay2
- 하드링크 대신 aufs처럼 overlay layer의 수를 늘리고 기존의 레이어 디렉터리가 하위 레이어까지 포함하던 대신, diff만 나타내게 함.
- 이렇게만 보면 aufs랑 비슷한데, aufs보다 cache의 효율이 좋아서 성능이 낫다고 한다. 이 부분은 레퍼런스를 찾기 힘들어서 stackexchange에 질문 올림
BTRFS (B-tree File System)
컨테이너용 파일시스템로 주목받는다기보다, Ext, ZFS, XFS 등의 backing filesystem로 떠오르고 있다. Ext4 maintainer가 스스로 Ext4는 금방 저물것이고 BtrFS가 미래라고 말했다곤 하는데, 아직은 좀 불안정한지 욕하는 사람들이 많다.
내용이 좀 긴데, 이부분은 리눅스를 따로 공부하면서 기본 파일시스템과 같이 정리하겠다.
데이터 볼륨 관리
데이터 볼륨
- 도커 컨테이너 안의 디렉터리를 디스크에 persisent 데이터로 남기기 위한 메커니즘으로, 컨테이너와 호스트 사이의 디렉터리 공유 및 재사용 기능을 제공한다. 이미지를 수정하고 새로 컨테이너를 생성해도 같은 데이터 볼륨을 계속 사용할 수 있다. 또 데이터 볼륨은 컨테이너를 파기해도 디스크에 그대로 남으므로 컨테이너로 상태를 갖는 애플리케이션을 실행하는 데 적합하다.
- 데이터 볼륨은 재사용 가능하며, 컨테이너들 간에 공유 할 수 있다.
- 데이터 볼륨은 호스트에서 직접 접근 할 수 있다.
- 데이터 볼륨은 컨테이너가 삭제되도 계속 유지된다. 기본적으로 컨테이너와 독립적으로 운영되기 때문이다.
Dockerfile에 VOLUME 명령어를 통해 VOLUME <컨테이너 디렉터리=""> 또는 VOLUME ["컨테이너 디렉터리 1", "컨테이너 디렉터리2"] 와 같이 컨테이너의 디렉터리를 데이터 볼륨으로 추가할 수 있고, 해당 컨테이너 디렉터리는 host의 /var/lib/docker/vfs/dir/~ 어딘가에 저장된다.컨테이너>
이후 타 컨테이너에서 –volumes-from <데이터 볼륨="" 컨테이너=""> 옵션을 이용해 컨테이너를 실행하면 해당 컨테이너가 데이터 볼륨 컨테이너의 volume에 해당하는 디렉터리를 이용하게 된다.데이터>
위처럼 volume을 이용해서 데이터 볼륨을 만들어 주는 것은 아래의 방법들 중 하나로, 다른 방법들도 있음.
non-persistent state data를 다룰 때는 tmpfs mount가 가장 좋다고 공식 문서에도 적혀있다.
도커의 네트워크 추상화
bridge
별다른 설정을 하지 않았을 때 사용되는 Default Network Driver다.
일반적으로 Bridge는 이더넷 네트워크와 MAC 주소를 이용해 Link Layer에서 여러 개의 네트워크 세그먼트를 연결하는 것을 말하지만, Host의 kernel에서 작동하는 software로 나타날 수 있다.
같은 Docker daemon host를 통해 생성된 컨테이너들은 동일한 bridge network에 연결되게 된다. NAT도 Docker가 제공하는 브릿지 인터페이스가 게이트웨이 역할을 하며 수행한다.
user-defined bridge를 직접 설정하여 컨테이너를 연결할 수도 있다.
host
실행된 컨테이너들이 브릿지 네트워크처럼 분리되지 않고 Host의 Port를 그대로 바인딩해 사용한다.
overlay
복수의 docker daemon host, swarm service 간의 네트워크
swarm을 생성하거나, 새로운 docker host를 swarm에 추가할 경우 두 가지 새로운 네트워크가 형성된다.
ingress라는 overlay network는 swarm service들의 트래픽을 컨트롤하며, swarm service를 생성하고 user-defined overlay network에 직접 연결해주지 않으면 default로 ingress 에 연결된다.
docker_gwbridge 라는 bridge network가 동일 swarm 내에 있는 Docker Daemon들을 연결한다.
macvlan
컨테이너에 MAC address를 할당한다.
overlay와 macvlan은 도커 스웜의 용어들을 사용하는 것으로 보여, 이 쪽은 도커 스웜 공부를 하면서 다시 보겠다.