Oracle Internet Directory Enterprise Scaling - Part 3
| |

Oracle Internet Directory(OID) 확장 Part 3: 웹로직/JVM 최적화 완벽 가이드

이 글은 “Oracle Internet Directory 확장 가이드” 시리즈의 세 번째 이야기입니다. 지난 시리즈에서는 기업 환경에서 OID를 효과적으로 확장하는 방법을 자세히 다뤘는데요, 이번 내용을 더 잘 이해하기 위해서는 이전 편을 먼저 읽어보시는 것이 좋습니다.

OID 성능의 핵심 이해하기

오케스트라 공연하는 것을 본 적이 있으신가요? 지휘자와 연주자들이 완벽한 조화를 이룰 때 비로소 감동적인 연주가 탄생하듯, Oracle Internet Directory(오라클 인터넷 디렉토리, 이하 OID)도 WebLogic Server(웹로직 서버, 이하 WebLogic)와 Java Virtual Machine(자바 가상 머신, 이하 JVM)이 조화롭게 작동할 때 최상의 성능을 발휘할 수 있습니다.

WebLogic은 지휘자처럼 시스템의 모든 구성 요소를 조율하고, JVM은 연주가 펼쳐지는 무대를 제공합니다. 이 두 요소가 서로 잘 맞물려 돌아갈 때, OID는 대규모 기업환경에서도 안정적이고 효율적으로 작동하죠.

이번 글에서는 한 글로벌 금융 서비스 회사의 사례를 통해 이러한 핵심 요소들을 어떻게 최적화할 수 있는지 자세히 알아보겠습니다. 5만 명의 사용자를 대상으로 하는 이 기업의 시스템은 처음에 평균 응답 시간이 2.3초나 걸렸고, 아시아 시장 거래 시간대에는 잦은 타임아웃이 발생했습니다. 하지만 WebLogic과 JVM의 세심한 튜닝을 통해 어떻게 문제를 해결했는지, 그 과정을 하나하나 살펴볼게요.

웹로직(WebLogic) 설정

오케스트라의 지휘자는 각 파트의 연주자 수를 조절하고, 필요한 시점에 적절한 악기들이 함께 연주되도록 이끕니다. WebLogic Server도 이와 마찬가지로 시스템의 각 구성 요소들이 얼마나 많은 작업을 처리할지, 어떤 순서로 처리할지를 결정하는 지휘자 역할을 합니다.

스레드 풀(Thread Pool) 관리

한 글로벌 금융 서비스 기업의 사례를 살펴볼까요? 이 기업은 전 세계 5만 명의 사용자를 대상으로 서비스를 제공하면서, 최대 15,000건의 동시 인증 요청을 처리해야 했습니다. 처음에는 다음과 같은 기본적인 스레드 풀 설정을 사용했습니다:

<thread-pool>
    <min-threads-constraint>20</min-threads-constraint>
    <max-threads-constraint>200</max-threads-constraint>
    <queue-size>500</queue-size>
</thread-pool>

이런 설정은 마치 소규모 오케스트라에 대규모 공연을 맡기는 것과 같았습니다. 적은 수의 연주자들이 너무 많은 파트를 소화하려다 보니, 결국 전체적인 연주의 질이 떨어질 수밖에 없었죠. 실제로 시스템의 평균 응답 시간은 2.3초나 걸렸고, 특히 아시아 시장 거래 시간대에는 잦은 타임아웃이 발생했습니다.

세심한 분석과 테스트를 거쳐 다음과 같이 설정을 최적화했습니다:

<thread-pool>
    <min-threads-constraint>50</min-threads-constraint>
    <max-threads-constraint>400</max-threads-constraint>
    <queue-size>-1</queue-size>
    <stuck-thread-max-time>600</stuck-thread-max-time>
</thread-pool>

각 설정값의 의미를 자세히 살펴볼까요?

최소 스레드 수(50개) 설정의 의미

<min-threads-constraint>50</min-threads-constraint>

최소 스레드 수(50개)는 마치 오케스트라의 상임단원과 같습니다. 언제든 연주할 준비가 되어 있는 이 상임단원들은 기본적인 연주를 안정적으로 수행할 수 있게 해줍니다. 평상시 30개 정도의 스레드가 필요하다는 분석 결과에 여유분을 더해 50으로 결정했습니다.

