컨테이너 격리

Gyullbb·2024년 8월 31일
0

K8S

목록 보기
3/13

1. chroot를 이용한 프로세스 격리

https://velog.io/@_gyullbb/1-1.-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B2%A9%EB%A6%AC

2. pivot_root + mnt를 이용한 프로세스 격리

https://velog.io/@_gyullbb/1-2.-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B2%A9%EB%A6%ACpivotroot

3. overlay 파일시스템 - 이미지 중복 문제 해결

필요한 환경을 이미지로 만들 때, 환경이 조금만 달라질 때마다 모든 환경별로 이미지를 만든다면 저장공간 부족, 배포 및 통신 속도 지연, 관리 포인트 증가, 보안 위험도 증가 등의 문제가 야기된다.

이러한 문제를 해결하기 위해 컨테이너에서는 layer개념을 이용하여 overlay 파일시스템을 통해 중복 문제를 해결한다.

overlay 파일 시스템의 구조이다.

  • Lower Dir(RO) : 읽기 전용 레이어로 이미지 저장소로부터 내려받는 "이미지"에 해당
  • Upper Dir(RW) : 새로운 파일에 대한 쓰기 및 Lower Dir 파일의 변경을 처리, 이때 Lower Dir 파일 변경 시, Upper Dir로 파일을 복사 한 후 변경을 처리하는 CoW(Copy-On-Write) 방식을 사용
  • Merged View : overlay 파일시스템이 마운트 되는 디렉터리로, Lower Dir과 Upper Dir의 통합된 뷰를 제공

각 특징들을 예제를 통해 확인해본다.

3-1. lowdir, upperdir, merged view 구성

lowdir1 구성 (/bin/{bash, ls, mkdir, mount, ps, sh})

bash, ls, mkdir, mount, ps, sh 명령어가 작동하도록 구성하고, 추가로 originalfile1이라는 파일을 생성한다.

# ldd /bin/bash
	linux-vdso.so.1 (0x00007ffe8f5ed000)
	libtinfo.so.6 => /lib64/libtinfo.so.6 (0x00007f56fd30e000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f56fd000000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f56fd49d000)
# ldd /bin/ls
	linux-vdso.so.1 (0x00007ffc1f5fa000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f4a8c2b3000)
	libcap.so.2 => /lib64/libcap.so.2 (0x00007f4a8c2a9000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f4a8c000000)
	libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007f4a8c20d000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4a8c30a000)
# ldd /bin/mkdir
	linux-vdso.so.1 (0x00007ffe93d6d000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f949bc09000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f949b800000)
	libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007f949bb6d000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f949bc4e000)
# ldd /bin/mount
	linux-vdso.so.1 (0x00007ffe91d3f000)
	libmount.so.1 => /lib64/libmount.so.1 (0x00007f368692a000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f36868fd000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f3686600000)
	libblkid.so.1 => /lib64/libblkid.so.1 (0x00007f36868c5000)
	libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007f3686829000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f3686982000)
# ldd /bin/ps
	linux-vdso.so.1 (0x00007ffff37ae000)
	libprocps.so.8 => /lib64/libprocps.so.8 (0x00007fd08e8ac000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fd08e600000)
	libsystemd.so.0 => /lib64/libsystemd.so.0 (0x00007fd08e523000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fd08e930000)
	libcap.so.2 => /lib64/libcap.so.2 (0x00007fd08e8a2000)
	libgcrypt.so.20 => /lib64/libgcrypt.so.20 (0x00007fd08e3ea000)
	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007fd08e876000)
	libzstd.so.1 => /lib64/libzstd.so.1 (0x00007fd08e313000)
	liblz4.so.1 => /lib64/liblz4.so.1 (0x00007fd08e850000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fd08e835000)
	libgpg-error.so.0 => /lib64/libgpg-error.so.0 (0x00007fd08e80f000)
# ldd /bin/sh
	linux-vdso.so.1 (0x00007ffc153f6000)
	libtinfo.so.6 => /lib64/libtinfo.so.6 (0x00007f04ba5a4000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f04ba200000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f04ba733000)

# mkdir -p /tmp/lowdir1
# mkdir -p /tmp/lowdir1/{bin,lib64}
# cp /lib64/{libtinfo.so.6,libc.so.6,ld-linux-x86-64.so.2,libselinux.so.1,libcap.so.2,libpcre2-8.so.0,libmount.so.1,libblkid.so.1,libprocps.so.8,libsystemd.so.0,libgcrypt.so.20,liblzma.so.5,libzstd.so.1,liblz4.so.1,libgcc_s.so.1,libgpg-error.so.0} /tmp/lowdir1/lib64/
# cp /bin/{bash,ls,mkdir,mount,ps,sh} /tmp/lowdir1/bin/
# mkdir -p /tmp/lowdir1/proc
# mount -t proc proc /tmp/lowdir1/proc
# touch /tmp/lowdir1/originalfile1

