본문 바로가기
Docker

Docker 없이 Container 만들기

by hotdog7778 2023. 12. 6.

"도커 없이 컨테이너를 만들기" 핸즈온 강의를 듣고 정리

 

실제 강의 주소

https://www.youtube.com/watch?v=mSD88FuST80

https://tech.kakaoenterprise.com/150

https://github.com/sam0kim/container-internal

https://tech.kakaoenterprise.com/154

 

 

 

본 포스팅은 위 핸즈온을 따라해보고 스스로 정리해보려고 작성한 글입니다.

링크를 타고 가면 잘 정리된 글과 강의를 보실수 있습니다.

 

 

 

1. 컨테이너 사용 목적

 - 앱전용 환경을 만들기 적합하기 때문, 전용 환경을 만들면 개발/운영단계에서 효율적으로 사용할 수 있음

 

왜 적합 ? 

 - 올인원 패키징 가능 (필요한 도구들을 패키징 할 수 있음)

 - 타 컨테이너와 격리 가능

 - 원하는 만큼 리소스 제어 가능

 

2. 컨테이너의 역사

 

 

도커 말고도 컨테이너 사용을 도와주는 도구들은 많다.

 

 

3. 키워드

 - 루트 디렉토리

파일 시스템에서의 경로 구조에서 최상위 디렉토리

 - 루트 파일시스템

운영 체제에서 사용되는 파일 시스템의 최상위 계층. 루트 디렉토리를 포함한다.

 - chroot (change root directory)

프로세스의 루트 디렉터리를 변경하는 리눅스 시스템콜/명령어. 이를 통해 프로세스는 변경된 루트 디렉토리를 시스템의 루트 디렉토리로 간주하게 되며, 해당 디렉토리를 기준으로 파일 경로를 해석

 - escape chroot

chroot를 사용해서 프로세스의 루트 디렉터리를 변경해도 호스트의 루트 디렉터리로 이동할 수 있는 방법이 있다. 치명적

 - 마운트

파일시스템을 루트파일시스템의 하위 디렉토리로 부착하는 시스템콜

 - 마운트포인트

파일시스템의 특정 위치를 가리키는 디렉토리로, 해당 디렉토리에 다른 파일시스템이나 저장 장치를 마운트할 수 있다.

 - pivot_root

현재 루트 파일 시스템을 다른 디렉토리로 변경하는 시스템 호출로, 주로 시스템 복구나 가상화 환경에서 사용됩니다.

 - 마운트 네임스페이스

프로세스 간에 파일 시스템 마운트 포인트를 격리시키는 기술로, 여러 개의 프로세스가 서로 독립된 파일 시스템을 가질 수 있게 한다.

 - 오버레이 파일시스템

여러 파일 시스템을 하나의 파일 시스템으로 합치는 기술로, 변경 사항은 새로운 레이어에 저장되며, 기존 레이어는 읽기 전용으로 유지

 - Lower 레이어 / Upper 레이어

Lower 레이어는 읽기 전용이고, Upper 레이어는 읽기/쓰기가 가능

 - 오버레이 마운트

여러 레이어를 조합하여 하나의 파일 시스템으로 마운트하는 작업

 - 네임스페이스(mount,uts,ipc,pid,Cgroups,net,user)

리눅스 커널이 제공하는 격리 기술로, 각 네임스페이스는 특정 리소스의 독립된 인스턴스를 제공

 - veth

가상 이더넷 장치로, 두 가지 끝을 가지고 있어서 하나의 끝은 호스트 네트워크 네임스페이스에, 다른 하나는 컨테이너 등의 다른 네트워크 네임스페이스에 속해 있다.

 - proc

리눅스에서 프로세스 정보와 시스템 정보를 제공하는 가상 파일 시스템입니다. /proc 디렉토리는 프로세스와 관련된 정보, 하드웨어, 커널 설정 등을 파일 형태로 제공

 - cpu.cfs_quota_us / cpu.cfs_period_us * 100

CPU 리소스를 제어하는데 사용되는 비율을 계산하는 공식 (ex. cpu사용률 30%로 제한)

cpu.cfs_quota_us

 

 

4. 패키징

오버레이 파일시스템을 통해 효율적인 이미지 패키징 구현.

 

문제점

우분투 컨테이너에 nginx 가 필요해서 다시 패키징..

또 mysql이 필요해서 다시 패키징..

또 tomcat이 필요해서 다시 패키징..