최대 스레드 수(400개)

최대 스레드 수(400개)는 오케스트라가 대규모 공연을 위해 추가로 초빙할 수 있는 연주자의 수와 비슷합니다.

<max-threads-constraint>400</max-threads-constraint>

이 400이라는 숫자는 아래와 같은 점을 고려하여 결정 했습니다.

  • 일반적으로, 프로덕션 환경의 CPU 코어 수는 보통 8-16개 정도라고 가정 (CPU 코어당 25-50개의 스레드를 할당하는 것이 일반적인 최적 범위), 
  • 스레드당 필요한 메모리는 약 512KB~1MB라는 가정 (각 스레드는 스택 메모리를 사용하므로, 400개 스레드는 약 200MB-400MB의 메모리가 필요)
  • 피크 시간대 동시 작업 패턴 등을 고려

대기열 크기(Queue Size)와 작업 중단 감지(Stuck Thread Detection)

<queue-size>-1</queue-size>
<stuck-thread-max-time>600</stuck-thread-max-time>

무제한 큐 크기(-1)는 공연장의 대기실과 같은 역할을 합니다. 바쁜 시간대에 요청이 몰리더라도 차례를 기다릴 수 있게 해주죠. 600초로 설정된 stuck-thread-max-time은 일종의 안전장치로, 특정 연주가 너무 오래 걸리는 경우를 감지할 수 있게 해줍니다.

스레드 풀 최적화의 효과

잘 조율된 오케스트라 연주처럼, 최적화 결과, 시스템은 다음과 같은 개선 포인트가 확인 되었습니다:

  • 기본 응답 시간이 45% 개선되었습니다. 충분한 수의 상임단원이 있으니 일상적인 요청들을 빠르게 처리할 수 있게 된 것이죠.
  • 15,000건의 동시 인증 요청도 문제없이 처리할 수 있게 되었습니다. 마치 대규모 오케스트라가 복잡한 곡도 완벽하게 소화해내는 것처럼요.
  • 무제한 큐 덕분에 피크 시간대에도 요청이 거부되는 일이 없어졌습니다. 대기 시간이 좀 길어질 수는 있지만, 모든 요청은 결국 처리된다는 확신을 줄 수 있게 되었죠.

Work Manager(워크 매니저) 설정

대형 오케스트라는 각 연주 파트의 중요도에 따라 적절한 수의 연주자를 배치합니다. 웅장한 클라이맥스에서는 모든 악기가 함께 연주하지만, 섬세한 독주 부분에서는 특정 악기만 집중적으로 다뤄지죠. 이 회사의 OID 시스템도 이와 비슷한 방식으로 작업의 우선순위를 관리해야 했습니다.
특히 연말 시즌에는 수많은 요청이 동시에 들어오는데, 이 중에서 결제 관련 인증은 절대 지연되어서는 안 됩니다. 이를 위해 다음과 같은 Work Manager 설정을 구현했습니다:

<work-manager>
    <name>OIDWorkManager</name>
    <!-- 중요 작업을 위한 전용 스레드 설정 -->
    <min-threads-constraint>
        <name>Critical-Min</name>
        <count>25</count>
        <pool-name>PriorityPool</pool-name>
    </min-threads-constraint>
    
    <!-- 전체 처리 용량 제어 -->
    <max-threads-constraint>
        <name>OID-Max</name>
        <count>400</count>
    </max-threads-constraint>
    
    <!-- 작업 우선순위 설정 -->
    <fair-share-request-class>
        <name>Critical-Ops</name>
        <fair-share>80</fair-share>
    </fair-share-request-class>
    <fair-share-request-class>
        <name>Routine-Ops</name>
        <fair-share>20</fair-share>
    </fair-share-request-class>
</work-manager>

이 설정은 마치 오케스트라에서 각 파트의 연주 시간과 강도를 세심하게 조절하는 것과 같습니다. 중요한 독주 부분을 위해 최고의 연주자들을 대기시키고, 나머지 연주자들은 반주를 담당하는 것처럼요.

중요 작업 설정(Critical Operations Configuration)을 보면:

