이쿠의 슬기로운 개발생활

함께 성장하기 위한 보안 개발자 EverNote 내용 공유

클라우드/Kubernetes

97. Container 관련 기술 리서치

이쿠우우 2022. 3. 6. 15:22
반응형

 

 

Container 관련 기술 리서치

 
개발하는데 Container에 대한 자세한 이해가 필요해서
Container관련 기술에 대해서 글로 정리하며
다시 한번 복습 및 리서치를 진행해봄.
리서치와 복습만이 살길이다....ㅠㅠ
 
 

Container란?

제가 아는 내용을 설명드릴려고 하면 일단 Linux와 Windows는 별개로 설명드려야할 것 같음.
 

linux Container

linux container는 
리눅스 네임스페이스(Linux namespaces), 컨트롤 그룹(cgroup),  루트 디렉터리 격리(chroot) 등의
커널 기능을 활용해서 linux 커널을 공유하면서 프로세스를 격리된 환경에서 실행하는 기술임.
 
[container의 대표적인 특징]
1. stateless하게 동작해서 container 상태를 보존하지 않음.
2. container는 독립적으로 실행이 되고 있어서 서로 다른 container간에 영향을 주지 않음.
3. portability로 이동성이 뛰어남. 
image화 시킨 container는 OS 구분 없이
다른 host, 또는 public cloud에서 동일하게 실행 시킬 수 있음.
4. 운영체제 수준으로 가상화 되어있어서 VM과 같은 별도의 가상OS없이 동작이 가능.
5. 프로세스 격리 방식을 사용하기 때문에 성능 손실이 거의 없는 특징이 있음.
 

Windows Container

windows의 경우에는 HCS, HNS 기반으로 container를 생성함으로
linux와는 조금 다른 구조를 가지고 있음.
HCS를 통해 실행 할 수 있는 container image는 isolation 방식에 따라 
Process isolation, Hyper-V isolation 2가지로 나누어짐.
 

[Process isolation]

Container가 Host의 kernel과 직접 상호작용함.
Host kernel을 공유하기 때문에 Windows container image 사용 시 
image의 OS version과 host의 OS version이 반드시 일치해야하는 특이점이 있음.
kubernetes에서는 제가 리서치 했을 당시 아직 process 격리 방식만 지원하고 있음.
dcoker도 별도로 설정하지 않으면 default로 process 격리 방식으로 동작함
 
[Hyper-V isolation]
Hyper-v 격리 방식은 Hyper-V 가상 머신을 활용하여 컨테이너를 실행함.
Windows container image에 내장되어있는 커널을 확인한 후 
Hyper-v 가상화 기술로 해당 kernel version이 포함된 Hyper-V VM 실행해서
Host와 분리된 별도의 실행 환경을 구성하고 해당 VM에서 container를 실행함.
Host와 분리되어있기 때문에 Host kernel과 상호작용하지 않고 hyper-v를 통해 생성된 VM kernel과 상호작용함.
VM kernel과 상호작용해서 container의 OS version이 host와 동일하지 않아도 되지만
전체가 격리된 VM은 아니기 때문에 Host OS version보다 높은 버전의 container OS는 실행이 불가능한 점이 있음.
kubernetes도 추후 Hyper-v 격리 방식을 지원할 것이라고 함.
 
 
 

시스템 컨테이너와 애플리케이션 컨테이너 차이점

 
컨테이너는 크게 시스템 컨테이너와 애플리케이션 컨테이너 2 종류로 나누어 짐.
 

시스템 컨테이너system container

시스템 컨테이너는 컨테이너 기술들을 사용해 운영체제 위에 하드웨어 가상화 없이 운영체제를 실행함. 
일반적인 리눅스처럼 init 프로세스 등을 사용해서 다수의 프로세스가 같은 환경을 공유하는 것을 목표로 함. 
시스템 컨테이너를 지향하는 컨테이너 런타임으로는 대표적으로 LXC와 LXD가 있음.
 

애플리케이션 컨테이너applcation container

애플리케이션 컨테이너는 컨테이너 기술을 활용해 하나의 애플리케이션(프로세스)를 실행하는 것을 목표로 함. 
독립적인 환경을 가진다는 점에서는 시스템 컨테이너와 동일하지만, 
단 하나의 프로세스만 실행한다는 점에서 확장이 쉽고 관리 요소가 거의 없음.
대표적인 애플리케이션 컨테이너 런타임으로는 도커Docker, crio, rkt등등이 있음.
 
 

LXC란?

LXC는 리눅스 컨테이너Linux Containers의 줄임말로, OS 수준의 가상화를 구현하는 도구. 
리눅스 커널의 cgroups와 namespaces를 이용해서 컨테이너를 관리하는 모듈로
도커가 처음 공개되었을 때는 내부적으로 컨테이너를 실행하는데 사용되기도 했음.
참고로 도커는 초반에는 LXC 기술을 사용하고 현재는 자체적으로 개발한 libContainer를 사용하고 있음.
 
 

LXD란?

