<컨테이너 가상화의 이해> chroot를 사용해 프로세스의 루트 바꾸기

in kr •  7 years ago  (edited)

image.png

안녕하세요. 프로그래머 @mishana입니다.

오늘 소개할 리눅스 명령어는 프로세스가 인식하는 루트 디렉터리를 바꿔주는 chroot입니다.

chroot를 직접 사용할 일은 별로 없습니다. 하지만 chroot를 사용해보면 리눅스 위에서 프로세스를 격리 시키는 기본적인 방법을 직접 경험해 볼 수 있습니다. 요즘 도커Docker와 컨테이너 가상화가 인기를 끌고 있는데, chroot를 사용해보면 이런 컨테이너 가상화 도구를 이해하는 데 큰 도움이 됩니다.

도커(Docker)와 프로세스 격리

도커를 처음 사용하는 사람들이 많이 하는 질문 중 하나가 "도커는 가상 머신인가요?"입니다. 오늘의 주제는 도커가 아니니 결론만 짚고 넘어가도록 하겠습니다.

도커는 가상 머신이 아닙니다.

가상 머신은 하드웨어를 소프트웨어로 구현합니다. 버추얼 박스VirtualBox나 VMWare 같은 도구를 사용해보셨다면 아시겠지만, 가상 머신을 생성할 때 CPU나 램도 설정해야하고, 설정을 잘 살펴보면 ODD, 사운드 카드, 랜카드 같은 것도 설정을 할 수 있습니다. 가상 머신은 정말로 컴퓨터 안에 만들어진 컴퓨터입니다.

하지만 도커는 가상 머신을 만들지 않습니다. 단지 프로세스를 격리된 환경에 실행하는 걸 도와주고, 이렇게 만들어진 프로세스를 컨테이너라고 합니다. 그래서 컨테이너는 단지 프로세스일 뿐입니다.

컨테이너에는 다양한 프로세스 격리 기술이 사용됩니다. 프로세스 네임스페이스와, 권한과 네트워크도 모두 격리됩니다. 그리고 컨테이너가 인식하는 루트 디렉터리도 호스트의 루트가 아닌 다른 "위치"가 됩니다. chroot는 정확히 이 루트 디렉터리를 바꿔주는 역할을 합니다.

chroot 입문

chroot는 이름에서 유추해볼 수 있듯이 "change root (directory)"라는 의미를 가지고 있습니다. 여기서 루트는 사용자는 아니고 /, 즉 시스템의 최상위 디렉터리를 의미합니다. 일반적인 프로세스는 이 루트 디렉터리를 기준으로 그 아래에 있는 디렉터리나 파일에 접근하는 게 가능합니다.

chroot를 사용하면 프로세스가 인식하는 루트 디렉터리를 변경해버립니다. 예를 들어 chroot에서 /tmp/chroot를 루트 디렉터리로 지정하여 어떤 프로세스를 실행하면, 이 프로세스는 /tmp/chroot/(루트 디렉터리)로 인식합니다. 이 프로세스는 호스트의 /tmp/chroot보다 디렉터리 구조 상에서 상위에 있는 디렉터리에 접근할 수 없습니다. 이 프로세스에게는 /tmp/chroot가 루트이고 오직 이 아래의 파일들만 접근 가능합니다.

루트 디렉터리 격리가 어떤 의미인지 대충 감이 오시죠?

chroot를 사용하는 건 의외로 어렵습니다. 루트 디렉터리가 바뀌기 때문에 어떤 프로세스를 실행할 때 참조하는 파일들도 모두 이 디렉터리 아래에 준비해야하기 때문입니다. 실제로 사용하는 방법과 사용하는 과정에 생기는 문제는 튜토리얼을 진행하면서 살펴보도록 하겠습니다.

실습 환경 준비와 chroot 설치

우선 chroot를 사용하기 위한 실습 환경을 준비해야합니다.

실습 환경은 우분투 16.04입니다. 저는 디지털 오션Digital Ocean에서 드롭릿Droplet을 하나 만들어서 사용했습니다. 우분투가 설치된 가상 머신이나 혹은 실제 우분투가 설치된 서버나 데스크톱 모두 사용할 수 있습니다. (편의상 아래 튜토리얼에서는 root 사용자로 진행합니다.)

$ lsb_release -d
Description:    Ubuntu 16.04.3 LTS

