flowchart TB
User([Internet User])
subgraph AWS["AWS Cloud"]
subgraph VPC["VPC (10.0.0.0/16)"]
subgraph Public["Public Subnets"]
IGW[Internet Gateway]
NAT[NAT Gateway]
Ingress[NGINX Ingress Controller]
end
subgraph Private["Private Subnets"]
subgraph EKS["EKS Cluster"]
FastAPI[FastAPI Service\nClusterIP :80]
vLLM[vLLM Service\nClusterIP :8000\nSmolLM2-135M-Instruct]
end
end
end
end
User -->|HTTP Request| IGW
IGW --> Ingress
Ingress -->|/ask| FastAPI
FastAPI -->|POST /v1/completions| vLLM
Private -->|Outbound via| NAT
NAT --> IGW
Components:
- VPC — Private subnets for EKS nodes, public subnets for NAT/ALB.
- EKS Cluster — Managed Kubernetes control plane.
- Node Group — EC2 worker nodes in private subnets.
- FastAPI Pod — Receives user questions, forwards to vLLM.
- vLLM Pod — Serves the SmolLM2-135M-Instruct model with an OpenAI-compatible API.
- Ingress — NGINX Ingress Controller exposes the FastAPI service to the internet.
- AWS CLI configured with appropriate credentials
- Terraform >= 1.5
- kubectl
- Helm 3
- Docker
cd terraform
terraform init
terraform plan
terraform applyaws eks update-kubeconfig --name llm-eks-cluster --region <your-region>Note: The NGINX Ingress Controller is deployed automatically using the Terraform Helm provider. No manual installation is required.
Note: The FastAPI Docker image is built and pushed to ECR automatically via Terraform (using a null_resource and local-exec). No manual Docker commands are required.
Note: The vLLM service is deployed automatically using the Terraform Helm provider. No manual helm install is required.
Note: The FastAPI application is deployed automatically using the Terraform Helm provider. You do not need to run a manual
helm installfor FastAPI. The deployment and configuration are managed in your Terraform code.
Before testing, ensure all pods are running:
kubectl get pods -AWait until the STATUS column for all relevant pods (FastAPI, vLLM, ingress-nginx) shows "Running" or "Completed".
To find the external endpoint for your FastAPI application, run:
kubectl get ingress -ALook for the ADDRESS column in the output. For example:
NAMESPACE NAME CLASS HOSTS ADDRESS
default qa-service-ingress nginx * ab151ad96701e487d83998689992032a-1807300597.us-east-2.elb.amazonaws.com
Your FastAPI application is exposed at:
http://ab151ad96701e487d83998689992032a-1807300597.us-east-2.elb.amazonaws.com
You can test the /ask endpoint with:
curl -X POST "http://ab151ad96701e487d83998689992032a-1807300597.us-east-2.elb.amazonaws.com/ask" \
-H "Content-Type: application/json" \
-d '{"question": "What is Kubernetes?"}'Scale vLLM replicas based on CPU/memory utilization or custom metrics (e.g., request queue depth). For the FastAPI service, scale based on request rate.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-vllm
minReplicas: 1
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Use Karpenter to automatically provision new nodes when pods are pending due to resource constraints. Define NodePools with GPU instance types (e.g., g5.xlarge) for LLM workloads and CPU instance types for the API layer.
For production, create a separate node group with GPU instances. Use node selectors or taints/tolerations to schedule vLLM pods on GPU nodes only.
Set accurate resource requests so the scheduler can pack pods efficiently. For vLLM, memory is the key constraint — the model must fit in memory.
vLLM supports serving multiple models. Consolidate smaller models onto shared instances to improve utilization.
vLLM handles continuous batching internally. Tune --max-num-batched-tokens and --max-num-seqs to balance throughput and latency.
Use Spot instances for non-critical or stateless workloads (FastAPI layer). For vLLM, prefer On-Demand to avoid model reload times on interruption.