# tree -L 2 /tmp/lowdir1/
/tmp/lowdir1/
├── bin
│   ├── bash
│   ├── ls
│   ├── mkdir
│   ├── mount
│   ├── ps
│   └── sh
├── lib64
│   ├── ld-linux-x86-64.so.2
│   ├── libblkid.so.1
│   ├── libc.so.6
│   ├── libcap.so.2
│   ├── libgcc_s.so.1
│   ├── libgcrypt.so.20
│   ├── libgpg-error.so.0
│   ├── liblz4.so.1
│   ├── liblzma.so.5
│   ├── libmount.so.1
│   ├── libpcre2-8.so.0
│   ├── libprocps.so.8
│   ├── libselinux.so.1
│   ├── libsystemd.so.0
│   ├── libtinfo.so.6
│   └── libzstd.so.1
├── originalfile1
└── proc
    ├── 1

lowdir2 구성 (/bin/rm)

# ldd /bin/rm
	linux-vdso.so.1 (0x00007ffce975e000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fa4d1400000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fa4d1808000)
# mkdir -p /tmp/lowdir2
# mkdir -p /tmp/lowdir2/{bin,lib64}
# cp /lib64/{libc.so.6,ld-linux-x86-64.so.2} /tmp/lowdir2/lib64/
# cp /bin/rm /tmp/lowdir2/bin/

# tree -L 2 /tmp/lowdir2
/tmp/lowdir2
├── bin
│   └── rm
└── lib64
    ├── ld-linux-x86-64.so.2
    └── libc.so.6

upperdir & merged view 구성

mount 옵션을 통해 lowerdir, upperdir, workdir을 지정한 후 특정 폴더에 merge 할 수 있다.


lowerdir 지정 시 이미지 레이어를 구분하여 적을 수 있는데, 이 때 레이어를 위에서 아래 순서대로 콜론(:)으로 구분하여 작성한다.


mount 이후 lowdir1, lowdir2의 파일시스템이 합쳐져 rootfs의 merge에 보이는 것을 확인할 수 있다.

# mkdir -p /tmp/rootfs/{container,work,merge}
# tree /tmp/rootfs
/tmp/rootfs
├── container
├── merge
└── work
3 directories, 0 files

# mount -t overlay overlay -o lowerdir=lowdir2:lowdir1,upperdir=rootfs/container,workdir=rootfs/work rootfs/merge
# tree /tmp/rootfs
/tmp/rootfs
├── container
├── merge
│   ├── bin
│   │   ├── bash
│   │   ├── ls
│   │   ├── mkdir
│   │   ├── mount
│   │   ├── ps
│   │   ├── rm
│   │   └── sh
│   ├── lib64
│   │   ├── ld-linux-x86-64.so.2
│   │   ├── libblkid.so.1
│   │   ├── libc.so.6
│   │   ├── libcap.so.2
│   │   ├── libgcc_s.so.1
│   │   ├── libgcrypt.so.20
│   │   ├── libgpg-error.so.0
│   │   ├── liblz4.so.1
│   │   ├── liblzma.so.5
│   │   ├── libmount.so.1
│   │   ├── libpcre2-8.so.0
│   │   ├── libprocps.so.8
│   │   ├── libselinux.so.1
│   │   ├── libsystemd.so.0
│   │   ├── libtinfo.so.6
│   │   └── libzstd.so.1
│   ├── originalfile1
│   └── proc
└── work
    └── work

3-2. overlay 파일 시스템 구조 특성 확인

merged view 파일 변경

lowdir1으로 인해 merge에 보이는 originalfile1을 수정해보자.


upperdir에 originalfile1이 생긴 것을 확인할 수 있다.

merge의 originalfile1은 upperdir의 originalfile1(lowdir1의 originalfile1 복제본)을 바라보기 때문에, 이제 lowdir1에 있는 originalfile1을 수정해도 merge에서 보이는 originalfile1은 변경되지 않고, upperdir의 originalfile1을 수정해야 merge의 originalfile1이 수정되는 것을 확인할 수 있다.

# echo "change1" > /tmp/rootfs/merge/originalfile1
# tree /tmp/rootfs/
/tmp/rootfs/
├── container
│   └── originalfile1
├── merge
│   ├── bin
│   │   ├── bash
│   │   ├── ls
│   │   ├── mkdir
│   │   ├── mount
│   │   ├── ps
│   │   ├── rm
│   │   └── sh
│   ├── lib64
│   │   ├── ld-linux-x86-64.so.2
│   │   ├── libblkid.so.1
│   │   ├── libc.so.6
│   │   ├── libcap.so.2
│   │   ├── libgcc_s.so.1
│   │   ├── libgcrypt.so.20
│   │   ├── libgpg-error.so.0
│   │   ├── liblz4.so.1
│   │   ├── liblzma.so.5
│   │   ├── libmount.so.1
│   │   ├── libpcre2-8.so.0
│   │   ├── libprocps.so.8
│   │   ├── libselinux.so.1
│   │   ├── libsystemd.so.0
│   │   ├── libtinfo.so.6
│   │   └── libzstd.so.1
│   ├── originalfile1
│   └── proc
└── work
    └── work

