빌드노트
컨테이너 파일에 로그가 있는데 docker logs는 비어 있다
앱의 로그 파일은 docker logs가 자동으로 읽는 대상이 아니다. 실제 컨테이너의 LogConfig·Docker 로그·앱 파일을 비교해 stdout·stderr로 보낼지 별도 파일 수집 경로를 사용할지 판단한다.
컨테이너 안의 로그 파일에는 기록이 쌓이는데 docker logs가 비어 있다면, 앱의 출력 경로부터 확인한다. Docker가 기본으로 수집하는 것은 stdout·stderr다. 앱이 일반 파일에만 쓴 로그는 그 파일이 컨테이너 안에 있다는 이유만으로 수집되지 않는다. stdout은 표준 출력, stderr는 표준 오류 출력이며, 파일에 쓰는 것과 다른 경로다. Docker의 컨테이너 로그 설명 (새 탭에서 열림)
실제 컨테이너의 설정과 파일을 함께 읽는다
Docker CLI가 대상 Linux 엔진에 연결돼 있어야 한다. PowerShell 7에서 $container에 조사할 컨테이너의 이름 또는 ID를 넣고, $logPath에 앱 설정에서 확인한 컨테이너 내부 로그 파일의 절대경로를 넣는다. 예를 들어 파일 경로가 /var/log/myapp/app.log라면 $logPath = '/var/log/myapp/app.log'로 지정한다. 호스트의 파일 경로를 넣는 자리가 아니다.
다음 세 명령을 한 줄씩 실행한다. 마지막 명령은 대상이 실행 중이고, 이미지에 cat이 있으며, 실행 사용자에게 파일 읽기 권한이 있을 때 쓸 수 있다. 작은 로그 파일을 읽는 예이며, 큰 파일에서는 전체 내용 대신 필요한 최근 부분을 확인한다.
docker inspect --format '{{json .HostConfig.LogConfig}}' $container
docker logs --tail 20 $container
docker exec $container cat $logPath
첫 명령은 그 컨테이너에 적용된 로그 드라이버와 옵션을 읽는다. 실습에서는 {"Type":"json-file","Config":{}}가 나왔다. json-file은 Docker가 수집한 로그를 JSON으로 저장하는 드라이버다. 앱의 임의 파일을 찾아 읽는다는 뜻은 아니다. 데몬의 현재 기본값보다 실제 컨테이너의 설정을 확인해야 한다. 로그 드라이버 설정 (새 탭에서 열림)
두 번째 명령은 Docker가 보관한 최근 20개 로그를, 세 번째는 지정한 앱 파일의 내용을 읽는다. docker logs가 종료 코드 0으로 끝나면서 비어 있고 파일에는 최근 기록이 있다면, 앱의 로깅 설정에서 파일 출력과 console 출력을 비교한다. 파일이 있다는 사실만으로 앱이 stdout·stderr에도 쓴다고 판단할 수는 없다.
조회 자체의 실패도 구별해야 한다. inspect가 대상을 찾지 못하면 이름·ID와 연결한 엔진을 확인한다. docker logs의 드라이버 오류는 빈 로그와 다르다. exec가 컨테이너 정지, 실행 파일 없음, 파일 없음 또는 권한 오류로 실패했다면 파일 내용을 확인한 것이 아니다. 앱이 쓰는 정확한 경로와 실행 상태부터 다시 확인한다. docker exec의 실행 조건 (새 탭에서 열림)
같은 파일이 있어도 Docker 로그는 달랐다
출력 경로만 비교하려고 새 컨테이너 두 개를 따로 실행했다. 하나는 /tmp/app.log에 FILE_ONLY 한 줄만 썼다. 다른 하나는 같은 파일 기록에 더해 stdout에 STDOUT_ONLY, stderr에 STDERR_ONLY를 한 줄씩 썼다. 실제 서비스 장애가 아니라 고정 문자열로 수집 입력을 구분한 실습이다.
Windows 11, PowerShell 7.6.6, Docker Desktop 4.92.0의 Linux 엔진 29.8.0, BusyBox 1.37.0에서 위 세 명령을 두 컨테이너에 각각 실행했다. 두 대상 모두 실행 중이었고 LogConfig는 json-file이었다. 네트워크는 none, 마운트는 없었으며 TTY도 할당하지 않았다. 관찰 결과는 다음과 같았다.
- 파일에만 쓴 컨테이너:
docker logs --tail 20은 종료 코드 0, stdout·stderr 모두 0바이트였다.exec ... cat은FILE_ONLY한 줄을 반환했다. - 파일과 표준 스트림에 쓴 컨테이너: Docker 로그의 stdout에는
STDOUT_ONLY, stderr에는STDERR_ONLY가 나왔다. 내부 파일은 여전히FILE_ONLY한 줄이었고, 그 문자열은 Docker 로그에 없었다. 터미널에 보이는 두 스트림의 순서는 비교하지 않았다.
파일을 읽은 뒤 docker logs를 다시 조회해도 두 결과는 그대로였다. docker exec ... cat의 stdout은 새로 실행한 cat 명령의 응답이다. 파일을 조회했다고 그 내용이 원래 앱의 Docker 로그로 추가되지는 않았다. 이 비교에서는 컨테이너 종료나 파일 삭제 없이도 파일 내용과 Docker 로그가 갈렸다.
출력 경로를 고친 뒤 같은 로그를 다시 확인한다
파일에는 새 기록이 있는데 console 출력이 꺼져 있다면, docker logs로 볼 로그를 앱의 stdout·stderr로 보내도록 로깅 설정을 바꾼다. console 출력 설정이 있어도 시작 스크립트에서 표준 스트림을 파일로 돌리고 있지 않은지 함께 확인한다. 변경을 적용한 뒤 로그를 남기는 동작을 한 번 실행하고, 파일과 Docker 로그에 그 기록이 각각 나타나는지 대조한다.
파일 기록을 계속 사용할 목적이라면 해당 경로를 읽는 별도 로그 수집기를 구성할 수 있다. 어느 쪽이든 실제 기록 경로를 수집 경로에 연결해야 한다. docker logs --follow는 stdout·stderr로 들어오는 새 로그를 계속 보여 주는 옵션이므로, 일반 파일을 읽게 만드는 해결책은 아니다. docker logs 명령 (새 탭에서 열림)
앱이 이미 표준 스트림으로 출력한다면 LogConfig의 Type과 Config를 확인한다. 공식 문서상 none 드라이버는 로그를 제공하지 않는다. 읽기를 지원하지 않는 원격 드라이버에서는 로컬 조회용 캐시인 dual logging이 기본으로 사용되지만, 그 캐시까지 비활성화하면 docker logs가 오류를 낼 수 있다. 이 경우에는 앱의 파일 출력과 별개로 드라이버와 캐시 설정을 조사한다. 원격 드라이버와 dual logging (새 탭에서 열림)
직접 실행한 범위는 Linux 엔진의 json-file, TTY 없는 두 컨테이너와 세 고정 문자열이다. 다른 드라이버, 앱별 console 설정 변경과 별도 파일 수집기 구성은 실행하지 않았다. 자기 앱에서는 파일의 최근 기록, 실제 표준 스트림 출력, 적용된 로그 드라이버를 대조해 어느 경로를 연결할지 결정한다.