Containerization에 대해서

칸중에칸·2025년 8월 6일

Kubernetes-Base

목록 보기
3/3

간단하게 말하자면
컨테이너는 가상 머신(VM)보다 가볍고 효율적
-> 컨테이너는 OS 레벨에서 가상화가 이루어지며, VM은 하드웨어 수준에서 가상화가 이뤄지기 때문
가상화 Layer가 다르다 이말

그렇다면 VM은 왜 필요했냐?

격리가 필요했기 때문

격리가 필요한 이유는?

하나의 머신에 여러가지 애플리케이션이 띄워졌을 경우 트러블 슈팅이 어렵기 때문

왜 어렵냐?

여러가지 dependency가 존재하게 될텐데 어느 애플리케이션 때문인지 알 수 없음
-> 환경을 통제할 수 있는 격리가 필요한 궁극적인 이유

Container가 나오기 전에 가상화가 있었지만

Linux Kernel의 발전으로 Container가 가능해졌음

cgroup

현재는 컨테이너에 할당된 CPU, 메모리, 네트워크 등을 제한하고 모니터링할 수 있는 기술이라고 알고있지만

사실 cgroup은 container라는 개념 이전에 개발된 기술

본래 목적 : 프로세스들 간에 CPU, 메모리, I/O 등을 조절할 수 있는 방법을 필요

이후 Container -> Process 가 된 것임
docker ps라는 명령어를 보면 알 수 있음

namespaces

각 프로세스가 자신만의 격리된 환경에서 실행되도록 하는 기술
pid, mnt, net, ipc, uts, user namespaces가 각각 프로세스 ID, 파일 시스템, 네트워크, 통신, 호스트 이름, 사용자 정보를 격리

본래 목적 : 프로세스 간 격리를 위해 개발 -> 서로 간섭 없이 독립적으로 실행될 수 있도록 지원

이후 Dockerfile들을 작성하고 Container들 간의 격리가 되는 것을 보면 알 수 있음

조금 더 자세한 설명

cgroup

계층적으로 그룹을 만들어서 각 그룹에 리소스를 할당

특정 애플리케이션이나 서비스가 사용하는 리소스를 제한하면서 다른 애플리케이션에 영향을 미치지 않게함

-> 이것 때문에 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

각 프로세스가 독립된 자원 환경을 가지도록 만들며, 리눅스 커널에서 주소 공간을 격리하는 방식

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에는 조금 더 많은 기능을 알고 싶다면 아래로


Union File System

여러 개의 파일 시스템 계층을 하나로 결합해서
각 계층을 독립적으로 읽기/쓰기할 수 있게 하는 기술

특히 Docker는 컨테이너 이미지를 계층화된 구조로 만들고 관리

각 계층은 Docker 이미지의 변경된 부분을 나타내며, UnionFS를 사용하여 이 계층들을 합쳐서 하나의 파일 시스템처럼 보이게 만듦

Dockerfile에서 이미지를 빌드할 때, 각 명령어(RUN, COPY, ADD)는 새로운 이미지 계층을 만드는데

FROM ubuntu:20.04
RUN apt-get update && apt-get install -y curl
COPY . /app

여러 계층을 독립적으로 관리하므로 이미지를 공유하거나 여러 컨테이너가 같은 계층을 사용 가능함

Chroot

https://man7.org/linux/man-pages/man2/chroot.2.html

프로세스의 루트 디렉토리를 변경해서
해당 프로세스가 변경된 루트 디렉토리 내에서만 작업하도록 제한하는 기능

프로세스는 시스템의 나머지 부분을 알 수 없고 접근할 수 없음
지정한 디렉토리 내에서만 파일을 접근 가능

chroot은 프로세스가 실행될 때 루트 파일 시스템을 다른 디렉토리로 변경
프로세스가 볼 수 있는 파일 시스템은 chroot로 설정된 디렉토리 안에 있는 파일만을 포함
-> Root 변경 및 파일 시스템 격리

-> 이게 발전해서 Namespace - UnionFS로 파일시스템 격리를 제공하게 됨

profile
부족한 일상에 글 한잔

0개의 댓글