chroot는 coreutils 패키지에 포함되어있고 일반적으로는 설치되어 있습니다. chroot가 설치되어있는지 확인해봅니다.

$ chroot --version
chroot (GNU coreutils) 8.25
Copyright (C) 2016 Free Software Foundation, Inc.
...

chroot 명령어가 설치 되어있지 않으면 다음과 같이 에러가 납니다.

$ chroot
chroot: command not found

chroot가 설치되어있지 않은 경우 apt-get으로 설치할 수 있습니다. coreutils 패키지를 설치합니다.

$ apt-get update
$ apt-get install coreutils

설치가 끝나면 다시 chroot --version 명령어로 설치가 되어있는지 확인해봅니다.

이제 실제로 chroot를 사용해서 프로세스의 루트 디렉터리를 변경해보겠습니다.

chroot 입문

chroot를 사용하는 기본적인 방법은 아주 간단합니다.

$ chroot <루트 디렉터리> <명령어>

그럼 디렉터리를 만들고, 그 아래에 셸을 복사하고 바로 사용해 보겠습니다. /tmp/root를 만들고 이 디렉터리를 루트로 bash를 실행해보고자 합니다. 이 디렉터리 아래에 bash도 필요하므로 호스트의 bash 명령어도 복사합니다.

$ mkdir /tmp/root
$ mkdir /tmp/root/bin
$ cp -v /bin/bash /tmp/root/bin/
'/bin/bash' -> '/tmp/root/bin/bash'

ls로 복사가 잘 되었는지 확인해봅니다.

$ cd /tmp/root
$ ls -R
.:
bin

./bin:
bash

그럼 바로 chroot를 실행해봅니다. 첫 번째 인자는 루트 디렉터리로 사용할 디렉터리를 지정합니다. 두 번째 인자는 루트 디렉터리를 기준으로 실행할 명령어를 지정합니다. bash/tmp/root/bin/bash에 있으므로 /tmp/root 아래의 /bin/bash에 있으므로 /bin/bash를 지정해줍니다.

$ chroot /tmp/root/ /bin/bash
- [ ] chroot: failed to run command '/bin/bash': No such file or directory

에러가 발생합니다. No such file or directory, 파일은 분명히 있는데?

bash가 의존하고 있는 라이브러리 파일들을 복사하고 실행하기

앞서 분명히 ls/bin/bash 파일을 확인했지만, 파일이 없다는 에러가 발생을 합니다. 이 메시지는 조금 불분명하니, 게의치 않고 문제를 해결해보겠습니다. 분명히 파일이 없어서 발생하는 문제는 아닙니다. 이 문제가 발생하는 이유는 bash를 실행하기 위한 라이브러리 파일이 /tmp/root 아래에 없기 때문입니다. 루트 디렉터리 아래에 어떤 프로세스를 실행시키기 위한 모든 파일이 준비되어 있어야합니다. 앞에서 이야기했지만, 바로 이러한 이유로 chroot는 간단하지만 실제로 사용하기는 상당히 까다롭습니다.

ldd를 사용해 bash를 실행할 때 필요한 파일들을 찾아보겠습니다.

$ ldd /bin/bash
        linux-vdso.so.1 =>  (0x00007fff86bf1000)
        libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f88b36b2000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f88b34ae000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f88b30e4000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f88b38db000)

이 파일들을 /tmp/root 아래에 같은 구조로 복사해줍니다.

$ mkdir /tmp/root/{lib,lib64}
$ cp /lib/x86_64-linux-gnu/libtinfo.so.5 \
  /lib/x86_64-linux-gnu/libdl.so.2 \
  /lib/x86_64-linux-gnu/libc.so.6 \
  /tmp/root/lib
$ cp /lib64/ld-linux-x86-64.so.2 /tmp/root/lib64

그럼 다시 chroot를 실행해봅니다.

$ chroot /tmp/root/ /bin/bash
bash-4.3#

프롬프트가 바뀌었습니다! bash가 새로 실행된 것을 알 수 있습니다.

그럼 정말 루트가 바뀌었을까요? ls를 한 번 실행해보겠습니다.

bash-4.3# ls
bash: ls: command not found

아... ls가 없네요.

ls로 루트 디렉터리가 변경된 것을 확인하기

ls도 같은 방식으로 실행 파일과 의존하고 있는 파일들을 복사를 해야합니다. exit를 실행해 일단 chroot로 실행한 bash를 종료합니다. ls를 복사합니다.

