26Y03e

Young-Kyoo Kim·2026년 7월 3일

step6_excel_builder.py에서 사용하는 openpyxl 라이브러리는 엑셀 파일(.xlsx)의 내부 XML 구조를 순차적으로 직렬화하고 압축해야 하는 특성과 파이썬의 GIL(Global Interpreter Lock) 제약 때문에 코드 내부적으로 스레드를 나눠 병렬화하는 것이 불가능합니다. 즉, 아무리 코어를 많이 할당해도 팟 내의 프로세스 1개는 무조건 코어 1개만 사용(100% 병목)하게 됩니다.

이를 해결하고 대용량 COMPUTESTORAGE 분석을 동시에 수행하여 가동 시간을 절반 이하로 단축하는 두 가지 병렬화 아키텍처 패턴(쿠버네티스 레벨 / 파이썬 런타임 레벨)을 알려드립니다. 환경에 맞는 방식을 선택하여 적용하시면 됩니다.


방법 1. Pod 내부 백그라운드 멀티프로세싱 체이닝 (가장 추천 / 공수 최소)

기존에 배포하신 단일 CronJob 또는 수동 Job 매니페스트의 args 영역만 수정하여, 리눅스 백그라운드 연산자(&)와 프로세스 대기 명령(wait)을 조합하는 방식입니다. 쿠버네티스 오브젝트를 여러 개 만들 필요가 없어 관리 공수가 가장 적습니다.

📄 기존 CronJob/Job 매니페스트의 args 수정 명세

이 방식을 사용할 때는 두 프로세스가 각각 코어 1개씩(총 2개 이상) 온전히 선점할 수 있도록 resources.limits.cpu를 최소 4 이상으로 넉넉하게 확장해 주어야 병렬 가속 효과가 나타납니다.

            # ─── 🏃 [수정 전 기본 구조 (순차 실행 - 병목 발생)] ───
            # command: ["/bin/sh", "-c"]
            # args:
            #   - |
            #     python3 step6_excel_builder.py --cluster COMPUTE && \
            #     python3 step6_excel_builder.py --cluster STORAGE

            # ─── 🚀 [수정 후 고도화 구조 (백그라운드 병렬 풀가동)] ───
            command: ["/bin/sh", "-c"]
            args:
              - |
                echo "⏳ [병렬 기동] COMPUTE 및 STORAGE 마스터 엑셀 빌더 동시 슈팅..."
                
                # 1. COMPUTE 백그라운드 기동 (&) 및 프로세스 ID(PID) 저장
                python3 step6_excel_builder.py --cluster COMPUTE &
                pid_compute=$!
                
                # 2. STORAGE 백그라운드 기동 (&) 및 프로세스 ID(PID) 저장
                python3 step6_excel_builder.py --cluster STORAGE &
                pid_storage=$!
                
                echo "📡 두 클러스터 배치가 병렬 백그라운드 프로세스(PID: $pid_compute, $pid_storage)로 쪼개졌습니다."
                print("커널 로그를 모니터링하며 두 작업이 모두 마감될 때까지 대기합니다...")
                
                # 3. 🛡️ 두 프로세스가 모두 끝날 때까지 팟이 종료되지 않도록 대기 가드레일 주입
                wait $pid_compute $pid_storage
                
                echo "🏁 === [병렬 컴파일 완료] 두 클러스터의 리포트가 오염 없이 AIStor로 동시 배포되었습니다. ==="

            # 🚨 중요: 병렬 연산 버스트를 위한 CPU 가드레일 상향 조정
            resources:
              requests:
                cpu: "4"      # 병렬 기동 시 코어 경합 예방
                memory: "16Gi"
              limits:
                cpu: "6"      # openpyxl 압축 피크 버스트 대응 공간
                memory: "32Gi"

방법 2. 쿠버네티스 가설 Jobs 분할 격리 (DevOps 표준 패턴)

