https://velog.io/@_gyullbb/1-1.-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B2%A9%EB%A6%AC
https://velog.io/@_gyullbb/1-2.-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B2%A9%EB%A6%ACpivotroot
필요한 환경을 이미지로 만들 때, 환경이 조금만 달라질 때마다 모든 환경별로 이미지를 만든다면 저장공간 부족, 배포 및 통신 속도 지연, 관리 포인트 증가, 보안 위험도 증가 등의 문제가 야기된다.
이러한 문제를 해결하기 위해 컨테이너에서는 layer개념을 이용하여 overlay 파일시스템을 통해 중복 문제를 해결한다.
overlay 파일 시스템의 구조이다.
각 특징들을 예제를 통해 확인해본다.
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
# 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
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
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
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
이 테스트에서 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
Linux에서 사용할 수 있는 namespace 종류는 아래와 같다.
이 중 time namespace는 리눅스 커널 5.6 이상 버전 부터 사용할 수 있다.
Namespace | Flag | Page | Isolates |
---|---|---|---|
Cgroup | CLONE_NEWCGROUP | cgroup_namespaces(7) | Cgroup root directory |
IPC | CLONE_NEWIPC | ipc_namespaces(7) | System V IPC, POSIX message queues |
Network | CLONE_NEWNET | network_namespaces(7) | Network devices, stacks, ports, etc. |
Mount | CLONE_NEWNS | mount_namespaces(7) | Mount points |
PID | CLONE_NEWPID | pid_namespaces(7) | Process IDs |
Time | CLONE_NEWTIME | time_namespaces(7) | Boot and monotonic clocks |
User | CLONE_NEWUSER | user_namespaces(7) | User and group IDs |
UTS | CLONE_NEWUTS | uts_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 별로 격리 테스트를 진행한다.
프로세스의 파일 시스템 마운트 지점을 격리한다.
터미널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
프로세스의 호스트 이름과 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
ipc란 독립된 구조의 프로세스가 서로 자원을 공유할 때 프로세스 간에 발생하는 통신으로, 이 통신 자원을 분리하여 관리할 수 있다.
프로세스 간 통신은 메세지 전달 방식과 공유 메모리 방식 두 가지 방식으로 이뤄진다.
docker의 ipc mode에는 private, sharable, host가 있다.
sharable mode를 통하여 컨테이너 프로세스에 공유 메모리를 할당 및 공유할 수 있다.
host mode를 통하여 컨테이너가 호스트의 IPC리소스에 직접 액세스할 수 있다. 단, 이 옵션의 경우 컨테이너에 할당된 리소스 보다 더 많은 리소스를 사용할 수 있게 설정될 수 있기 때문에 관리적 측면으로 좋지 않다.
docker의 ipc mode 중 shareable, host mode를 통하여 프로세스 격리를 확인할 수 있다.
터미널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
터미널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
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
터미널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
사용자 및 그룹 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
문제 상황
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)
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
cgroupv2를 활용하여 CPU, MEM, DISK IO 자원 제한을 설정할 수 있다.
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
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
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
network interface, iptables 등 network 리소스를 격리하여 사용가능하다.
https://tech.kakaoenterprise.com/171
https://tech.kakaoenterprise.com/154