인터넷이 차단된 에어갭 환경에서 GPU(B300) 노드 검증, vLLM 서빙, 스토리지 연동, 성능 벤치마크를 차질 없이 완결할 수 있도록 외부망(MacBook)에서 사전 다운로드해야 하는 전체 에셋 리스트를 정리했습니다.
사내 Nexus 프록시가 있더라도 방화벽 타임아웃, 토큰 인증 제한, 레지스트리 캐시 미스(Cache Miss)에 대비한 백업용 컨테이너 이미지 아카이브와 옵셔널(비상용/선택) 항목까지 모두 포함했습니다.
이 항목들이 없으면 노드 부팅 후 드라이버 인식, NVLink 활성화, 모델 서빙 자체가 불가능합니다.
.run 파일:NVIDIA-Linux-x86_64-<VERSION>.run (예: R570/R580 Production Branch)--kernel-module-type=open) 및 기본 CUDA 드라이버 설치.nvidia-fabricmanager RPM:nvidia-fabricmanager-<VERSION>-1.x86_64.rpm.run 파일과 빌드 마이너 버전 번호가 100% 일치해야 함 (B300 NVLink/NVSwitch 구동 필수).nvidia-container-toolkit-*.x86_64.rpmnvidia-container-toolkit-base-*.x86_64.rpmlibnvidia-container1-*.x86_64.rpmlibnvidia-container-tools-*.x86_64.rpmHugging Face CLI의 snapshot_download를 이용해 온전히 수집해야 합니다 (약 140GB).
config.json, generation_config.jsontokenizer.json, tokenizer_config.json, special_tokens_map.jsonmodel.safetensors.index.json (샤드 매핑 인덱스)model-00001-of-00030.safetensors ~ model-00030-of-00030.safetensors (총 30개 파티션)mc): Linux amd64 정적 바이너리 (AIStor 버킷 생성, 모델 업로드/다운로드용).k6 또는 locust 바이너리: Linux amd64 실행 바이너리 (Compute 클러스터 GPU 부하 생성용)..tar.gz) (Nexus 프록시 장애 대비 백업)Nexus Proxy를 통해 pull이 가능하더라도, 대용량 이미지 풀 도중 네트워크 단절이나 에어갭 인증 실패에 대비해 MacBook에서 docker pull --platform linux/amd64 후 docker save | gzip으로 백업해 둘 이미지들입니다.
| 컨테이너 이미지 (Tag) | 용도 및 단계 | 비고 |
|---|---|---|
vllm/vllm-openai:latest | Phase 3 추론 서빙 및 Phase 5 벤치마크 | 필수 |
wilic/gpu-burn:latest | Phase 2 전체 GPU 100% 풀로드 번인 테스트 | 필수 |
nvcr.io/nvidia/k8s/cuda-sample:vectorAdd-cuda12.5.0 | Phase 2 NVLink P2P 대역폭 실측 (p2pBandwidthLatencyTest) | 필수 |
grafana/k6:latest | Phase 5 K8s Pod 기반 분산 부하 인가기 | 필수 |
prom/prometheus:latest | K8s 조인 전 단독 모니터링 (9090 포트) | 권장 |
grafana/grafana:latest | K8s 조인 전 실시간 시각화 대시보드 (3000 포트) | 권장 |
nvidia/k8s-device-plugin/dcgm-exporter:latest | GPU 전력, 온도, SM 사용률 메트릭 노출 | 권장 |
networkstatic/iperf3:latest | Phase 4 스토리지 대역폭 실측용 컨테이너 | 옵션 |
현장에서 네트워크 파라미터 튜닝이나 예기치 못한 컴파일, 디스크 성능 병목 진단 시 유용한 항목들입니다.
warp): Linux x86_64 바이너리 (AIStor의 순수 S3 GET/PUT 처리량 멀티파트 한계 측정 시 필요).ShareGPT_V3_unfiltered_cleaned_split.json (vLLM 내장 벤치마크 benchmark_serving.py 구동 시 실제 유저 트래픽 모사용).datacenter-gpu-manager-*.x86_64.rpm (호스트 OS 레벨에서 dcgmi diag -r 3 진단 시 필요).stress-ng 및 하위 라이브러리 RPM: Phase 1 CPU/메모리 부하 검증용 (EPEL 저장소 패키지).kernel-devel, kernel-headers (현재 커널 버전용): 드라이버 오픈 커널 모듈 빌드 시 필요.fio, iperf3, numactl, pciutils RPM: 베어메탈 OS 점검 도구.meta-llama/Llama-3.1-8B-Instruct (약 16GB): 70B를 올리기 전 파이프라인(외부망 L3 통신, K8s Egress 등) 빠른 검증용.맥북 터미널에서 실행하여 한 폴더로 정리하는 명령입니다.
# 1. 수집 디렉터리 준비
mkdir -p ~/airgap_bundle/{binaries,datasets,images}
# 2. Standalone 바이너리 다운로드 (Linux x86_64 전용)
curl -sSL https://dl.min.io/client/mc/release/linux-amd64/mc -o ~/airgap_bundle/binaries/mc
chmod +x ~/airgap_bundle/binaries/mc
# k6 바이너리
curl -sSL https://github.com/grafana/k6/releases/download/v0.53.0/k6-v0.53.0-linux-amd64.tar.gz -o /tmp/k6.tar.gz
tar -xzf /tmp/k6.tar.gz -C /tmp && mv /tmp/k6-*-linux-amd64/k6 ~/airgap_bundle/binaries/ && rm -rf /tmp/k6*
chmod +x ~/airgap_bundle/binaries/k6
# 3. ShareGPT 벤치마크 데이터셋
curl -sSL https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json \
-o ~/airgap_bundle/datasets/ShareGPT_V3_unfiltered_cleaned_split.json
# 4. 백업용 핵심 컨테이너 이미지 아카이브 (명시적 linux/amd64)
docker pull --platform linux/amd64 vllm/vllm-openai:latest
docker pull --platform linux/amd64 wilic/gpu-burn:latest
docker pull --platform linux/amd64 nvcr.io/nvidia/k8s/cuda-sample:vectorAdd-cuda12.5.0
docker save vllm/vllm-openai:latest | gzip > ~/airgap_bundle/images/vllm_openai_amd64.tar.gz
docker save wilic/gpu-burn:latest | gzip > ~/airgap_bundle/images/gpu_burn_amd64.tar.gz
docker save nvcr.io/nvidia/k8s/cuda-sample:vectorAdd-cuda12.5.0 | gzip > ~/airgap_bundle/images/cuda_samples_amd64.tar.gz
===
K8s 클러스터(또는 단일 노드)에서 GPU 노드의 vLLM 서빙 성능(처리량, 응답 지연 시간 등)을 측정하기 위해 k6와 Locust를 사용하는 실전 가이드입니다.
vLLM은 OpenAI 호환 API(POST /v1/chat/completions)를 제공하므로, 스트리밍(stream: true) 또는 비스트리밍(stream: false) 방식으로 LLM 특화 지표(TTFT, 토큰 생성 속도, P99 레이턴시)를 부하 테스트할 수 있습니다.
k6는 Go 언어 기반 단일 바이너리로 구동되어 리소스를 매우 적게 먹으면서도 높은 RPS(초당 요청 수)를 뽑아낼 수 있어 베어메탈/에어갭 환경에서 가장 추천됩니다.
vllm_test.js)가상 사용자(VU)를 점진적으로 늘려가며 vLLM에 채팅 완결 요청을 날리는 스크립트입니다.
import http from 'k6/http';
import { check, sleep } from 'k6';
// 1. 테스트 실행 옵션 (단계별 부하 증가)
export const options = {
stages: [
{ duration: '30s', target: 5 }, // 30초 동안 5명의 VU(동시 사용자)로 증가 (웜업)
{ duration: '1m', target: 20 }, // 1분 동안 20명으로 점진 증가
{ duration: '2m', target: 20 }, // 20명 동시 요청 유지 (지속 부하)
{ duration: '30s', target: 0 }, // 30초 동안 0명으로 쿨다운
],
thresholds: {
// 95%의 요청 지연 시간이 5초 이하이고, 에러율이 1% 미만이어야 테스트 통과
http_req_duration: ['p(95)<5000'],
http_req_failed: ['rate<0.01'],
},
};
const TARGET_URL = __ENV.VLLM_URL || 'http://127.0.0.1:8000/v1/chat/completions';
const MODEL_NAME = __ENV.MODEL_NAME || 'meta-llama/Llama-3.1-70B-Instruct';
export default function () {
const payload = JSON.stringify({
model: MODEL_NAME,
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'Explain the difference between TCP and UDP in 3 paragraphs.' }
],
max_tokens: 256,
temperature: 0.7,
stream: false, // 기본 HTTP 요청 시간 측정을 위해 false (TTFT 정밀 측정 시 true 설정 후 SSE 파싱)
});
const params = {
headers: {
'Content-Type': 'application/json',
},
timeout: '30s',
};
const res = http.post(TARGET_URL, payload, params);
// 응답 상태 코드 및 토큰 생성 여부 검증
check(res, {
'status is 200': (r) => r.status === 200,
'has generated content': (r) => {
try {
const json = JSON.parse(r.body);
return json.choices && json.choices.length > 0;
} catch (e) {
return false;
}
},
});
sleep(1); // 사용자 대기 시간(Think time) 1초
}
# 기본 실행
./k6 run vllm_test.js
# 대상 URL 및 모델명을 환경변수로 전달
./k6 run -e VLLM_URL="http://192.168.10.50:8000/v1/chat/completions" \
-e MODEL_NAME="meta-llama/Llama-3.1-70B-Instruct" \
vllm_test.js
podman run --rm -i --net=host \
-v $(pwd)/vllm_test.js:/vllm_test.js:ro \
grafana/k6:latest run /vllm_test.js
Locust는 Python 코드로 사용자 시나리오를 작성하며, 내장 웹 UI(기본 포트 8089)를 제공하여 실시간 차트 확인이 매우 편합니다.
locustfile.py)import json
import os
from locust import HttpUser, task, between
MODEL_NAME = os.getenv("MODEL_NAME", "meta-llama/Llama-3.1-70B-Instruct")
class VLLMUser(HttpUser):
# 각 요청 사이 대기 시간 (1초~3초 무작위)
wait_time = between(1, 3)
@task
def generate_chat_completion(self):
payload = {
"model": MODEL_NAME,
"messages": [
{"role": "system", "content": "You are a concise AI assistant."},
{"role": "user", "content": "Summarize the core benefits of Kubernetes in 3 bullet points."}
],
"max_tokens": 200,
"temperature": 0.5,
"stream": False
}
headers = {"Content-Type": "application/json"}
with self.client.post("/v1/chat/completions", json=payload, headers=headers, catch_response=True) as response:
if response.status_code == 200:
try:
data = response.json()
# vLLM usage 통계 확인
total_tokens = data.get("usage", {}).get("total_tokens", 0)
response.success()
except json.JSONDecodeError:
response.failure("Response body is not valid JSON")
else:
response.failure(f"Failed with status code: {response.status_code}")
# 웹 UI 모드로 기동 (호스트의 8089 포트 오픈)
locust -f locustfile.py --host http://<GPU_NODE_IP>:8000
http://<실행노드_IP>:8089 접속locust -f locustfile.py \
--headless \
--host http://127.0.0.1:8000 \
-u 30 -r 5 \
--run-time 3m \
--csv=locust_result
-u 30: 최대 30명의 사용자-r 5: 초당 5명씩 증가--run-time 3m: 3분간 실행| 비교 항목 | k6 (권장) | Locust |
|---|---|---|
| 언어/런타임 | Go 기반 (단일 바이너리) | Python (파이썬 런타임 및 pip 필요) |
| 에어갭 배포 난이도 | 매우 쉬움 (k6 파일 하나만 복사) | 다소 복잡 (Python wheel 패키지 오프라인 설치 필요) |
| 리소스 효율성 | CPU/RAM 소비 극도로 낮음 (단일 머신에서 수만 VU 가능) | GIL 제약으로 CPU 사용량 높음 (멀티코어 분산 필요) |
| 사용자 인터페이스 | CLI 터미널 출력 요약 중심 | 직관적인 내장 Web 대시보드 그래프 제공 |
| 추천 시나리오 | CI/CD 파이프라인, 에어갭 베어메탈 노드 간이 검증 | 실시간 그래프를 보며 임계치를 수동 조절하고 싶을 때 |
http://<NODE>:3000) 또는 curl http://127.0.0.1:8000/metrics를 확인합니다.vllm:num_requests_waiting(큐 대기 건수)이 급증하는 시점이 GPU 서빙 파이프라인의 임계 한계(Saturation Point)입니다.===
사내 Nexus 프록시 레지스트리에 등록해야 하는 컨테이너 이미지별 원본 도메인(Registry FQDN)과, RHEL 10.2 환경에서 라이선스(서브스크립션) 없이 RPM을 다운로드할 수 있는지 여부 및 현실적인 대안을 명확히 정리해 드립니다.
Nexus에서 Docker Proxy Repository를 구성할 때 Remote Storage로 등록해야 하는 원본 레지스트리 도메인 목록입니다.
| 대상 이미지 | 원본 호스트 도메인 (Registry URL) | Nexus Proxy 대상 FQDN | 비고 |
|---|---|---|---|
vllm/vllm-openai | Docker Hub | registry-1.docker.io | 기본 공식 Docker Registry |
wilic/gpu-burn | Docker Hub | registry-1.docker.io | 커뮤니티 GPU 번인 툴 |
grafana/k6 | Docker Hub | registry-1.docker.io | 부하 생성 툴 |
prom/prometheus | Docker Hub | registry-1.docker.io | 모니터링 엔진 |
grafana/grafana | Docker Hub | registry-1.docker.io | 대시보드 시각화 |
| nvcr.io/nvidia/...
(cuda-sample, dcgm-exporter) | NVIDIA GPU Cloud (NGC) | nvcr.io | NVIDIA 공식 엔터프라이즈 레지스트리 |
| quay.io/centos/...
(테스트용 OS 베이스 컨테이너) | Red Hat Quay.io | quay.io | CentOS / CoreOS 오픈 레지스트리 |
| registry.access.redhat.com
(UBI 기반 이미지) | Red Hat 공식 레지스트리 | registry.access.redhat.com | Red Hat UBI 무료 기본 이미지 |
Nexus 프록시 개설 팁:
대부분의 툴(vllm,k6,prometheus,grafana,gpu-burn)은 Docker Hub(registry-1.docker.io) 하나만 프록시로 열려 있어도 해결됩니다. 다만 NVIDIA 공식 도구(cuda-sample,dcgm-exporter)를 끌어오려면nvcr.io프록시 레포지토리가 사내 Nexus에 추가로 등록되어 있어야 합니다.
결론부터 말씀드리면, 순수 RHEL 정규 패키지(BaseOS, AppStream 등)는 원칙적으로 유효한 Red Hat Subscription(계정 인증) 없이는 일반 미러 사이트처럼 익명으로 다운로드할 수 없습니다.
하지만 인프라 테스트 및 구축 단계에서 라이선스 없이 해결할 수 있는 3가지 현실적인 방법이 있습니다.
Red Hat은 개발자 및 엔지니어에게 최대 16대 노드까지 무료로 정식 RHEL을 사용할 수 있는 'Red Hat Developer Subscription for Individuals'를 제공합니다.
developers.redhat.com에서 무료 개인 계정 생성.subscription-manager register --username <계정ID> --password <비밀번호>
subscription-manager attach --auto
dnf install 및 dnf download할 수 있습니다.RHEL 10과 바이너리 레벨에서 호환되는 CentOS Stream 10 저장소를 이용하는 방법입니다.
stress-ng, fio, pciutils, iproute, kernel-devel 등의 기반 오픈소스 패키지는 CentOS Stream 10의 RPM과 빌드 ABI가 완전히 동일합니다.[http://mirror.stream.centos.org/](http://mirror.stream.centos.org/)...)에서 누구나 curl 또는 dnf로 익명 다운로드가 가능합니다.본 작업에서 가장 중요한 패키지들은 애초에 Red Hat 라이선스 검증 대상이 아닙니다.
nvidia-fabricmanager, datacenter-gpu-manager, nvidia-container-toolkit):developer.download.nvidia.com)에 호스팅되어 있으므로, Red Hat 라이선스나 계정 로그인 없이 누구나 무료로 직접 다운로드할 수 있습니다.registry-1.docker.io)와 NVIDIA NGC(nvcr.io) 2개 도메인을 열어두시면 모든 이미지를 커버할 수 있습니다.stress-ng 등 순수 RHEL OS 패키지가 추가로 필요하다면, CentOS Stream 10 공개 저장소에서 받거나 Red Hat 무료 개발자 계정(subscription-manager)을 등록하여 받으시면 됩니다.