James McCool commited on
Commit
ef06cec
Β·
1 Parent(s): 7928ec7

Added QB force option for micro

Browse files
Files changed (3) hide show
  1. AWS_Load_Balancer_Setup_Guide.md +480 -0
  2. app.py +13 -0
  3. launch-template.json +17 -0
AWS_Load_Balancer_Setup_Guide.md ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AWS Load Balancer Setup Guide
2
+ ## DFS Portfolio Manager - Production Deployment
3
+
4
+ ### Overview
5
+ This guide documents the complete process of migrating from a single EC2 instance to a load-balanced architecture with 3 instances for improved performance, reliability, and cost efficiency.
6
+
7
+ ---
8
+
9
+ ## πŸ“Š Architecture Comparison
10
+
11
+ ### Before (Single Instance)
12
+ - **Instance**: 1x m5.xlarge (4 vCPUs, 16GB RAM)
13
+ - **Cost**: ~$280/month
14
+ - **Issues**: Memory crashes, single point of failure
15
+ - **SSL**: Certbot/Let's Encrypt on instance
16
+
17
+ ### After (Load Balanced)
18
+ - **Instances**: 3x m5.large (2 vCPUs, 8GB RAM each)
19
+ - **Load Balancer**: Application Load Balancer (ALB)
20
+ - **Cost**: ~$220/month (20% savings)
21
+ - **Benefits**: Better performance, auto-scaling, high availability
22
+ - **SSL**: AWS Certificate Manager (free, auto-renewing)
23
+
24
+ ---
25
+
26
+ ## πŸš€ Complete Setup Process
27
+
28
+ ### Prerequisites
29
+ - AWS CLI configured with appropriate permissions
30
+ - Existing EC2 instance with working Streamlit application
31
+ - Domain managed by Cloudflare
32
+ - PowerShell (Windows) or Bash (Linux/Mac)
33
+
34
+ ---
35
+
36
+ ### Step 1: Gather Current Instance Information
37
+
38
+ ```powershell
39
+ # Get current instance ID
40
+ $INSTANCE_ID = aws ec2 describe-instances --filters "Name=instance-state-name,Values=running" --query 'Reservations[0].Instances[0].InstanceId' --output text
41
+
42
+ Write-Host "Current Instance ID: $INSTANCE_ID"
43
+
44
+ # List all running instances if needed
45
+ aws ec2 describe-instances --filters "Name=instance-state-name,Values=running" --query 'Reservations[*].Instances[*].[InstanceId,InstanceType,Tags[?Key==`Name`].Value|[0],PublicIpAddress]' --output table
46
+ ```
47
+
48
+ ---
49
+
50
+ ### Step 2: Create AMI from Current Instance
51
+
52
+ ```powershell
53
+ # Create AMI snapshot of optimized setup
54
+ $AMI_ID = aws ec2 create-image --instance-id $INSTANCE_ID --name "DFS-Portfolio-Manager-$(Get-Date -Format 'yyyyMMdd-HHmm')" --description "DFS Portfolio Manager with memory optimizations and supervisord" --no-reboot --query 'ImageId' --output text
55
+
56
+ Write-Host "Creating AMI: $AMI_ID"
57
+ Write-Host "This will take 5-10 minutes..."
58
+
59
+ # Wait for AMI to be ready
60
+ aws ec2 wait image-available --image-ids $AMI_ID
61
+ Write-Host "AMI is ready!"
62
+ ```
63
+
64
+ ---
65
+
66
+ ### Step 3: Extract Network Configuration
67
+
68
+ ```powershell
69
+ # Get VPC, subnet, and security group info
70
+ $VPC_ID = aws ec2 describe-instances --instance-ids $INSTANCE_ID --query 'Reservations[0].Instances[0].VpcId' --output text
71
+
72
+ $SUBNET_IDS = aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" --query 'Subnets[*].SubnetId' --output text
73
+
74
+ $SECURITY_GROUP_ID = aws ec2 describe-instances --instance-ids $INSTANCE_ID --query 'Reservations[0].Instances[0].SecurityGroups[0].GroupId' --output text
75
+
76
+ Write-Host "VPC ID: $VPC_ID"
77
+ Write-Host "Subnet IDs: $SUBNET_IDS"
78
+ Write-Host "Security Group: $SECURITY_GROUP_ID"
79
+ ```
80
+
81
+ ---
82
+
83
+ ### Step 4: Create Target Group
84
+
85
+ ```powershell
86
+ # Create target group for health checks
87
+ $TARGET_GROUP_ARN = aws elbv2 create-target-group --name "portfolio-manager-targets" --protocol HTTP --port 5000 --vpc-id $VPC_ID --health-check-path "/" --health-check-interval-seconds 30 --health-check-timeout-seconds 10 --healthy-threshold-count 2 --unhealthy-threshold-count 3 --query 'TargetGroups[0].TargetGroupArn' --output text
88
+
89
+ Write-Host "Target Group ARN: $TARGET_GROUP_ARN"
90
+ ```
91
+
92
+ **Target Group Configuration:**
93
+ - **Port**: 5000 (Streamlit application port)
94
+ - **Health Check**: Every 30 seconds at root path "/"
95
+ - **Healthy Threshold**: 2 consecutive successful checks
96
+ - **Unhealthy Threshold**: 3 consecutive failed checks
97
+
98
+ ---
99
+
100
+ ### Step 5: Create Application Load Balancer
101
+
102
+ ```powershell
103
+ # Create ALB
104
+ $ALB_ARN = aws elbv2 create-load-balancer --name "portfolio-manager-alb" --subnets $SUBNET_IDS.Split() --security-groups $SECURITY_GROUP_ID --scheme internet-facing --type application --ip-address-type ipv4 --query 'LoadBalancers[0].LoadBalancerArn' --output text
105
+
106
+ # Get ALB DNS name
107
+ $ALB_DNS = aws elbv2 describe-load-balancers --load-balancer-arns $ALB_ARN --query 'LoadBalancers[0].DNSName' --output text
108
+
109
+ Write-Host "ALB ARN: $ALB_ARN"
110
+ Write-Host "ALB DNS: $ALB_DNS"
111
+ ```
112
+
113
+ ---
114
+
115
+ ### Step 6: Create HTTP Listener
116
+
117
+ ```powershell
118
+ # Create HTTP listener
119
+ aws elbv2 create-listener --load-balancer-arn $ALB_ARN --protocol HTTP --port 80 --default-actions Type=forward,TargetGroupArn=$TARGET_GROUP_ARN
120
+
121
+ Write-Host "HTTP Listener created successfully"
122
+ ```
123
+
124
+ ---
125
+
126
+ ### Step 7: Create Launch Template
127
+
128
+ ```powershell
129
+ # Create JSON content for launch template
130
+ $JsonContent = @"
131
+ {
132
+ "ImageId": "$AMI_ID",
133
+ "InstanceType": "m5.large",
134
+ "SecurityGroupIds": ["$SECURITY_GROUP_ID"],
135
+ "UserData": "$USER_DATA",
136
+ "TagSpecifications": [
137
+ {
138
+ "ResourceType": "instance",
139
+ "Tags": [
140
+ {
141
+ "Key": "Name",
142
+ "Value": "Portfolio-Manager-Instance"
143
+ }
144
+ ]
145
+ }
146
+ ]
147
+ }
148
+ "@
149
+
150
+ # Save launch template data
151
+ [System.IO.File]::WriteAllText("launch-template.json", $JsonContent)
152
+
153
+ # Create launch template
154
+ $LAUNCH_TEMPLATE_ID = aws ec2 create-launch-template --launch-template-name "portfolio-manager-template" --launch-template-data file://launch-template.json --query 'LaunchTemplate.LaunchTemplateId' --output text
155
+
156
+ Write-Host "Launch Template ID: $LAUNCH_TEMPLATE_ID"
157
+ ```
158
+
159
+ **Launch Template Configuration:**
160
+ - **Instance Type**: m5.large (2 vCPUs, 8GB RAM)
161
+ - **User Data**: Automatically restarts Streamlit service on boot
162
+ - **Application Path**: `/home/ec2-user/AWS_Portfolio_Manager`
163
+
164
+ ---
165
+
166
+ ### Step 8: Create Auto Scaling Group
167
+
168
+ ```powershell
169
+ # Convert subnet IDs to comma-separated format
170
+ $SUBNET_LIST = $SUBNET_IDS -replace '\s+', ','
171
+
172
+ # Create Auto Scaling Group
173
+ aws autoscaling create-auto-scaling-group --auto-scaling-group-name "portfolio-manager-asg" --launch-template LaunchTemplateId=$LAUNCH_TEMPLATE_ID,Version='$Latest' --min-size 3 --max-size 5 --desired-capacity 3 --target-group-arns $TARGET_GROUP_ARN --health-check-type ELB --health-check-grace-period 300 --vpc-zone-identifier $SUBNET_LIST
174
+
175
+ # Add tags to ASG
176
+ aws autoscaling create-or-update-tags --tags ResourceId=portfolio-manager-asg,ResourceType=auto-scaling-group,Key=Name,Value=Portfolio-Manager-ASG,PropagateAtLaunch=true
177
+
178
+ Write-Host "Auto Scaling Group created with 3 instances"
179
+ ```
180
+
181
+ **Auto Scaling Group Configuration:**
182
+ - **Desired Capacity**: 3 instances
183
+ - **Minimum**: 3 instances
184
+ - **Maximum**: 5 instances
185
+ - **Health Check**: ELB-based (more reliable)
186
+ - **Grace Period**: 5 minutes for instances to be ready
187
+
188
+ ---
189
+
190
+ ### Step 9: Enable Sticky Sessions
191
+
192
+ ```powershell
193
+ # Enable sticky sessions for session state management
194
+ aws elbv2 modify-target-group-attributes --target-group-arn $TARGET_GROUP_ARN --attributes Key=stickiness.enabled,Value=true Key=stickiness.lb_cookie.duration_seconds,Value=86400
195
+
196
+ Write-Host "Sticky sessions enabled - users stay on same instance for 24 hours"
197
+ ```
198
+
199
+ **Why Sticky Sessions are Critical:**
200
+ - Streamlit stores user session state locally on each instance
201
+ - File uploads and user data must stay on the same instance
202
+ - Without sticky sessions: 400 errors on file uploads
203
+ - Duration: 24 hours (86400 seconds)
204
+
205
+ ---
206
+
207
+ ### Step 10: Set Up SSL Certificate
208
+
209
+ ```powershell
210
+ # Request SSL certificate from AWS Certificate Manager
211
+ $CERT_ARN = aws acm request-certificate --domain-name "portfolio-manager.paydirtapps.com" --validation-method DNS --query 'CertificateArn' --output text
212
+
213
+ Write-Host "Certificate ARN: $CERT_ARN"
214
+
215
+ # Get DNS validation records
216
+ aws acm describe-certificate --certificate-arn $CERT_ARN --query 'Certificate.DomainValidationOptions[0].ResourceRecord.[Name,Value,Type]' --output table
217
+ ```
218
+
219
+ **DNS Validation Process:**
220
+ 1. Add the CNAME record shown in the output to Cloudflare
221
+ 2. **Name**: `_[hash].portfolio-manager` (without the domain suffix)
222
+ 3. **Value**: `_[hash].xlfgrmvvlj.acm-validations.aws.`
223
+ 4. **Proxy Status**: DNS only (gray cloud, not orange)
224
+ 5. Wait 5-15 minutes for validation
225
+
226
+ ```powershell
227
+ # Check certificate status
228
+ aws acm describe-certificate --certificate-arn $CERT_ARN --query 'Certificate.Status' --output text
229
+ # Should show "ISSUED" when ready
230
+ ```
231
+
232
+ ---
233
+
234
+ ### Step 11: Add HTTPS Listener
235
+
236
+ ```powershell
237
+ # Add HTTPS listener once certificate is issued
238
+ aws elbv2 create-listener --load-balancer-arn $ALB_ARN --protocol HTTPS --port 443 --certificates CertificateArn=$CERT_ARN --default-actions Type=forward,TargetGroupArn=$TARGET_GROUP_ARN
239
+
240
+ Write-Host "HTTPS listener added successfully!"
241
+
242
+ # Optional: Redirect HTTP to HTTPS
243
+ $HTTP_LISTENER_ARN = aws elbv2 describe-listeners --load-balancer-arn $ALB_ARN --query 'Listeners[?Port==`80`].ListenerArn' --output text
244
+ aws elbv2 modify-listener --listener-arn $HTTP_LISTENER_ARN --default-actions Type=redirect,RedirectConfig='{Protocol=HTTPS,Port=443,StatusCode=HTTP_301}'
245
+
246
+ Write-Host "HTTP to HTTPS redirect enabled!"
247
+ ```
248
+
249
+ ---
250
+
251
+ ### Step 12: Update Cloudflare DNS
252
+
253
+ **Final DNS Configuration:**
254
+ 1. Go to Cloudflare Dashboard β†’ Your domain β†’ DNS
255
+ 2. Update the main record for `portfolio-manager.paydirtapps.com`:
256
+ - **Type**: CNAME
257
+ - **Name**: `portfolio-manager`
258
+ - **Target**: `[your-alb-dns-name].us-east-2.elb.amazonaws.com`
259
+ - **Proxy Status**: DNS only (gray cloud)
260
+ - **TTL**: Auto
261
+
262
+ ---
263
+
264
+ ## πŸ”§ Verification Commands
265
+
266
+ ### Check Instance Health
267
+ ```powershell
268
+ # Check Auto Scaling Group status
269
+ aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names "portfolio-manager-asg" --query 'AutoScalingGroups[0].Instances[*].[InstanceId,LifecycleState,HealthStatus]' --output table
270
+
271
+ # Check target health
272
+ aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN --query 'TargetHealthDescriptions[*].[Target.Id,TargetHealth.State,TargetHealth.Description]' --output table
273
+ ```
274
+
275
+ ### Check Load Balancer Configuration
276
+ ```powershell
277
+ # Verify listeners
278
+ aws elbv2 describe-listeners --load-balancer-arn $ALB_ARN --query 'Listeners[*].[Port,Protocol]' --output table
279
+
280
+ # Check load balancer attributes
281
+ aws elbv2 describe-load-balancer-attributes --load-balancer-arn $ALB_ARN --output table
282
+ ```
283
+
284
+ ---
285
+
286
+ ## πŸ“ˆ Scaling Options
287
+
288
+ ### Manual Scaling
289
+ ```powershell
290
+ # Scale up to 5 instances during peak hours
291
+ aws autoscaling set-desired-capacity --auto-scaling-group-name "portfolio-manager-asg" --desired-capacity 5
292
+
293
+ # Scale down to 2 instances during off-hours
294
+ aws autoscaling set-desired-capacity --auto-scaling-group-name "portfolio-manager-asg" --desired-capacity 2
295
+ ```
296
+
297
+ ### Automatic CPU-Based Scaling
298
+ ```powershell
299
+ # Set up auto-scaling based on CPU usage
300
+ aws autoscaling put-scaling-policy --auto-scaling-group-name "portfolio-manager-asg" --policy-name "scale-up" --policy-type "TargetTrackingScaling" --target-tracking-configuration '{
301
+ "TargetValue": 70.0,
302
+ "PredefinedMetricSpecification": {
303
+ "PredefinedMetricType": "ASGAverageCPUUtilization"
304
+ }
305
+ }'
306
+ ```
307
+
308
+ ### Scheduled Scaling
309
+ ```powershell
310
+ # Scale up during peak hours (6 PM EST)
311
+ aws autoscaling put-scheduled-update-group-action --auto-scaling-group-name "portfolio-manager-asg" --scheduled-action-name "evening-scale-up" --recurrence "0 22 * * *" --desired-capacity 5
312
+
313
+ # Scale down during off hours (2 AM EST)
314
+ aws autoscaling put-scheduled-update-group-action --auto-scaling-group-name "portfolio-manager-asg" --scheduled-action-name "morning-scale-down" --recurrence "0 6 * * *" --desired-capacity 2
315
+ ```
316
+
317
+ ---
318
+
319
+ ## πŸ”„ Instance Type Changes
320
+
321
+ ### Upgrade to Larger Instances
322
+ ```powershell
323
+ # Create new launch template version with m5.xlarge
324
+ aws ec2 create-launch-template-version --launch-template-id $LAUNCH_TEMPLATE_ID --launch-template-data '{
325
+ "ImageId": "'$AMI_ID'",
326
+ "InstanceType": "m5.xlarge",
327
+ "SecurityGroupIds": ["'$SECURITY_GROUP_ID'"],
328
+ "UserData": "'$USER_DATA'"
329
+ }'
330
+
331
+ # Update ASG to use new version
332
+ aws autoscaling update-auto-scaling-group --auto-scaling-group-name "portfolio-manager-asg" --launch-template LaunchTemplateId=$LAUNCH_TEMPLATE_ID,Version='$Latest'
333
+
334
+ # Force refresh to replace all instances
335
+ aws autoscaling start-instance-refresh --auto-scaling-group-name "portfolio-manager-asg"
336
+ ```
337
+
338
+ ---
339
+
340
+ ## πŸ’° Cost Analysis
341
+
342
+ ### Monthly Costs (US East 2)
343
+ | Component | Before | After | Savings |
344
+ |-----------|--------|-------|---------|
345
+ | **Compute** | 1x m5.xlarge: $280 | 3x m5.large: $207 | $73 |
346
+ | **Load Balancer** | None: $0 | ALB: $16 | -$16 |
347
+ | **SSL Certificate** | Let's Encrypt: $0 | ACM: $0 | $0 |
348
+ | **Total** | **$280/month** | **$223/month** | **$57/month (20% savings)** |
349
+
350
+ ### Additional Benefits
351
+ - **Performance**: 6 total vCPUs vs 4 vCPUs (50% more processing power)
352
+ - **Reliability**: 3 instances vs 1 instance (high availability)
353
+ - **Memory**: 24GB total vs 16GB total (50% more memory)
354
+ - **Auto-scaling**: Can scale up to 5 instances during peak times
355
+
356
+ ---
357
+
358
+ ## 🚨 Troubleshooting
359
+
360
+ ### Common Issues
361
+
362
+ #### 1. File Upload 400 Errors
363
+ **Symptom**: First upload fails, retry succeeds
364
+ **Cause**: User routed to different instance without session state
365
+ **Solution**: Enable sticky sessions
366
+ ```powershell
367
+ aws elbv2 modify-target-group-attributes --target-group-arn $TARGET_GROUP_ARN --attributes Key=stickiness.enabled,Value=true Key=stickiness.lb_cookie.duration_seconds,Value=86400
368
+ ```
369
+
370
+ #### 2. Certificate "Not Secure" Warning
371
+ **Symptom**: Browser shows "Not Secure" despite valid certificate
372
+ **Cause**: Accessing load balancer DNS instead of domain name
373
+ **Solution**: Update Cloudflare DNS to point domain to load balancer
374
+
375
+ #### 3. Health Check Failures
376
+ **Symptom**: Instances show "unhealthy" in target group
377
+ **Cause**: Streamlit not responding on port 5000
378
+ **Solution**: Check supervisord status on instances
379
+ ```bash
380
+ # SSH into instance
381
+ sudo supervisorctl status streamlit
382
+ sudo supervisorctl restart streamlit
383
+ ```
384
+
385
+ #### 4. SSL Certificate Validation Stuck
386
+ **Symptom**: Certificate stays "PENDING_VALIDATION" for hours
387
+ **Cause**: DNS validation record not added correctly
388
+ **Solution**: Verify CNAME record in Cloudflare, ensure "DNS only" (gray cloud)
389
+
390
+ ---
391
+
392
+ ## πŸ”§ Maintenance Commands
393
+
394
+ ### Update Application Code
395
+ ```powershell
396
+ # Create new AMI with updated code
397
+ $NEW_AMI_ID = aws ec2 create-image --instance-id $UPDATED_INSTANCE_ID --name "DFS-Portfolio-Manager-$(Get-Date -Format 'yyyyMMdd-HHmm')" --description "Updated application code" --no-reboot --query 'ImageId' --output text
398
+
399
+ # Update launch template
400
+ aws ec2 create-launch-template-version --launch-template-id $LAUNCH_TEMPLATE_ID --launch-template-data '{"ImageId": "'$NEW_AMI_ID'"}'
401
+
402
+ # Refresh instances with new code
403
+ aws autoscaling start-instance-refresh --auto-scaling-group-name "portfolio-manager-asg"
404
+ ```
405
+
406
+ ### Monitor Performance
407
+ ```powershell
408
+ # Check CPU utilization
409
+ aws cloudwatch get-metric-statistics --namespace AWS/EC2 --metric-name CPUUtilization --dimensions Name=AutoScalingGroupName,Value=portfolio-manager-asg --statistics Average --start-time $(Get-Date).AddHours(-1) --end-time $(Get-Date) --period 300
410
+
411
+ # Check load balancer metrics
412
+ aws cloudwatch get-metric-statistics --namespace AWS/ApplicationELB --metric-name RequestCount --dimensions Name=LoadBalancer,Value=app/portfolio-manager-alb/[load-balancer-id] --statistics Sum --start-time $(Get-Date).AddHours(-1) --end-time $(Get-Date) --period 300
413
+ ```
414
+
415
+ ### Cleanup Old Resources
416
+ ```powershell
417
+ # Stop original single instance (once everything is working)
418
+ aws ec2 stop-instances --instance-ids $ORIGINAL_INSTANCE_ID
419
+
420
+ # Delete old AMIs (keep recent ones)
421
+ aws ec2 describe-images --owners self --query 'Images[?Name==`DFS-Portfolio-Manager*`].[ImageId,Name,CreationDate]' --output table
422
+ ```
423
+
424
+ ---
425
+
426
+ ## πŸ“ Key Variables Reference
427
+
428
+ Save these variables for future maintenance:
429
+
430
+ ```powershell
431
+ # Core Infrastructure
432
+ $INSTANCE_ID = "i-xxxxxxxxx" # Original instance
433
+ $AMI_ID = "ami-xxxxxxxxx" # Application AMI
434
+ $VPC_ID = "vpc-xxxxxxxxx" # Virtual Private Cloud
435
+ $SECURITY_GROUP_ID = "sg-xxxxxxxxx" # Security Group
436
+ $SUBNET_IDS = "subnet-xxx subnet-yyy subnet-zzz" # Subnets
437
+
438
+ # Load Balancer
439
+ $ALB_ARN = "arn:aws:elasticloadbalancing:us-east-2:xxxx:loadbalancer/app/portfolio-manager-alb/xxxxxxxxxx"
440
+ $ALB_DNS = "portfolio-manager-alb-xxxxxxxxxx.us-east-2.elb.amazonaws.com"
441
+ $TARGET_GROUP_ARN = "arn:aws:elasticloadbalancing:us-east-2:xxxx:targetgroup/portfolio-manager-targets/xxxxxxxxxx"
442
+
443
+ # Auto Scaling
444
+ $LAUNCH_TEMPLATE_ID = "lt-xxxxxxxxx" # Launch Template
445
+ $ASG_NAME = "portfolio-manager-asg" # Auto Scaling Group
446
+
447
+ # SSL
448
+ $CERT_ARN = "arn:aws:acm:us-east-2:xxxx:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
449
+ ```
450
+
451
+ ---
452
+
453
+ ## 🎯 Success Metrics
454
+
455
+ After completing this setup, you should have:
456
+
457
+ - βœ… **3 healthy instances** running your application
458
+ - βœ… **Load balancer** distributing traffic evenly
459
+ - βœ… **HTTPS/SSL** working with valid certificate
460
+ - βœ… **Sticky sessions** preventing upload errors
461
+ - βœ… **Auto-scaling** capability (3-5 instances)
462
+ - βœ… **High availability** across multiple availability zones
463
+ - βœ… **Cost savings** of ~20% compared to single large instance
464
+ - βœ… **Better performance** with 50% more total CPU and memory
465
+
466
+ ---
467
+
468
+ ## πŸ“ž Support
469
+
470
+ For issues or questions:
471
+ 1. Check the troubleshooting section above
472
+ 2. Verify all health checks are passing
473
+ 3. Review AWS CloudWatch logs for detailed error information
474
+ 4. Ensure Cloudflare DNS settings are correct
475
+
476
+ ---
477
+
478
+ *Document created: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')*
479
+ *Architecture: AWS Application Load Balancer + Auto Scaling Group*
480
+ *Application: DFS Portfolio Manager (Streamlit)*
app.py CHANGED
@@ -1477,6 +1477,10 @@ if selected_tab == 'Manage Portfolio':
1477
  size_include = st.multiselect("Include sizes?", options=sorted(list(set(st.session_state['working_frame']['Size'].unique()))), default=[])