$ cp -v /bin/ls /tmp/root/bin/
$ ldd /bin/ls
        linux-vdso.so.1 =>  (0x00007ffcc2cfe000)
        libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f3111a97000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f31116cd000)
        libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f311145d000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3111259000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3111cb9000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f311103c000)
$ cp /lib/x86_64-linux-gnu/libselinux.so.1 \
  /lib/x86_64-linux-gnu/libc.so.6 \
  /lib/x86_64-linux-gnu/libpcre.so.3 \
  /lib/x86_64-linux-gnu/libdl.so.2 \
  /lib/x86_64-linux-gnu/libpthread.so.0 \
  /tmp/root/lib
$ cp /lib64/ld-linux-x86-64.so.2 /tmp/root/lib64

이제 다시 chrootbash를 실행하고 ls를 실행해봅니다.

$ chroot /tmp/root/ /bin/bash
$ ls -l /
total 12
drwxr-xr-x 2 0 0 4096 Jan 18 13:45 bin
drwxr-xr-x 2 0 0 4096 Jan 18 13:44 lib
drwxr-xr-x 2 0 0 4096 Jan 18 13:27 lib64

이제 ls가 잘 실행 됩니다. 루트 디렉터리가 /tmp/root인 것을 알 수 있습니다. 루트에서 cd ..로 호스트의 디렉터리에 접근을 시도해봅니다.

bash-4.3# cd /
bash-4.3# pwd
/
bash-4.3# cd ..
bash-4.3# pwd
/
bash-4.3# ls
bin lib lib64

최상위 디렉터리(호스트 기준/tmp/root)에서 더 이상 위로 올라갈 수 없는 것을 알 수 있습니다.

여기까지 프로세스의 루트(실행 위치)가 성공적으로 격리되었습니다!

호스트에서는?

호스트에서는 이 bash 프로세스가 어떻게 보일까요? 먼저 chrootbash에서 현재 프로세스의 프로세스ID를 확인해보겠습니다.

bash-4.3# echo $$
13251

$$bash의 프로세스ID를 담은 특수한 환경변수입니다. 호스트에서 이 프로세스를 찾아보겠습니다.

터미널을 하나 더 띄워 호스트에 접속합니다. ps 명령어로 이 프로세스를 찾아보겠습니다.

$ ps aux | grep 13251
root     13251  0.0  0.2   9680  2960 pts/1    S+   13:45   0:00 /bin/bash

호스트에서는 다른 프로세스들과 마찬가지로 호스트의 프로세스로 보이는 것을 알 수 있습니다.

여기까지 chroot 기본적인 사용법을 알아보았습니다.

마치며

chroot를 실제로 사용할 일은 별로 없긴 합니다. 하지만 처음에 이야기했듯이 컨테이너 가상화에서 사용하는 기본적인 프로세스 격리 방법 중 하나이므로 익혀두시면 도움이 많이 될 것입니다. chroot를 써보고 도커 컨테이너를 사용한다면 기술적으로 거의 차이가 없다는 것을 바로 이해할 수 있습니다.

오늘 이야기는 여기서 마치고, 또 다른 컨테이너 이야기로 다시 돌아오겠습니다 ;)


최근에 공개한 글 목록입니다.


576C3781-B0F1-4BAE-879D-ABAC4E47520B.png
글쓰고, 프로그래밍하고, 투자하는 @mishana입니다.

웹사이트 | 트위터 | RSS | Feedly에서 구독

  • 이 글이 도움이 되셨다면 리스팀, 팔로우 부탁드려요 :)
Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Cheer Up!

  • from Clean STEEM activity supporter

스스로 홍보하는 프로젝트에서 나왔습니다.
오늘도 좋은글 잘 읽었습니다.
오늘도 화이팅입니다.!

짱짱맨도 외칩니다! 가즈아!!!
날씨가 다시 추워진거같아요
따뜻하게!! 봄날씨로 가즈아!!!

오늘도 감사합니다 ;)

아..
하도 Docker docker 하길래 어떤건지 궁금했는데..
Linux에서 이런식으로 격리가능할거란 생각은 안해봤는데. 신기하네요.🤗
좋은글 감사합니다!😀

컨테이너 흥미로운 주제죠 ㅎㅎ. 이 기세로 조만간 도커까지 다뤄보겠습니다 :)