LXC와 마찬가지로 컨테이너를 실행하고 관리하는 도구인데 
LXC에 비해서 보안적인 기능이 강화됨.
LXD는 단순히 LXC를 개선한 도구는 아니며, 내부적으로 컨테이너를 실행할 때는 LXC를 사용함.
LXD는 REST API로 조작하는 것이 가능하고
옵션 설정 없이 디폴트 상태가 Secure한 상태로 실행됨.
그리고 root가 아니어도 컨테이너를 띄울 수 있음.
docker의 경우 root 계정으로만 container를 실행할 수 있는 치명적인 보안 문제가 있는데
docker 이외에 crio등 containerd는 root계정이 아니더라도 container를 실행할 수 있음.
그 이유가 LXD와 연관되어있다고 알고 있음.
 
 

LXC를 사용할 때의 주의점

LXC로 만들어진 가상 환경은 호스트 환경에서 완전히 고립된 상태이기 때문에 
가상 환경에서 호스트 환경의 리소스를 직접 액세스 할 수 없음. 
그러나 주의해야 할 것은 root 권한으로 동작하기 때문에 컨테이너에서 
root 권한으로 실행되는 프로세스는 호스트 환경에서 root 권한을 가지게 됨. 
이 문제를 해결하기 위해 LXC 1.0 컨테이너에서는
root 사용자를 컨테이너 외부에서 다른 사용자로 처리하는 방식으로 구현됨. 
이것은 커널쪽에서도 지원이 필요하기 때문에, 
환경에 따라서는 사용할 수없는 경우도 있으므로 주의해야함.
 
 
 

리눅스 네임스페이스(Linux namespaces)란?

리눅스 네임스페이스는 커널의 리소스를 격리된 공간으로 만들어주는 리눅스 커널 기능으로, 
HOST OS로 부터 Mount, UTS, IPC, PID, Network, User  등을 격리시켜줌.
 

격리 가능 항목

Cgroup 네임스페이스(cgorup)
IPC 네임스페이스(ipc)
네트워크 네임스페이스(network)
마운트 네임스페이스(mnt)
PID 네임스페이스(pid)
UTS 네임스페이스(user)
사용자 네임스페이스(uts)
시간 네임스페이스(time)
 
[자세한 설명을 해주는 사이트]
 
 

컨트롤 그룹 (cgroup)이란?

Cgroup은 CPU, Network, Memory 등 하드웨어 자원을
Process Group 단위로 용량을 제한하고 격리시키는 Linux의 기능임.
주로 Container의 Resource 제어를 위해서 많이 사용됨.
Cgroup Driver 종류로는 cgroupfs, systemd 2가지가 있음.
 
cgroup이 관리할 수 있는 자원은 컨트롤러라는 파일 시스템 형태로 있음.
Linux Kernel이 갖고 있는 Cgroup 정보는 cgroupfs의 Directory와 File로 나타나며 
Cgroup 제어는 Directory 생성, 삭제 및 File의 내용 변경을 통해서 이루어짐. 
 
systemd는 Linux의 Init Process로써 Daemon Process 제어 역할과 더불어 Cgroup을 제어하는 역할도 수행함.
systemd의 Cgroup 제어 기능은 자신이 제어하는 Daemon Process의 Resource 사용량을 제어하기 위해서 사용됨.
내부적으로는 systemd도 결국 cgroupfs을 사용하여 Cgroup을 제어하는 것이기 때문에
systemd가 cgroupfs을 Mount하는 형식으로 동작함.
 
즉 cgroupfs Driver는 자신이 직접 cgroupfs을 통해서 Cgroup을 제어함. 
반면 systemd Driver는 systemd를 통해서 Cgroup을 제어함.
 
docker의 경우 현재 version 기준 default로 cgroupfs로 동작하고
kubernetes의 kubelet의 경우 v1.22 부터 systemd 로 동작하고 있음.
low level container runtime에 해당하는 runC의 경우 내부적으로 
libContainer 라이브러리를 사용하는데
해당 libContainer가 cgroupfs, systemd 를 선택해서 동작함.
 
[자세한 설명 참고 사이트]
 
 
 

Change Root 격리(chroot)란?

프로세스가 실행되는 root directory를 변경해주는 것
[자세한 설명 참고 사이트]
 
 

리눅스 캐퍼빌리티(Linux capabilities)란?

리눅스 캐퍼빌리티Linux capabilities는 프로세스의 권한을 제어하는 기능. 
리눅스의 프로세스는 크게 루트 권한(사용자 ID 0)으로 실행되는
 특권 프로세스와 일반 사용자(사용자 ID 0 이외)가 실행하는 비특권 프로세스로 나누어짐. 
루트의 권한을 세분화해서 프로세스 적용할 수 있도록 만든 기능이 바로 리눅스 캐퍼빌리티임.
컨테이너 런타임에서도 일부 루트 권한이 필요한 경우
리눅스 캐퍼빌리티를 사용해 필요한 권한을 지정하는 방식을 지원하고 있음.
 
 