7 directories, 25 files
# cat /tmp/rootfs/merge/originalfile1
change1

# echo "change2" > /tmp/lowdir1/originalfile1
# cat /tmp/rootfs/merge/originalfile1
change1

# echo "chnage3" > /tmp/rootfs/container/originalfile1
# cat /tmp/rootfs/merge/originalfile1
chnage3

merged view 파일 삭제

merge에 있는 originalfile1을 삭제하면 merged view에서는 해당 파일이 삭제되고, upperdir인 container폴더 하위의 originalfile1에 마킹 표기가 되는 것을 확인할 수 있다.


실제 lowdir1의 originalfile1을 삭제하는 것이 아니라, 카피본인 upperdir 즉, container폴더 하위의 originalfile1에 삭제마킹이 되면서 merged view에서는 보이지 않게 된다.

# rm /tmp/rootfs/merge/originalfile1
rm: remove regular file '/tmp/rootfs/merge/originalfile1'? y

# tree /tmp/rootfs
/tmp/rootfs
├── container
│   └── originalfile1
├── merge
│   ├── bin
│   │   ├── bash
│   │   ├── ls
│   │   ├── mkdir
│   │   ├── mount
│   │   ├── ps
│   │   ├── rm
│   │   └── sh
│   ├── lib64
│   │   ├── ld-linux-x86-64.so.2
│   │   ├── libblkid.so.1
│   │   ├── libc.so.6
│   │   ├── libcap.so.2
│   │   ├── libgcc_s.so.1
│   │   ├── libgcrypt.so.20
│   │   ├── libgpg-error.so.0
│   │   ├── liblz4.so.1
│   │   ├── liblzma.so.5
│   │   ├── libmount.so.1
│   │   ├── libpcre2-8.so.0
│   │   ├── libprocps.so.8
│   │   ├── libselinux.so.1
│   │   ├── libsystemd.so.0
│   │   ├── libtinfo.so.6
│   │   └── libzstd.so.1
│   └── proc
└── work
    └── work
        └── #5

# tree -L 1 /tmp/lowdir1
/tmp/lowdir1
├── bin
├── lib64
├── originalfile1
└── proc

3 directories, 1 file

# cat /tmp/lowdir1/originalfile1
change2

lowdir 파일 변경

이 테스트에서 upperdir에 반영이 되지 않은 lowdir의 파일을 수정하면 merged view에서도 수정된 상태로 표시되는 것을 확인할 수 있다.


lowdir은 수정이 되지 않는 것이 원칙이나, 해당 테스트에서는 lowdir이 호스트의 root 파일시스템에 속해있기 때문에, 유저가 root 파일시스템을 직접 수정하면 merged view에서는 lowdir의 수정된 파일을 출력하게 된다.

# rm container/originalfile1
rm: remove character special file 'container/originalfile1'? y
# umount /tmp/rootfs/merge/

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

# cat /tmp/rootfs/merge/originalfile1
change2

# echo "change4" > /tmp/lowdir1/originalfile1
# cat /tmp/rootfs/merge/originalfile1
change4

4. Linux namespace 종류 별 격리 테스트

Linux에서 사용할 수 있는 namespace 종류는 아래와 같다.
이 중 time namespace는 리눅스 커널 5.6 이상 버전 부터 사용할 수 있다.

NamespaceFlagPageIsolates
CgroupCLONE_NEWCGROUPcgroup_namespaces(7)Cgroup root directory
IPCCLONE_NEWIPCipc_namespaces(7)System V IPC, POSIX message queues
NetworkCLONE_NEWNETnetwork_namespaces(7)Network devices, stacks, ports, etc.
MountCLONE_NEWNSmount_namespaces(7)Mount points
PIDCLONE_NEWPIDpid_namespaces(7)Process IDs
TimeCLONE_NEWTIMEtime_namespaces(7)Boot and monotonic clocks
UserCLONE_NEWUSERuser_namespaces(7)User and group IDs
UTSCLONE_NEWUTSuts_namespaces(7)Hostname and NIS domain name

예시)

# lsns -p $$

        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      126   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531835 cgroup    126   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531836 pid       126   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531837 user      126   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531838 uts       123   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531839 ipc       126   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531840 net       126   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531841 mnt       116   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31

namespace API 중 unshare system call을 이용하여 프로세스를 새로운 namespace로 격리시킬 수 있다.
각 namespace 별로 격리 테스트를 진행한다.

4-1. mnt

프로세스의 파일 시스템 마운트 지점을 격리한다.

unshare를 통한 mnt ns 격리 확인

