Skip to content

Commit 83ae318

Browse files
committed
--amend
1 parent 4568452 commit 83ae318

1 file changed

Lines changed: 286 additions & 0 deletions

File tree

‎README.md‎

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
# gpuaudit
2+
3+
Scan your cloud for GPU waste and get actionable recommendations to cut your spend.
4+
5+
```
6+
$ gpuaudit scan --skip-eks
7+
8+
Found 38 GPU nodes across 47 nodes in gpu-cluster
9+
10+
gpuaudit — GPU Cost Audit for AWS
11+
Account: 123456789012 | Regions: us-east-1 | Duration: 4.2s
12+
13+
┌──────────────────────────────────────────────────────────┐
14+
│ GPU Fleet Summary │
15+
├──────────────────────────────────────────────────────────┤
16+
│ Total GPU instances: 38 │
17+
│ Total monthly GPU spend: $127450 │
18+
│ Estimated monthly waste: $18200 ( 14%) │
19+
└──────────────────────────────────────────────────────────┘
20+
21+
CRITICAL — 3 instance(s), $15400/mo potential savings
22+
23+
Instance Type Monthly Signal Recommendation
24+
──────────────────────────────────── ────────────────────────── ──────── ──────────────── ──────────────────────────────────────────────
25+
gpu-cluster/ip-10-15-255-248 g6e.16xlarge (1× L40S) $ 6752 idle Node up 13 days with 0 GPU pods scheduled.
26+
gpu-cluster/ip-10-22-250-15 g6e.16xlarge (1× L40S) $ 6752 idle Node up 1 days with 0 GPU pods scheduled.
27+
...
28+
```
29+
30+
## What it scans
31+
32+
- **EC2** — GPU instances (g4dn, g5, g6, g6e, p4d, p4de, p5, inf2, trn1) with CloudWatch metrics
33+
- **SageMaker** — Endpoints with GPU utilization and invocation metrics
34+
- **EKS** — Managed GPU node groups via the AWS EKS API
35+
- **Kubernetes** — GPU nodes and pod allocation via the Kubernetes API (Karpenter, self-managed, any CNI)
36+
37+
## What it detects
38+
39+
- **Idle GPU instances** — running but doing nothing (low CPU + near-zero network for 24+ hours)
40+
- **Oversized GPU** — multi-GPU instances where utilization suggests a single GPU would suffice
41+
- **Pricing mismatch** — on-demand instances running 30+ days that should be Reserved Instances
42+
- **Stale instances** — non-production instances running 90+ days
43+
- **SageMaker low utilization** — endpoints with <10% GPU utilization
44+
- **SageMaker oversized** — endpoints using <30% GPU memory on multi-GPU instances
45+
- **K8s unallocated GPUs** — nodes with GPU capacity but no pods requesting GPUs
46+
47+
## Install
48+
49+
```bash
50+
go install github.com/gpuaudit/cli/cmd/gpuaudit@latest
51+
```
52+
53+
Or build from source:
54+
55+
```bash
56+
git clone https://github.com/gpuaudit/cli.git
57+
cd cli
58+
go build -o gpuaudit ./cmd/gpuaudit
59+
```
60+
61+
## Quick start
62+
63+
```bash
64+
# Uses default AWS credentials (~/.aws/credentials or environment variables)
65+
gpuaudit scan
66+
67+
# Specific profile and region
68+
gpuaudit scan --profile production --region us-east-1
69+
70+
# Kubernetes cluster scan (uses KUBECONFIG or ~/.kube/config)
71+
gpuaudit scan --skip-eks
72+
73+
# Specific kubeconfig and context
74+
gpuaudit scan --kubeconfig ~/.kube/config --kube-context gpu-cluster
75+
76+
# JSON output for automation
77+
gpuaudit scan --format json -o report.json
78+
79+
# Compare two scans to see what changed
80+
gpuaudit diff old-report.json new-report.json
81+
82+
# Slack Block Kit payload (pipe to webhook)
83+
gpuaudit scan --format slack -o - | \
84+
curl -X POST -H 'Content-Type: application/json' -d @- $SLACK_WEBHOOK
85+
86+
# Skip specific scanners
87+
gpuaudit scan --skip-metrics # faster, less accurate
88+
gpuaudit scan --skip-sagemaker
89+
gpuaudit scan --skip-eks # skip AWS EKS API (use --skip-k8s for Kubernetes API)
90+
gpuaudit scan --skip-k8s
91+
```
92+
93+
## Comparing scans
94+
95+
Save scan results as JSON, then diff them later:
96+
97+
```bash
98+
gpuaudit scan --format json -o scan-apr-08.json
99+
# ... time passes, changes happen ...
100+
gpuaudit scan --format json -o scan-apr-15.json
101+
gpuaudit diff scan-apr-08.json scan-apr-15.json
102+
```
103+
104+
```
105+
gpuaudit diff — 2026-04-08 12:00 UTC → 2026-04-15 12:00 UTC
106+
107+
┌──────────────────────────────────────────────────────────┐
108+
│ Cost Delta │
109+
├──────────────────────────────────────────────────────────┤
110+
│ Monthly spend: $142000 → $127450 (-$14550) │
111+
│ Estimated waste: $31000 → $18200 (-$12800) │
112+
│ Instances: 45 → 38 (-9 removed, +2 added) │
113+
└──────────────────────────────────────────────────────────┘
114+
115+
REMOVED — 9 instance(s), -$16200/mo
116+
...
117+
```
118+
119+
Matches instances by ID. Reports added, removed, and changed instances with per-field diffs (instance type, pricing model, cost, state, GPU allocation, waste severity).
120+
121+
## Multi-Account Scanning
122+
123+
Scan multiple AWS accounts in a single invocation using STS AssumeRole.
124+
125+
### Prerequisites
126+
127+
Deploy a read-only IAM role (`gpuaudit-reader`) to each target account. See [Cross-Account Role Setup](#cross-account-role-setup) below.
128+
129+
### Usage
130+
131+
```bash
132+
# Scan specific accounts
133+
gpuaudit scan --targets 111111111111,222222222222 --role gpuaudit-reader
134+
135+
# Scan entire AWS Organization
136+
gpuaudit scan --org --role gpuaudit-reader
137+
138+
# Exclude management account
139+
gpuaudit scan --org --role gpuaudit-reader --skip-self
140+
141+
# With external ID
142+
gpuaudit scan --targets 111111111111 --role gpuaudit-reader --external-id my-secret
143+
```
144+
145+
### Cross-Account Role Setup
146+
147+
#### Terraform
148+
149+
```hcl
150+
variable "management_account_id" {
151+
description = "AWS account ID where gpuaudit runs"
152+
type = string
153+
}
154+
155+
resource "aws_iam_role" "gpuaudit_reader" {
156+
name = "gpuaudit-reader"
157+
assume_role_policy = jsonencode({
158+
Version = "2012-10-17"
159+
Statement = [{
160+
Effect = "Allow"
161+
Principal = { AWS = "arn:aws:iam::${var.management_account_id}:root" }
162+
Action = "sts:AssumeRole"
163+
}]
164+
})
165+
}
166+
167+
resource "aws_iam_role_policy" "gpuaudit_reader" {
168+
name = "gpuaudit-policy"
169+
role = aws_iam_role.gpuaudit_reader.id
170+
policy = file("gpuaudit-policy.json") # from: gpuaudit iam-policy > gpuaudit-policy.json
171+
}
172+
```
173+
174+
Deploy to all accounts using Terraform workspaces or CloudFormation StackSets.
175+
176+
#### CloudFormation StackSet
177+
178+
```yaml
179+
AWSTemplateFormatVersion: "2010-09-09"
180+
Parameters:
181+
ManagementAccountId:
182+
Type: String
183+
Resources:
184+
GpuAuditRole:
185+
Type: AWS::IAM::Role
186+
Properties:
187+
RoleName: gpuaudit-reader
188+
AssumeRolePolicyDocument:
189+
Version: "2012-10-17"
190+
Statement:
191+
- Effect: Allow
192+
Principal:
193+
AWS: !Sub "arn:aws:iam::${ManagementAccountId}:root"
194+
Action: sts:AssumeRole
195+
Policies:
196+
- PolicyName: gpuaudit-policy
197+
PolicyDocument:
198+
Version: "2012-10-17"
199+
Statement:
200+
- Effect: Allow
201+
Action:
202+
- ec2:DescribeInstances
203+
- ec2:DescribeInstanceTypes
204+
- ec2:DescribeRegions
205+
- sagemaker:ListEndpoints
206+
- sagemaker:DescribeEndpoint
207+
- sagemaker:DescribeEndpointConfig
208+
- eks:ListClusters
209+
- eks:ListNodegroups
210+
- eks:DescribeNodegroup
211+
- cloudwatch:GetMetricData
212+
- cloudwatch:GetMetricStatistics
213+
- cloudwatch:ListMetrics
214+
- ce:GetCostAndUsage
215+
- ce:GetReservationUtilization
216+
- ce:GetSavingsPlansUtilization
217+
- pricing:GetProducts
218+
Resource: "*"
219+
```
220+
221+
## IAM permissions
222+
223+
gpuaudit is read-only. It never modifies your infrastructure. Generate the minimal IAM policy:
224+
225+
```bash
226+
gpuaudit iam-policy
227+
```
228+
229+
For Kubernetes scanning, gpuaudit needs `get`/`list` on `nodes` and `pods` cluster-wide.
230+
231+
## GPU pricing reference
232+
233+
```bash
234+
# List all GPU instance pricing
235+
gpuaudit pricing
236+
237+
# Filter by GPU model
238+
gpuaudit pricing --gpu H100
239+
gpuaudit pricing --gpu L4
240+
```
241+
242+
## Output formats
243+
244+
| Format | Flag | Use case |
245+
|---|---|---|
246+
| Table | `--format table` (default) | Terminal viewing |
247+
| JSON | `--format json` | Automation, CI/CD, `gpuaudit diff` |
248+
| Markdown | `--format markdown` | PRs, wikis, docs |
249+
| Slack | `--format slack` | Slack webhook integration |
250+
251+
## How it works
252+
253+
1. **Discovery** — Scans EC2, SageMaker, EKS node groups, and Kubernetes API across multiple regions for GPU resources
254+
2. **Metrics** — Collects 7-day CloudWatch metrics: CPU, network I/O for EC2; GPU utilization, GPU memory, invocations for SageMaker
255+
3. **K8s allocation** — Lists pods requesting `nvidia.com/gpu` resources and maps them to nodes
256+
4. **Analysis** — Applies 7 waste detection rules with severity levels (critical/warning/info)
257+
5. **Recommendations** — Generates specific actions (terminate, downsize, switch pricing) with estimated monthly savings
258+
259+
## Project structure
260+
261+
```
262+
gpuaudit/
263+
├── cmd/gpuaudit/ CLI entry point (cobra)
264+
├── internal/
265+
│ ├── models/ Core data types (GPUInstance, WasteSignal, Recommendation)
266+
│ ├── pricing/ Bundled GPU pricing database (40+ instance types)
267+
│ ├── analysis/ Waste detection rules engine (7 rules)
268+
│ ├── diff/ Scan comparison logic
269+
│ ├── output/ Formatters (table, JSON, markdown, Slack, diff)
270+
│ └── providers/
271+
│ ├── aws/ EC2, SageMaker, EKS, CloudWatch, Cost Explorer
272+
│ └── k8s/ Kubernetes API GPU node/pod discovery
273+
└── LICENSE Apache 2.0
274+
```
275+
276+
## Roadmap
277+
278+
- [ ] DCGM GPU metrics via Kubernetes (actual GPU utilization, not just allocation)
279+
- [ ] SageMaker training job analysis
280+
- [ ] Multi-account (AWS Organizations) scanning
281+
- [ ] GCP + Azure support
282+
- [ ] GitHub Action for scheduled scans
283+
284+
## License
285+
286+
Apache 2.0

0 commit comments

Comments
 (0)