과거의 내가 미래의 나에게
docker logs를 살펴봄 본문
사건의 발단은 nginx.conf의 지시문들을 하나하나 뜯어보며 공부하다가 access.log를 직접 보고 싶은 열망으로 인해 시작되었다. 하지만 내가 임시로 설치한 wsl의 우분투 안의 nginx는 아직 conf 파일만 들락날락하느라 서버의 요청을 받은 적이 없고 따라서 로그는 없을 것이었다.
그래서 vpn을 통해 회사 서버에 접속하여 access.log를 까보려하는데 왠걸 너무 느린 것이다. access.log의 파일 크기가 너무 큰가 싶어서 파일 크기를 알 수 있는 명령어( ls -lh access.log)로 크기를 확인한 후 너무 크다면 로그 롤링이라는 것을 해봐야겠다! 했지만 출력된 것은 아래와 같았다.
Irwxrwxrwx 1 root root 11 Nov 12 02:03 access.log —> /dev/stdout
해당 내용을 gpt에게 물어봤더니 I는 이 파일이 심볼릭 링크임을 나타낸다는 것과 이 심볼릭 링크는 /dev/stdout을 가리킨다는 것을 알 수 있었다.
< 심볼릭 링크 >
파일 시스템에서 하나의 파일이나 폴더를 가리키는 특수한 파일이다. 심볼릭 링크 파일은 다른 내용은 없고 이 파일이 가리키는 파일이나 디렉토리의 위치만 기억한다.
nginx.conf나 docker-compose, 해당 컨테이너의 도커 이미지를 생성하는 도커 파일 전부를 봤지만 access.log를 심볼릭 링크로 설정하는 것은 없는데? 그리고 이게 가리키고 있는 /dev/stdout은 대체 뭐지? 와 같은 여러 질문들이 생겼고, 그에 대한 해답은 docker log와 관련있었다.
그렇다. 지금까지는 서론이었고, 오늘의 학습 내용은 docker logs에 대해 맛보는 것이다.
docker logs
다양한 컨테이너들이 도커에 쌓이고 돌아간다. 각 컨테이너들은 계속해서 동작을 하고 각종 로그를 쌓음으로 인해 개발자가 컨테이너에서 어떤 일이 있었는지 파악할 수 있는 수단이 되어준다.
하지만 모든 컨테이너들을 일일이 들어가서 파악하기엔 비효율적이다. 따라서 도커는 컨테이너들의 로그를 표춘 출력(stdout: 프로그램이 정상적인 상태에서 생성한 로그들)과 표준 에러(stderr: 프로그램이 에러가 발생했을 때 생성한 로그들)를 통하여 로그를 별도로 저장하고 이를 확인할 수 있는 명령어를 제공해주는데 이것이 docker logs 명령어이다.
이를 nginx의 예시로 들어보면 다음과 같다.
1. nginx access 로그가 발생
2. nginx는 access_log의 설정대로 "/var/log/nginx/access.log"에 로그로 이동
3. access.log는 /dev/stdout으로 연결된 심볼릭 링크로 이어져 있기에 access.log에 기록되는 것이 아닌 표준 출력으로 전달됨
< 참조 >
/dec/stdout은 폴더나 일반 파일이 아니라 특수한 파일이다. 이는 표준 출력 장치를 의미하는 것으로 로그 데이터를 파일로 저장하는 것이 아닌 터미널같은 곳으로 바로 출력 시켜버리는 것이다. 그니깐 로그가 들어오면 터미널에 출력해버린다는 느낌?4. docker는 표준 출력으로 발생한 로그를 수집하여 컨테이너의 정보와 함께 JSON 형태의 파일로 기록
5. docker logs 명령어를 통해 위와 같이 쌓인 수많은 JSON 형태의 파일을 보여줌!
docker logs는 이러한 과정을 거쳐서 로그를 볼 수 있게 해주는 것이고, 내가 위에서 accecss.log에 대한 파일을 찾을 수 없는 이유도 이러한 것이었기 때문이다.
참고로, 내가 심볼릭 링크로 하는 설정을 하지 않았음에도 된것은 nginx 기본 도커 이미지에서 그렇게 처리하지 않았나 추론하고있다.
logging 설정
위에서 docker가 표준 출력으로 발생한 로그를 수집하여 JSON 형태의 파일로 기록한다는데, 이는 도커가 기본적으로 json-file 로깅 드라이버를 사용하기에 그렇다. 이는 모든 컨테이너의 표준 출력 로그와 표준 에러 로그를 캡쳐해 JSON 형식의 파일로 저장하여 /var/lib/docker/containers/{container-id}/{container-id}-json.log에 기록하는 방식이다.
docker-compose 파일에서 아래와 같이 설정할 수 있다.(설정 안해도 기본적으로 "json-file"이다)
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
option으로 max-size와 max-file이 있고, 각각 로그의 최대 사이즈와 로그 파일의 수를 설정한다. 설정한 것을 초과한다면 오래된 파일은 제거하여 로그 파일이 무한정 커지는 것을 방지한다.
"json-file"외에 "syslog"와 "none" 옵션도 있다. syslog는 별도 서버로 로그를 전송하여 중앙집중된 로그 관리 방식을 추구 할 수 있게끔 하고, "none"의 경우 아예 로그를 안쓴다는 것이다. 특히 syslog는 rsyslog를 써서 중앙 컨테이너로 로그를 저장할 수 있다고 하는데 이는 기회가 되면 나중에 좀 더 알아보도록 하겠다.
docker logs 명령어 정리
docker logs #containerId || name
- containerId || name: 컨테이너명을 써서 해당 컨테이너의 로그만을 살펴볼 수 있다.
- --tail: 로그 파일의 가장 끝부분을 표출하는 것이다. 구체적인 숫자를 뒤에 넣어 줄 수를 설정할 수 있다.
- --since: 특정 시간 이후의 로그를 출력한다. 정확한 시간 입력도 가능하고 1시간 동안의 로그 같은 상대적 시간도 가능하다.
- --until: 특정 시간까지의 로그를 출력한다.
- -t: 로그에 타임스탬프 정보를 추가하여 출력한다.
- -f: 로그를 실시간으로 모니터링 할 때 사용된다. 로그 파일에 새로운 로그가 추가될 때마다 출력된다.
로그를 쌓고 관리하는 것은 프로그램을 유지보수하는데에 있어서 대단히 중요한 일이다. 유지보수뿐만이겠나, 이를 활용해 프로그램을 더 강화할 수도 있을 것이다.
오늘 본 것은 도커의 기본적인 로깅 방법이다. 로그를 쌓고 관리하는 데 더 힘을 쏟으려면 수많은 라이브러리도 탐방해야할 것이고 또 네트워크 연결적인 문제도 봐야할 것이고 알아가야할 것은 많을 것이다.
선택과 집중으로 인해 나는 여기까지만 알아보고 끝내지만 또 언젠간 이를 선택할 일이 생긴다면 그 때는 더 많이 배워야하겠단 생각이 든다.
참고 문서
- [Docker Container의 로깅 구조에 대한 요약정리] - https://silentbrain.tistory.com/20