Complete troubleshooting reference for AWS Trainium (Trn1), Inferentia2 (Inf2), and vLLM-Neuron deployments on Amazon EKS.
- Trainium Training Issues
- Inferentia2 Inference Issues
- Kubernetes/EKS Issues
- Validation Checklist
- Quick Reference
Symptom:
RuntimeError: No Neuron devices found
Error: Could not find NeuronCore devices
Traceback: neuron_cc: command not found
Diagnosis:
Check if Neuron driver is loaded:
# SSH into the Trn1 node
kubectl get nodes -l node.kubernetes.io/instance-type=trn1.32xlarge
NODE_NAME=$(kubectl get nodes -l node.kubernetes.io/instance-type=trn1.32xlarge -o jsonpath='{.items[0].metadata.name}')
kubectl debug node/$NODE_NAME -it --image=ubuntu
# In the debug container
chroot /host bash
# Check driver
ls /dev/neuron*
# Expected: /dev/neuron0 /dev/neuron1 ...
# Check kernel module
lsmod | grep neuron
# Expected: neuron 1234567 0
# Check Neuron runtime version
neuron-ls
# Expected: Neuron runtime version X.Y.ZSolution:
Option A: Driver not installed (most common)
# Install Neuron driver on the node
# Add to EC2NodeClass userData or AMI
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: trainium-nodeclass
spec:
userData: |
#!/bin/bash
# Install Neuron driver
sudo yum install aws-neuronx-dkms -y
sudo yum install aws-neuronx-collectives -y
sudo yum install aws-neuronx-runtime-lib -y
# Load kernel module
sudo modprobe neuron
# Verify
ls /dev/neuron*Option B: Use AWS Deep Learning AMI (recommended)
# Get latest DL AMI ID
aws ec2 describe-images \
--owners amazon \
--filters "Name=name,Values=Deep Learning AMI Neuron PyTorch*" \
--query 'Images | sort_by(@, &CreationDate) | [-1].ImageId' \
--output text
# Update EC2NodeClass
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: trainium-nodeclass
spec:
amiFamily: AL2
amiSelectorTerms:
- id: ami-0123456789abcdef0 # DL AMIOption C: Fix missing kernel module
# On the node
sudo modprobe neuron
# Make persistent
echo "neuron" | sudo tee -a /etc/modules-load.d/neuron.conf
# Verify
lsmod | grep neuron
ls -la /dev/neuron*Prevention:
- Use AWS Deep Learning AMI with Neuron pre-installed
- Add Neuron driver installation to EC2NodeClass userData
- Validate nodes before scheduling training jobs:
# Add node selector to training pod
spec:
nodeSelector:
aws.amazon.com/neuron-device-count: "32"
tolerations:
- key: "aws.amazon.com/neuron"
operator: "Exists"Symptom:
RuntimeError: NEURON_RT_EXEC_ERROR: Out of memory
NeuronCore memory allocation failed
torch.cuda.OutOfMemoryError (even though not using CUDA)
Pod killed by OOMKiller (exit code 137)
Diagnosis:
Check memory usage:
# Get pod name
POD=$(kubectl get pods -l job-name=llama-training -o jsonpath='{.items[0].metadata.name}')
# Check pod memory limits
kubectl describe pod $POD | grep -A 5 "Limits:"
# Check NeuronCore memory usage
kubectl exec $POD -- neuron-top
# Check host memory
kubectl exec $POD -- free -h
# Check logs for memory allocation failures
kubectl logs $POD | grep -i "memory\|oom"Solution:
Option A: Increase batch size / enable gradient checkpointing
# training_config.yaml
training_args:
per_device_train_batch_size: 1 # Reduce from 4 to 1
gradient_accumulation_steps: 16 # Increase to maintain effective batch size
gradient_checkpointing: true # Enable to save memory
# Or in Python
from transformers import TrainingArguments
training_args = TrainingArguments(
per_device_train_batch_size=1,
gradient_accumulation_steps=16,
gradient_checkpointing=True,
fp16=True, # Use mixed precision
)Option B: Enable Neuron memory optimization
# Enable tensor parallelism to distribute model across NeuronCores
import torch_neuronx
from neuronx_distributed.parallel_layers import parallel_state
# Initialize tensor parallel group
parallel_state.initialize_model_parallel(tensor_model_parallel_size=8)
# Enable activation checkpointing
model.gradient_checkpointing_enable()Option C: Increase pod memory limits
# llama-training-job.yaml
spec:
containers:
- name: training
resources:
limits:
memory: 256Gi # Increase from 128Gi
aws.amazon.com/neuroncore: 32
requests:
memory: 256Gi
aws.amazon.com/neuroncore: 32Option D: Use larger instance type
# NodePool update - move from trn1.2xlarge to trn1.32xlarge
spec:
template:
spec:
requirements:
- key: node.kubernetes.io/instance-type
operator: In
values:
- trn1.32xlarge # 32 cores, 512GB memoryPrevention:
- Profile model memory before full training:
# memory_profiling.py
import torch
import torch_neuronx
model = YourModel()
model.to('xla')
# Dry run with profiling
with torch.no_grad():
dummy_input = torch.randn(1, 512).to('xla')
output = model(dummy_input)
# Check memory
print(torch_neuronx.memory_summary())- Start with small batch size and increase gradually
- Use gradient checkpointing for models >7B parameters
- Monitor memory with Neuron Monitor dashboard
Symptom:
Distributed training stuck at initialization
Timeout waiting for rendezvous
nccl.so: cannot open shared object file
EFA device not found
AllReduce operation timeout
Diagnosis:
Check EFA adapter:
# Get training pod
POD=$(kubectl get pods -l job-name=distributed-training -o jsonpath='{.items[0].metadata.name}')
# Check EFA interface
kubectl exec $POD -- fi_info -p efa
# Expected output:
# provider: efa
# version: X.Y.Z
# Check Neuron Collective Communication Library
kubectl exec $POD -- ls -la /opt/aws/neuron/lib | grep nccl
# Check EFA network interface
kubectl exec $POD -- ip link show | grep efa
# Expected: efa0: <BROADCAST,MULTICAST,UP,LOWER_UP>
# Test EFA connectivity between pods
kubectl exec $POD -- efa-testSolution:
Option A: Enable EFA in NodePool
# karpenter/nodepool-trainium.yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: trainium-distributed
spec:
template:
spec:
requirements:
- key: node.kubernetes.io/instance-type
operator: In
values:
- trn1.32xlarge
kubelet:
systemReserved:
cpu: "1"
memory: 2Gi
---
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: trainium-efa-nodeclass
spec:
networkInterfaces:
- deviceIndex: 0
interfaceType: efa # Enable EFA
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: my-clusterOption B: Fix Pod networking for EFA
# distributed-training-job.yaml
apiVersion: v1
kind: Pod
metadata:
name: training-worker
annotations:
k8s.amazonaws.com/efa: "1" # Request EFA device
spec:
hostNetwork: true # Required for EFA
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: training
image: your-training-image
securityContext:
capabilities:
add:
- IPC_LOCK # Required for EFA
resources:
limits:
vpc.amazonaws.com/efa: 1
aws.amazon.com/neuroncore: 32
volumeMounts:
- name: efa-device
mountPath: /dev/infiniband
volumes:
- name: efa-device
hostPath:
path: /dev/infinibandOption C: Install EFA driver in container
# Dockerfile
FROM pytorch/pytorch:2.0-cuda11.8-cudnn8-runtime
# Install Neuron SDK
RUN pip install torch-neuronx neuronx-cc --extra-index-url=https://pip.repos.neuron.amazonaws.com
# Install EFA driver
RUN apt-get update && apt-get install -y \
libfabric-dev \
libhwloc-dev \
&& rm -rf /var/lib/apt/lists/*
# Install Neuron Collective Communication
RUN pip install neuronx-distributedOption D: Fix security group for EFA traffic
# Allow all traffic within security group (required for EFA)
SECURITY_GROUP_ID=$(aws eks describe-cluster \
--name my-cluster \
--query 'cluster.resourcesVpcConfig.clusterSecurityGroupId' \
--output text)
aws ec2 authorize-security-group-ingress \
--group-id $SECURITY_GROUP_ID \
--protocol all \
--source-group $SECURITY_GROUP_IDPrevention:
- Always use hostNetwork for distributed training
- Enable EFA in EC2NodeClass from the start
- Test EFA connectivity before multi-node training:
# test_efa.py
import torch
import torch_neuronx
import torch.distributed as dist
dist.init_process_group(backend='xla')
rank = dist.get_rank()
world_size = dist.get_world_size()
print(f"Rank {rank}/{world_size} - EFA initialized")
# Test collective operation
tensor = torch.ones(1).to('xla')
dist.all_reduce(tensor)
print(f"Rank {rank} - AllReduce result: {tensor.item()}")- Use placement groups for low-latency EFA communication
Symptom:
Training speed: 100 tokens/s (expected: 500+ tokens/s)
Model compilation taking >30 minutes
Repeated graph recompilation during training
XLA HLO compilation warnings in logs
Diagnosis:
Check performance metrics:
# Monitor NeuronCore utilization
kubectl exec $POD -- neuron-top
# Expected: NeuronCore 0-31 at 90%+ utilization
# Check compilation logs
kubectl logs $POD | grep "compilation\|XLA"
# Check if model is recompiling
kubectl logs $POD | grep "Compiling" | wc -l
# Should be 1 (initial compilation only)
# Monitor throughput
kubectl exec $POD -- cat /tmp/training_metrics.txtSolution:
Option A: Enable graph caching to avoid recompilation
# training_script.py
import torch_neuronx
import os
# Enable persistent graph cache
os.environ['NEURON_CC_FLAGS'] = '--cache_dir=/tmp/neuron_cache'
os.environ['NEURON_COMPILE_CACHE_URL'] = '/tmp/neuron_cache'
# Persist cache across runs via PVC
# See volume mount below# Mount persistent cache volume
spec:
containers:
- name: training
volumeMounts:
- name: neuron-cache
mountPath: /tmp/neuron_cache
volumes:
- name: neuron-cache
persistentVolumeClaim:
claimName: neuron-compile-cacheOption B: Optimize data loading
# training_script.py
from torch.utils.data import DataLoader
# Use multiple workers for data loading
train_dataloader = DataLoader(
train_dataset,
batch_size=8,
num_workers=8, # Increase from 0
pin_memory=True, # Enable pin memory
prefetch_factor=2 # Prefetch batches
)Option C: Enable mixed precision training
# training_script.py
import torch
from transformers import TrainingArguments
training_args = TrainingArguments(
fp16=True, # Enable FP16
bf16=False, # Or use BF16
fp16_opt_level='O1', # Optimization level
)
# For Neuron-specific optimization
import torch_neuronx
torch_neuronx.amp.autocast(enabled=True)Option D: Profile and optimize hotspots
# Enable Neuron profiling
kubectl exec $POD -- neuron-profile -p $TRAINING_PID -o /tmp/profile.json
# Analyze profile
kubectl cp $POD:/tmp/profile.json ./profile.json
# View profile (requires Neuron Tools)
neuron-profile view profile.jsonPrevention:
- Always use persistent compilation cache
- Pre-compile models before training:
# pre_compile.py
import torch
import torch_neuronx
model = YourModel()
dummy_input = torch.randn(1, 512)
# Compile model
traced_model = torch_neuronx.trace(model, dummy_input)
torch.jit.save(traced_model, 'compiled_model.pt')- Monitor NeuronCore utilization - should be >80%
- Use Neuron Monitor dashboard to track throughput trends
Symptom:
NeuronCompilerError: Compilation failed
RuntimeError: Graph execution failed
UnsupportedOperator: aten::some_operation
ERROR: Maximum supported tensor size exceeded
Segmentation fault during compilation
Diagnosis:
Check compilation logs:
# Get detailed compilation logs
kubectl logs $POD --tail=1000 | grep -A 20 "Compilation"
# Check Neuron compiler version
kubectl exec $POD -- neuron-cc --version
# Check for unsupported operations
kubectl logs $POD | grep "UnsupportedOperator"
# Check model architecture
kubectl exec $POD -- python -c "from transformers import AutoModel; \
model = AutoModel.from_pretrained('meta-llama/Llama-2-7b-hf'); \
print(model)"Solution:
Option A: Update Neuron SDK to latest version
# Dockerfile
FROM public.ecr.aws/neuron/pytorch-training-neuronx:1.13.1-neuronx-py310-sdk2.16.0-ubuntu20.04
# Or update in running environment
RUN pip install --upgrade torch-neuronx neuronx-ccOption B: Replace unsupported operations
# model_fixes.py
import torch
import torch.nn as nn
# Example: Replace unsupported layer norm
class NeuronLayerNorm(nn.Module):
def __init__(self, normalized_shape, eps=1e-5):
super().__init__()
self.weight = nn.Parameter(torch.ones(normalized_shape))
self.bias = nn.Parameter(torch.zeros(normalized_shape))
self.eps = eps
def forward(self, x):
return torch.nn.functional.layer_norm(
x, self.weight.shape, self.weight, self.bias, self.eps
)
# Patch model
def patch_model_for_neuron(model):
for name, module in model.named_modules():
if isinstance(module, nn.LayerNorm):
setattr(model, name, NeuronLayerNorm(
module.normalized_shape, module.eps
))
return model
model = patch_model_for_neuron(model)Option C: Reduce model size to fit compiler limits
# training_script.py
from transformers import AutoConfig, AutoModelForCausalLM
# Load config and modify
config = AutoConfig.from_pretrained('meta-llama/Llama-2-7b-hf')
config.max_position_embeddings = 2048 # Reduce from 4096
config.num_hidden_layers = 24 # Reduce from 32
model = AutoModelForCausalLM.from_config(config)Option D: Split large model across multiple NeuronCores
# distributed_model.py
from neuronx_distributed.parallel_layers import parallel_state
from neuronx_distributed.parallel_layers import ColumnParallelLinear
# Enable tensor parallelism
parallel_state.initialize_model_parallel(tensor_model_parallel_size=8)
# Replace large layers with parallel versions
model.lm_head = ColumnParallelLinear(
in_features=4096,
out_features=50257,
gather_output=True
)Prevention:
- Test model compilation before full training:
# test_compilation.py
import torch
import torch_neuronx
model = YourModel()
model.eval()
# Test compile with small input
dummy_input = torch.randn(1, 128)
try:
traced = torch_neuronx.trace(model, dummy_input)
print("Compilation successful!")
except Exception as e:
print(f"Compilation failed: {e}")-
Check Neuron SDK compatibility matrix for your model architecture
-
Use reference implementations from AWS Neuron Samples:
-
Enable verbose compilation logs:
import os
os.environ['NEURON_CC_FLAGS'] = '--verbose ALL'Symptom:
RuntimeError: Failed to load model on NeuronCore
Error: No available NeuronCores for model allocation
NeuronCoreAllocationError: Insufficient NeuronCore memory
vllm.engine: Cannot allocate model across available devices
Diagnosis:
Check NeuronCore availability:
# Get inference pod
POD=$(kubectl get pods -l app=vllm-llama3 -o jsonpath='{.items[0].metadata.name}')
# Check NeuronCore allocation
kubectl exec $POD -- neuron-ls
# Expected output:
# +--------+--------+--------+
# | Device | Status | Memory |
# +--------+--------+--------+
# | 0 | In-use | 16 GB |
# | 1 | Free | 16 GB |
# ...
# Check container resource limits
kubectl describe pod $POD | grep -A 10 "Limits:"
# Check for resource allocation errors
kubectl describe pod $POD | grep -i "neuron"Solution:
Option A: Request correct number of NeuronCores
# vllm-llama3-inf2.yaml
spec:
containers:
- name: vllm-server
image: vllm/vllm-neuron:latest
resources:
limits:
aws.amazon.com/neuroncore: 2 # Llama 3 8B needs 2 cores
memory: 32Gi
requests:
aws.amazon.com/neuroncore: 2
memory: 32GiModel size to NeuronCore mapping:
- 7-8B models: 2 NeuronCores
- 13B models: 4 NeuronCores
- 70B models: 24 NeuronCores
Option B: Reduce model size to fit available cores
# Use smaller model or quantized version
kubectl set env deployment/vllm-llama3 \
MODEL_NAME=meta-llama/Meta-Llama-3-8B-Instruct \
QUANTIZATION=awq # Enable quantizationOption C: Fix NeuronCore fragmentation
# Restart pod to reset NeuronCore allocation
kubectl delete pod $POD
# Or drain node and restart
NODE=$(kubectl get pod $POD -o jsonpath='{.spec.nodeName}')
kubectl drain $NODE --ignore-daemonsets --delete-emptydir-data
kubectl uncordon $NODEOption D: Verify Device Plugin is registering cores correctly
# Check device plugin logs
kubectl logs -n kube-system -l app.kubernetes.io/name=neuron-device-plugin
# Check node capacity
kubectl get node -o json | jq '.items[].status.capacity["aws.amazon.com/neuroncore"]'
# Should show total NeuronCores (e.g., "2" for Inf2.xlarge)Prevention:
- Always specify exact NeuronCore requirements in pod spec
- Use pod anti-affinity to prevent core fragmentation:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- vllm-llama3
topologyKey: kubernetes.io/hostname- Monitor NeuronCore allocation with Neuron Monitor
- Test model loading before production deployment:
# test_model_load.py
from vllm import LLM
try:
llm = LLM(
model="meta-llama/Meta-Llama-3-8B",
device="neuron",
tensor_parallel_size=2
)
print("Model loaded successfully!")
except Exception as e:
print(f"Model loading failed: {e}")Symptom:
Observed throughput: 300 tokens/s
Expected throughput: 1100+ tokens/s for Llama 3 8B on Inf2.xlarge
High latency per request (>500ms P99)
vLLM dashboard showing low batch utilization
Diagnosis:
Check throughput metrics:
# Check vLLM metrics
kubectl exec $POD -- curl http://localhost:8000/metrics | grep throughput
# Check batch size
kubectl logs $POD | grep "batch_size"
# Check NeuronCore utilization
kubectl exec $POD -- neuron-top
# Should show >80% utilization
# Run benchmark
kubectl exec $POD -- python3 /app/benchmark.py \
--model meta-llama/Meta-Llama-3-8B \
--num-prompts 100Solution:
Option A: Increase batch size for better throughput
# vllm-llama3-inf2.yaml
spec:
containers:
- name: vllm-server
env:
- name: MAX_BATCH_SIZE
value: "32" # Increase from default 8
- name: MAX_NUM_SEQS
value: "256" # Increase max concurrent sequences
command:
- python3
- -m
- vllm.entrypoints.openai.api_server
- --model
- meta-llama/Meta-Llama-3-8B
- --device
- neuron
- --tensor-parallel-size
- "2"
- --max-model-len
- "2048"
- --max-num-batched-tokens
- "4096" # Increase batchingOption B: Enable continuous batching
# vllm_config.py
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Meta-Llama-3-8B",
device="neuron",
tensor_parallel_size=2,
max_num_batched_tokens=4096,
max_num_seqs=256,
enable_chunked_prefill=True # Enable continuous batching
)Option C: Optimize model compilation for inference
# Set Neuron compiler flags for inference optimization
kubectl set env deployment/vllm-llama3 \
NEURON_CC_FLAGS="--model-type=transformer-inference --auto-cast=matmult --auto-cast-type=bf16"Option D: Use larger instance type for higher throughput
# Update NodePool to use Inf2.48xlarge
spec:
template:
spec:
requirements:
- key: node.kubernetes.io/instance-type
operator: In
values:
- inf2.48xlarge # 12 NeuronCores, higher memory bandwidth# Update deployment for inf2.48xlarge
spec:
containers:
- name: vllm-server
resources:
limits:
aws.amazon.com/neuroncore: 12 # Use all 12 cores
memory: 192Gi
env:
- name: TENSOR_PARALLEL_SIZE
value: "12"Prevention:
- Always benchmark with realistic traffic patterns:
# benchmark_inference.py
import asyncio
import aiohttp
import time
async def send_request(session, prompt):
async with session.post(
'http://localhost:8000/v1/completions',
json={
'model': 'meta-llama/Meta-Llama-3-8B',
'prompt': prompt,
'max_tokens': 100
}
) as response:
return await response.json()
async def benchmark(num_requests=100):
start = time.time()
async with aiohttp.ClientSession() as session:
tasks = [
send_request(session, f"Prompt {i}")
for i in range(num_requests)
]
responses = await asyncio.gather(*tasks)
duration = time.time() - start
tokens_generated = num_requests * 100
throughput = tokens_generated / duration
print(f"Throughput: {throughput:.2f} tokens/s")
asyncio.run(benchmark())- Monitor throughput continuously with Prometheus:
# prometheus-rule.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: vllm-throughput-alert
spec:
groups:
- name: vllm
rules:
- alert: LowThroughput
expr: rate(vllm_tokens_generated_total[5m]) < 500
annotations:
summary: "vLLM throughput below 500 tokens/s"- Use appropriate batch sizes based on model size
- Enable PagedAttention for memory efficiency
Symptom:
P99 latency: >500ms (expected: <200ms)
Inconsistent response times
Long tail latencies affecting user experience
Timeout errors for some requests
Diagnosis:
Check latency metrics:
# Get latency distribution from vLLM metrics
kubectl exec $POD -- curl http://localhost:8000/metrics | grep latency
# Check request queue depth
kubectl logs $POD | grep "queue"
# Monitor with custom script
kubectl exec $POD -- python3 - <<EOF
import time
import requests
latencies = []
for i in range(100):
start = time.time()
resp = requests.post('http://localhost:8000/v1/completions', json={
'model': 'meta-llama/Meta-Llama-3-8B',
'prompt': 'Hello',
'max_tokens': 50
})
latency = time.time() - start
latencies.append(latency)
latencies.sort()
print(f"P50: {latencies[50]*1000:.2f}ms")
print(f"P99: {latencies[99]*1000:.2f}ms")
EOFSolution:
Option A: Reduce KV cache size to avoid memory pressure
spec:
containers:
- name: vllm-server
env:
- name: VLLM_GPU_MEMORY_UTILIZATION # Also applies to Neuron
value: "0.8" # Reduce from 0.9 to avoid OOM spikes
command:
- python3
- -m
- vllm.entrypoints.openai.api_server
- --model
- meta-llama/Meta-Llama-3-8B
- --max-model-len
- "2048" # Reduce from 4096
- --gpu-memory-utilization
- "0.8"Option B: Enable speculative decoding
# vllm_server.py
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Meta-Llama-3-8B",
device="neuron",
tensor_parallel_size=2,
speculative_model="facebook/opt-125m", # Small draft model
num_speculative_tokens=5
)Option C: Implement request prioritization
# Create separate deployments for latency-sensitive traffic
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-llama3-low-latency
spec:
replicas: 2
template:
spec:
containers:
- name: vllm-server
env:
- name: MAX_NUM_SEQS
value: "64" # Lower concurrency for lower latency
- name: MAX_BATCH_SIZE
value: "8" # Smaller batchesOption D: Add HPA for scaling under load
# hpa-vllm.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm-llama3-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-llama3
minReplicas: 2
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: vllm_request_latency_seconds_p99
target:
type: Value
value: "200m" # 200msPrevention:
- Set appropriate timeouts:
# client_config.py
import requests
response = requests.post(
'http://vllm-service:8000/v1/completions',
json={'model': 'meta-llama/Meta-Llama-3-8B', 'prompt': 'Hello'},
timeout=5.0 # 5 second timeout
)- Monitor P99 latency continuously:
# grafana-dashboard.json (excerpt)
{
"targets": [{
"expr": "histogram_quantile(0.99, rate(vllm_request_duration_seconds_bucket[5m]))",
"legendFormat": "P99 Latency"
}]
}- Use pod disruption budgets to prevent latency spikes during updates:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: vllm-pdb
spec:
minAvailable: 1
selector:
matchLabels:
app: vllm-llama3Symptom:
High memory usage for KV cache
OOM errors with multiple concurrent requests
vLLM logs not showing PagedAttention initialization
Memory usage not scaling efficiently with request count
Diagnosis:
Check PagedAttention status:
# Check vLLM logs for PagedAttention
kubectl logs $POD | grep -i "paged\|attention"
# Expected: "PagedAttention enabled"
# Check memory usage pattern
kubectl exec $POD -- neuron-top -t memory
# Check vLLM version (PagedAttention requires v0.3.0+)
kubectl exec $POD -- pip show vllm-neuronSolution:
Option A: Upgrade to vLLM-Neuron with PagedAttention support
# Dockerfile
FROM public.ecr.aws/neuron/pytorch-inference-neuronx:2.1.2-neuronx-py310-sdk2.16.0-ubuntu20.04
# Install vLLM-Neuron with PagedAttention
RUN pip install vllm-neuron>=0.3.0Option B: Explicitly enable PagedAttention
# vllm_server.py
from vllm import LLM
llm = LLM(
model="meta-llama/Meta-Llama-3-8B",
device="neuron",
tensor_parallel_size=2,
block_size=16, # Enable PagedAttention
max_num_batched_tokens=2048,
enable_paged_kv_cache=True # Explicit flag
)Option C: Configure KV cache block size
spec:
containers:
- name: vllm-server
command:
- python3
- -m
- vllm.entrypoints.openai.api_server
- --model
- meta-llama/Meta-Llama-3-8B
- --device
- neuron
- --block-size
- "16" # PagedAttention block size
- --max-num-batched-tokens
- "2048"Option D: Verify Neuron SDK version supports PagedAttention
# Check Neuron SDK version (requires 2.16.0+)
kubectl exec $POD -- python3 -c "import torch_neuronx; print(torch_neuronx.__version__)"
# Upgrade if needed
kubectl set image deployment/vllm-llama3 \
vllm-server=public.ecr.aws/neuron/pytorch-inference-neuronx:2.1.2-neuronx-py310-sdk2.16.0-ubuntu20.04Prevention:
- Always use latest vLLM-Neuron release
- Verify PagedAttention in logs on startup:
kubectl logs -f $POD | grep -A 5 "Initializing"
# Should show: "Using PagedAttention with block_size=16"- Monitor memory efficiency:
# memory_efficiency_check.py
# Before PagedAttention: ~10GB per request
# After PagedAttention: ~500MB per request shared pool- Test with multiple concurrent requests:
# concurrent_test.sh
for i in {1..10}; do
curl -X POST http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Meta-Llama-3-8B",
"prompt": "Hello",
"max_tokens": 100
}' &
done
waitSymptom:
Pod status: ContainerCreating for >5 minutes
Events: "Failed to attach NeuronDevice"
Events: "Insufficient aws.amazon.com/neuroncore"
Node has NeuronCores but pod not scheduling
Diagnosis:
Check pod status and events:
# Get pod details
POD=$(kubectl get pods -l app=vllm-llama3 --field-selector=status.phase=Pending -o jsonpath='{.items[0].metadata.name}')
kubectl describe pod $POD
# Look for:
# - "Insufficient aws.amazon.com/neuroncore"
# - "FailedScheduling"
# - "FailedMount"
# Check node capacity
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, capacity: .status.capacity, allocatable: .status.allocatable}'
# Check device plugin
kubectl get daemonset -n kube-system neuron-device-plugin-daemonsetSolution:
Option A: Device plugin not running on target nodes
# Check device plugin pods
kubectl get pods -n kube-system -l app.kubernetes.io/name=neuron-device-plugin -o wide
# Redeploy device plugin
kubectl delete daemonset -n kube-system neuron-device-plugin-daemonset
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/main/src/k8s/k8s-neuron-device-plugin.yml
# Verify
kubectl wait --for=condition=ready pod -n kube-system -l app.kubernetes.io/name=neuron-device-plugin --timeout=120sOption B: Insufficient NeuronCores on available nodes
# Check current allocation
kubectl describe nodes | grep -A 5 "aws.amazon.com/neuroncore"
# Scale up node pool
kubectl scale nodepool inferentia2-pool --replicas=3
# Or use Karpenter to provision more nodes (automatic)Option C: Volume mount issues
# Check for volume mount errors
kubectl describe pod $POD | grep -A 10 "Volumes:"
# Common issue: PVC not bound
kubectl get pvc
# Delete and recreate pod
kubectl delete pod $PODOption D: Node taints preventing scheduling
# Check node taints
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, taints: .spec.taints}'
# Add toleration to pod spec if needed
kubectl patch deployment vllm-llama3 --type=json -p='[
{
"op": "add",
"path": "/spec/template/spec/tolerations",
"value": [{
"key": "aws.amazon.com/neuron",
"operator": "Exists",
"effect": "NoSchedule"
}]
}
]'Prevention:
- Always verify device plugin is running before deploying workloads:
# validate-neuron.sh
#!/bin/bash
kubectl get daemonset -n kube-system neuron-device-plugin-daemonset || {
echo "Device plugin not found. Installing..."
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/main/src/k8s/k8s-neuron-device-plugin.yml
}- Set appropriate resource requests:
spec:
containers:
- name: vllm-server
resources:
requests: # Requests == Limits for Neuron resources
aws.amazon.com/neuroncore: 2
memory: 32Gi
limits:
aws.amazon.com/neuroncore: 2
memory: 32Gi- Use pod affinity to prefer nodes with available cores:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: aws.amazon.com/neuron-device-count
operator: Gt
values: ["0"]Symptom:
DaemonSet not creating pods on Inf2/Trn1 nodes
Resource "aws.amazon.com/neuroncore" not showing in node capacity
Device plugin logs showing "Failed to discover Neuron devices"
Pods failing with "UnexpectedAdmissionError"
Diagnosis:
Check device plugin status:
# Check DaemonSet
kubectl get daemonset -n kube-system neuron-device-plugin-daemonset
# Check device plugin pods
kubectl get pods -n kube-system -l app.kubernetes.io/name=neuron-device-plugin -o wide
# Check logs
kubectl logs -n kube-system -l app.kubernetes.io/name=neuron-device-plugin --tail=100
# Check if device plugin registered with kubelet
kubectl get nodes -o json | jq '.items[0].status.capacity'
# Should include "aws.amazon.com/neuroncore" and "aws.amazon.com/neurondevice"Solution:
Option A: Install device plugin
# Install from official repository
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/main/src/k8s/k8s-neuron-device-plugin.yml
# Verify installation
kubectl wait --for=condition=ready pod \
-n kube-system \
-l app.kubernetes.io/name=neuron-device-plugin \
--timeout=120s
# Check registration
kubectl get nodes -o json | jq '.items[].status.capacity["aws.amazon.com/neuroncore"]'Option B: Fix DaemonSet node selector
# Check if DaemonSet has restrictive node selectors
kubectl get daemonset -n kube-system neuron-device-plugin-daemonset -o yaml | grep -A 5 nodeSelector
# Remove node selector if too restrictive
kubectl patch daemonset -n kube-system neuron-device-plugin-daemonset --type=json -p='[
{
"op": "remove",
"path": "/spec/template/spec/nodeSelector"
}
]'
# Or update to match your nodes
kubectl patch daemonset -n kube-system neuron-device-plugin-daemonset --type=json -p='[
{
"op": "replace",
"path": "/spec/template/spec/nodeSelector",
"value": {"node.kubernetes.io/instance-type": "inf2.xlarge"}
}
]'Option C: Fix device plugin permissions
# device-plugin-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: neuron-device-plugin
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: neuron-device-plugin
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["nodes/status"]
verbs: ["patch", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: neuron-device-plugin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: neuron-device-plugin
subjects:
- kind: ServiceAccount
name: neuron-device-plugin
namespace: kube-systemkubectl apply -f device-plugin-rbac.yamlOption D: Restart device plugin pods
# Delete pods to force recreation
kubectl delete pods -n kube-system -l app.kubernetes.io/name=neuron-device-plugin
# Wait for new pods
kubectl wait --for=condition=ready pod \
-n kube-system \
-l app.kubernetes.io/name=neuron-device-plugin \
--timeout=120sPrevention:
- Add device plugin to cluster bootstrap process
- Monitor device plugin health:
# prometheus-alert.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: neuron-device-plugin-alert
spec:
groups:
- name: neuron-device-plugin
rules:
- alert: DevicePluginDown
expr: kube_daemonset_status_number_ready{daemonset="neuron-device-plugin-daemonset"} == 0
for: 5m
annotations:
summary: "Neuron device plugin is down"- Validate device plugin after node addition:
# validate-node.sh
#!/bin/bash
NODE=$1
kubectl wait --for=condition=ready node $NODE --timeout=300s
kubectl get node $NODE -o json | jq '.status.capacity["aws.amazon.com/neuroncore"]' | grep -q "[0-9]" || {
echo "NeuronCores not registered on node $NODE"
exit 1
}Symptom:
Pods pending with "Insufficient aws.amazon.com/neuroncore"
Karpenter logs showing "No viable instance types"
Nodes not launching despite pending pods
NodePool not selecting Inf2/Trn1 instance types
Diagnosis:
Check Karpenter status:
# Check Karpenter logs
kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter --tail=200
# Look for:
# - "No viable instance types"
# - "Launch failed"
# - "Insufficient capacity"
# Check NodePool configuration
kubectl get nodepool -o yaml
# Check EC2NodeClass
kubectl get ec2nodeclass -o yaml
# Check pending pods
kubectl get pods --field-selector=status.phase=Pending
kubectl describe pod <pending-pod> | grep -A 10 "Events:"
# Check AWS capacity
aws ec2 describe-instance-type-offerings \
--location-type availability-zone \
--filters Name=instance-type,Values=inf2.xlarge \
--region us-east-1Solution:
Option A: Fix NodePool instance type requirements
# karpenter/nodepool-inferentia2.yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: inferentia2-pool
spec:
template:
spec:
requirements:
- key: karpenter.k8s.aws/instance-family
operator: In
values:
- inf2 # Ensure Inf2 family is included
- key: node.kubernetes.io/instance-type
operator: In
values:
- inf2.xlarge
- inf2.8xlarge
- inf2.24xlarge
- inf2.48xlarge
- key: karpenter.sh/capacity-type
operator: In
values:
- on-demand # Use on-demand for ML workloads
nodeClassRef:
name: inferentia2-nodeclass
limits:
cpu: "1000"
memory: 1000GiOption B: Fix EC2NodeClass subnet/security group selection
# karpenter/ec2nodeclass-neuron.yaml
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: inferentia2-nodeclass
spec:
amiFamily: AL2
role: KarpenterNodeRole-my-cluster
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster # Match your cluster name
kubernetes.io/role/internal-elb: "1" # Private subnets
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster
tags:
Name: karpenter-inferentia2-node
Environment: production
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 100Gi
volumeType: gp3
encrypted: true
userData: |
#!/bin/bash
# Optional: Install Neuron driver if not using DL AMI
# yum install -y aws-neuronx-dkms aws-neuronx-runtime-libOption C: Check IAM permissions for Karpenter
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:RunInstances",
"ec2:CreateFleet",
"ec2:CreateTags",
"ec2:DescribeInstances",
"ec2:DescribeInstanceTypes",
"ec2:DescribeInstanceTypeOfferings",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeImages",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeLaunchTemplates",
"iam:PassRole",
"pricing:GetProducts",
"ssm:GetParameter"
],
"Resource": "*"
}
]
}# Verify Karpenter IAM role has permissions
aws iam get-role-policy \
--role-name KarpenterControllerRole-my-cluster \
--policy-name KarpenterControllerPolicyOption D: Check availability zone capacity
# List AZs with Inf2 capacity
aws ec2 describe-instance-type-offerings \
--filters Name=instance-type,Values=inf2.xlarge \
--location-type availability-zone \
--query 'InstanceTypeOfferings[].Location' \
--output table
# Update NodePool to use AZs with capacity
kubectl patch nodepool inferentia2-pool --type=json -p='[
{
"op": "add",
"path": "/spec/template/spec/requirements/-",
"value": {
"key": "topology.kubernetes.io/zone",
"operator": "In",
"values": ["us-east-1a", "us-east-1b", "us-east-1c"]
}
}
]'Prevention:
- Test NodePool configuration before deploying workloads:
# Create test pod that requires NeuronCores
kubectl run neuron-test \
--image=public.ecr.aws/neuron/pytorch-inference-neuronx:latest \
--limits="aws.amazon.com/neuroncore=2" \
--command -- sleep infinity
# Watch Karpenter provision node
kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter -f
# Cleanup
kubectl delete pod neuron-test- Monitor Karpenter metrics:
# grafana-dashboard.json (excerpt)
{
"targets": [{
"expr": "karpenter_nodes_created_total",
"legendFormat": "Nodes Created"
}]
}- Set up alerts for provisioning failures:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: karpenter-alerts
spec:
groups:
- name: karpenter
rules:
- alert: KarpenterProvisioningFailed
expr: rate(karpenter_provisioning_errors_total[5m]) > 0
annotations:
summary: "Karpenter failing to provision nodes"Symptom:
Pod scheduling error: "Insufficient aws.amazon.com/neuroncore"
Node capacity doesn't show aws.amazon.com/neuroncore
Device plugin running but resources not registered
kubectl describe node shows 0 NeuronCores
Diagnosis:
Check resource availability:
# Check node capacity
kubectl get nodes -o json | jq '.items[] | {
name: .metadata.name,
capacity: .status.capacity,
allocatable: .status.allocatable
}'
# Should show:
# "aws.amazon.com/neuroncore": "2"
# "aws.amazon.com/neurondevice": "1"
# Check device plugin pods on node
NODE_NAME=$(kubectl get nodes -l node.kubernetes.io/instance-type=inf2.xlarge -o jsonpath='{.items[0].metadata.name}')
kubectl get pods -n kube-system -o wide | grep $NODE_NAME | grep neuron
# Check device plugin logs for that node
DEVICE_PLUGIN_POD=$(kubectl get pods -n kube-system -l app.kubernetes.io/name=neuron-device-plugin --field-selector spec.nodeName=$NODE_NAME -o jsonpath='{.items[0].metadata.name}')
kubectl logs -n kube-system $DEVICE_PLUGIN_PODSolution:
Option A: Verify Neuron devices exist on node
# Access node
kubectl debug node/$NODE_NAME -it --image=ubuntu
# In debug container
chroot /host bash
ls -la /dev/neuron*
# Expected: /dev/neuron0 (for Inf2.xlarge with 2 cores)
# If devices missing, check driver
lsmod | grep neuron
dmesg | grep -i neuronOption B: Restart device plugin on specific node
# Delete device plugin pod on problematic node
kubectl delete pod -n kube-system $DEVICE_PLUGIN_POD
# Wait for new pod
kubectl wait --for=condition=ready pod \
-n kube-system \
-l app.kubernetes.io/name=neuron-device-plugin \
--field-selector spec.nodeName=$NODE_NAME \
--timeout=60s
# Verify resources registered
kubectl get node $NODE_NAME -o json | jq '.status.capacity'Option C: Check kubelet configuration
# SSH to node
kubectl debug node/$NODE_NAME -it --image=ubuntu
chroot /host bash
# Check kubelet config
cat /var/lib/kubelet/config.yaml | grep -i plugin
# Ensure device plugin registration path exists
ls -la /var/lib/kubelet/device-plugins/
# Check kubelet logs
journalctl -u kubelet -n 100 | grep -i "device plugin\|neuron"Option D: Manually patch node capacity (temporary workaround)
# This is a workaround, fix root cause instead
kubectl proxy &
curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{
"op": "add",
"path": "/status/capacity/aws.amazon.com~1neuroncore",
"value": "2"
}]' \
http://localhost:8001/api/v1/nodes/$NODE_NAME/statusPrevention:
- Validate node readiness with custom checks:
# node-validation-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: neuron-node-validator
namespace: kube-system
spec:
selector:
matchLabels:
name: neuron-validator
template:
metadata:
labels:
name: neuron-validator
spec:
nodeSelector:
aws.amazon.com/neuron: "true"
containers:
- name: validator
image: ubuntu:22.04
command:
- /bin/bash
- -c
- |
apt-get update && apt-get install -y jq curl
while true; do
if [ ! -e /dev/neuron0 ]; then
echo "ERROR: Neuron device not found!"
fi
sleep 60
done
volumeMounts:
- name: dev
mountPath: /dev
volumes:
- name: dev
hostPath:
path: /dev- Add node labels after validation:
# validate-and-label.sh
#!/bin/bash
NODE=$1
NEURON_CORES=$(kubectl get node $NODE -o json | jq -r '.status.capacity["aws.amazon.com/neuroncore"]' || echo "0")
if [ "$NEURON_CORES" -gt "0" ]; then
kubectl label node $NODE aws.amazon.com/neuron-validated=true
echo "Node $NODE validated: $NEURON_CORES cores"
else
echo "ERROR: Node $NODE has no NeuronCores!"
kubectl label node $NODE aws.amazon.com/neuron-validated=false
fi- Use node selectors to avoid unvalidated nodes:
spec:
nodeSelector:
aws.amazon.com/neuron-validated: "true"Symptom:
Distributed training falling back to TCP
EFA device not detected in container
fi_info command fails
NCCL timeout errors in multi-node training
Poor distributed training performance
Diagnosis:
Check EFA configuration:
# Check if node supports EFA
kubectl get nodes -o json | jq '.items[] | {
name: .metadata.name,
instanceType: .metadata.labels["node.kubernetes.io/instance-type"]
}'
# Trn1 instances support EFA by default
# Check EFA device plugin
kubectl get daemonset -n kube-system aws-efa-k8s-device-plugin
# Check pod EFA allocation
kubectl describe pod $TRAINING_POD | grep -A 5 "vpc.amazonaws.com/efa"
# Test EFA in pod
kubectl exec $TRAINING_POD -- fi_info -p efaSolution:
Option A: Install EFA device plugin
# Install EFA device plugin
kubectl apply -f https://raw.githubusercontent.com/aws/eks-charts/master/stable/aws-efa-k8s-device-plugin/deploy-all.yaml
# Verify
kubectl get daemonset -n kube-system aws-efa-k8s-device-pluginOption B: Enable EFA in EC2NodeClass
# karpenter/ec2nodeclass-trainium.yaml
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: trainium-efa-nodeclass
spec:
amiFamily: AL2
role: KarpenterNodeRole-my-cluster
networkInterfaces:
- deviceIndex: 0
interfaceType: efa # Enable EFA
groups:
- sg-0123456789abcdef0 # Security group with all-all rule
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster
userData: |
#!/bin/bash
# Install EFA driver (if not in AMI)
curl -O https://efa-installer.amazonaws.com/aws-efa-installer-latest.tar.gz
tar -xf aws-efa-installer-latest.tar.gz
cd aws-efa-installer
sudo ./efa_installer.sh -yOption C: Configure pod for EFA
# distributed-training-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: distributed-training
spec:
template:
metadata:
annotations:
k8s.amazonaws.com/efa: "1" # Request EFA
spec:
hostNetwork: true # Required for EFA
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: training
image: your-training-image
securityContext:
capabilities:
add:
- IPC_LOCK # Required for EFA
- SYS_PTRACE
resources:
limits:
vpc.amazonaws.com/efa: 1
aws.amazon.com/neuroncore: 32
requests:
vpc.amazonaws.com/efa: 1
aws.amazon.com/neuroncore: 32
volumeMounts:
- name: efa-device
mountPath: /dev/infiniband
- name: shm
mountPath: /dev/shm
volumes:
- name: efa-device
hostPath:
path: /dev/infiniband
- name: shm
emptyDir:
medium: Memory
sizeLimit: 64Gi # Large shared memory for EFAOption D: Fix security group for EFA
# EFA requires all-all traffic within security group
SECURITY_GROUP_ID=$(kubectl get ec2nodeclass trainium-efa-nodeclass -o jsonpath='{.spec.securityGroupSelectorTerms[0].id}')
# Add all traffic rule
aws ec2 authorize-security-group-ingress \
--group-id $SECURITY_GROUP_ID \
--protocol all \
--source-group $SECURITY_GROUP_ID
# Verify
aws ec2 describe-security-groups \
--group-ids $SECURITY_GROUP_ID \
--query 'SecurityGroups[0].IpPermissions'Prevention:
- Test EFA before distributed training:
# test_efa.py
import subprocess
import sys
# Check EFA device
result = subprocess.run(['fi_info', '-p', 'efa'], capture_output=True)
if result.returncode != 0:
print("ERROR: EFA not available")
sys.exit(1)
print("EFA device found:")
print(result.stdout.decode())
# Test NCCL with EFA
import torch
import torch.distributed as dist
dist.init_process_group(backend='xla')
print(f"Distributed initialized with EFA: rank {dist.get_rank()}")- Enable EFA from the start for distributed workloads
- Use placement groups for co-located nodes:
# ec2nodeclass-with-placement-group.yaml
spec:
launchTemplate:
placement:
groupName: distributed-training-pgSymptom:
Neuron metrics missing from Prometheus
Grafana dashboard showing "No Data"
neuron_execution_latency metrics not found
Neuron Monitor pod running but no metrics
ServiceMonitor not scraping
Diagnosis:
Check Neuron Monitor status:
# Check Neuron Monitor DaemonSet
kubectl get daemonset -n kube-system neuron-monitor
# Check Neuron Monitor pods
kubectl get pods -n kube-system -l app=neuron-monitor -o wide
# Check Neuron Monitor logs
kubectl logs -n kube-system -l app=neuron-monitor --tail=50
# Check metrics endpoint
MONITOR_POD=$(kubectl get pods -n kube-system -l app=neuron-monitor -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n kube-system $MONITOR_POD -- curl http://localhost:8000/metrics
# Check ServiceMonitor
kubectl get servicemonitor -n kube-system neuron-monitor
# Check Prometheus targets
kubectl port-forward -n monitoring svc/prometheus-operated 9090:9090 &
curl http://localhost:9090/api/v1/targets | jq '.data.activeTargets[] | select(.labels.job == "neuron-monitor")'Solution:
Option A: Install Neuron Monitor DaemonSet
# monitoring/neuron-monitor-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: neuron-monitor
namespace: kube-system
labels:
app: neuron-monitor
spec:
selector:
matchLabels:
app: neuron-monitor
template:
metadata:
labels:
app: neuron-monitor
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8000"
prometheus.io/path: "/metrics"
spec:
nodeSelector:
aws.amazon.com/neuron-device-count: "2" # Only on Neuron nodes
containers:
- name: neuron-monitor
image: public.ecr.aws/neuron/neuron-monitor:latest
ports:
- containerPort: 8000
name: metrics
securityContext:
privileged: true
volumeMounts:
- name: neuron-devices
mountPath: /dev
volumes:
- name: neuron-devices
hostPath:
path: /dev
---
apiVersion: v1
kind: Service
metadata:
name: neuron-monitor
namespace: kube-system
labels:
app: neuron-monitor
spec:
ports:
- port: 8000
name: metrics
selector:
app: neuron-monitor
type: ClusterIPkubectl apply -f monitoring/neuron-monitor-daemonset.yamlOption B: Create ServiceMonitor for Prometheus Operator
# monitoring/neuron-monitor-servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: neuron-monitor
namespace: kube-system
labels:
app: neuron-monitor
spec:
selector:
matchLabels:
app: neuron-monitor
endpoints:
- port: metrics
interval: 30s
path: /metricskubectl apply -f monitoring/neuron-monitor-servicemonitor.yaml
# Verify ServiceMonitor is created
kubectl get servicemonitor -n kube-system neuron-monitorOption C: Add Prometheus scrape configuration (if not using Operator)
# prometheus-additional-scrape-configs.yaml
- job_name: 'neuron-monitor'
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- kube-system
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: neuron-monitor
- source_labels: [__meta_kubernetes_pod_ip]
action: replace
target_label: __address__
replacement: $1:8000
- source_labels: [__meta_kubernetes_pod_node_name]
action: replace
target_label: node# Update Prometheus configuration
kubectl create secret generic prometheus-additional-scrape-configs \
--from-file=prometheus-additional-scrape-configs.yaml \
--dry-run=client -o yaml | kubectl apply -n monitoring -f -
# Reload Prometheus
kubectl rollout restart statefulset/prometheus-prometheus -n monitoringOption D: Check Prometheus RBAC permissions
# prometheus-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus
rules:
- apiGroups: [""]
resources:
- nodes
- nodes/metrics
- services
- endpoints
- pods
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources:
- configmaps
verbs: ["get"]
- nonResourceURLs: ["/metrics"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitoringkubectl apply -f prometheus-rbac.yamlPrevention:
- Deploy Neuron Monitor as part of cluster setup
- Validate metrics after deployment:
# validate-metrics.sh
#!/bin/bash
kubectl wait --for=condition=ready pod -n kube-system -l app=neuron-monitor --timeout=120s
MONITOR_POD=$(kubectl get pods -n kube-system -l app=neuron-monitor -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n kube-system $MONITOR_POD -- curl -s http://localhost:8000/metrics | grep -q "neuron_" || {
echo "ERROR: Neuron metrics not available"
exit 1
}
echo "Neuron metrics validated successfully"- Set up alerts for missing metrics:
# prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: neuron-monitor-alerts
namespace: monitoring
spec:
groups:
- name: neuron-monitor
rules:
- alert: NeuronMetricsMissing
expr: absent(neuron_execution_latency_seconds_count)
for: 5m
annotations:
summary: "Neuron metrics missing from Prometheus"- Import Grafana dashboard for Neuron metrics:
# Import official Neuron dashboard
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/main/src/k8s/neuron-monitor-grafana-dashboard.jsonUse this checklist to validate your AWS Neuron on EKS deployment.
# On each Neuron node
kubectl debug node/<node-name> -it --image=ubuntu
chroot /host bash
# Check driver
ls /dev/neuron* | wc -l
# Should be > 0
# Check kernel module
lsmod | grep neuron
# Should show: neuron <size> 0
# Check runtime
neuron-ls
# Should show list of NeuronCoresExpected Result: Neuron devices present, kernel module loaded, runtime responding.
# Check DaemonSet
kubectl get daemonset -n kube-system neuron-device-plugin-daemonset
# Expected: DESIRED == READY
# Check pods on Neuron nodes
kubectl get pods -n kube-system -l app.kubernetes.io/name=neuron-device-plugin -o wide
# Verify no errors in logs
kubectl logs -n kube-system -l app.kubernetes.io/name=neuron-device-plugin --tail=50 | grep -i error
# Expected: No errorsExpected Result: Device plugin pod on every Neuron node, no error logs.
# Check node capacity
kubectl get nodes -o json | jq '.items[] | {
name: .metadata.name,
neuronCores: .status.capacity["aws.amazon.com/neuroncore"],
neuronDevices: .status.capacity["aws.amazon.com/neurondevice"]
}'
# Expected for Inf2.xlarge: "neuronCores": "2", "neuronDevices": "1"
# Expected for Trn1.32xlarge: "neuronCores": "32", "neuronDevices": "16"Expected Result: All Neuron nodes show correct neuroncore count.
For Inference (Inferentia2):
# Test model loading
kubectl run neuron-test --rm -it \
--image=public.ecr.aws/neuron/pytorch-inference-neuronx:latest \
--limits="aws.amazon.com/neuroncore=2" \
--command -- python3 -c "
import torch
import torch_neuronx
from transformers import AutoTokenizer, AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
'gpt2',
torchscript=True
)
tokenizer = AutoTokenizer.from_pretrained('gpt2')
# Trace model
sample_input = tokenizer('Hello world', return_tensors='pt')
traced = torch_neuronx.trace(model, sample_input['input_ids'])
print('Model loaded and traced successfully!')
"Expected Result: Model loads without errors, tracing completes.
For Training (Trainium):
# Test training setup
kubectl run training-test --rm -it \
--image=public.ecr.aws/neuron/pytorch-training-neuronx:latest \
--limits="aws.amazon.com/neuroncore=2" \
--command -- python3 -c "
import torch
import torch_neuronx
import torch.nn as nn
class SimpleModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(10, 10)
def forward(self, x):
return self.linear(x)
model = SimpleModel()
device = 'xla'
model.to(device)
# Test forward pass
x = torch.randn(1, 10).to(device)
y = model(x)
print('Training model initialized successfully!')
print(f'Device: {device}')
"Expected Result: Model initializes on XLA device, forward pass completes.
Inference Test (Full Pipeline):
# Deploy vLLM inference server
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: vllm-test
spec:
containers:
- name: vllm
image: vllm/vllm-neuron:latest
command:
- python3
- -m
- vllm.entrypoints.openai.api_server
- --model
- gpt2
- --device
- neuron
- --tensor-parallel-size
- "2"
ports:
- containerPort: 8000
resources:
limits:
aws.amazon.com/neuroncore: 2
memory: 16Gi
EOF
# Wait for pod ready
kubectl wait --for=condition=ready pod/vllm-test --timeout=300s
# Test inference
kubectl exec vllm-test -- curl -s http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt2",
"prompt": "Hello, my name is",
"max_tokens": 50
}' | jq '.choices[0].text'
# Expected: Returns generated text
# Cleanup
kubectl delete pod vllm-testTraining Test (Simple Job):
# Run simple training job
kubectl apply -f - <<EOF
apiVersion: batch/v1
kind: Job
metadata:
name: training-test
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: training
image: public.ecr.aws/neuron/pytorch-training-neuronx:latest
command:
- python3
- -c
- |
import torch
import torch_neuronx
import torch.nn as nn
class SimpleModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(100, 10)
def forward(self, x):
return self.linear(x)
model = SimpleModel().to('xla')
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
for step in range(10):
x = torch.randn(32, 100).to('xla')
y = torch.randint(0, 10, (32,)).to('xla')
output = model(x)
loss = nn.functional.cross_entropy(output, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f'Step {step}, Loss: {loss.item()}')
print('Training completed successfully!')
resources:
limits:
aws.amazon.com/neuroncore: 2
memory: 16Gi
EOF
# Watch job
kubectl wait --for=condition=complete job/training-test --timeout=600s
kubectl logs job/training-test
# Expected: Training completes with loss decreasing
# Cleanup
kubectl delete job training-testExpected Result: Both inference and training complete without errors.
#!/bin/bash
# validate-neuron-deployment.sh
set -e
echo "=== AWS Neuron on EKS Validation ==="
# 1. Check Neuron nodes
echo -e "\n[1/5] Checking Neuron nodes..."
NEURON_NODES=$(kubectl get nodes -l node.kubernetes.io/instance-type=inf2.xlarge -o name | wc -l)
if [ "$NEURON_NODES" -eq 0 ]; then
echo "ERROR: No Inf2 nodes found"
exit 1
fi
echo "✓ Found $NEURON_NODES Neuron nodes"
# 2. Check Device Plugin
echo -e "\n[2/5] Checking Device Plugin..."
kubectl wait --for=condition=ready pod \
-n kube-system \
-l app.kubernetes.io/name=neuron-device-plugin \
--timeout=60s > /dev/null
echo "✓ Device Plugin running"
# 3. Check NeuronCore resources
echo -e "\n[3/5] Checking NeuronCore resources..."
CORES=$(kubectl get nodes -o json | jq -r '.items[0].status.capacity["aws.amazon.com/neuroncore"]')
if [ "$CORES" == "null" ] || [ "$CORES" == "" ]; then
echo "ERROR: NeuronCores not registered"
exit 1
fi
echo "✓ NeuronCores registered: $CORES per node"
# 4. Test inference
echo -e "\n[4/5] Testing inference..."
kubectl run neuron-inference-test --rm -i \
--image=public.ecr.aws/neuron/pytorch-inference-neuronx:latest \
--limits="aws.amazon.com/neuroncore=2" \
--restart=Never \
--command -- python3 -c "
import torch
import torch_neuronx
print('Inference test passed')
" 2>/dev/null
echo "✓ Inference test passed"
# 5. Test training
echo -e "\n[5/5] Testing training..."
kubectl run neuron-training-test --rm -i \
--image=public.ecr.aws/neuron/pytorch-training-neuronx:latest \
--limits="aws.amazon.com/neuroncore=2" \
--restart=Never \
--command -- python3 -c "
import torch
import torch_neuronx
device = 'xla'
x = torch.ones(1).to(device)
print('Training test passed')
" 2>/dev/null
echo "✓ Training test passed"
echo -e "\n=== All validation checks passed! ==="Usage:
chmod +x validate-neuron-deployment.sh
./validate-neuron-deployment.shCheck Neuron Resources:
# Node capacity
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, neuronCores: .status.capacity["aws.amazon.com/neuroncore"]}'
# Pod allocation
kubectl describe pod <pod-name> | grep -A 5 "Limits:"
# Available cores on node
kubectl describe node <node-name> | grep -A 5 "aws.amazon.com/neuroncore"Monitor NeuronCores:
# In pod
kubectl exec <pod-name> -- neuron-top
# From node
kubectl debug node/<node-name> -it --image=ubuntu
chroot /host bash
neuron-topCheck Device Plugin:
# Status
kubectl get daemonset -n kube-system neuron-device-plugin-daemonset
# Logs
kubectl logs -n kube-system -l app.kubernetes.io/name=neuron-device-plugin --tail=100Restart Components:
# Restart device plugin
kubectl rollout restart daemonset -n kube-system neuron-device-plugin-daemonset
# Restart Neuron Monitor
kubectl rollout restart daemonset -n kube-system neuron-monitor
# Restart inference deployment
kubectl rollout restart deployment vllm-llama3| Instance Type | NeuronCores | Memory | Use Case |
|---|---|---|---|
| inf2.xlarge | 2 | 16 GB | Small models (7-8B) |
| inf2.8xlarge | 2 | 32 GB | Small models (7-8B) |
| inf2.24xlarge | 6 | 192 GB | Medium models (13B) |
| inf2.48xlarge | 12 | 384 GB | Large models (70B) |
| trn1.2xlarge | 2 | 32 GB | Small training |
| trn1.32xlarge | 32 | 512 GB | Large-scale training |
| Model | NeuronCores | Memory | Instance Type |
|---|---|---|---|
| Llama 3 8B | 2 | 16 GB | inf2.xlarge |
| Llama 3 70B | 24 | 384 GB | 2× inf2.48xlarge |
| Mistral 7B | 2 | 16 GB | inf2.xlarge |
| GPT-J 6B | 2 | 16 GB | inf2.xlarge |
| Llama 2 13B | 4 | 32 GB | inf2.24xlarge |
Compilation Logs:
kubectl logs <pod-name> | grep "Compiling\|compilation"Memory Errors:
kubectl logs <pod-name> | grep -i "oom\|memory\|allocation"EFA Issues:
kubectl logs <pod-name> | grep -i "efa\|fabric\|rdma"Device Errors:
kubectl logs <pod-name> | grep -i "neuron\|device\|core"AWS Documentation:
GitHub:
Community:
For additional support: Open an issue at https://github.com/aws-neuron/aws-neuron-sdk/issues