본문 바로가기
DevOps/Docker

chroot - 컨테이너를 직접 만들어보기!

by 민우's 코딩 2024. 9. 15.

출처 : https://tech.kakaoenterprise.com/154 [카카오엔터프라이즈 기술블로그 Tech&(테크앤):티스토리]

 

[컨테이너 인터널 #1] 컨테이너 톺아보기

시작하며 안녕하세요. 카카오엔터프라이즈에서 검색 서비스를 개발하고 있는 검색클라우드기술파트의 Sam(김삼영)입니다. 지난 글 서비스 개발자를 위한 컨테이너 뽀개기 (a.k.a 컨테이너 인터널

tech.kakaoenterprise.com

 

(전체적인 글 및 실습은 위 주소를 많이 참고 하였으며 더 자세한 글은 위 주소에서 직접 읽어보시길 강력히 추천합니다!)

 

 

chroot 

출처 : https://tech.kakaoenterprise.com/154 - 컨테이너의 역사

 

위 사진은 컨테이너가 발전해온 기록들입니다. 그 중 제일 맨 처음으로 거슬러 올라가면 chroot가 있는 걸 보실 수 있습니다.

 

chroot(Change Root directory)는 프로세스의 루트 디렉토리를 변경하는 리눅스 시스템 콜/ 명령어 입니다.

특정 디렉토리를 루트 디렉토리로 지정이 가능하다? -> 루트 디렉토리 위로는 못 나가기 때문에 해당 경로에 프로세스를 가둘 수 있다?

 

chroot 갇힌 프로세스는 현재 디렉터리를 루트로 인지하여 작동하기에 프로세스를 실행할 필요한 커맨드 프로그램, 라이브러리, 설정 등을 chroot 지정할 경로에 함께 넣어 주어야 합니다.

 

그럼 이 chroot를 사용해 리눅스에서 프로세스를 격리하는 기본적인 방법을 직접 경험해보아서 체득해보겠습니다!

 

 

chroot로 bash 실행하기

 

# 실습 경로로 이동해 주세요
root@kakao-techbootcamp-eddy:/# mkdir template
root@kakao-techbootcamp-eddy:/# cd template

# 루트로 사용할 디렉터리를 만듭니다.
root@kakao-techbootcamp-eddy:/template# mkdir new-root

# chroot로 new-root를 루트 디렉터리로 지정하여 /bin/bash를 실행합니다.
root@kakao-techbootcamp-eddy:/template# chroot new-root /bin/bash
chroot: failed to run command ‘/bin/bash’: No such file or directory

 

 실행 시 에러 메시지가 출력됩니다. chroot에서 지정한 루트 디렉터리를 기준으로 "커맨드"(/bin/bash)를 찾기 때문에 (인자로 넘기는) "커맨드"가 new-root 밑에 있어야 합니다. 또한 /bin/bash에서 사용하는 의존성 라이브러리들을 확인해서 해당 경로에 복사해주어야 합니다!

# bash 커맨드의 위치를 확인한 후 
root@kakao-techbootcamp-eddy:/template# which bash
/usr/bin/bash

# 동일한 경로를 만들고 복사해 줍니다. 
root@kakao-techbootcamp-eddy:/template# mkdir -p new-root/usr/bin
root@kakao-techbootcamp-eddy:/template# cp /bin/bash new-root/usr/bin

# chroot를 실행해 보세요
root@kakao-techbootcamp-eddy:/template# chroot new-root /bin/bash
chroot: failed to run command ‘/bin/bash’: No such file or directory

# /bin/bash 의존성 라이브러리를 확인합니다.
root@kakao-techbootcamp-eddy:/template# ldd /bin/bash
	linux-vdso.so.1 (0x0000e5e8a6e1b000)
	libtinfo.so.6 => /lib/aarch64-linux-gnu/libtinfo.so.6 (0x0000e5e8a6bf0000)
	libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000e5e8a6a30000)
	/lib/ld-linux-aarch64.so.1 (0x0000e5e8a6dde000)

# 의존성 라이브러리들을 복사할 디렉터리를 만듭니다
root@kakao-techbootcamp-eddy:/template# mkdir -p new-root/{lib/aarch64-linux-gnu,lib}
root@kakao-techbootcamp-eddy:/template# tree new-root
new-root
├── lib
│   └── aarch64-linux-gnu
└── usr
    └── bin
        └── bash

# 의존성 라이브러리들을 new-root 디렉터리로 복사합니다. (vdso 파일은 제외)
root@kakao-techbootcamp-eddy:/template# cp /lib/aarch64-linux-gnu/{libtinfo.so.6,libc.so.6} new-root/lib/aarch64-linux-gnu
root@kakao-techbootcamp-eddy:/template# cp /lib/ld-linux-aarch64.so.1 new-root/lib


# 의존성 라이브러리들이 잘 복사됐는지 확인해보세요
root@kakao-techbootcamp-eddy:/template# tree new-root
new-root
├── lib
│   ├── aarch64-linux-gnu
│   │   ├── libc.so.6
│   │   └── libtinfo.so.6
│   └── ld-linux-aarch64.so.1
└── usr
    └── bin
        └── bash

# chroot를 실행합니다.
root@kakao-techbootcamp-eddy:/template# chroot new-root /usr/bin/bash
bash-5.2#

 

이렇게하면 chroot로 new-root를 지정하여 /bin/bash를 실행시키게 된 것입니다!

 

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

 

하지만 아직 ls 커맨드 관련 작업을 해주지 않아서 ls 커맨드가 작동하지 않습니다. 그렇다면 앞에서 /bin/bash 프로그램을 복사해온 것 처럼 ls 관련 라이브러리들을 복사해오면 되지 않을까요?

 

chroot한 Bash 셸에서 ls 해보기

 

그러면 이번에도 ls 커맨드의 의존성 라이브러리들을 확인해보겠습니다.

root@kakao-techbootcamp-eddy:/template# which ls
/usr/bin/ls
root@kakao-techbootcamp-eddy:/template# ldd /usr/bin/ls
	linux-vdso.so.1 (0x0000e1aeae59a000)
	libselinux.so.1 => /lib/aarch64-linux-gnu/libselinux.so.1 (0x0000e1aeae4d0000)
	libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000e1aeae310000)
	/lib/ld-linux-aarch64.so.1 (0x0000e1aeae55d000)
	libpcre2-8.so.0 => /lib/aarch64-linux-gnu/libpcre2-8.so.0 (0x0000e1aeae260000)

 

 