장애 복구(Ad-hoc)를 하거나 두 클러스터의 물리적 리소스 점유(CPU/메모리)를 완벽하게 격리하여 서로 간섭받지 않게 하고 싶을 때 사용하는 전통적인 분할 정복 방식입니다. yaml 파일을 분리하여 동시에 클러스터에 던집니다.

📄 finops-step6-parallel.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: finops-step6-compute
  namespace: devops-finops
spec:
  backoffLimit: 0
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: builder
        image: harbor.internal.zone/devops/finops-pipeline:v2
        command: ["python3", "step6_excel_builder.py", "--cluster", "COMPUTE"]
        resources:
          requests: { cpu: "2", memory: "8Gi" }
          limits: { cpu: "4", memory: "16Gi" }
        envFrom:
        - configMapRef: { name: finops-minio-config }
---
apiVersion: batch/v1
kind: Job
metadata:
  name: finops-step6-storage
  namespace: devops-finops
spec:
  backoffLimit: 0
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: builder
        image: harbor.internal.zone/devops/finops-pipeline:v2
        command: ["python3", "step6_excel_builder.py", "--cluster", "STORAGE"]
        resources:
          requests: { cpu: "2", memory: "8Gi" }
          limits: { cpu: "4", memory: "16Gi" }
        envFrom:
        - configMapRef: { name: finops-minio-config }
  • 실행 방법: kubectl apply -f finops-step6-parallel.yaml
  • 아키텍처적 이점: 쿠버네티스 스케줄러가 클러스터 내 여유 자원이 있는 서로 다른 물리 노드COMPUTE 파드와 STORAGE 파드를 각각 꽂아버리기 때문에, 단일 노드 내부의 CPU 경합 레이텐시마저 원천 차단됩니다.

방법 3. 파이썬 멀티프로세싱 오케스트레이터 도입 (스크립트 레벨)

만약 명령어를 무조건 단일 파이썬 명령어(python3 run_all.py) 형태로 유지해야 하는 제약이 있다면, concurrent.futures 패키지를 사용해 2개의 서브 프로세스를 강제로 포크(Fork)시키는 파이썬 오케스트레이터를 작성하여 파이프라인 전면부에 배치하면 됩니다.

📄 step6_orchestrator.py (일종의 실행기 래퍼)

import subprocess
import os
from concurrent.futures import ProcessPoolExecutor

def run_script(cluster_name):
    print(f"🚀 [Subprocess Start] {cluster_name} Excel Compilation initiated...")
    # 메인 바이너리를 서브프로세스로 분리 기동 (GIL 우회 및 별도 코어 선점)
    cmd = ["python3", "step6_excel_builder.py", "--cluster", cluster_name]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        return f"❌ {cluster_name} Failed:\n{result.stderr}"
    return f"✅ {cluster_name} Completed Successfully."

def main():
    clusters = ["COMPUTE", "STORAGE"]
    print(f"⚡ 파이썬 멀티프로세싱 오케스트레이터 기동 -> 대상: {clusters}")
    
    # 프로세스 풀을 열어 독립된 코어에 프로세스 배정
    with ProcessPoolExecutor(max_workers=2) as executor:
        results = executor.map(run_script, clusters)
        
    for res in results:
        print(res)

if __name__ == "__main__":
    main()

💡 최종 의사결정 가이드

  • 가장 깔끔한 프로덕션 자동화 환경을 원하시면 [방법 1 (Pod 내 백그라운드 & wait 체이닝)]을 기존 CronJob 매니페스트에 이식하는 것이 가장 우아합니다.
  • 특정 날짜의 정산 데이터가 꼬여서 특정 클러스터만 급하게 ad-hoc으로 단독 복구해야 하거나, 개발/운영 환경에서 리소스 모니터링을 명확히 분리하고 싶다면 [방법 2 (K8s Jobs 분리 배포)]를 상시 서랍에 넣어두고 트리거하시는 것이 좋습니다.

0개의 댓글