1478
  else:
1479
  size_include = []
 
 
 
 
1480
 
1481
  submitted_col, export_col = st.columns(2)
1482
  st.info("Portfolio Button applies to your overall Portfolio, Export button applies to your Custom Export")
@@ -1600,6 +1604,15 @@ if selected_tab == 'Manage Portfolio':
1600
 
1601
  if size_include:
1602
  parsed_frame = parsed_frame[parsed_frame['Size'].isin(size_include)]
 
 
 
 
 
 
 
 
 
1603
  st.session_state['working_frame'] = parsed_frame.sort_values(by='median', ascending=False).reset_index(drop=True)
1604
  st.session_state['export_merge'] = st.session_state['working_frame'].copy()
1605
  elif exp_submitted:
 
1477
  size_include = st.multiselect("Include sizes?", options=sorted(list(set(st.session_state['working_frame']['Size'].unique()))), default=[])
1478
  else:
1479
  size_include = []
1480
+ if sport_var == 'NFL':
1481
+ qb_force = st.selectbox("Force QB Stacks?", options=['No', 'Yes'], index=0)
1482
+ else:
1483
+ qb_force = 'No'
1484
 
1485
  submitted_col, export_col = st.columns(2)
1486
  st.info("Portfolio Button applies to your overall Portfolio, Export button applies to your Custom Export")
 
