간단하게 말하자면
컨테이너는 가상 머신(VM)보다 가볍고 효율적
-> 컨테이너는 OS 레벨에서 가상화가 이루어지며, VM은 하드웨어 수준에서 가상화가 이뤄지기 때문
가상화 Layer가 다르다 이말
격리가 필요했기 때문
하나의 머신에 여러가지 애플리케이션이 띄워졌을 경우 트러블 슈팅이 어렵기 때문
여러가지 dependency가 존재하게 될텐데 어느 애플리케이션 때문인지 알 수 없음
-> 환경을 통제할 수 있는 격리가 필요한 궁극적인 이유
Container가 나오기 전에 가상화가 있었지만
Linux Kernel의 발전으로 Container가 가능해졌음
현재는 컨테이너에 할당된 CPU, 메모리, 네트워크 등을 제한하고 모니터링할 수 있는 기술이라고 알고있지만
사실 cgroup은 container라는 개념 이전에 개발된 기술
본래 목적 : 프로세스들 간에 CPU, 메모리, I/O 등을 조절할 수 있는 방법을 필요
이후 Container -> Process 가 된 것임
docker ps라는 명령어를 보면 알 수 있음
각 프로세스가 자신만의 격리된 환경에서 실행되도록 하는 기술
pid, mnt, net, ipc, uts, user namespaces가 각각 프로세스 ID, 파일 시스템, 네트워크, 통신, 호스트 이름, 사용자 정보를 격리
본래 목적 : 프로세스 간 격리를 위해 개발 -> 서로 간섭 없이 독립적으로 실행될 수 있도록 지원
이후 Dockerfile들을 작성하고 Container들 간의 격리가 되는 것을 보면 알 수 있음
계층적으로 그룹을 만들어서 각 그룹에 리소스를 할당
특정 애플리케이션이나 서비스가 사용하는 리소스를 제한하면서 다른 애플리케이션에 영향을 미치지 않게함
-> 이것 때문에 OOM이 나는 것(제한을 받으니)
각 Cgroup은 cgroup이라는 디렉토리 구조에 정의
디렉토리 구조는 /sys/fs/cgroup에 위치
각 Cgroup은 특정 리소스 서브시스템을 관리
/sys/fs/cgroup
├── root
│ ├── cpu
│ ├── memory
│ ├── user
│ ├── group1
│ │ ├── cpu
│ │ └── memory
│ └── group2
│ ├── cpu
│ └── memory```
group1과 group2: 각기 다른 Cgroup 그룹으로, 상위 그룹인 root에서 리소스를 상속받고, 각 그룹에 대해 별도의 리소스 제한을 설정
이를 테면
CPU 사용량 제어: Cgroup은 cpu.cfs_quota_us와 같은 파라미터를 설정하여 프로세스가 사용할 수 있는 CPU 시간의 비율을 제한
https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu
메모리 사용량 제어: memory.limit_in_bytes 파일을 통해 Cgroup 내에서 사용 가능한 최대 메모리 크기를 설정
https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/resource_management_guide/sec-memory
각 프로세스가 독립된 자원 환경을 가지도록 만들며, 리눅스 커널에서 주소 공간을 격리하는 방식
Namespace는 여러 종류가 존재함
pid, mnt, net, ipc, uts, user namespaces
https://man7.org/linux/man-pages/man2/clone.2.html
Namespace는 리눅스 커널에서 clone() 시스템 호출을 기반으로 구현
-> clone()은 프로세스 생성 시, 기존의 네임스페이스를 복제하거나 새로운 네임스페이스를 설정함
CLONE_NEWCGROUP (since Linux 4.6)
CLONE_NEWIPC (since Linux 2.6.19)
CLONE_NEWNET (since Linux 2.6.24)
CLONE_NEWNS (since Linux 2.4.19)
CLONE_NEWPID (since Linux 2.6.24)
...
아주 많음

clone() 시스템 호출은 새로운 프로세스를 만들 때, 네임스페이스를 격리하는 핵심 메커니즘
https://linux-kernel-labs.github.io/refs/heads/master/lectures/processes.html
-> 여긴 보면 아주 좋을 것
struct task_struct {
struct thread_info thread_info; /* 0 8 */
volatile long int state; /* 8 4 */
void * stack; /* 12 4 */
...
/* --- cacheline 45 boundary (2880 bytes) --- */
struct thread_struct thread __attribute__((__aligned__(64))); /* 2880 4288 */
/* size: 7168, cachelines: 112, members: 155 */
/* sum members: 7148, holes: 2, sum holes: 12 */
/* sum bitfield members: 7 bits, bit holes: 2, sum bit holes: 57 bits */
/* paddings: 1, sum paddings: 2 */
/* forced alignments: 6, forced holes: 2, sum forced holes: 12 */
} __attribute__((__aligned__(64)));
리눅스 커널은 각 프로세스에 대해 어떤 네임스페이스에 속하는지 관리함
커널은 task_struct라는 구조체로 프로세스를 관리하며, 이 구조체 내에 각 네임스페이스의 정보를 저장
대표적인 기술은 2가지고
사실 Containerization에는 조금 더 많은 기능을 알고 싶다면 아래로
여러 개의 파일 시스템 계층을 하나로 결합해서
각 계층을 독립적으로 읽기/쓰기할 수 있게 하는 기술
특히 Docker는 컨테이너 이미지를 계층화된 구조로 만들고 관리
각 계층은 Docker 이미지의 변경된 부분을 나타내며, UnionFS를 사용하여 이 계층들을 합쳐서 하나의 파일 시스템처럼 보이게 만듦
Dockerfile에서 이미지를 빌드할 때, 각 명령어(RUN, COPY, ADD)는 새로운 이미지 계층을 만드는데
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y curl
COPY . /app
여러 계층을 독립적으로 관리하므로 이미지를 공유하거나 여러 컨테이너가 같은 계층을 사용 가능함
https://man7.org/linux/man-pages/man2/chroot.2.html
프로세스의 루트 디렉토리를 변경해서
해당 프로세스가 변경된 루트 디렉토리 내에서만 작업하도록 제한하는 기능
프로세스는 시스템의 나머지 부분을 알 수 없고 접근할 수 없음
지정한 디렉토리 내에서만 파일을 접근 가능
chroot은 프로세스가 실행될 때 루트 파일 시스템을 다른 디렉토리로 변경
프로세스가 볼 수 있는 파일 시스템은 chroot로 설정된 디렉토리 안에 있는 파일만을 포함
-> Root 변경 및 파일 시스템 격리
-> 이게 발전해서 Namespace - UnionFS로 파일시스템 격리를 제공하게 됨