<min-threads-constraint>
    <name>Critical-Min</name>
    <count>25</count>
    <pool-name>PriorityPool</pool-name>
</min-threads-constraint>

이는 마치 응급실에서 중증 환자를 위해 항상 의료진을 대기시켜두는 것과 같습니다. 중요 작업을 위해 25개의 스레드를 항상 확보해두어, 시스템이 아무리 바빠도 핵심 업무는 지연 없이 처리할 수 있게 합니다.

작업 부하 우선순위 설정(Workload Prioritization)은 다음과 같이 리소스를 배분합니다:

<fair-share-request-class>
    <name>Critical-Ops</name>
    <fair-share>80</fair-share>
</fair-share-request-class>
<fair-share-request-class>
    <name>Routine-Ops</name>
    <fair-share>20</fair-share>
</fair-share-request-class>

이 80:20 비율의 리소스 분배는 중요 작업에 우선순위를 부여하면서도, 일반 작업도 적절히 처리할 수 있게 해줍니다. 응급실에서 전체 의료 자원의 80%를 응급환자에게 할당하고, 20%는 일반 진료에 배정하는 것과 비슷한 개념이죠.

작업에 이런 지능적인 우선 순위를 부여한 덕분에 다음과 같은 성과가 확인 되었습니:

  • 인증 응답 시간이 85% 개선되었습니다
  • 중요 작업들은 99.9%의 SLA를 달성할 수 있게 되었습니다
  • 연말 정산 같은 극단적인 피크 시간대에도 타임아웃 에러가 전혀 발생하지 않았습니다
  • 모든 종류의 작업이 균형있게 처리될 수 있었습니다

JVM 최적화: 메모리 관리의 기술

공연을 보러 온 콘서트홀을 상상해 보시죠. 연주자들이 무대에서 연주하고, 대기실에서 준비하고, 연습실에서 리허설을 하는 것처럼 JVM도 각각의 용도에 맞는 메모리 공간이 필요합니다. 한 정부 기관의 사례를 통해 200만 명의 시민을 위한 서비스를 어떻게 안정화했는지 살펴보겠습니다.

JVM 메모리 구조의 이해

시스템을 최적화하기 전에, 먼저 JVM 메모리가 어떻게 구성되어 있는지 이해해야 합니다. 콘서트홀의 설계도를 살펴보듯이, JVM의 주요 메모리 영역을 알아보겠습니다:

JVM Memory Structure
웹로직 JVM 메모리 구조
  1. Young Generation(신규 영역) 새로운 객체들이 처음 생성되는 공간입니다. 이 공간은 다시 세 부분으로 나뉩니다:
  • Eden Space(에덴 영역): 객체가 처음 생성되는 공간입니다. 마치 새로운 음악 작품이 처음 연습되는 공간과 같죠.
  • Survivor Spaces(생존자 영역, S0와 S1): 가비지 컬렉션 후에도 살아남은 객체들이 이동하는 중간 단계 공간입니다. 여러 번의 리허설을 거치며 완성도를 높이는 과정과 비슷합니다.
  1. Old Generation(구세대 영역) 여러 번의 가비지 컬렉션에서 살아남은 오래된 객체들이 저장되는 공간입니다. 마치 오랫동안 사랑받은 클래식 레퍼토리처럼, 이곳의 객체들은 장기간 시스템에서 가치를 인정받은 것들이죠.

초기 상황 평가

이 기관의 처음 JVM 설정을 보면:

# 초기 메모리 설정
JAVA_OPTIONS="-Xms2048m -Xmx4096m"
JAVA_OPTIONS="${JAVA_OPTIONS} -XX:+UseParallelGC"

이런 기본적인 설정은 마치 콘서트홀의 규모가 2GB에서 4GB 사이에서 들쑥날쑥하게 변하는 것과 같습니다. 공연 도중에 객석 배치를 바꾸는 셈이죠. 이로 인해 여러 문제가 발생했습니다:

  1. 가비지 컬렉션 중에 2-3초씩 시스템이 멈추는 현상이 발생했습니다. 마치 공연 중간에 객석을 재배치하느라 연주가 중단되는 것과 같은 상황이죠.
  2. 시간이 지날수록 성능이 점점 저하되었습니다. 콘서트홀이 점점 지저분해지고 정리가 안 되는 것처럼, 메모리 단편화가 심해졌기 때문입니다.
  3. 피크 시간대의 응답 시간이 예측 불가능했습니다. 마치 관객이 많이 오는 공연에서 좌석 배치가 제대로 안 되어 있는 것과 같은 상황이었죠.

