[Kubernetes] 로그 & 로깅 아키텍처
[Kubernetes] 로그 & 로깅 아키텍처
안녕하세요? 정리하는 개발자 워니즈입니다. 이번시간에는 쿠버네티스 로깅에 대해서 정리를 해보려고 합니다. 필자가 속한 프로젝트에서는 쿠버네티스 구성을 마치고 서비스 운영을 하고 있습니다. 그런데, 한가지 우려스러운 부분이 있었습니다. 바로 로깅쪽인데요.
아키텍처를 구성하는 가운데, 위험 요소가 있는 부분이 있었습니다. 바로 EFS내에 모든 POD들을 mount 시켜서 로그를 한데 모으는 구성을 했었습니다. 그러다보니 급격하게 쌓이는 로그때문에 Bursting Credit을 모두 소비하고 NetworkI/O가 평소보다 떨어지면서 각 POD에서는 CPU Load가 걸렸고 서비스 요청에 대한 처리가 지연이 되는 아주 큰 묹네가 있었습니다.
이슈가 발생했을 당시, EFS의 모드를 Bursting 모드에서 Provisioned 모드로 변경하여 이슈를 해결했었습니다. EFS의 내용에 대해서는 추후에 정리하도록 하겠습니다.
지난 글들은 아래를 참고 해주시면 됩니다.
- 쿠버네티스 1편 : 설치 가이드
- 쿠버네티스 2편 : pod
- 쿠버네티스 3편 : service
- 쿠버네티스 4편 : deployment
- 쿠버네티스 5편 : pod 설정
- 쿠버네티스 6편 : 배포 전략
- 쿠버네티스 7편 : volume
- 쿠버네티스 8편 : daemonset
- 쿠버네티스 9편 : 테라폼을 통한 클러스터 구성
- 쿠버네티스 10편 : eks에서 volume 사용하기
- 쿠버네티스 11편 : helm
- 쿠버네티스 12편 : helm chart template
- 쿠버네티스 13편 : helm deploy
- 쿠버네티스 14편 : fluentd를 통한 log수집
- 쿠버네티스 15편 : chartmuseum
- 쿠버네티스 16편 : 배포툴(ArgoCD 설치방법/사용법)
- 쿠버네티스 17편 : 배포툴(ArgoCD 구성/알람)
- 쿠버네티스 18편 : 쿠버네티스 Autoscailing
- 쿠버네티스 19편 : 쿠버네티스 로깅 아키텍처
1. Docker의 로그 관리
우선 Docker에서 로그를 관리하는 방법에 대해서 정리를 해보겠습니다. 도커를 조금 사용해보시면 알겠지만, 기본적으로 Docker Hub에서 제공해주는 이미지를 받아서 docker run 명령을 통해서 실행하게 되면, docker logs와 같은 명령으로 터미널 창에서 바로 로그를 볼 수 있었습니다.
여기서 궁금한게, 그럼 저 명령어는 어디에 있는 로그를 읽어서 보여주는 것일까요? 도커 컨테이너의 로그는 JSON 포맷으로 로그를 쌓아 둡니다.
/var/lib/docker/containers 라는 공간으로 이동하게 되면, 도커의 id값을 갖고, 폴더를 생성해 둡니다. 그리고 하위로 들어가면 json 포맷의 타입으로 로깅을 하고 있습니다.
1-1. 컨테이너에서의 로그 관리
로그는 표준 출력으로 남기고 이 내용을 호스트에서 파일에 수집하는 것이 더 간단하며 이것이 도커에서는 정석으로 여겨지고 있습니다.
Docker에서는 위에 보시는것과 같이, container의 application로그를 stdout/stderr로 출력을 하게되면, Docker daemon이 해당 로그를 특정 경로(/var/lib/docker/containers)로 적재를 해둡니다.
1-2. Nginx 컨테이너에서의 로깅
하나의 예시를 들기 위해서 Nginx 이미지를 받고 run을 해보았습니다. 그리고 그 안으로 접속을 한 뒤, 로그 파일을 찾아가 보면, access.log자체가 심볼릭 링크로 /dev/stdout으로 걸려있는것이 보일 것입니다.
- Nginx access로그 발생 -> /var/log/nginx/access.log 파일에 Writing
- /varlog/nginx/access.log –> /dev/stdout 으로 심볼릭 링크
- Docker daemon은 컨테이너의 표준 출력으로부터 발생하는 로그에 컨테이너의 추가 Value(Container ID, Timestamp)등을 추가하여 Json 파일 형태로 기록
- docker logs라는 명령어는 json파일을 보여줌
docker info라는 명령어를 치게 되면, Logging Driver: json-file 와 같이 나타나게 되는데, 기본적으로 Docker daemon에서는 json형식의 로깅을 사용합니다. 이부분을 변경하기 위해서는 /etc/docker에 daemon.json파일을 수정해야 합니다.
2. Kubernetes 로깅 아키텍처
필자가 속한 프로젝트에서는 기존에 EFS에서 공통으로 쌓던 로그를 분리하고, 각 worker host내에 docker daemon에 의해서 표준 출력과 표준 에러로 redirection되는 로그를 적재 하도록 변경했습니다. 우선 아래와 같이 2가지 방식에 의해서 쿠버네티스 로깅 아키텍처는 정리가 됩니다 .
- Cluster-level
클러스터 레벨에서의 로깅은 다음의 3가지를 주로 제공하고 있습니다.
- 모든 node에서 동작하는 node-level logging agent 활용
Logging agent는 각각의 node에 저장된 log file들에 대해서 수집하는 장치이다. 이런 방식은 각 node마다 한개의 logging agent pod이 띄어지기 때문에 가장 보편적이고 쉬운 접근방식일 것이다. 하지만, node-level logging은 오직 애플레케이션의 standard output과 standard error에 대해서만 확인 가능하다.
- 각각의 application pod에 로깅을 위한 sidecar 합치기
Side car container 방식은 로그를 확인하고자 하는 application의 다른 format에 대해 각각의 stdout, stderr stream을 생성한다. 이는, application의 로깅 특성에 맞추어서 분리 수집 가능하다.
- application이 특정 로깅 db에 직접 log를 푸시
만약 node-level logging agent가 node에 flexible하게 작동하지 않는다면? 사이드카 컨테이너가 직접 로그를 로그 수집기에 전달하는 방식도 있다.
- Node-level
컨테이너화 된 앱들은 stdout 혹은 stderr을 통해 어딘가로 로그를 떨어트리게 된다. 예를들어 Docker container engine은 두개의 stream을 logging driver로 redirect 시킨다.
만약 컨테이너가 재시작되면, kubelet은 이 로그를 삭제시키게 된다. 만약 pod이 node로 부터 삭제되면, 컨테이너 뿐만아니라 로그들도 사라지게 된다.
node의 storage가 다 차면 위험하므로, node-level-logging에서 log rotation은 매우 중요하다.
3. Logrotation 적용
필자가 속한 프로젝트에서는 아직 클러스터 단위의 로그를 모두 수집할 필요는 없어서, 위의 소개 된 내용중 Node 레벨에서 로그를 적재 하고 있습니다. 기본적으로 출력되는 로그를 모두 stdout/stderr로 심볼릭 링크를 걸어서 보내고, 각 worker node의 docker daemon에서 로그를 수집해서 적재하도록 했습니다.
그런데, /var/lib/docker/containers의 용량이 문제가 있었습니다. 그래서 Logrotation을 걸어두기로 했고, 각 worker node에 logratate.d를 이용하여 걸어두기로했습니다.
-/usr/sbin/logrotate : 데몬의 위치 및 데몬프로그램
– /etc/logrotate.conf : 설정 파일.
– /etc/logrotate.d : logrotate를 적용할 로그 파일 보관 디렉토리.
– /var/lib/logrotate.status : logrotate가 작업 내역 보관 파일.
– /etc/cron.daily/logrotate : logrotate : cron 에 의해 일 단위로 실행한다.
/var/lib/docker/containers/*/*.log {
rotate 30
maxage 7
size=10M
daily
notifempty
compress
missingok
delaycompress
datetext
copytruncate
}
- 옵션 상세 설명
rotate 30(숫자) : log파일 30개 이상 되면 삭제
maxage 30(숫자) : 30일 이상된 로그 파일 삭제
size : 지정한 용량이 되면 로그로테이트를 실행한다. 10k, 10M 이런식으로 지정한다.
create : [권한 유저 그룹] 으로 rotation된 로그파일 생성
ifempty/notifempty : log 내용이 있으면/없으면 rotation 하지 않는다.
daily/weekly/monthly : 일/주/월 단위로 로테이트 한다.
compress/nocompress : rotate 된 로그 gzip 압축/압축을 하지 않음.
missingok : 로그 파일이 발견되지 않는 경우 에러처리 하지 않음.
datetext : 백업 파일의 이름에 날짜가 들어가도록 함.
prerotate-endscript : 사이의 명령어를 로그파일 처리전에 실행한다.
postrotate-endscript : 사이의 명령어를 로그파일 처리후에 실행한다.
copytruncate : 이옵션을 넣지 않으면 현재 사용중인 로그를 다른이름으로 move하고 새로운 파일을 생성한다.
4. 마치며..
처음에 쿠버네티스 개념부터 로깅 아키텍처까지 얼추 한싸이클로 겉핡기지만 쿠버네티스를 운영할 수 있는 개념을 잡은거 같습니다. 아직 세밀하게 조정하거나 알아야될 부분이 많지만 그래도 쿠버네티스에게 배포나 인프라 관리를 위임하고 그위에서 마치 블록을 쌓듯 플랫폼을 활용하니, 정말 많은 장점이 있는 것 같습니다.
이번 시간에는 쿠버네티스의 로그를 분리하고 Disk I/O를 해결하기 위한 방안으로 노드레벨에서의 로깅 아키텍처에 대해서 정리를 하였고 또 로그 로테이션의 옵션에 대해서 간단하게 정리를 해 보았습니다.
추후에는 좀더 고급화된 기술로 쿠버네티스 포스팅을 이어나갈 계획입니다. 앞으로 많은 관심 부탁드립니다.
5. 참고
Writing and Managing Application Logs with Docker