레이블이 pivot_root인 게시물을 표시합니다. 모든 게시물 표시
레이블이 pivot_root인 게시물을 표시합니다. 모든 게시물 표시

container overlay filesystem

# union filesystem mount 는 n개의 파일시스템을 하나로 마운트하는 것으로
# overlayFS, overlayFS2, AUFS 등이 있다.

# overlayFS 는 다음과 같은 layer 구조다.
# 아래 layer 파일은 상위 layer 에 투영되어 사용자는 최종 merged view 에서 모든 이전 layer 들의 파일이 있다.
# 같은 파일이라면 상위 layer 쪽이 선택된다.
# overlayFS2 는 다음과 같은 layer 로 구성된다.
[merged dir] - container mount 로 보여지는 내용으로, 최종 통합된 레이어
c.txt(from lower dir2) b.txt(from upper dir) a.txt(from lower dir1)

[upper dir] - container 에 변경 사항을 기록 하는 레이어
b.txt

[lower dir2] - 기존 이미지 readonly
c.txt b.txt

[lower dir1] - 기존 base 이미지 readonly
a.txt

# 위 4개의 layer 외에 추가로 work dir 이 있다.
[work dir] - atomic 보장을 위해, merged 에 반영되기전에 사용하는 임시 공간

# overlay filesystem 마운트 테스트
# 마운트 테스트에 사용할 디렉토리 생성
mkdir ysoftman_overlay1
cd ysoftman_overlay1
mkdir image1 image2 upper work merge
touch image1/a.txt image2/{b.txt,c.txt}

# -o 옵션으로 각 layer 에 해당하는 디렉토리 설정
# workdir 는 merged 에 반영전 준비를 위한 공간
# 최종 merged view layer 는 merge 디렉토리에 마운트 된다.
mount -t overlay overlay \
-o lowerdir=image2:image1,upperdir=upper,workdir=work \
merge

# 마운트 후 work 디렉토리를 보면 변경된 내용을 파악할 수 있다.
tree -I work
.
├── image1
│   └── a.txt
├── image2
│   ├── b.txt
│   └── c.txt
├── merge
│   ├── a.txt
│   ├── b.txt
│   └── c.txt
├── upper
└── work
    └── work

# 파일을 삭제하면 upper에 whiteout(파일 변경되었음을 표시)된다.
rm -rf merge/a.txt
ls -ahl upper/a.txt
c--------- 1 root root 0, 0 Dec  1 05:00 upper/a.txt
tree -I work
.
├── image1
│   └── a.txt
├── image2
│   ├── b.txt
│   └── c.txt
├── merge
│   ├── b.txt
│   └── c.txt
└── upper
    └── a.txt # whiteout


#####


# docker image layer 정보 확인
# 최신 nginx 도커 이미지 다운로드
docker pull nginx:latest

# layer id 확인
docker image inspect nginx:latest | jq '.[].RootFS'

# docker storage driver, root dir 확인해
docker info | grep -i -E "storage driver|root dir"

# (layer 정보를 가지고 있는)layerdb id 확인
cd /var/lib/docker/image/overlay2/layerdb
tree ./sha256/ -L 1

# layer id 확인
ls sha256/*/diff | awk '{system("cat "$0"; echo")}'

# 이 layer 의 부모 layer 를 가진 layerdb id 가 기록되어 있다.
ls sha256/*/parent | awk '{system("cat "$0"; echo")}'