메모리 설정: 최적의 공간 구성하기

면밀한 분석과 테스트를 거쳐 다음과 같이 설정을 최적화했습니다:


# Optimized memory settings JAVA_OPTIONS="${JAVA_OPTIONS} -Xms6144m -Xmx6144m" # G1GC configuration JAVA_OPTIONS="${JAVA_OPTIONS} -XX:+UseG1GC" JAVA_OPTIONS="${JAVA_OPTIONS} -XX:MaxGCPauseMillis=200" JAVA_OPTIONS="${JAVA_OPTIONS} -XX:ParallelGCThreads=8" JAVA_OPTIONS="${JAVA_OPTIONS} -XX:ConcGCThreads=2" JAVA_OPTIONS="${JAVA_OPTIONS} -XX:InitiatingHeapOccupancyPercent=45" # Metaspace configuration JAVA_OPTIONS="${JAVA_OPTIONS} -XX:MetaspaceSize=256m" JAVA_OPTIONS="${JAVA_OPTIONS} -XX:MaxMetaspaceSize=512m"

이러한 설정이 어떤 의미를 가지는지, 콘서트홀에 비유해서 하나씩 살펴보겠습니다.

힙 크기 (Heap Size) 설정

JAVA_OPTIONS="${JAVA_OPTIONS} -Xms6144m -Xmx6144m"

최소(-Xms)와 최대(-Xmx) 힙 크기를 동일하게 6GB로 설정했습니다. 이는 마치 콘서트홀의 규모를 처음부터 적절한 크기로 고정하는 것과 같습니다. 공연 중에 객석을 늘리거나 줄이는 등 재배치 할 필요가 없어졌습니다. 처음부터 충분한 규모로 시작하고 그 크기를 유지하는 거죠.

가비지 컬렉션(Garbage Collection) 전략

JAVA_OPTIONS="${JAVA_OPTIONS} -XX:+UseG1GC"
JAVA_OPTIONS="${JAVA_OPTIONS} -XX:MaxGCPauseMillis=200"
JAVA_OPTIONS="${JAVA_OPTIONS} -XX:ParallelGCThreads=8"

가비지 컬렉션 전략을 살펴볼까요? G1(Garbage First) 컬렉터는 현대적인 방식의 객석 관리 시스템과 같습니다. 전체 홀을 한 번에 정리하는 대신, 각 구역을 독립적으로 효율적으로 관리할 수 있게 해줍니다. MaxGCPauseMillis 200은 정리 시간을 0.2초 이내로 제한하라는 의미입니다. 마치 관객들이 거의 느끼지 못할 정도로 빠르게 객석을 정리하는 것과 같죠. ParallelGCThreads의 8개 스레드는 8명의 관리 인원이 동시에 일하는 것과 같습니다.

메모리 관리 트리거 설정

JAVA_OPTIONS="${JAVA_OPTIONS} -XX:InitiatingHeapOccupancyPercent=45"
JAVA_OPTIONS="${JAVA_OPTIONS} -XX:ConcGCThreads=2"

메모리 관리 트리거는 더 세밀한 조정을 가능하게 합니다. InitiatingHeapOccupancyPercent=45 설정은 힙 사용률, 즉 객석의 수용 인원이 45%에 도달하면 정리를 시작하라는 의미입니다. 공간이 완전히 차기 전에 미리 정리를 시작함으로써, 나중에 황급히 서두르는 상황을 예방하는 거죠. ConcGCThreads=2 설정은 무대 뒤에서 지속적으로 일하는 관리 인원을 의미합니다. 2개의 동시 실행 스레드를 통해 백그라운드에서 방해하지 않으면서 지속적으로 정리 작업을 수행합니다.

메타스페이스 설정(Metaspace Configuration)

JAVA_OPTIONS="${JAVA_OPTIONS} -XX:MetaspaceSize=256m"
JAVA_OPTIONS="${JAVA_OPTIONS} -XX:MaxMetaspaceSize=512m"

