본문 바로가기

Programming/프로그래밍 내용 정리

[Linux] top과 jstat -gc 명령어로 서버와 GC 살펴보기(with 챗지피티)

📌 크롬은 용량을 너무 많이 차지해....😢

 

최근에 크롬창을 띄워 스크래핑을 진행하는 서버를 개발하면서 만난 이슈가 하나 있었다.

 

테스트를 위한 스테이지 서버는 t3.small 인스턴스 유형으로 세팅이 되어 있었는데,

하나의 스크래핑 요청만 진행할 때는 이상없이 진행됐지만

2개의 스크래핑을 동시에 진행하니 서버가 뻗어버렸다.  (겨우 2개에 뻗다니 ㄷㄷㄷ)

 

 

 

 

 

가장 큰 원인은 스크래핑이 실패하는 경우 무조건 크롬창을 닫아야 하는데, 

부분부분 특정 단계에서 크롬창이 닫히지 않고 그대로 떠있어서

크롬이 많은 용량을 차지하고 있었기 때문이었다.

(크롬의 용량은 상당히 크다..........!)

 

그래서 어떤 단계에서든 스크래핑이 실패하면 무조건 크롬창을 닫도록 코드를 수정하니 

동시의 2개의 스크래핑 요청이 온다고 해서 서버가 뻗는 일은 없었다.

 

 

 

상용서버는 t3.medium 으로 용량을 늘렸고,

서버가 동시에 최대 몇 개까지 스크래핑 요청을 수용할 수 있는지 개발 실장님과 테스트를 진행했다.

 

API가 동시에 여러 번 호출되는 것과 비교하여 크롬을 여러 개 띄우는 것은 확실히 용량 차이가 커서

이 부분에 대한 테스트가 반드시 필요했다.

 

 

테스트를 진행하면서 주로 사용한 리눅스 명령어가 'top' 과 'jstat -gc <pid>' 였다.

 

먼저 해당 리눅스 서버에 접속하여 'top' 이라고 명령어를 치면 아래와 같은 데이터를 확인할 수 있다.

테스트를 하면서 여러 데이터 중 주로 'CPU 사용량'과 '메모리 사용량' 위주로 확인했다.

 

 

CPU 사용량

빨간색 박스로 표시된 %Cpu(s)라는 영역은

CPU가 어떻게 사용되고 있는지 그 사용율을 보여주는 영역이다.

 

모든 값의 총 합은 100% 이며, 이를 퍼센테이지로 나누어서 보여준다.

각 요소의 의미는 아래와 같다.

 

  • us : 프로세스의 유저 영역에서의 CPU 사용률
  • sy : 프로세스의 커널 영역에서의 CPU 사용률
  • ni : 프로세스의 우선순위(priority) 설정에 사용하는 CPU 사용률
  • id : 사용하고 있지 않는 비율
  • wa : IO가 완료될때까지 기다리고 있는 CPU 비율
  • hi : 하드웨어 인터럽트에 사용되는 CPU 사용률
  • si : 소프트웨어 인터럽트에 사용되는 CPU 사용률
  • st : CPU를 VM에서 사용하여 대기하는 CPU 비율

 

메모리 사용량

노란색 박스로 표시된 부분은 메모리 사용량을 보여주는 영역이다.

 

첫번째 줄은 RAM의 메모리 영역으로 Mem이라 표시되어있는 부분이고

아랫줄은 디스크를 메모리 처럼 이용하는 Swap 메모리 영역이다.

일반적으로 Mem의 사용량이 거의 가득 찼을때 Swap 메모리 영역을 사용한다.

 

  • total : 총 메모리 양
  • free : 사용가능한 메모리 양
  • used : 사용중인 메모리 양

 

buff/cache는 IO와 관련되어 사용되는 버퍼에 사용되는 메모리를 말한다.

이 메모리가 있으므로써 IO에 상대적으로 빠른 속도를 가질 수 있다.

avail Mem은 swap 메모리를 사용하지 않고 사용할 수 있는 메모리의 크기를 말한다.

 

 

 