터미널1

# lsns -t mnt -p 1
        NS TYPE NPROCS PID USER COMMAND
4026531841 mnt     123   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
# lsns -t mnt -p $$
        NS TYPE NPROCS PID USER COMMAND
4026531841 mnt     123   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31

# unshare -m
# lsns -p 1
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      135   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531835 cgroup    135   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531836 pid       135   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531837 user      135   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531838 uts       132   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531839 ipc       135   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531840 net       134   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531841 mnt       122   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
# lsns -p $$
        NS TYPE   NPROCS   PID USER COMMAND
4026531834 time      135     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531835 cgroup    135     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531836 pid       135     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531837 user      135     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531838 uts       132     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531839 ipc       135     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531840 net       134     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026532223 mnt         2  2677 root -bash

터미널2
mnt namespace 분리 확인

# lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      134   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531835 cgroup    134   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531836 pid       134   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531837 user      134   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531838 uts       131   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531839 ipc       134   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531840 net       134   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531841 mnt       123   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31

# ps -ef | grep 2677
root        2677    2604  0 03:50 pts/0    00:00:00 -bash
root        2705    2646  0 03:53 pts/1    00:00:00 grep --color=auto 2677

4-2. uts

프로세스의 호스트 이름과 NIS(Network Information Service)도메인 네임을 격리한다.
*NIS 도메인 네임 : 호스트명이나 사용자명등 동일한 네트워크 자원을 공유할 수 있는 네트워크 영역의 이름

터미널1

# unshare -u
# lsns -p 1
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      132   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531835 cgroup    132   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531836 pid       132   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531837 user      132   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531838 uts       127   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531839 ipc       132   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531840 net       131   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531841 mnt       121   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
# lsns -p $$
        NS TYPE   NPROCS   PID USER COMMAND
4026531834 time      132     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531835 cgroup    132     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531836 pid       132     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531837 user      132     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531839 ipc       132     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531840 net       131     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026531841 mnt       121     1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 31
4026532223 uts         2  2747 root -bash

# hostname bgr
# hostname
bgr

# nisdomainname
nisdomainname: Local domain name not set

# nisdomainname superbgr
# nisdomainname
superbgr

터미널2

# hostname
bgrprac.novalocal

# nisdomainname
nisdomainname: Local domain name not set

4-3. ipc

ipc란 독립된 구조의 프로세스가 서로 자원을 공유할 때 프로세스 간에 발생하는 통신으로, 이 통신 자원을 분리하여 관리할 수 있다.
프로세스 간 통신은 메세지 전달 방식과 공유 메모리 방식 두 가지 방식으로 이뤄진다.

메세지 전달 방식

  • 커널에 있는 공유 공간 속에 데이터를 주고 받는 방식
  • 메시지를 전달할 때마다 커널, 즉 운영체제가 개입하다보니 커널 의존성이 높아지고 속도가 낮아질 수 있다.
  • 단 커널이 메세지 전달을 관리하기 때문에 동기화 문제가 발생하지 않는다.
  • 프로세스가 커널에 있는 메시지 큐 공간으로 자원을 전달하면 직접 통신, 간접 통신 두가지 방법으로 다른 프로세스에게 자원이 전달된다.
    • 직접 통신 : 프로세스 A가 자원 a를 커널의 메세지 큐로 전달 -> 커널에서 자원 a를 프로세스 B로 전달 (시스템 콜 : msgsnd(), msgrcv())
    • 간접 통신 : 프로세스 A가 자원 a를 커널의 메세지 큐로 전달 -> 커널에서 프로세스 B에게 자원을 읽어가라고 alert 전달

공유 메모리 방식

  • 프로세스들이 주소 공간의 일부를 공유하는 방식
  • 운영체제에서 공유 메모리를 사용하는 시스템 콜을 지원하여 서로 다른 프로세스 들이 주소 공간 중 일부를 공유할 수 있다.
  • 프로세스가 커널에게 시스템 콜을 통해 공유메모리 할당을 요청 -> 해당 프로세스에 메모리 공간을 할당 -> 타 프로세스가 해당 메모리 영역에 접근 가능
  • 프로세스가 중개자 없이 바로 다른 프로세스의 공유메모리에 접근할 수 있기 때문에 속도가 빠르다.
  • 단 커널이 직접 모든 것을 관리하지 않기 대문에 데이터 간 일관성 문제 즉, 동기화 문제가 발생할 수 있다. -> 세마포어 필요

docker의 ipc mode

docker의 ipc mode에는 private, sharable, host가 있다.
sharable mode를 통하여 컨테이너 프로세스에 공유 메모리를 할당 및 공유할 수 있다.
host mode를 통하여 컨테이너가 호스트의 IPC리소스에 직접 액세스할 수 있다. 단, 이 옵션의 경우 컨테이너에 할당된 리소스 보다 더 많은 리소스를 사용할 수 있게 설정될 수 있기 때문에 관리적 측면으로 좋지 않다.