Metaspace는 콘서트홀의 관리 사무실과 같습니다. 공연에 필요한 모든 정보와 진행 계획을 보관하는 공간이죠. 클래스 정보와 메소드 데이터 등 중요한 런타임 데이터가 이곳에 저장됩니다. 초기 크기를 256MB로, 최대 크기를 512MB로 설정하여 관리 업무가 원활하게 진행되면서도 무제한으로 커지지는 않도록 했습니다. 이는 마치 공연에 필요한 악보와 프로그램 정보를 저장하는 공간을 효율적으로 관리하는 것과 같습니다.

메모리 관리 트리거 상세

여러 메모리 관리 설정들이 어떻게 함께 작동하는지, 마치 잘 조율된 공연장 관리 시스템처럼 자세히 들여다보겠습니다.

InitiatingHeapOccupancyPercent 설정은 우리의 조기 경보 시스템 역할을 합니다:

JAVA_OPTIONS="${JAVA_OPTIONS} -XX:InitiatingHeapOccupancyPercent=45"

이는 마치 무대 매니저들에게 “공연장이 45% 찼을 때 정리를 시작하세요”라고 지시하는 것과 같습니다. 공연장이 거의 가득 찰 때까지 기다렸다가 급하게 정리하는 대신, 미리 준비함으로써 혼잡을 예방하는 거죠. 이러한 선제적 접근 방식은 시스템이 바빠질 때도 원활한 운영을 가능하게 합니다.

예를 들어, 2,000석 규모의 공연장에서 900석이 채워졌을 때 이미 다음 관객들을 위한 준비를 시작한다고 생각해보세요. 이렇게 하면 나중에 1,800석이 찼을 때 갑자기 서두르며 정리할 필요가 없어집니다.

ConcGCThreads 설정은 전담 청소 인력을 제공합니다:

JAVA_OPTIONS="${JAVA_OPTIONS} -XX:ConcGCThreads=2"

이는 무대 뒤에서 지속적으로 일하는 관리 인력과 같습니다. 공연을 방해하지 않으면서도 환경을 깔끔하게 유지하는 거죠. 두 개의 동시 스레드가 있다는 것은, 마치 두 명의 전담 관리인이 시스템이 계속 요청을 처리하는 동안에도 일상적인 정리 작업을 수행할 수 있다는 의미입니다.

실제로 이런 설정의 효과는 매우 명확했습니다. 공연장에서 관객들이 공연에 집중하는 동안 눈에 띄지 않게 계속해서 정리가 이루어지는 것처럼, 사용자들은 시스템이 내부적으로 메모리를 정리하고 있다는 사실을 전혀 눈치채지 못했습니다. 덕분에 시스템은 항상 새로운 요청을 처리할 준비가 되어 있었고, 긴 중단 시간 없이도 효율적인 메모리 관리가 가능했습니다.

성능 최적화 효과

잘 조율된 오케스트라가 마침내 완벽한 하모니를 이룰 때, 그 감동은 이전과는 비교할 수 없습니다. WebLogic과 JVM의 최적화 효과도 이와 비슷했습니다. 모든 구성 요소가 조화롭게 작동하기 시작하면서, 시스템의 성능이 극적으로 향상되었죠. 네 가지 주요 영역에서 큰 변화가 있었습니다.

응답 시간 개선

가장 눈에 띄는 변화는 응답 시간이었습니다. 마치 음악이 깨끗하고 선명하게 모든 관객에게 전달되는 것처럼, 최적화된 메모리 설정은 시스템의 반응 속도를 크게 향상시켰습니다:

  • 평균 응답 시간이 2.5초에서 500밀리초로 80% 감소했습니다
  • 피크 시간대에도 95%의 요청이 750밀리초 이내에 처리되었습니다
  • 가장 느린 응답조차도 이전보다 훨씬 빨라졌습니다
  • 부하가 갑자기 증가해도 응답 시간이 안정적으로 유지되었습니다

가비지 컬렉션 효율화