위에서 진행했던것처럼 ls 복사도 진행하고 chroot를 실행시킵니다.

# /bin/ls를 복사합니다.
root@kakao-techbootcamp-eddy:/template# cp /usr/bin/ls new-root/bin

# ls 의존성 라이브러리를 복사합니다.
root@kakao-techbootcamp-eddy:/template# cp /lib/aarch64-linux-gnu/{libselinux.so.1,libc.so.6,libpcre2-8.so.0} new-root/lib/aarch64-linux-gnu
root@kakao-techbootcamp-eddy:/template# cp /lib/ld-linux-aarch64.so.1 new-root/lib

# chroot를 실행합니다.
root@kakao-techbootcamp-eddy:/template# chroot new-root /usr/bin/bash
bash-5.2# ls
bin  lib  usr


# 실제 환경의 ls /
root@kakao-techbootcamp-eddy:/template# ls /
bin                cdrom  etc   lib                media  proc  sbin                srv       template  var
bin.usr-is-merged  data   home  lib.usr-is-merged  mnt    root  sbin.usr-is-merged  swap.img  tmp
boot               dev    host  lost+found         opt    run   snap                sys       usr

 

이로써 실제 환경의 ls, chroot를 통해 실행한 컨테이너 환경의 ls의 결과가 확연히 다르단 것을 알 수 있습니다.

 

그럼 chroot한 컨테이너 환경에서 루트 디렉토리 밖으로 벗어날 수 있는지 체크해보겠습니다.

# 루트 조회
bash-5.2# cd /
bash-5.2# ls
bin  lib  usr

# 루트 탈출 시도 및 확인
bash-5.2# cd ../../../../
bash-5.2# ls
bin  lib  usr

 

이처럼 사용자에게 제공하고 싶은 프로그램을 한 곳에 모으고, 사용자 프로세스가 활동할 수 있는 경로를 제한해서 실행한 것이 컨테이너의 초창기 모습입니다.

 

그런데 지금과 같이 일일히 필요한 라이브러리들을 직접 복사해오는 이 행동 자체가 컨테이너가 필요할 때마다 매번 이래야 한다면 매우 불편하고 손이 많이 갑니다. 그러면 우리가 컨테이너하면 자동으로 따라오는 그 친구, 바로 이미지(Image)가 떠오를겁니다. 비유를 하자면 지금 집(컨테이너)을 만들었는데 안이 텅 비어있는 집인 겁니다. 그러면 이삿짐(이미지)을 가져와서 집 안을 채워야 겠죠?

 

이미지를 통해 chroot 하기

Bash, Is로 chroot 사용하기에서는 사용할 프로그램들(bash, ls)을 복사하여 chroot로 실행해 보았다면, 이미지로 chroot 사용하기에서는 누군가 만들어 놓은 이미지(nginx)를 가져다가 실행해 보도록 하겠습니다.

# nginx-root 디렉터리를 만듭니다. 
root@kakao-techbootcamp-eddy:/template# mkdir nginx-root

# 도커를 이용하여 nginx 이미지를 nginx-root에 압축을 풀어줍니다.
root@kakao-techbootcamp-eddy:/template# docker export $(docker create nginx:latest) | tar -C nginx-root -xvf -
## (출력 생략)

# nginx-root 경로를 한번 살펴보세요
root@kakao-techbootcamp-eddy:/template# tree -L 1 nginx-root
nginx-root
├── bin -> usr/bin
├── boot
├── dev
├── docker-entrypoint.d
├── docker-entrypoint.sh
├── etc
├── home
├── lib -> usr/lib
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin -> usr/sbin
├── srv
├── sys
├── tmp
├── usr
└── var

# chroot로 셸을 실행해 보세요
root@kakao-techbootcamp-eddy:/template# chroot nginx-root /bin/sh
# ls /
bin   dev		   docker-entrypoint.sh  home  media  opt   root  sbin	sys  usr
boot  docker-entrypoint.d  etc			 lib   mnt    proc  run   srv	tmp  var
#

이제 nginx를 chroot로 만든 컨테이너에서 실행시켜 봅니다!

# nginx를 실행합니다. 
# nginx -g "daemon off;"

root@kakao-techbootcamp-eddy:/home/eddy# curl http://localhost:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
root@kakao-techbootcamp-eddy:/home/eddy#

 

 

다음 글에서는 컨테이너 구현 시 chroot의 한계 부터 네임스페이스들에 관하여 정리해보겠습니다 감사합니다!