-> 이로인해 시간, 저장공간을 낭비하게 되고 이는 전부 비용이 증가했다고 볼 수 있다.

 

 

오버레이

이미지의 레이어를 나눠서 쌓고.. 쌓인것 들을 잘 합쳐서 마운트하여 사용하면 더 효율적으로 완성품을 만들 수 있다.

 

Lower 레이어가 이미지 레이어이다. 이 부분이 도커 레파지토리에서 받아오는 이미지 라고 할 수 있다.

Lower 레이어는 읽기전용이며, 따라서 원본이 보장된다.

반대로 Upper 레이어는 읽기/쓰기가 가능하다. 수정 가능한 레이어라는건 원본이 보장되지 않아도 된다는 것이며,

컨테이너 내부에서 변경이 발생할 수 있는데 이때, 이 변경사항에 대해 Upper레이어에 새롭게 올리는것이다.

 

Lower 레이어와 Upper 레이어를 중첩해서 Merged View로 보여준다.

이런 과정의 결과로 하나처럼 보이는 오버레이 파일 시스템을 만들어서 사용할 수 있게 됨.

 

 

 

 

 

5. 격리

마운트 네임스페이스로 격리된 환경을 만든 다음, 

pivot_root를 사용하여 해당 환경에 새로운 루트 디렉토리를 만든다. 

이를 통해 파일 시스템을 격리된 환경으로 변경하고, 

해당 환경에서는 독립적인 파일 시스템을 유지할 수 있다.

 

아래 여러 네임스페이스가 있지만, 결국에는 각각 역할에따라 무언가를 격리시키기 위해 존재한다.

 

 

6. 도커 없이 컨테이너 만들기 실습

 

구성도 및 실습 목표

 - 각각 RED, BLUE 컨테이너를 만들고 서로 통신, 각각 리소스 제한까지 실습

 

 

1. 이미지 준비

ls, mkdir, ping 등등 이 포함된 myroot, tools 디렉토리를 준비해준다. -> 결국 합칠꺼임

 

각각 도구들을 복사 붙여넣기 해주고 의존성 라이브러리까지 포함시켜주는 작업 뿐이다.

 

 

2. 컨테이너 네트워크 세팅

# 네트워크 네임스페이스 "RED"를 생성합니다.
ip netns add RED

# 네트워크 네임스페이스 "BLUE"를 생성합니다.
ip netns add BLUE

# "RED" 네임스페이스에 속하는 veth0와 "BLUE" 네임스페이스에 속하는 veth1을 생성하고 서로 연결합니다.
ip link add veth0 netns RED type veth peer name veth1 netns BLUE

# "RED" 네임스페이스에서 veth0 인터페이스에 IP 주소를 할당하고 활성화합니다.
ip netns exec RED ip addr add dev veth0 11.11.11.2/24
ip netns exec RED ip link set veth0 up

# "BLUE" 네임스페이스에서 veth1 인터페이스에 IP 주소를 할당하고 활성화합니다.
ip netns exec BLUE ip addr add dev veth1 11.11.11.3/24
ip netns exec BLUE ip link set veth1 up

 

 

3. Cgroups 으로 자원 제한 설정

# RED Cgroups 생성
mkdir /sys/fs/cgroup/cpu/red; # 생성한 디렉토리에 들어가보면 설정 파일들이 자동으로 생성되어있음!
mkdir /sys/fs/cgroup/memory/red;


# RED Cgroups 설정
echo 40000 > /sys/fs/cgroup/cpu/red/cpu.cfs_quota_us #CPU 40% 로 제한
echo 209715200 > /sys/fs/cgroup/memory/red/memory.limit_in_bytes #메모리 200 MB로 제한
echo 0 > /sys/fs/cgroup/memory/red/memory.swappiness #swap off
# swap off 를하면 메모리가 부족할때 프로세스를 종료시킴

 

cpu.cfs_period_us 는 10만

 

 

4. 네임스페이스를 사용해서 RED 격리

unshare -m -u -i -fp nsenter --net=/var/run/netns/RED /bin/sh

# (사용법) unshare [옵션] [프로그램 [arguments ... ]]
# (옵션)
# -m, --mount
# -u, --uts
# -i, --ipc
# -p, --pid
# -f, --fork
# -n, --net
# -U, --user

# nsenter는 네임스페이스에 들어가거나 해당 네임스페이스에서 명령을 실행하기 위한 도구
# nsenter에 전달된 --net 옵션은 특정한 네트워크 네임스페이스로 들어가기 위한 것
# 여기서는 /var/run/netns/RED 경로에 있는 "RED" 네임스페이스로 진입

 

 

