Infra

[prolog] prolog 에 로그 모니터링 시스템 구축기(2)

자바생 2023. 8. 2. 21:08
728x90

 

이전 글과 이어지는 글입니다!

 

 

로그 레벨마다 따로 패널(모니터)을 보여주는 게 어떨까?

 

현재 레벨마다 로그들이 다른 디렉터리에 저장되어있으므로 레벨마다 다른 모니터(패널)로 보여주는 게 어떨까라는 생각이 들었습니다.

 

추후에 JSON 형식으로 로그를 남긴 후에 이 로그들을 ES로 전달하는 방법을 생각하고 있어서, 기존 로그들은 유지한 채로 인덱싱 하기로 결정했습니다.

 

 

이전 로그들을 legacy-log, 나중에 JSON 형식의 로그들은 new-log(예시)라는 인덱스에 저장하기로 했습니다.

 

그래서 아래와 같이 filebeat.yml 을 수정해 보았습니다.

 

 

                                                                                                                  42,17        모두
filebeat.inputs:

- type: log

  enabled: true

  paths:
    - /var/app/current/logs/info/info.log
    - /var/app/current/logs/error/error.log
    - /var/app/current/logs/warn/*.log
  fields:
    log_type: legacy-log

- type: log

  enabled: true
  paths:
    - /var/log/nginx/access.log
  fields:
    log_type: access-log

- type: log
  enabled: true
  paths:
    - /var/log/nginx/error.log
  fields:
    log_type: access-error-log

output.elasticsearch:
  hosts: [ES주소:포트번호]
  username: [username]
  password: [password]
  indices:
    - index: "filebeat-%{[agent.version]}-access-log"
      when.equals:
        fields.log_type: "access-log"
    - index: "filebeat-%{[agent.version]}-access-error-log"
      when.equals:
        fields.log_type: "access-error-log"
    - index: "filebeat-%{[agent.version]}-legacy-log"
      when.equals:
        fields.log_type: "legacy-log"

 

 

아까랑 다르게 많이 추가된 것 같지만, 결국 info.error.warn 로그는 legacy-log로, nginx-log는 access-log 타입으로 저장한 것입니다.

 

 

그리고 output.es의 indices는 인덱스의 이름을 그 타입에 따라 저장한 부분입니다.

 

 

 

 

 

결과

 

 

 

 

 

 

 

운영 서버로부터 배포

 

현재까지 과정은 운영서버에서 직접 명령어를 입력하고, 실행한 결과입니다.

 

 

지금부터가 이제 진짜 시작입니다,,(이 부분에서 한 달 반 가량 쓴 것 같네요)

 

 

 

지금 프롤로그는 Elastic beanstalk을 통해서 배포하고 있습니다.

그래서 환경변수들이 beanstalk에 저장되어 있고, beanstalk 이 배포하면서 해당 서버에 저장하게 됩니다.

 

 

하지만 지금 filebeat.yml에서 ES에 관한 username, password가 필요하기 때문에 해당 환경변수들의 값이 필요합니다.

 

 

당연히 해당 정보는 아주 중요한 것이기 때문에 스크립트에 바로 명시할 수 없어요.

 

 

 

서칭을 하다 보니 beanstalk의 Environment Properties가 저장되어 있는 디렉터리가 존재하고, 명령어를 통해서 지정된 Key 값을 통해 값을 가져올 수 있었습니다.

 

 

echo $(sudo /opt/elasticbeanstalk/bin/get-config environment -k [KEY])

 

 

 

그러면 이제. ebextensions를 수정해 볼게요.

 

 

files:
  "/tmp/make_filebeat_yml.sh":
    mode: "000755"
    content: |
      #!/bin/bash
      PASSWORD=$(sudo /opt/elasticbeanstalk/bin/get-config environment -k ELASTICSEARCH_PASSWORD)
      HOST=$(sudo /opt/elasticbeanstalk/bin/get-config environment -k ELASTICSEARCH_HOST)
      PORT=$(sudo /opt/elasticbeanstalk/bin/get-config environment -k ELASTICSEARCH_PORT)
      USER=$(sudo /opt/elasticbeanstalk/bin/get-config environment -k ELASTICSEARCH_USER)
      cat > /home/ec2-user/filebeat-7.14.1-linux-x86_64/filebeat.yml <https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.14.1-linux-x86_64.tar.gz>
      tar xzvf filebeat-7.14.1-linux-x86_64.tar.gz
  02_run_make_filebeat_yml_script:
    command: "bash /tmp/make_filebeat_yml.sh"
	03_run_filebeat_yml:
		command: "nohup /home/ec2-user/filebeat-7.14.1-linux-x86_64/filebeat -e &"

 

 

. ebextensions란 elastic beanstalk에서 세부 사항(config)들을 배포할 때 실행해 주는 파일이라고 생각하면 됩니다. json이나 yml 형식으로 작성해야 하는 특징을 가지고 있어요.

 

 

간단히 설명하면 files 명령어를 통해서 해당 스크립트를 생성하고, commands에 있는 명령어들을 실행해요.

 

 

 

PROD 서버에서도 환경변수의 키 값이 같기 때문에 이대로 스크립트를 배포해도 이상 없었습니다.

 

 

 

이제 DEV에 배포하니 제대로 filebeat가 실행됐습니다.

 

 

 

하지만 문제는 PROD 서버에서 생겼죠,,

 

 

PROD 서버에서 문제 발생

 

현재. ebextensions에 있는 config 파일은 yml로 작성되어 있습니다.

가독성이 좋기 때문에 yml을 선택했죠.

 

 

 

하지만 DEV에서는 멀쩡하게 배포된 친구가 갑자기 PROD에 배포하니까 EOF가 아주 지 마음대로 왼쪽에 안 붙어있고 자유분방하게 움직이더라고요.

 

 

 

그래서 제대로 스크립트를 읽지 못하고 계속해서 에러가 발생했습니다.

 

 

몇 번 시도한 끝에 결국 이 EOF는 자리를 잡지 못하고, 계속 방황을 하여 “json 으로 수정하자”라고 생각했습니다.

 

 

이것 때문에 토미에게 DEV 재배포를 몇 번이나 말씀드렸었습니다,,

 

 

json으로 수정한 후에 DEV부터 다시 차근차근 배포해 보기로 했습니다.

 

 

filebeat 재실행 시 문제 발생

 

 

앞에서 적어놓았던 filebeat가 실행되고 있는데 또 실행하게 되면 충돌이 발생하게 됩니다.

 

 

 

 

그래서 애초에 배포할 때, 현재 filebeat가 동작하고 있으면 kill 하고 실행해야 합니다.

 

 

다시. ebextensions에서 filebeat 재 시작 스크립트를 작성해야 합니다.

 

 

 

#!/bin/bash

PID=$(ps -ef | grep filebeat | grep -v grep | awk '{print $2}')

if [[ -n "$PID" ]]; then
  echo "Filebeat PID : $PID"
  kill -9 $PID
else
    echo "Filebeat is not running."
fi

echo "Restarting Filebeat"
nohup ./filebeat -e &

echo "Filebeat restarted"

 

 

 

 

 

. ebextensions의 config

 

{
  "files": {
    "/tmp/make_filebeat_yml.sh": {
      "mode": "000755",
      "content": "#!/bin/bash\\nPASSWORD=$(sudo /opt/elasticbeanstalk/bin/get-config environment -k ELASTICSEARCH_PASSWORD)\\nHOST=$(sudo /opt/elasticbeanstalk/bin/get-config environment -k ELASTICSEARCH_HOST)\\nPORT=$(sudo /opt/elasticbeanstalk/bin/get-config environment -k ELASTICSEARCH_PORT)\\nUSER=$(sudo /opt/elasticbeanstalk/bin/get-config environment -k ELASTICSEARCH_USER)\\ncat > /home/ec2-user/filebeat-7.14.1-linux-x86_64/filebeat.yml <<eof\\nfilebeat.inputs:\\n- type:="" log\\n="" enabled:="" true\\n="" paths:\\n="" -="" var="" app="" current="" logs="" info="" info.log\\n="" error="" error.log\\n="" warn="" *.log\\n="" fields:\\n="" log_type:="" legacy-log\\n-="" log="" nginx="" access.log\\n="" access-log\\noutput.elasticsearch:\\n="" hosts:="" [\\"$host:$port\\"]\\n="" username:="" \\"$user\\"\\n="" password:="" \\"$password\\"\\n="" indices:\\n="" index:="" \\"filebeat-%{[agent.version]}-access-log\\"\\n="" when.equals:\\n="" fields.log_type:="" \\"access-log\\"\\n="" \\"filebeat-%{[agent.version]}-legacy-log\\"\\n="" \\"legacy-log\\"\\neof"="" },="" "="" tmp="" install_filebeat.sh":="" {="" "mode":="" "000755",="" "content":="" "#!="" bin="" bash\\nwget="" <<a="" href="https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.14.1-linux-x86_64.tar.gz">https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.14.1-linux-x86_64.tar.gz\\ntar> xzvf filebeat-7.14.1-linux-x86_64.tar.gz -C /home/ec2-user\\n"
    },
    "/tmp/restart_filebeat.sh": {
      "mode": "000755",
      "content": "#!/bin/bash\\n\\nPID=$(ps -ef | grep ./filebeat | grep -v grep | awk '{print $2}')\\n\\nif [[ -n \\"$PID\\" ]]; then\\n  echo \\"Filebeat PID : $PID\\"\\n  sudo kill -9 $PID\\nelse\\n    echo \\"Filebeat is not running.\\"\\nfi\\n\\necho \\"Restarting Filebeat\\"\\ncd /home/ec2-user/filebeat-7.14.1-linux-x86_64 && sudo chmod 644 filebeat.yml && (nohup ./filebeat -e > filebeat.log 2>&1 &)\\necho \\"Filebeat restarted\\"\\n"
    }
  },
  "commands": {
    "01_run_install_filebeat": {
      "command": "bash /tmp/install_filebeat.sh"
    },
    "02_run_make_filebeat_yml_script": {
      "command": "bash /tmp/make_filebeat_yml.sh"
    },
    "03_run_filebeat": {
      "command": "bash /tmp/restart_filebeat.sh"
    }
  }
}
</eof\\nfilebeat.inputs:\\n->

 

 

 

 

 

결론

 

 

 

이렇게 하나의 목표를 정하고, 약 두 달이라는 긴 시간에 걸쳐서 성공한 경험이 처음입니다.

대부분 많이 걸려도 일주일 내외였는데 말이죠,,(그만큼 큰일이 아니었던 일들)

 

 

이번 트러블 슈팅을 통해서는 정말 많은 것들을 얻을 수 있었습니다.

문제를 마주했을 때, 어디에서 발생하는지 그 원인을 찾아야 하는 게 가장 중요해요.

다만 이 원인을 찾기 위해서는 명확한 근거가 필요하죠.

 

 

그 명확한 근거는 다양한 방법이 있겠지만 이번에 제가 배운 것은 바로 “로그”였습니다.

 

 

물론 사람들이 “로그 중요해, 로그 너무 중요해” 아무리 외쳐도 직접 느끼는 것보다 덜하잖아요?

이번에 확실히 느꼈습니다. 로그는 진짜 보물이에요..

 

 

아무튼 로그가 없으면 뇌피셜로 아무리 문제를 정의해도 명확한 근거가 없기 때문에 상대방에게 말하기도 애매하고, 직접 해결하려는 나 조차도 믿을 수 없는 거죠.

 

 

“일단 해보자”라는 마인드도 중요하지만, 해당 마인드도 통하지 않는 상황이 존재합니다.

이번 상황에서 예로 들자면 혼자 하는 프로젝트가 아니기 때문에 계속해서 혼자 배포만 하기 어렵습니다. 그래서 한번 할 때 최대한 신중하게 해야 하는데, 이럴 때 명확한 근거를 기준으로 한다면 실패 횟수를 조금이나마 줄일 수 있지 않을까라는 생각이 들었습니다.

 

 

 

또 하나는 리눅스 명령어예요,,

 

 

눅린이도 아니고 거의 눅아기 수준이었는데, 이번 과정을 통해서 리눅스와 많이 친해질 수 있었던 것 같아요. 몇 십 번을 쳐보니까 그냥 외워지더라고요.

 

그리고 절대 경로,, 무조건 자동 배포 이런 거 할 때는 절대 경로 사용하십시오,, 절대 상대 경로를 믿지 마세요,,라고 느꼈습니다,, 물론 눅린이가 하는 소리라 그냥 가볍게 넘기시면 됩니다.

 

상대경로로 계속 안 돼서 절대경로로 변경하고나서부터는 잘 해결되더라고요,,

 

 

약 두 달간의 글을 이렇게 작성해 보려니까 “얘 얼마 안 해보고 성공했네”라고 생각이 들더라고요.(절대 아님)

 

그 시간 동안 진짜 재밌었지만 머리도 많이 아팠습니다,,

그래서 흔적을 남기고 싶어서 배포한 흔적이랑 커밋 한번 올려보고 글을 마무리해보겠습니다.

 

 

 

 

 

별로 없어 보일 수 있지만 그 안에 re-run 한 job들이 숨겨져 있습니다,,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90