"도커 없이 컨테이너를 만들기" 핸즈온 강의를 듣고 정리
실제 강의 주소
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 명령어로 확인 할 수 있음
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 |