5. RED Cgroups 할당

# cgroup "red"에 속하는 프로세스들의 PID를 지정하는 파일
echo "1" > /sys/fs/cgroup/cpu/red/cgroup.procs
echo "1" > /sys/fs/cgroup/memory/red/cgroup.procs

 

컨테이너 내부에서 pid가 1 인 프로세스의 cpu와 메모리를 제한하기 위해 "red" cgroup에 추가하는것

 

 

6. RED 컨테이너의 파일시스템을 만들기

 

 

오버레이 마운트 준비 (tools, myroot는 이미 이전에 세팅해놓음)

mkdir /redfs
mkdir /redfs/container
mkdir /redfs/work
mkdir /redfs/merge

 

오버레이 마운트 하기 (lower, upper, work dir 설정)

mount -t overlay overlay -o lowerdir=/tmp/tools:/tmp/myroot,upperdir=/redfs/container,workdir=/redfs/work /redfs/merge

 

 

결과

 

 

pivot_root를 사용하여 현재의 루트 파일 시스템을 변경

/redfs/merge 디렉토리가 새로운 루트 파일 시스템이 되며,

이전의 루트 파일 시스템은 /redfs/merge/put_old 디렉토리로 이동

mkdir -p /redfs/merge/put_old
cd /redfs/merge
pivot_root . put_old
cd / 
# 변경된 파일 시스템에서 루트 디렉토리로 이동.
# 이제 /redfs/merge가 새로운 루트가 되었으므로, 루트 디렉토리로 이동하면 실제로는 /redfs/merge로 이동한 것

mount -t proc proc /proc;
umount -l put_old;
rm -rf put_old;

 

 

결과.

 

 

7. 격리된 RED 컨테이너에서 프로세스 확인 + 호스트네임 변경

ps -ef
hostname RED

 

 

완성된 RED 컨테이너

 

 

8. 같은 방법으로 BLUE 컨테이너 생성 (똑같음)

 

9. 컨테이너 테스트

 

 - 통신 테스트 (ping)

# red -> blue
ping 11.11.11.3

# blue -> red
ping 11.11.11.2

 

 - 자원 테스트 (CPU / Memory)

# CPU core하나 테스트
stress -c 1

# memory 테스트
stress --vm 1 --vm-bytes 195M 
stress --vm 1 --vm-bytes 196M 
stress --vm 1 --vm-bytes 200M # 여기서는 제한에 걸려 프로세스는 실행되지 않음, 종료 로그를 호스트 시스템에서도 demsg 명령어로 확인 할 수 있음

 

CPU 제한이 30% 였을때 찍어놓은 사진

 

10. 도커 없이 컨테이너 만들기 실습 결과

 

 

 

 

 

※ USER 네임스페이스

컨테이너에서 root 유저는 실제로 호스트의 root 유저일까?

>> docker의 기본 설정에서는 YES

>> 하지만, 설정을 추가하면 NO

 

이렇게 컨테이너의 유저를 격리하기위해 USER 네임스페이스를 사용

 

 

일반계정으로 만든 컨테이너에 접속해서 id 명령어로 유저를 확인해보면 root 계정이다.
이 root 계정이 컨테이너에서만 root 계정이어야 하고 그걸 확인해보려 한다.
but, 네임스페이스로 확인해본 결과 컨테이너의 root는 host의 root 였다.

일반계정에 도커권한을 줘서 사용하면 (sudo usermod -aG docker '일반계정')
→ 일반계정에 루트권한을 주는것과 동일
→ docker로 컨테이너를 기동할때 시스템 주요볼륨들을 마운트해서 컨테이너를 기동할수 있게되는건데 보안상 취약

 

도커의 root 사용

 - 패키지 인스톨이 쉽다.(권한이 만땅이니까)

 - 시스템 리소스 이용에 제약이 없다.(권한이 만땅이거든)

 

USER 네임스페이스 격리해보기

unshare -U --map-root-user /bin/sh

리맵한다?

도커로 실행한 결과와는 다르게

컨테이너의 root계정은 컨테이너에서만 root 계정인것이고 호스트에서는 일반계정이었다.

유저네임스페이스의 inode 값도 달랐다.

즉 컨테이너 안에서만 루트로 보였다.

 

 

 

 

 

 

'Docker' 카테고리의 다른 글

Docker Engine  (0) 2023.11.29
Docker, Container 개요  (0) 2023.11.29