# layer 가 저장된 경로 id 확인
# /var/lib/docker/overlay2/xxx 디렉토리 이름을 나타낸다.
ls sha256/*/cache-id | awk '{system("cat "$0"; echo")}'


#####


# nginx image layer 로 container 띄우기
# nginx GraphDriver(layer) 확인
docker image inspect nginx | jq '.[].GraphDriver'

# LowerDir 값중 마지막 부분은 최상위 부모 이미지로 내용을 확인해 볼 수 있다.
tree -L 1 /var/lib/docker/overlay2/1431186159df61d78822ed287754ce0e079739695e8966adb816f509687a1d92/diff

# nginx overlayFS 가 마운트될 디렉토리 생성
mkdir nginx_overlay
cd nginx_overlay
mkdir upper work merge

# nginx overlay 마운트
# lower 는 기존 이미지 사용
# upper,worker 는 생성한 디렉토리로 연결
# 모든 layer 가 오버레이된 디렉토리 merge 연결
mount -t overlay overlay \
-o "lowerdir=/var/lib/docker/overlay2/cfe31bc0b1bcc056093f9d1b73a090c81f4ce284eccf37e50344258a434fa5b1/diff:/var/lib/docker/overlay2/a9bcb65c3bfbad6d4fb692677b94fcda85b8856e4836490cc987296a5f902f4a/diff:/var/lib/docker/overlay2/3d6e60f9c348c81d24ecafbfb23a6e531541778efc36d41dd064c1e8b20dd4ad/diff:/var/lib/docker/overlay2/6d60e98403eadb29449726645f338d8a47dd8cd6fcfa22b6565d5c46d83519bc/diff:/var/lib/docker/overlay2/1431186159df61d78822ed287754ce0e079739695e8966adb816f509687a1d92/diff,\
upperdir=upper,\
workdir=work" \
merge

# nginx 마지막 layer 에서 nginx 실행 커맨드 파악
docker history nginx:latest --no-trunc | sed -n '1,2p' | tr -d ' '

# -m 새로운 마운트 네임스페이스 시작
unshare -m

# 새로운 마운트 네임스페이스에서 chroot 커맨드로 ./merge 를 root 디렉토리로 설정
chroot ./merge
# 또는 pivot_root 로 root 를 ./merge 로 변경
cd merge
mkdir put_old
pivot_root . put_old

# nginx 백그라운드로 실행하고 access 로그를 확인
nginx -g "daemon off;" & tail -F /var/log/nginx/access.log

# 새 터미널을 열고 nginx 동작 확인
curl localhost

# 위 과정 실행 결과

# unshare 종료하고, nginx 프로세스 마운트 해제
exit
killall nginx
umount /tmp/nginx_overlay/merge

chroot, pvirot_root, unshare

# container 없던 시절 완벽하진 않은 격리효과를 위해서 사용할 수 있는 명령들

# 가상작업환경 : virtualbox + vagrant + ubuntu box image
# vagrant up 후 vagrant ssh 로 접속
# root 환경에서 작업하기
sudo -Es

# 마운트등의 이슈로 /tmp 에서 작업한다.
cd /tmp

# chroot 사용하기
# 새로운 root 경로가 될 디렉토리 생성
mkdir new_root

# 단순히 chroot new_root 하면
chroot new_root
# 다음과 같은 에러가 발생한다.
chroot: failed to run command ‘/bin/bash’: No such file or directory

# chroot 하면 bash 가 실행하는데 new_root 에서는 bin/bash 가 없어 이를 새루트에 복사해야 한다.
# bash 구동을 위해 필요한 파일들 확인하자
ldd /bin/bash
linux-vdso.so.1 (0x00007ffe49bfb000)
libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007fc6e4d28000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc6e4b24000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc6e4733000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc6e526c000)

# bash 및 관련 라이브러리 복사
mkdir -p new_root/{bin,usr,lib,lib64} new_root/lib/x86_64-linux-gnu/ new_root/usr/lib/x86_64-linux-gnu
cp /bin/bash new_root/bin
cp /lib/x86_64-linux-gnu/{libtinfo.so.5,libdl.so.2,libc.so.6} new_root/lib
cp /lib64/ld-linux-x86-64.so.2 new_root/lib64

# ls 외 필요한 기본 명령어도 확인하고 복사하자.
# ps 는 /proc 디렉토리를 생성(mkdir)하고 mount 해야 사용할 수 있다.
ldd /bin/ps
linux-vdso.so.1 (0x00007ffc403b6000)
libprocps.so.6 => /lib/x86_64-linux-gnu/libprocps.so.6 (0x00007f594c99d000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f594c799000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f594c3a8000)
libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f594c124000)
/lib64/ld-linux-x86-64.so.2 (0x00007f594ce03000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f594bf1c000)
liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f594bcf6000)
liblz4.so.1 => /usr/lib/x86_64-linux-gnu/liblz4.so.1 (0x00007f594bada000)
libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007f594b7be000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f594b59f000)
libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007f594b38a000)

# 일일히 lib 복사하지 말고 그냥 lib,lib64 파일 모두 복사해놓자.
cp -rv /bin/{ls,cat,mount,umount,mkdir,rmdir,rm,cp,grep,ps} new_root/bin
cp -rv /usr/lib/x86_64-linux-gnu/* new_root/usr/lib/x86_64-linux-gnu
cp -rv /lib/x86_64-linux-gnu/* new_root/lib/x86_64-linux-gnu
cp -rv /lib64/* new_root/lib64

# 이제 chroot 를 해보면 /bin/bash 가 실행된다.
chroot new_root
# root 경로가 바뀐것 확인(cat,mkdir,ps 등도 동작한다.)
ls /

# ps 명령을 실행하기 위해선 /proc 디렉토리를 만들고 마운트해야한다.
mkdir -p /proc
mount -t proc proc /proc

# chroot 는 다음코드를 실행하면 현재 root 경로를 나갈수 있다(탈옥가능)
cat > break_chroot.c << EOF
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
    mkdir("foo", 0755);
    chroot("foo");
    chdir("../../../../../");
    chroot(".");
    return execl("/bin/bash", "-i", NULL);
}
EOF
# 빌드
gcc break_chroot.c -o break_chroot
# new_root 에 복사하고
cp -v break_chroot new_root
# new_root 에서 실행하면 chroot 의 / 를 빠져나가는 이슈가 있다.
chroot new_root
./break_chroot

# chroot 나가기
exit


#####


# pivot_root : root 를 new_root 아래로 mount 하여 경로 탈옥을 막아보자.

# 새로운 root 경로가될 디렉토리 생성
mkdir new_root2

# unshare 는 부모로 부터 격리된 특정 네임스페이스로 프로그램 실행�한다.
# -m : unshare mount namespace
# 프로그램 명시하지 않으면 기본 $SHELL(/bin/sh 등) 를 실행한다.
unshare -m

# 새로운 파일시스템을 생성해 new_root2 에 마운트
# 참고로 
# chroot 테스트에서 사용했던 파일들 모두 사용하기 위해 디스크 크기를 넉넉하게 한다.
# 디스크 크기를 너무 적게 설정하면 mount 오류로 다음과 같은 에러가 발생할 수 있다.
# -bash: /bin/ls: Input/output error
mount -t tmpfs -o size=500M none new_root2

# chroot 테스트에서 사용했던 파일들 복사해오기
cp -rv new_root/* new_root2/

# root 경로를 마운트할 디렉토리 생성
mkdir new_root2/put_old
cd new_root2

# pivot_root: change the root filesystem
# 현재 프로세스의 root 파일시스템을 put_old 로 옮기고 . 를 새로운 root 파일 시스템으로 만든다.
pivot_root . put_old

# root 파일 시스템 자체가 바뀌었음으로
# chroot 탈옥 코드로 root 경로 탈옥이 되지 않는다.
./break_chroot

# unshare 나가기
exit