가비지 컬렉션 작업은 마치 공연 중의 막간과 같습니다. 필요하긴 하지만 관객들의 경험을 방해하지 않도록 최대한 짧고 효율적으로 진행되어야 하죠. 최적화 후의 변화는 놀라웠습니다:

  • 전체 가비지 컬렉션(Full GC) 횟수가 95% 감소했습니다. 매시간 발생하던 중단이 하루에 몇 번으로 줄어든 거죠
  • GC 중단 시간이 2-3초에서 200밀리초 이하로 줄었습니다. 대부분의 사용자들은 이제 중단을 거의 느끼지 못합니다
  • 작은 규모의 가비지 컬렉션은 15밀리초 이내에 완료됩니다. 마치 무대 뒤에서 조용히 정리가 이루어지는 것처럼요

메모리 사용률 향상

더 효율적인 메모리 사용은 더 나은 응답 시간과 부드러운 가비지 컬렉션을 가능하게 했습니다. 콘서트홀의 모든 공간이 효율적으로 활용되는 것처럼요:

  • 평균 힙 메모리 사용률이 60%에서 85%로 증가했습니다. 가용 메모리를 최대한 활용하면서도 과부하는 피한 거죠 
  • 힙 외 메모리 사용이 40% 감소했습니다. 불필요한 캐시를 정리하고 문자열 인터닝을 최적화한 결과입니다 
  • Metaspace 사용량이 안정화되었습니다. 더 이상 예측할 수 없는 증가가 발생하지 않게 된 거죠

확장성 개선

응답 시간이 개선되고, GC 중단이 줄어들고, 메모리가 효율적으로 관리되면서 전체 시스템의 확장성도 크게 향상되었습니다. 마치 음향이 훌륭한 콘서트홀이 더 많은 관객을 수용하면서도 동일한 음질을 유지하는 것처럼요:

  • 동시 사용자 수용량이 50% 증가했습니다
  • 초당 처리 요청 수가 2배 이상 증가했습니다
  • 분당 20,000건이 넘는 요청이 들어와도 타임아웃이나 큰 성능 저하가 발생하지 않습니다

이러한 개선은 단순한 수치의 향상을 넘어, 실제 사용자 경험의 질적 향상으로 이어졌습니다. 시스템이 더 예측 가능하고 안정적으로 동작하면서, 사용자들은 더 신뢰할 수 있는 서비스를 경험하게 되었습니다.

Oracle Internet Directory 확장 시리즈 Part 3: 결론과 핵심 요약

이번 글에서는 WLS와 JVM 최적화를 통해 어떻게 OID의 성능을 획기적으로 개선할 수 있는지 살펴보았습니다. 마치 오케스트라가 최고의 공연을 위해 모든 요소를 세밀하게 조율하듯, 우리도 시스템의 각 구성 요소를 섬세하게 최적화했습니다.

웹로직 서버 최적화를 통해 달성한 주요 성과를 보면, 인증 응답 속도가 85% 개선되었고 피크 타임에도 타임아웃이 발생하지 않게 되었습니다. 특히 스레드 풀과 Work Manager의 적절한 설정으로 시스템 리소스를 효율적으로 분배할 수 있게 되었죠.

JVM 최적화 측면에서는 세심한 메모리 관리 전략이 큰 효과를 보였습니다. 가비지 컬렉션 중단 시간이 2-3초에서 0.2초 이하로 줄었고, 메모리 단편화도 75% 감소했습니다. 그 결과 시스템은 일관되게 1초 미만의 응답 시간을 유지할 수 있게 되었습니다.

다음 시리즈인 Part 4: ‘고급 모니터링과 검증’에서는 이러한 최적화의 효과를 어떻게 측정하고 검증할 수 있는지 다룰 예정입니다. 구체적으로 다음과 같은 내용을 살펴볼 것입니다:

  • OID 성능 모니터링의 기술과 과학
  • 견고한 모니터링 프레임워크 구축 방법
  • 고급 모니터링 패턴의 구현
  • 포괄적인 검증 전략 수립

성능 최적화는 한 번의 설정으로 끝나는 것이 아닌, 지속적인 관찰과 조정이 필요한 여정입니다. 이번 시리즈에서 다룬 기본적인 최적화 방법들을 토대로, 여러분의 환경에 맞는 최적의 설정을 찾아가시길 바랍니다.

Similar Posts

One Comment

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다