📌 그래서 어떻게 테스트를 진행했을까

 

동시에 스크래핑을 요청하는 개수를 2개에서부터 10개까지 늘려가며

몇 개까지는 이상없이 스크래핑 요청이 처리되는지,

몇 개부터는 서버가 뻗는지 'top' 명령어를 통해 그 때 그 때마다 CPU와 메모리 사용량을 보면서 일단 체크를 했다.

 

그리고 스크래핑 서버가 최대 수용할 수 있는 요청 갯수를 알아낸 뒤, 

AWS SQS(메세징 큐)를 통해 조절하고자 했다.

 

 

원래는 스크래핑 요청이 오면 그 때 그 때마다 크롬창을 띄우는 방식이었는데

실장님께서 '크롬을 띄우는 시간도 아끼는 것이 좋겠다.'고 의견을 내셨고, 아래와 같이 방식을 변경했다.

 

- AS-IS : 스크래핑 요청이 올 때마다 서버에서 크롬창을 띄워서 진행 (최대 요청 개수는 메세지 큐로 조절)

 

- TO-BE : 미리 크롬창을 6개를 띄워놓고, 사용가능한 크롬창이 있는 경우 그 크롬창을 리스트에서 get하여 스크래핑 진행

               스크래핑을 완료한 크롬창은 로그아웃 처리 하여 다시 리스트에 반납

               (메모리 및 CPU 테스트를 통해 크롬은 최소 6개 / 최대 10개까지 띄우는 것으로 설정)

 

 

TO-BE 방식으로 변경한 이유는

한 꺼번에 크롬을 띄우게 되면 처음 랜더링 되는 시간이 오래 걸릴 수 있어 조금 더 안정적으로 진행하기 위함

프로세스를 죽이고 띄우는 것 또한 비용이 드는 일이기 때문에 

미리 크롬을 띄워놓고 로그아웃 처리 등을 하는 것이 유리하다고 생각했기 때문이다.

 

 


 

📌 jstat -gc 명령어

 

이 명령어의 내용물을 제대로 본 것이 이번이 처음이기는하다.😂

 

ps -ef | grep java 라는 명령어로 pid를 알아낸 후,

jstat -gc <pid> 라는 명령어를 치면 아래와 같이 데이터를 보여준다.

 

(아래 데이터는 예시를 위한 숫자)

 S0C  S1C          ... OC OU YGC YGCT FGC FGCT GCT
4608.0 4608.0 ... 699392.0 100047.0 58 0.490 4 0.521 1.011

 

 

각 부분에 대해서 설명해달라고 챗지피티에게 물어보았다. 

 

💻 챗지피티 답변

 

  • S0C: Survivor 0 영역의 용량(Capacity)은 4608.0입니다. 이는 Survivor 0 영역이 수용할 수 있는 최대 용량입니다.
  • S1C: Survivor 1 영역의 용량(Capacity)은 4608.0입니다. 마찬가지로 Survivor 1 영역이 수용할 수 있는 최대 용량입니다.
  • S0U: Survivor 0 영역의 사용량(Used)은 1696.9입니다. Survivor 0 영역에서 현재 사용 중인 용량을 나타냅니다.
  • S1U: Survivor 1 영역의 사용량(Used)은 0.0입니다. Survivor 1 영역은 비어있는 상태입니다.
  • EC: Eden 영역의 용량(Capacity)은 339968.0입니다. Eden 영역이 수용할 수 있는 최대 용량입니다.
  • EU: Eden 영역의 사용량(Used)은 37926.4입니다. Eden 영역에서 현재 사용 중인 용량을 나타냅니다.
  • OC: Old 영역의 용량(Capacity)은 699392.0입니다. Old 영역이 수용할 수 있는 최대 용량입니다.
  • OU: Old 영역의 사용량(Used)은 100047.0입니다. Old 영역에서 현재 사용 중인 용량을 나타냅니다.
  • MC: Metaspace의 용량(Capacity)은 111744.0입니다. Metaspace가 수용할 수 있는 최대 용량입니다.
  • MU: Metaspace의 사용량(Used)은 110929.8입니다. Metaspace에서 현재 사용 중인 용량을 나타냅니다.
  • CCSC: Compressed Class Space의 용량(Capacity)은 13632.0입니다. Compressed Class Space가 수용할 수 있는 최대 용량입니다.
  • CCSU: Compressed Class Space의 사용량(Used)은 13259.4입니다. Compressed Class Space에서 현재 사용 중인 용량을 나타냅니다.
  • YGC: Young Generation GC의 횟수는 58회입니다. Young Generation GC는 새로 생성된 객체를 처리하는 Garbage Collection 작업입니다.
  • YGCT: Young Generation GC에 소요된 시간은 0.490초입니다. Young Generation GC 작업에 소요된 시간을 나타냅니다.
  • FGC: Full GC의 횟수는 4회입니다. Full GC는 전체 힙 영역을 대상으로 하는 Garbage Collection 작업입니다.
  • FGCT: Full GC에 소요된 시간은 0.521초입니다. Full GC 작업에 소요된 시간을 나타냅니다.
  • CGC: Concurrent Mode Failure(GC의 일종)의 횟수는 없습니다.
  • CGCT: Concurrent Mode Failure에 소요된 시간은 없습니다.
  • GCT: 총 GC에 소요된 시간은 1.011초입니다.

 

 

