# Persistent ALB Pattern (bg-common) The persistent ALB pattern ensures that ALB DNS names remain constant even when service stacks are deleted and recreated. This matches the legacy `bg-common` pattern. ## How It Works The bg-common ALB stack is **always created separately**. Services reference it via `albLoadBalancerArn`: 1. **bg-common ALB stack is deployed separately** (`{serviceStackName}-alb` or via `spicyALB`) 2. **The ALB stack persists** across service deployments 3. **Service stacks reference the existing ALB** via `albLoadBalancerArn` (maps to `existingALB` in construct) 4. **ALB DNS name stays constant** even when service stacks are deleted/recreated ### Stack Naming - **ALB Stack**: `{serviceStackName}-alb` (shared between blue/green) - **Service Stack (Rolling)**: `{serviceStackName}` - **Service Stack (Blue/Green)**: `{serviceStackName}-blue` and `{serviceStackName}-green` For blue/green deployments, both blue and green services share the same ALB stack. ## Usage ### Rolling Deployment (Non-Blue/Green) ```groovy @Library(["spicy-automation@main"]) _ spicyECSService( jenkinsAwsCredentialsId: "aws-credentials", region: "ca-central-1", stackName: "my-api-dev", serviceName: "my-api", // ... cluster/VPC config ... // Container image: "nexus.kodeniks.com/docker-hosted/my-api:latest", containerPort: 3000, // Reference bg-common ALB (always created separately) - auto-imports from ALB stack albStackName: "my-api-dev-alb", // Auto-imports ALB ARN and listener ARNs // Routing hostHeader: "api-dev.example.com", priority: 100, // Tags ownerTag: "MyTeam", productTag: "my-product", componentTag: "api", environment: "dev", ) ``` **What happens:** 1. Pipeline deploys `my-api-dev-alb` stack (ALB) 2. Pipeline gets ALB outputs (DNS, listener ARNs) 3. Pipeline deploys `my-api-dev` stack (service) referencing the ALB 4. ALB DNS name stays constant: `my-api-dev-alb-123456789.ca-central-1.elb.amazonaws.com` ### Blue/Green Deployment ```groovy @Library(["spicy-automation@main"]) _ spicyECSService( jenkinsAwsCredentialsId: "aws-credentials", region: "ca-central-1", stackName: "my-api-prod", // Base name (blue/green suffixes added automatically) serviceName: "my-api", // ... cluster/VPC config ... // Container image: "nexus.kodeniks.com/docker-hosted/my-api:latest", containerPort: 3000, // Blue/Green blueGreen: true, activeHostname: "api.example.com", inactiveHostname: "inactive-api.example.com", bgHostedZoneId: "Z1234567890", // Route53 hosted zone // Reference bg-common ALB (always created separately) - auto-imports from ALB stack albStackName: "my-api-prod-alb", // Auto-imports ALB ARN and listener ARNs healthCheckPath: "/health", // Tags ownerTag: "MyTeam", productTag: "my-product", componentTag: "api", environment: "prod", ) ``` **What happens:** 1. Pipeline deploys `my-api-prod-alb` stack (ALB) - **only once, shared by blue/green** 2. Pipeline gets ALB outputs (or uses `albStackName` for auto-import) 3. Pipeline deploys `my-api-prod-green` stack (inactive service) referencing the ALB 4. Service creates listener rules on **both** HTTP and HTTPS listeners (if both available) 5. Pipeline swaps hostname routing via ALB listener rule priorities (no DNS changes) 6. ALB DNS name stays constant across all deployments **How Blue/Green Routing Works:** - **DNS Records**: Both `api.example.com` and `inactive-api.example.com` DNS records are created and always point to the same ALB - **Traffic Routing**: Controlled by ALB listener rule priorities, not DNS: - Active service: Listener rule with priority 100 for `api.example.com` - Inactive service: Listener rule with priority 200 for `inactive-api.example.com` - **Swapping**: When swapping, only the listener rule priorities are updated (via CDK deployment). DNS records never change. - **Result**: Zero-downtime swaps in ~30 seconds (just ALB rule updates, no DNS propagation delays) ## Benefits 1. **Static DNS**: ALB DNS name never changes (unless ALB stack is deleted) 2. **No DNS Updates**: Cloudflare/Route53 records set once and never need updating 3. **Faster Deployments**: ALB stack only updates when ALB config changes 4. **Cost Efficient**: ALB persists, avoiding recreation costs ## ALB Stack Lifecycle - **Created**: Deployed separately via `spicyALB` pipeline function or manually - **Updated**: When ALB configuration changes (certificate, scheme, etc.) - **Deleted**: Only when explicitly destroyed (not deleted with service stacks) ## Stack Outputs The ALB stack exports these outputs (used by service stacks via CloudFormation imports): | Output Key | Description | Export Name | | ------------------ | ------------------ | -------------------------------------------- | | `LoadBalancerDNS` | ALB DNS name | `{stackName}-internet-facing-url` | | `LoadBalancerArn` | ALB ARN | `{stackName}-internet-facing-arn` | | `HTTPListenerArn` | HTTP listener ARN | `{stackName}-internet-facing-http-listener` | | `HTTPSListenerArn` | HTTPS listener ARN | `{stackName}-internet-facing-https-listener` | **Minimal Props:** When using `albStackName` in service stack, these are auto-imported via CloudFormation `Fn::ImportValue`. The service creates listener rules on **both** HTTP and HTTPS listeners (if both are available), matching the old template behavior. | `SecurityGroupId` | ALB security group ID | `{stackName}-internet-facing-security-group` | ## Deploying the bg-common ALB Stack The bg-common ALB stack must be deployed separately before deploying services: ```groovy @Library(["spicy-automation@main"]) _ // Deploy bg-common ALB stack spicyALB( jenkinsAwsCredentialsId: "aws-credentials", region: "ca-central-1", stackName: "my-api-dev-alb", vpcId: "vpc-12345678", availabilityZones: "ca-central-1a,ca-central-1b", subnetIds: "subnet-xxx,subnet-yyy", scheme: "internet-facing", certificateArn: "arn:aws:acm:...", ownerTag: "MyTeam", productTag: "my-product", componentTag: "alb", environment: "dev", ) // Then deploy service referencing the ALB spicyECSService( // ... config ... albLoadBalancerArn: "arn:aws:elasticloadbalancing:...", // From ALB stack outputs albHttpsListenerArn: "arn:aws:elasticloadbalancing:...", // From ALB stack outputs ) ``` ## Comparison with Legacy bg-common | Feature | Legacy bg-common | New Persistent ALB | | ------------------- | ---------------------- | ------------------------------------------ | | **Stack Name** | `{envName}-bg-common` | `{serviceStackName}-alb` | | **Deployment** | Separate pipeline step | Separate pipeline step (via `spicyALB`) | | **Blue/Green** | Shared ALB | Shared ALB | | **DNS Management** | Manual | Automatic (Route53) or Manual (Cloudflare) | | **Stack Lifecycle** | Manual | Manual (deployed separately) | ## Troubleshooting ### ALB Stack Not Found If the ALB stack doesn't exist, the pipeline will create it automatically. If you see errors: 1. Check the ALB stack name: `{serviceStackName}-alb` 2. Verify the stack exists: `aws cloudformation describe-stacks --stack-name {stackName}-alb` 3. Check stack outputs: `aws cloudformation describe-stacks --stack-name {stackName}-alb --query 'Stacks[0].Outputs'` ### ALB DNS Name Changed This should never happen with the persistent ALB pattern. If it does: 1. Check if the ALB stack was deleted/recreated 2. Verify the ALB stack name is consistent 3. Check CloudFormation stack events for ALB replacement ### Service Can't Find ALB If the service stack can't reference the ALB: 1. Verify ALB stack outputs are available 2. Check that `albLoadBalancerArn` is set in the service context 3. Verify the ALB ARN is correct