1604
 
1605
  if size_include:
1606
  parsed_frame = parsed_frame[parsed_frame['Size'].isin(size_include)]
1607
+
1608
+ if qb_force == 'Yes':
1609
+ if type_var == 'Classic':
1610
+ # Get team for the first player column for each lineup
1611
+ team_frame = parsed_frame.iloc[:, 0].map(st.session_state['map_dict']['team_map'])
1612
+
1613
+ # Create mask where the first player's team matches the Stack column
1614
+ include_mask = team_frame == parsed_frame['Stack']
1615
+ parsed_frame = parsed_frame[include_mask]
1616
  st.session_state['working_frame'] = parsed_frame.sort_values(by='median', ascending=False).reset_index(drop=True)
1617
  st.session_state['export_merge'] = st.session_state['working_frame'].copy()
1618
  elif exp_submitted:
launch-template.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ImageId": "ami-09659b1d5af762870",
3
+ "InstanceType": "m5.large",
4
+ "SecurityGroupIds": ["sg-0af436f2cd59a1ea4"],
5
+ "UserData": "IyEvYmluL2Jhc2gKY2QgL2hvbWUvZWMyLXVzZXIvQVdTX1BvcnRmb2xpb19NYW5hZ2VyCnN1ZG8gc3VwZXJ2aXNvcmN0bCByZXN0YXJ0IHN0cmVhbWxpdA==",
6
+ "TagSpecifications": [
7
+ {
8
+ "ResourceType": "instance",
9
+ "Tags": [
10
+ {
11
+ "Key": "Name",
12
+ "Value": "Portfolio-Manager-Instance"
13
+ }
14
+ ]
15
+ }
16
+ ]
17
+ }