docker의 ipc mode 중 shareable, host mode를 통하여 프로세스 격리를 확인할 수 있다.

(1) ipc mode : shareable

터미널1
ipcmk로 도커 컨테이너 내에서 공유메모리를 생성한다.

# docker run --rm --name test1 --ipc=shareable -it ubuntu bash
root@8ada959737a1:/# ipcs -m

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

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

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

root@8ada959737a1:/# lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time        2   1 root bash
4026531835 cgroup      2   1 root bash
4026531837 user        2   1 root bash
4026532719 mnt         2   1 root bash
4026532720 uts         2   1 root bash
4026532721 ipc         2   1 root bash
4026532722 pid         2   1 root bash
4026532724 net         2   1 root bash
root@8ada959737a1:/# lsns -t ipc -p $$
        NS TYPE NPROCS PID USER COMMAND
4026532721 ipc       2   1 root bash

터미널2
docker ipc shareable mode를 통해 test1 컨테이너의 공유 메모리에 접근 가능한 것을 확인할 수 있다.


이 때 두 컨테이너가 동일한 ipc 네임스페이스에 속해있다.

# docker run --rm --name test2 --ipc=container:test1 -it ubuntu bash
root@055126df0f88:/# ipcs -m

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

root@055126df0f88:/# lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time        2   1 root bash
4026531835 cgroup      2   1 root bash
4026531837 user        2   1 root bash
4026532721 ipc         2   1 root bash
4026532794 mnt         2   1 root bash
4026532795 uts         2   1 root bash
4026532796 pid         2   1 root bash
4026532798 net         2   1 root bash
root@055126df0f88:/# lsns -t ipc -p $$
        NS TYPE NPROCS PID USER COMMAND
4026532721 ipc       2   1 root bash

(2) ipc mode : host

터미널1

# docker run --rm --name test3 --ipc=host -it ubuntu bash
root@11d0e13b0220:/# ipcs -m

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

터미널2
host VM에서 수행

# ipcmk -M 2000
Shared memory id: 4
# ipcs -m

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

# lsns -t ipc -p $$
        NS TYPE NPROCS PID USER COMMAND
4026531839 ipc     189   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 16

root@11d0e13b0220:/# ipcs -m

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

root@11d0e13b0220:/# lsns -t ipc -p $$
        NS TYPE NPROCS PID USER COMMAND
4026531839 ipc       2   1 root bash

터미널1
host VM의 ipc네임스페이스와 별개의 네임스페이스임에도 test3 컨테이너에서 host VM의 메모리에 접근가능한 것 확인 가능.

root@11d0e13b0220:/# ipcs -m

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

root@11d0e13b0220:/# lsns -t ipc -p $$
        NS TYPE NPROCS PID USER COMMAND
4026531839 ipc       2   1 root bash

4-4. pid

unshare를 통해 pid를 격리할 수 있다.

터미널1

# echo $$
3156706

# unshare -fp --mount-proc /bin/sh
sh-4.4# echo $$
1
sh-4.4# lsns -t pid -p 1
        NS TYPE NPROCS PID USER COMMAND
4026532718 pid       2   1 root /bin/sh

터미널2
host VM에서 격리된 pid 네임스페이스를 조회할 수 있다.

# lsns -t pid -p 1
        NS TYPE NPROCS PID USER COMMAND
4026531836 pid     188   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 16

# ps aux | grep '/bin/sh'
root        2566  0.0  0.0   2316  1720 ?        Ss   Aug28   0:00 /bin/sh /usr/bin/nginx-proxy CP_HOSTS=172.21.27.183,172.21.26.91,172.21.27.194
root     3159289  0.0  0.0 217084   932 pts/0    S    10:22   0:00 unshare -fp --mount-proc /bin/sh
root     3159290  0.0  0.0 224784  3636 pts/0    S+   10:22   0:00 /bin/sh
root     3160579  0.0  0.0 221912  1136 pts/1    S+   10:24   0:00 grep --color=auto /bin/sh

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

호스트 VM에서 격리 프로세스 종료

터미널1

# unshare -fp --mount-proc /bin/sh
sh-4.4# echo $$
1
sh-4.4# lsns -t pid -p 1
        NS TYPE NPROCS PID USER COMMAND
4026532718 pid       2   1 root /bin/sh
sh-4.4# sleep 10000
Killed
sh-4.4# Killed

터미널2

# kill -SIGKILL $(pgrep sleep)

# ps aux | grep '/bin/sh'
root        2566  0.0  0.0   2316  1720 ?        Ss   Aug28   0:00 /bin/sh /usr/bin/nginx-proxy CP_HOSTS=172.21.27.183,172.21.26.91,172.21.27.194
root     3159289  0.0  0.0 217084   932 pts/0    S    10:22   0:00 unshare -fp --mount-proc /bin/sh
root     3159290  0.0  0.0 224784  3784 pts/0    S+   10:22   0:00 /bin/sh
root     3165529  0.0  0.0 221912  1176 pts/1    S+   10:32   0:00 grep --color=auto /bin/sh

