docker debug
docker debug
docker debug는 어떤 컨테이너나 이미지에든 디버그 셸을 얻는 명령이에요. 슬림(slim) 이미지처럼 디버깅 도구가 다 제거된 환경에서도 쓸 수 있는, docker exec 대안이에요.
출처: 문서
본문
Docker Debug는 이미지를 작고 안전하게 유지하는 모범 사례를 따르도록 도와주는 CLI 명령이에요. 애플리케이션을 실행하는 데 필요한 최소한만 담긴 이미지를 디버깅할 수 있게 해 주죠. 모든 도구가 제거돼 있어 디버깅이 어려운 슬림 이미지나 컨테이너를 만들고 작업할 수 있게 해요.
예를 들어 docker exec -it my-app bash 같은 일반적인 디버깅 방법이 슬림 컨테이너에서는 동작하지 않을 수 있지만, docker debug는 동작해요. docker debug로는 셸이 들어 있지 않은 컨테이너나 이미지에도 디버그 셸을 얻을 수 있어요.
Docker Debug를 쓰기 위해 이미지를 수정할 필요도 없고, Docker Debug를 써도 이미지가 수정되지 않아요. Docker Debug는 쉽게 커스터마이즈할 수 있는 자체 toolbox를 가져오는데, vim·nano·htop·curl 같은 표준 Linux 도구가 미리 설치돼 있어요. https://search.nixos.org/packages에 있는 추가 도구는 내장 install 명령으로 더할 수 있어요.
Docker Debug는 bash, fish, zsh를 지원해요. 기본적으로 셸을 자동 감지하려고 해요.
내장 사용자 도구(builtin tools):
install [tool1] [tool2]: https://search.nixos.org/packages에서 Nix 패키지를 추가해요 (예제 참고)uninstall [tool1] [tool2]: Nix 패키지를 제거해요entrypoint: entrypoint를 출력·린트·실행해요 (예제 참고)builtins: 내장 사용자 도구를 보여 줘요
참고: 이미지와 정지된 컨테이너의 경우 셸을 떠나면 모든 변경이 폐기돼요. 어떤 시점에도 변경이 실제 이미지나 컨테이너에 영향을 주지 않아요. 실행 중이거나 일시 중지된 컨테이너에 접근할 때 파일시스템 변경은 컨테이너에 직접 보여요.
/nix디렉터리는 실제 이미지나 컨테이너에 절대 보이지 않아요.
사용법 (Usage)
debug [OPTIONS] {CONTAINER|IMAGE}
옵션 (Options)
| 옵션 | 기본값 | 설명 |
|---|---|---|
--shell |
auto |
사용할 셸을 선택해요. 지원: bash, fish, zsh, auto |
-c, --command |
대화형 세션 대신 지정한 명령을 평가해요 (예제 참고) | |
--host |
연결할 데몬 docker 소켓이에요. 예: ssh://[email protected], unix:///some/path/docker.sock (예제 참고) |
예제 (Examples)
셸이 없는 컨테이너 디버깅하기 (슬림 컨테이너)
hello-world 이미지는 매우 단순하고 /hello 바이너리만 담고 있어요. 다른 도구도 셸도 없는 슬림 이미지의 좋은 예죠.
hello-world 이미지에서 컨테이너를 실행해요.
$ docker run --name my-app hello-world
컨테이너는 즉시 종료돼요. 안에 디버그 셸을 얻으려면 이렇게 실행해요.
$ docker debug my-app
디버그 셸로 파일시스템을 살펴볼 수 있어요.
docker > ls
dev etc hello nix proc sys
/hello 파일은 컨테이너를 실행할 때 실행된 바이너리예요. 직접 실행해 확인할 수 있어요.
docker > /hello
바이너리를 실행하면 같은 출력이 나와요.
슬림 이미지 디버깅하기
이미지를 직접 실행해 디버깅할 수 있어요.
$ docker debug hello-world
...
docker > ls
dev etc hello nix proc sys
docker debug는 docker run 명령처럼 이미지를 자동으로 가져오므로 이미지를 pull할 필요조차 없어요.
실행 중인 컨테이너의 파일 수정하기
Docker debug는 어떤 실행 중인 컨테이너의 파일도 수정할 수 있게 해 줘요. toolbox에는 vim과 nano가 미리 설치돼 있어요.
nginx 컨테이너를 실행하고 기본 index.html을 바꿔 볼게요.
$ docker run -d --name web-app -p 8080:80 nginx
d3d6074d0ea901c96cac8e49e6dad21359616bef3dc0623b3c2dfa536c31dfdb
nginx가 실행 중인지 확인하려면 브라우저를 열고 http://localhost:8080으로 이동해요. 기본 nginx 페이지가 보일 거예요. 이제 vim으로 바꿔요.
vim /usr/share/nginx/html/index.html
제목을 "Welcome to my app!"으로 바꾸고 파일을 저장해요. 이제 브라우저에서 페이지를 새로고침하면 갱신된 페이지가 보여요.
install 명령으로 toolbox 관리하기
내장 install 명령으로 https://search.nixos.org/packages의 어떤 도구든 toolbox에 추가할 수 있어요. 도구 추가는 실제 이미지나 컨테이너를 절대 수정하지 않고 toolbox에만 더해진다는 점을 기억해요.
docker debug를 실행한 뒤 nmap을 설치해요.
$ docker debug nginx
...
docker > install nmap
Tip: You can install any package available at: https://search.nixos.org/packages.
installing 'nmap-7.93'
these 2 paths will be fetched (5.58 MiB download, 26.27 MiB unpacked):
/nix/store/brqjf4i23fagizaq2gn4d6z0f406d0kg-lua-5.3.6
/nix/store/xqd17rhgmn6pg85a3g18yqxpcya6d06r-nmap-7.93
copying path '/nix/store/brqjf4i23fagizaq2gn4d6z0f406d0kg-lua-5.3.6' from 'https://cache.nixos.org'...
copying path '/nix/store/xqd17rhgmn6pg85a3g18yqxpcya6d06r-nmap-7.93' from 'https://cache.nixos.org'...
building '/nix/store/k8xw5wwarh8dc1dvh5zx8rlwamxfsk3d-user-environment.drv'...
docker > nmap --version
Nmap version 7.93 ( https://nmap.org )
Platform: x86_64-unknown-linux-gnu
Compiled with: liblua-5.3.6 openssl-3.0.11 libssh2-1.11.0 nmap-libz-1.2.12 libpcre-8.45 libpcap-1.10.4 nmap-libdnet-1.12 ipv6
Compiled without:
Available nsock engines: epoll poll select
다른 이미지에 디버그 셸을 얻어 nmap이 이제 toolbox의 일부인지 확인할 수 있어요.
$ docker debug hello-world
...
docker > nmap --version
Nmap version 7.93 ( https://nmap.org )
Platform: x86_64-unknown-linux-gnu
Compiled with: liblua-5.3.6 openssl-3.0.11 libssh2-1.11.0 nmap-libz-1.2.12 libpcre-8.45 libpcap-1.10.4 nmap-libdnet-1.12 ipv6
Compiled without:
Available nsock engines: epoll poll select
docker > exit
nmap은 여전히 남아 있어요.
컨테이너의 기본 시작 명령 이해하기 (entry point)
Docker Debug에는 내장 도구 entrypoint가 있어요. hello-world 이미지에 들어가 entrypoint가 /hello인지 확인해요.
$ docker debug hello-world
...
docker > entrypoint --print
/hello
entrypoint 명령은 기본 이미지의 ENTRYPOINT와 CMD 문을 평가해서 결과 entrypoint를 출력·린트·실행할 수 있게 해 줘요. 다만 Understand how CMD and ENTRYPOINT interact에서 모든 모서리 케이스를 이해하는 건 어려울 수 있어요. 이런 상황에서 entrypoint가 도움이 돼요.
entrypoint로 Nginx 이미지에서 컨테이너를 실행할 때 실제로 무슨 일이 일어나는지 조사해요.
$ docker debug nginx
...
docker > entrypoint
Understand how ENTRYPOINT/CMD work and if they are set correctly.
From CMD in Dockerfile:
['nginx', '-g', 'daemon off;']
From ENTRYPOINT in Dockerfile:
['/docker-entrypoint.sh']
By default, any container from this image will be started with following command:
/docker-entrypoint.sh nginx -g daemon off;
path: /docker-entrypoint.sh
args: nginx -g daemon off;
cwd:
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Lint results:
PASS: '/docker-entrypoint.sh' found
PASS: no mixing of shell and exec form
PASS: no double use of shell form
Docs:
- https://docs.docker.com/reference/dockerfile/#cmd
- https://docs.docker.com/reference/dockerfile/#entrypoint
- https://docs.docker.com/reference/dockerfile/#understand-how-cmd-and-entrypoint-interact
출력은 nginx 이미지가 시작될 때 /docker-entrypoint.sh 스크립트가 인자 nginx -g daemon off;로 실행된다는 걸 알려 줘요. --run 옵션으로 entrypoint를 테스트할 수 있어요.
$ docker debug nginx
...
docker > entrypoint --run
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2024/01/19 17:34:39 [notice] 50#50: using the "epoll" event method
2024/01/19 17:34:39 [notice] 50#50: nginx/1.25.3
2024/01/19 17:34:39 [notice] 50#50: built by gcc 12.2.0 (Debian 12.2.0-14)
2024/01/19 17:34:39 [notice] 50#50: OS: Linux 5.15.133.1-microsoft-standard-WSL2
2024/01/19 17:34:39 [notice] 50#50: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/01/19 17:34:39 [notice] 50#50: start worker processes
2024/01/19 17:34:39 [notice] 50#50: start worker process 77
...
이렇게 하면 실제로 컨테이너를 실행하지 않고도 디버그 셸에서 nginx를 시작해요. Ctrl+C로 nginx를 종료할 수 있어요.
명령 직접 실행하기 (예: 스크립팅)
대화형 세션을 시작하는 대신 --command 옵션으로 명령을 직접 평가할 수 있어요. 예를 들어 bash -c "arg1 arg2 ..."와 비슷해요. 다음 예제는 대화형 세션을 시작하지 않고 nginx 이미지에서 cat 명령을 실행해요.
$ docker debug --command "cat /usr/share/nginx/html/index.html" nginx
<!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>
--host 옵션으로 원격 디버깅하기
다음 예제들은 --host 옵션을 쓰는 방법을 보여 줘요. 첫 번째 예제는 SSH로 example.org의 원격 Docker 인스턴스에 root 사용자로 연결해 my-container 컨테이너에 셸을 얻어요.
$ docker debug --host ssh://[email protected] my-container
다음 예제는 다른 로컬 Docker Engine에 연결해 my-container 컨테이너에 셸을 얻어요.
$ docker debug --host=unix:///some/path/docker.sock my-container