그래서 챗지피티의 종합적인 분석은....?

 

1. Young Generation의 Survivor 영역(S0, S1)은 용량을 충분히 가지고 있으며, 사용량도 적당한 수준입니다.

이 영역들은 임시 객체를 저장하는 데 사용됩니다.


2. Eden 영역은 사용량이 용량의 일부를 초과하고 있으며,

사용 용량이 일정 기준을 넘어서는 경우 메모리 부족 상황이 발생할 수 있습니다.

이는 객체가 Eden 영역에 빠르게 생성되고 제거되지 않는 경우에 발생할 수 있습니다.


3. Old 영역은 사용량이 일정 기준을 넘어서는 경향이 있습니다.

이는 오래된 객체가 메모리에 계속 유지되어 메모리 부족 상황을 야기할 수 있습니다.


4. Metaspace는 거의 차 있는 상태로 사용 용량이 용량의 대부분을 차지하고 있습니다.

이는 클래스 로딩 및 메타데이터 관리에 대한 활발한 활동을 나타낼 수 있습니다.


5. Compressed Class Space는 사용량이 용량의 일부를 초과하고 있습니다.

이는 클래스 데이터 압축을 사용하여 메모리를 절약하려는 시도를 나타냅니다.


6. Young Generation GC와 Full GC가 발생하고 있으며, 총 GC에 소요된 시간은 1.011초로 상당히 긴 시간입니다.

GC 작업이 빈번하게 발생하거나 GC 시간이 지나치게 긴 경우 애플리케이션의 성능에 영향을 줄 수 있습니다.

 


 

사실 많은 데이터 중에서 무엇을 중점적으로 봐야할 지 궁금하여 

개발 실장님께 질문을 드렸더니 아래와 같이 답변해 주셨다.

 

[ 답변 ]

저는 jstat에서는 gc가 얼마나 자주 일어나는지 모니터링하거나

평균 gc 시간이 얼마나 걸리는지 보는 용도로 주로 활용해요.

 

gc가 너무 빈번하게 발생하면 heap용량이 부족하거나 memory leak이 있을 확률이 있고

시간이 너무 길면 gc type이 잘못됐거나 설정이 잘못된거라고 보는거죠. 

 


 

사실 스크래핑을 개발하면서 고려해야 할 것도 많았고, 변수도 많아서

고민하는 시간이 많았는데

그만큼 서버 용량 / 메모리 부분도 고려해볼 수 있는 경험이 되어서 좋았던 것 같다.

 

물론... 개발실장님이 정-말 많이 도와주셨다.

감사한게 너무 많은데 더 성장함으로 이 은혜를 갚아야겠다.(?)