# kill -SIGKILL 3159290

4-5. user

사용자 및 그룹 ID 매핑의 격리를 제공하는 Linux 커널 기능으로, 각 프로세스가 숫자적으로는 동일한 사용자 및 그룹ID를 사용하더라도 서로 다른 권한을 가질 수 있다.

터미널1

# unshare -U --map-root-user /bin/sh
sh-4.4# whoami
root

sh-4.4# id
uid=0(root) gid=0(root) groups=0(root)

sh-4.4# readlink /proc/$$/ns/user
user:[4026532717]

sh-4.4# lsns -p $$
        NS TYPE   NPROCS     PID USER COMMAND
4026531835 cgroup      2 3176595 root /bin/sh
4026531836 pid         2 3176595 root /bin/sh
4026531838 uts         2 3176595 root /bin/sh
4026531839 ipc         2 3176595 root /bin/sh
4026531840 mnt         2 3176595 root /bin/sh
4026531992 net         2 3176595 root /bin/sh
4026532717 user        2 3176595 root /bin/sh

터미널2
unshare로 네임스페이스가 격리된 것을 host VM에서 확인할 수 있다.

# readlink /proc/$$/ns/user
user:[4026531837]

# lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531835 cgroup    237   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 16
4026531836 pid       188   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 16
4026531837 user      236   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 16
4026531838 uts       200   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 16
4026531839 ipc       186   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 16
4026531840 mnt       180   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 16
4026531992 net       214   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 16

# ps -ef | grep "/bin/sh"
root     3176595 3176498  0 10:49 pts/0    00:00:00 /bin/sh
root     3177167 3119323  0 10:50 pts/1    00:00:00 grep --color=auto /bin/sh

# lsns -t user -p 3176595
        NS TYPE  NPROCS     PID USER COMMAND
4026532717 user       1 3176595 root /bin/sh

user namespace in k8s 활용 사례

문제 상황

Host Filesystem에서 NFS 서버와 클라이언트 간 문제로 인해 Stale File Handle Error 발생.
telegraf daemonset에서 'df -h 2>&1 | grep -i 'stale file handle' | wc -l' 이 1 이상일 때 alert가 울리도록 구성 필요


telegraf configmap의 input.exec 활용 & telegraf daemonset에서 host root 파일시스템을 RO로 mount 구성.


telegraf의 기본 구성의 경우 telegraf user와 group으로 실행되기 때문에 df -h 명령어를 시행하였을 때, telegraf user에는 /var/lib/kubelet 하위 접근 권한이 없어 Permission denied와 함께 정상적으로 에러 검출 실패


telegraf 공식 github Common Issues : https://github.com/influxdata/telegraf/blob/master/plugins/inputs/exec/README.md#common-issues


문제 상황 확인

# docker exec --user telegraf -it telegraf-ds sh

/ $ df -h
df: /hostfs/var/lib/kubelet/pods/...: Permission denied
df: /hostfs/var/lib/kubelet/pods/...: Permission denied
df: /hostfs/var/lib/kubelet/pods/...: Permission denied

/ $ id
uid=100(telegraf) gid=101(telegraf) groups=101(telegraf)

해결 방법

securityContext fsGroup에 root 그룹을 지정하여 컨테이너 user(telegraf)가 telegraf 그룹과 root 그룹에 속할 수 있도록 설정.


컨테이너를 telegraf 유저(100)로 실행하고 최소한의 권한만 허용하게 하여 보안적 위험도를 낮춤.


사용자의 UID가 100으로, root 그룹에 속해 있지만, 루트 권한 자체를 가지는 것이 아님. 접근하려는 파일의 그룹 소유자가 root(GID 0)이고, 해당 파일의 그룹에 대한 권한이 부여되어 있다면, telegraf user는 root 그룹의 권한을 통해 root 파일에 접근 가능.

spec: 
  containers: 
  ... 
    securityContext: 
      allowPrivilegeEscalation: false 
      privileged: false 
      readOnlyRootFilesystem: false 
      runAsNonRoot: false 
      runAsUser: 100 
      capabilities: {} 
  dnsPolicy: ClusterFirst 
  restartPolicy: Always 
  securityContext: 
    fsGroup: 0
    ...

해결 방법 확인

# docker exec --user telegraf -it telegraf-ds sh

/ $ df -h 2>&1 | grep -i 'stale' 
df: ‘/var/lib/kubelet/pods/c5fa7996-d8b0-4340-a580-96620af88aad/volume-subpaths/pvc-3dfe4830-bb98-43ef-b34a-8472591dc73e/vauth/1’: Stale file handle df: ‘/var/lib/kubelet/pods/c5fa7996-d8b0-4340-a580-96620af88aad/volume-subpaths/pvc-3dfe4830-bb98-43ef-b34a-8472591dc73e/auth-sidecar/1’: Stale file handle 
...