유니온 마운트(Union Mount)란?

유니온 마운트는 파일을 계층으로 나눠서 저장할 수 있는 기술로
container image 에 사용되는 기술임.
유니온 마운트를 지원하는 파일 시스템으로는 OverlayFS, Overlay2 등이 있음.
레이어는 아래에서부터 위로 쌓아올라감.
따라서 layer1이 바닥에 있고, 
그 위에 layer2가 올라가고 마지막으로 layer3가 위에 올라가서 비로소 container image가 됨.
그리고 이러한 이미지의 layer들은 모든 읽기 전용임. 
container에서 무슨 짓을 하더라도 절대로 이미지의 내용이 달라지지 않음.

해당 기술을 실생활 물건과 비교를 하자면 셀로판지와 굉장히 비슷함.

 

Union Mount와 Container image layer 관계

container image를 pull 받으면 layer들은 독립적으로 저장됨. 
그리고 container를 실행할 때는 이 layer들을 차례대로 쌓아올려서 특정 위치에 마운트를 함. 
기본적으로 이미지에 속하는 layer들은 읽기 전용이기 때문에 절대로 변하지 않음. 
그리고 그 위에 마지막으로 container 전용 쓰기 가능한 layer를 한 층 더 쌓고, 
container에서 일어나는 모든 변경 사항을 이 layer에 저장함.
 
 
 

High, Low level container runtime 이란

 

High Level Container Runtime

이미지 관리, 압축해제, Low Level Container Runtime으로 전달 등
조금 더 고수준 작업을 수행함.
보통은 Daemon 방식으로 동작하며 Remote API를 제공하기 때문에,
외부에서 컨테이너를 실행하거나 모니터링이 가능함.
 

Low Level Container Runtime

OCI Runtime으로 부르기도 하는 Low Level Container Runtime은 오로지 컨테이너를 실행하는 기능만 제공함.
컨테이너는 linux namespace와 cgroups를 통해 구현됨.
linux namespace는 시스템 리소스(Filesystem, Network 등)의 가상화하고,
cgroups는 컨테이너 안에서 사용할 수 있는 리소스의 양을 제한하는데 사용됨.
저수준 컨테이너 런타임은 이러한 namespace와 cgroup을 설정한 다음 해당 namespace 및 cgroup 내에서 명령을 실행.
 
lmctfy, rkt, railcar 등 다양한 Low Level Container Runtime이 존재했지만,
현재 OCI 표준 스펙을 지키면서 살아남은 건 많지않으며 runC가 사실상 시장을 지배했다고 보면 됨.
 
 

CRI(Container Runtime Interface)란?

CRI가 생기기 전에는 대표적인 container runtime tool인 Docker 또는 rkt가 kubelet에 직접 통합되어있었음.
하지만 문제가 발생함.
새로운 container runtime tool이 docker, rkt이외에도 많이 생겨남에 따라,
kubernetes도 다양한 container runtime tool을 지원했어야 했는데
container runtime tool 마다 kubelet을 수정하기는 무리가 있었음.
그래서 kubelet 수정없이 다양한 container runtime tool을 지원하기 위해
kubelet과 container runtime간의 인터페이스를 통일화할 수 있게 해주는 CRI(Container Runtime Interface)가 등장함.
CRI가 등장한 이후 container runtime tool은 해당 CRI 스펙에 맞춰서 CRI 컴포넌트를 구현하면
쉽게 kubelet과 호환될 수 있게되었음.
 
 

OCI란?

Container라는 개념이 생긴 초반에는 많은 IT 회사들이 Container기반의 제품을 출시했는데
Container 기술에 대한 특정한 규격이 없다 보니 각자 만든 Container 마다 규격이 모두 달랐음.
이러한 문제를 해결하기 위해 도커, Redhat, 구글, 마이크로소프트, IBM 등 주요 업체들이 
Container 포맷과 런타임에 대한 업계 표준을 만들었는데 이것이 바로 OCI(Open Container Initiative) 임.
이후 OCI는 Container 개발의 기준이 됨.
OCI가 명시되서 서로 다른 container runtime에서 생성한 image가 모든 container runtime에서 실행될 수 있음.
 
 

runC란?

 
runC는 OCI Runtime Spec(Open Container Initiative)을 준수하고 있는 저수준(Low-level) 컨테이너 런타임에 해당함.
최초 이름은 libcontainer이였는데 docker project에서 OCI에 기부되었고, 이후 이름이 runC가 됨.
즉 runC는 libcontainer의 리팩토링 구현체임.
docker는 1.8version 이전에는 LXC 드라이버를 기준으로 동작했었는데
이후 docker에서 자체 개발한 libcontainer Go library를 사용하면서
host kernel의 namesapce,  cgroups에 의존되지 않고
OS에서 독립되어 동작할 수 있음.
libcontainer에서 cgroups 관리 모듈로는 cgroupfs 또는 systemd 둘중 하나를 사용할 수 있지만
모든 모듈이 systemd를 사용하는 쪽으로 update되고있음.
 
 

 

반응형