저번 포스팅에 이어서 docker가 실제로 돌아가는 모습을 엿보러 가보자
docker에 대한 로그를 남기기 위해 터미널에서 다음과 같은 명령어를 사용해보자
strace -f -p $(pidof container) -o docker_log.txt
그럼 위처럼 containerd 프로세스를 추적하게 되고
docker run -it --name debian debain:buster
위의 명령어로 docker 컨테이너를 새로 생성해서 실행시켜보자
그럼 attached 됐다는 메세지와 함께
docker_log.txt 파일에 로그가 남겨지게 되고
로그파일에서 syscall을 추적할 수 있다
log에서는 앞서 공부했던 내용처럼 execve 명령어로
runc 를 실행하는 모습을 볼 수 있고 runc가 실행된 후 로그들을 따라가보면
unshare systemcall을 호출하는데 unshare을 호출하는 바로 윗줄을 주목해보자
prctl(PR_SET_NAME, "runc:[1:CHILD]") = 0
https://man7.org/linux/man-pages/man2/prctl.2.html - prctl
prctl은 process 와 thread를 조작하는 명령어이며 PR_SET_NAME 옵션은 호출 thread의 이름을 지정하는 옵션이다.하지만 thread를 따로 생성해주지는 않아서 혹여 thread를 미리 생성해주는지 찾아봤는데 없었다.
https://stackoverflow.com/questions/30453048/using-prctl-pr-set-name-to-set-name-for-process-or-thread
찾아보니 PR_SET_NAME 옵션이 프로세스의 이름도 바꿀수 있고, 프로세스 상에서 쓰이면 프로세스의 이름이 바뀌고 소속된 thread도 영향을 받고 특정 thread에서 호출하면 그 thread의 이름만 바뀐다고 한다. 실제로 그렇게 동작하는지는 검증해봐야 알 것 같다.
여튼 여기서는 linux 메뉴얼대로 작동하고 있다고 보고 runc:[1:CHILD] 라는 이름의 runc의 하위 thread의 이름이 변경되었다고 생각하자.
https://man7.org/linux/man-pages/man2/unshare.2.html - unshare
이 thread가 unshare을 호출하게 되면 자식 프로세스가 새로 생성이 된다.
여기서 주목할만한 점은 CLONE_NEWPID 라는 옵션이다.
CLONE_NEWPID
This flag has the same effect as the clone(2) CLONE_NEWPID
flag. Unshare the PID namespace, so that the calling
process has a new PID namespace for its children which is
not shared with any previously existing process. The
calling process is not moved into the new namespace. The
first child created by the calling process will have the
process ID 1 and will assume the role of init(1) in the
new namespace. CLONE_NEWPID automatically implies
CLONE_THREAD as well. Use of CLONE_NEWPID requires the
CAP_SYS_ADMIN capability. For further information, see
pid_namespaces(7).
간단하게 정리해보면
이전에 공부했던 docker container의 모든 핵심 동작이 다 들어가있음을 알 수 있다.
컨테이너는 namespace로 격리된 공간이고 runc가 실제로 컨테이너를 생성 및 관리를 하고 있으며 runc의 하위 쓰레드에서 unshare() systemcall 로 새로운 namespace를 만들고 있다는 사실을 알게 되었다.