/ $ id
uid=100(telegraf) gid=101(telegraf) groups=0(root),101(telegraf)

4-6. time

time 네임스페이스는 리눅스 커널 5.6이상 버전에서 사용가능한 네임스페이스이다.
https://www.man7.org/linux/man-pages/man7/time_namespaces.7.html

터미널1

# which uptime
/bin/uptime

# ldd /bin/uptime

# uptime
 13:00:44 up 1 day,  5:28,  2 users,  load average: 0.00, 0.00, 0.00

# unshare --mount-proc -T --boottime=86400 --root=/tmp/myroot/
-bash-5.1# uptime
 13:01:00 up 2 days,  5:29,  0 users,  load average: 0.00, 0.00, 0.00

-bash-5.1# lsns -p $$
        NS TYPE   NPROCS   PID USER COMMAND
4026531835 cgroup    131     1 0    /usr/lib/systemd/systemd --switched-root --system --deserial
4026531836 pid       131     1 0    /usr/lib/systemd/systemd --switched-root --system --deserial
4026531837 user      131     1 0    /usr/lib/systemd/systemd --switched-root --system --deserial
4026531838 uts       128     1 0    /usr/lib/systemd/systemd --switched-root --system --deserial
4026531839 ipc       131     1 0    /usr/lib/systemd/systemd --switched-root --system --deserial
4026531840 net       131     1 0    /usr/lib/systemd/systemd --switched-root --system --deserial
4026532223 mnt         2  3291 0    -bash
4026532224 time        2  3291 0    -bash

터미널2

$ uptime
 13:01:04 up 1 day,  5:29,  2 users,  load average: 0.00, 0.00, 0.00

4-7. cgroup

cgroupv2를 활용하여 CPU, MEM, DISK IO 자원 제한을 설정할 수 있다.

4-7-1. CPU

CPU제한을 주지 않은 상황과 제한을 준 상황에서의 프로세스 CPU 사용률을 확인한다.

CPU제한 X

# sudo mount -t cgroup2 none /sys/fs/cgroup
mount: /sys/fs/cgroup: none already mounted on /run/credentials/systemd-tmpfiles-setup-dev.service.

# mkdir /sys/fs/cgroup/parent

# echo "+cpu" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
+cpu

# echo "+cpu" | sudo tee /sys/fs/cgroup/parent/cgroup.subtree_control
+cpu

# mkdir /sys/fs/cgroup/parent/child

# echo $$ > /sys/fs/cgroup/parent/child/cgroup.procs

# cat /proc/$$/cgroup
0::/parent/child

# stress -c 1
stress: info: [3350] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd

top - 13:16:36 up 1 day,  5:44,  2 users,  load average: 0.08, 0.02, 0.01
Tasks: 129 total,   2 running, 127 sleeping,   0 stopped,   0 zombie
%Cpu(s): 25.0 us,  0.1 sy,  0.0 ni, 74.7 id,  0.0 wa,  0.1 hi,  0.1 si,  0.0 st
MiB Mem :  15737.8 total,  15247.5 free,    458.4 used,    322.9 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.  15279.4 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   3351 root      20   0    3516    112      0 R  99.3   0.0   0:05.73 stress

CPU제한 O

# echo 100000 1000000 > /sys/fs/cgroup/parent/cpu.max

# echo $$ > /sys/fs/cgroup/parent/child/cgroup.procs

# stress -c 1
stress: info: [3354] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd

top - 13:19:24 up 1 day,  5:47,  2 users,  load average: 0.01, 0.02, 0.00
Tasks: 130 total,   2 running, 128 sleeping,   0 stopped,   0 zombie
%Cpu(s):  2.6 us,  0.0 sy,  0.0 ni, 97.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :  15737.8 total,  15247.5 free,    458.4 used,    322.9 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.  15279.4 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   3355 root      20   0    3516    112      0 R  10.0   0.0   0:01.51 stress

4-7-2. MEM

MEM제한을 주지 않은 상황과 제한을 준 상황에서의 프로세스 MEM 사용률을 확인한다.
MEM제한 X

# mkdir /sys/fs/cgroup/parent

# echo "+memory" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
+memory

# echo "+memory" | sudo tee /sys/fs/cgroup/parent/cgroup.subtree_control
+memory

# mkdir /sys/fs/cgroup/parent/child

# echo $$ > /sys/fs/cgroup/parent/child/cgroup.procs

# stress-ng --vm 1 --vm-bytes 100% --vm-keep --timeout 60s
stress-ng: info:  [3403] setting to a 1 min, 0 secs run per stressor
stress-ng: info:  [3403] dispatching hogs: 1 vm
...
stress-ng: info:  [3403] successful run completed in 18.24 secs

top - 13:23:40 up 1 day,  5:51,  2 users,  load average: 0.15, 0.03, 0.01
Tasks: 136 total,   2 running, 134 sleeping,   0 stopped,   0 zombie
%Cpu(s): 18.8 us,  6.2 sy,  0.0 ni, 75.0 id,  0.0 wa,  0.1 hi,  0.0 si,  0.0 st
MiB Mem :  15737.8 total,    163.2 free,  15668.6 used,    158.1 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.     69.1 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   3405 root      20   0   14.9g  14.9g    984 R  99.7  96.8   0:10.42 stress-ng-vm

MEM제한 O

# echo "500M" | sudo tee /sys/fs/cgroup/parent/memory.max
500M

# stress-ng --vm 1 --vm-bytes 100% --vm-keep --timeout 60s
stress-ng: info:  [3421] setting to a 1 min, 0 secs run per stressor
stress-ng: info:  [3421] dispatching hogs: 1 vm
...
stress-ng: info:  [3421] successful run completed in 30.25 secs

top - 13:25:29 up 1 day,  5:53,  2 users,  load average: 0.39, 0.13, 0.04
Tasks: 135 total,   2 running, 133 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.2 us, 21.6 sy,  0.0 ni, 74.9 id,  0.1 wa,  0.2 hi,  0.0 si,  0.0 st
MiB Mem :  15737.8 total,  14918.1 free,    911.0 used,    163.5 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.  14826.8 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   3538 root      20   0   15.1g 480316    412 R   6.0   3.0   0:00.18 stress-ng-vm

# dmesg
[107618.823784] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=/,mems_allowed=0,oom_memcg=/parent,task_memcg=/parent/child,task=stress-ng-vm,pid=3546,uid=0
[107618.824690] Memory cgroup out of memory: Killed process 3546 (stress-ng-vm) total-vm:15818924kB, anon-rss:479904kB, file-rss:396kB, shmem-rss:36kB, UID:0 pgtables:30948kB oom_score_adj:1000
[107619.036717] Memory cgroup out of memory: Killed process 3547 (stress-ng-vm) total-vm:15818924kB, anon-rss:479904kB, file-rss:448kB, shmem-rss:36kB, UID:0 pgtables:30948kB oom_score_adj:1000
[107619.251595] Memory cgroup out of memory: Killed process 3548 (stress-ng-vm) total-vm:15818924kB, anon-rss:479904kB, file-rss:388kB, shmem-rss:36kB, UID:0 pgtables:30948kB oom_score_adj:1000

4-7-3. DISK IO

IO제한을 주지 않은 상황과 제한을 준 상황에서의 프로세스 IO 성능을 확인한다.

IO제한 X

# mkdir /sys/fs/cgroup/parent
# echo "+io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
+io
# echo "+io" | sudo tee /sys/fs/cgroup/parent/cgroup.subtree_control
+io

# mkdir /sys/fs/cgroup/parent/child
# echo $$ > /sys/fs/cgroup/parent/child/cgroup.procs

# dd if=/dev/zero of=/tmp/file1 bs=512M count=1
1+0 records in
1+0 records out
536870912 bytes (537 MB, 512 MiB) copied, 0.269322 s, 2.0 GB/s

# iostat -d 1
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
vda               3.00        24.00         0.00         0.00         24          0          0

IO제한 O

# cat /proc/partitions
major minor  #blocks  name
 252        0   52428800 vda
 252        1     101376 vda1
 252        2    1024000 vda2
 252        3       4096 vda3
 252        4       1024 vda4
 252        5   51296239 vda5

# echo "252:0 wbps=10485760" > /sys/fs/cgroup/parent/io.max

# echo $$ > /sys/fs/cgroup/parent/child/cgroup.procs

# dd if=/dev/zero of=/tmp/file1 bs=512M count=1
Killed

# journalctl -f
Aug 31 13:42:17 bgrprac.novalocal kernel: INFO: task dd:3653 blocked for more than 122 seconds.
Aug 31 13:42:17 bgrprac.novalocal kernel:       Not tainted 5.14.0-284.11.1.el9_2.x86_64 #1
Aug 31 13:42:17 bgrprac.novalocal kernel: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.

# ps -ef | grep dd
root           2       0  0 Aug30 ?        00:00:00 [kthreadd]
root          76       2  0 Aug30 ?        00:00:00 [ipv6_addrconf]
root        3653    3624  0 13:39 pts/0    00:00:00 dd if=/dev/zero of=/tmp/file1 bs=512M count=1
root        3661    3299  0 13:43 pts/1    00:00:00 grep --color=auto dd

# kill -9 3624

4-8. net

network interface, iptables 등 network 리소스를 격리하여 사용가능하다.

참고 문서

https://tech.kakaoenterprise.com/171
https://tech.kakaoenterprise.com/154

0개의 댓글