- Having no background with aws/devops and after a lot of efforts and trial/error I succeeded building the here bellow stack for my django app:
- The stack relies heavily on celery which consumes a lot of tasks so I migrated to sqs for cost reasons (instead of aws redis). For the same reason I decided to disable the nat gateways because it costs so much and I rely only on security groups and acl for security
- Initially I had two services: One with nginx, Django, celery, pgadmin4, flower, and a second with Celery desired count 0 to scale up on heavy loads. When celery starts working it tend to consume 100% CPU and launch a lot of process taking all the available connections of postgres, new process fail, so I added pgbouncer for connection pooling
- I decided to migrate to 3 services, one admin service with nginx, pgbouncer and pgadmin4, a second non scalable minimal service with django and one celery worker, and a third scalable celery service with desired count to 0 which will be launched and scaled down by an alarm on sqs queues. (I am also considering a fourth service with django and a desired count to 0 which will be launched and scaled down by an alarm on elb target response time).
- The actual problem is the following : my django and celery containers (in the django app service) talk with pgbouncer without a problem, but the nginx container (in the admin service) can't reach the django container ?
- I tried playing with the securitygroups without success (I'm no expert), any thoughts guys?
Thank you. Here's the stack code:
# VPC Template Parameters
CIDRBlockIP:
Description: The first two values of the IP for the VPC
Type: String
MinLength: '3'
MaxLength: '7'
Default: 10.20
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})"
ConstraintDescription: must be a valid start to an IP range of the form x.x
AvailabilityZone1:
Description: The first availability zone in the region
Default: us-west-2a
Type: AWS::EC2::AvailabilityZone::Name
ConstraintDescription: Must be a valid availability zone
AvailabilityZone2:
Description: The second availability zone in the region
Default: us-west-2b
Type: AWS::EC2::AvailabilityZone::Name
ConstraintDescription: Must be a valid availability zone
ELBIngressPort:
Description: The ELB ingress port used by security groups
Type: Number
MinValue: 0
MaxValue: 65535
ConstraintDescription: TCP ports must be between 0 - 65535
Default: 443
ELBHttpIngressPort:
Description: The ELB ingress port used by security groups
Type: Number
MinValue: 0
MaxValue: 65535
ConstraintDescription: TCP ports must be between 0 - 65535
Default: 80
AppIngressPort:
Description: The application ingress port used by security groups
Type: Number
MinValue: 0
MaxValue: 65535
ConstraintDescription: TCP ports must be between 0 - 65535
Default: 80
DbInVpc:
Type: String
Default: true
AllowedValues:
- true
- false
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Region Availability Zones
Parameters:
- AvailabilityZone1
- AvailabilityZone2
- Label:
default: Ingress Ports
Parameters:
- ELBIngressPort
- AppIngressPort
ParameterLabels:
AvailabilityZone1:
default: Availability Zone 1
AvailabilityZone2:
default: Availability Zone 2
ELBIngressPort:
default: Load Balancer Port
AppIngressPort:
default: Application Port
Conditions:
CreateDbSecurityGroup: !Equals [ !Ref DbInVpc, true ]
Mappings:
# Django App Mappings
appConfigs:
prod:
loadbalancerCertArn: "xxxx"
# Maps CIDR blocks to VPC and various subnets
CIDRMap:
VPC:
CIDR: ".0.0/16"
Public1:
CIDR: ".2.0/24"
Public2:
CIDR: ".3.0/24"
Private1:
CIDR: ".64.0/19"
Private2:
CIDR: ".96.0/19"
Resources:
###############
# VPC Template#
###############
VPC:
Type: AWS::EC2::VPC
UpdateReplacePolicy: Delete
Properties:
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, VPC, CIDR ]
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Ref "AWS::StackName"
PublicSubnet1:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Public1, CIDR ]
AvailabilityZone: !Ref AvailabilityZone1
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet1"
PublicSubnet2:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
DependsOn:
- PublicSubnet1
- PrivateSubnet1
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Public2, CIDR ]
AvailabilityZone: !Ref AvailabilityZone2
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet2"
PrivateSubnet1:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Private1, CIDR ]
AvailabilityZone: !Ref AvailabilityZone1
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet1"
PrivateSubnet2:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
DependsOn:
- PublicSubnet1
- PrivateSubnet1
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Private2, CIDR ]
AvailabilityZone: !Ref AvailabilityZone2
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet2"
InternetGateway:
Type: AWS::EC2::InternetGateway
UpdateReplacePolicy: Delete
Properties:
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-igw"
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicRouteTable:
Type: AWS::EC2::RouteTable
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-public-rt"
PublicRoute:
Type: AWS::EC2::Route
UpdateReplacePolicy: Delete
DependsOn: VPCGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnetRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
PublicSubnetNetworkAclAssociation1:
Type: AWS::EC2::SubnetNetworkAclAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet1
NetworkAclId: !GetAtt VPC.DefaultNetworkAcl
PublicSubnetNetworkAclAssociation2:
Type: AWS::EC2::SubnetNetworkAclAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet2
NetworkAclId: !GetAtt VPC.DefaultNetworkAcl
ELBSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Properties:
GroupDescription: Enable HTTP/HTTPs ingress
VpcId: !Ref VPC
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: !Ref ELBIngressPort
FromPort: !Ref ELBIngressPort
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: !Ref ELBHttpIngressPort # ALB should redirect to HTTPS
FromPort: !Ref ELBHttpIngressPort
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-ELBSecurityGroup"
ELBSecurityGroupToAppEgress:
Type: AWS::EC2::SecurityGroupEgress # prevent security group circular references
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref ELBSecurityGroup
IpProtocol: tcp
ToPort: !Ref AppIngressPort
FromPort: !Ref AppIngressPort
DestinationSecurityGroupId: !Ref AppSecurityGroup
AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Properties:
GroupDescription: Enable access from ELB to app
VpcId: !Ref VPC
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref ELBSecurityGroup
IpProtocol: tcp
ToPort: !Ref AppIngressPort
FromPort: !Ref AppIngressPort
- SourceSecurityGroupId: !Ref BastionSecurityGroup
IpProtocol: tcp
ToPort: 22
FromPort: 22
#!!! New, to enable intra vpc communication ??!!
- CidrIp: '0.0.0.0/0'
IpProtocol: tcp
ToPort: 80
FromPort: 80
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-AppSecurityGroup"
AppSecurityGroupFromELBIngress:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref AppSecurityGroup
IpProtocol: tcp
ToPort: !Ref AppIngressPort
FromPort: !Ref AppIngressPort
SourceSecurityGroupId: !Ref ELBSecurityGroup
AppSecurityGroupFromAppSecurityGroupIngressTcp:
Type: AWS::EC2::SecurityGroupIngress
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref AppSecurityGroup
IpProtocol: tcp
ToPort: 65535
FromPort: 0
SourceSecurityGroupId: !Ref AppSecurityGroup
AppSecurityGroupFromAppSecurityGroupIngressUdp:
Type: AWS::EC2::SecurityGroupIngress
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref AppSecurityGroup
IpProtocol: udp
ToPort: 65535
FromPort: 0
SourceSecurityGroupId: !Ref AppSecurityGroup
BastionSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Properties:
GroupDescription: Enable access to the bastion host
VpcId: !Ref VPC
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: 65535
FromPort: 0
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-BastionSecurityGroup"
BastionSecurityGroupToAppEgress:
Type: AWS::EC2::SecurityGroupEgress # prevent security group circular references
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref BastionSecurityGroup
IpProtocol: tcp
ToPort: 22
FromPort: 22
DestinationSecurityGroupId: !Ref AppSecurityGroup
#####################################################
# Database security groups, only create if required #
#####################################################
DbSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupDescription: Enable access to the RDS DB
VpcId: !Ref VPC
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: 6379
FromPort: 6379
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: 5432
FromPort: 5432
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-DbSecurityGroup"
DbSecurityGroupFromBastionIngressRedis:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 6379 # Redis
FromPort: 6379
SourceSecurityGroupId: !Ref BastionSecurityGroup
DbSecurityGroupFromBastionIngressPostgres:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 5432 # Postgres
FromPort: 5432
SourceSecurityGroupId: !Ref BastionSecurityGroup
DbSecurityGroupFromAppIngressRedis:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 6379
FromPort: 6379
SourceSecurityGroupId: !Ref AppSecurityGroup
DbSecurityGroupFromAppIngressPostgres:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 5432
FromPort: 5432
SourceSecurityGroupId: !Ref AppSecurityGroup
#################
# Admin Apps Service#
#################
AdminAppsTaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- AdminAppsLogGroup
- NginxRepository
- Pgadmin4Repository
- PgbouncerRepository
Properties:
Family: !Join ['-', [!Ref AwsIamUserId, !Ref AdminAppsServiceName, TaskDefinition]]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: "0.5GB" #512 (0.5GB), 1024 (1GB), 2048 (2GB), 3072 (3GB), 4096 (4GB)
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
TaskRoleArn: !GetAtt EcsTaskRole.Arn
ContainerDefinitions:
- Name: !Sub "${AwsIamUserId}_nginx"
Image: !Sub "${NginxRepository.RepositoryUri}:latest"
Essential: true
PortMappings:
- ContainerPort: !Ref AppContainerPort
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref AdminAppsLogGroup
awslogs-stream-prefix: Nginx
- Name: !Sub "${AwsIamUserId}_pgbouncer"
Image: !Sub "${PgbouncerRepository.RepositoryUri}:latest"
PortMappings:
- ContainerPort: 5432
Protocol: tcp
Essential: true
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref AdminAppsLogGroup
awslogs-stream-prefix: Pgbouncer
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
- Name: !Sub "${AwsIamUserId}_pgadmin4"
Image: !Sub "${Pgadmin4Repository.RepositoryUri}:latest"
Essential: false
PortMappings:
- ContainerPort: 5050
Protocol: tcp
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref AdminAppsLogGroup
awslogs-stream-prefix: Pgadmin4
Command:
- "/generate_servers_json_and_pgpass_conf_files"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
AdminAppsService:
Type: AWS::ECS::Service
DependsOn:
- DjangoAppHttpsListener
- GeneralECSCluster
Properties:
ServiceName: !Ref AdminAppsServiceName
Cluster: !Ref GeneralECSCluster
TaskDefinition: !Ref AdminAppsTaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 0
MaximumPercent: 200
DesiredCount: 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #!! Switch to DISABLED to use nat, if DISABLED uncomment the nat section above of this file
Subnets:
#- !Ref PrivateSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
#- !Ref PrivateSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
SecurityGroups:
- !Ref AppSecurityGroup
LoadBalancers:
- ContainerName: !Sub "${AwsIamUserId}_nginx"
ContainerPort: !Ref AppContainerPort
TargetGroupArn: !Ref DjangoAppTargetGroup
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
#################
# Django Service#
#################
DjangoAppTaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- DjangoAppLogGroup
- DjangoCeleryRepository
Properties:
Family: !Join ['-', [!Ref AwsIamUserId, !Ref DjangoAppServiceName, TaskDefinition]]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: "0.5GB" #512 (0.5GB), 1024 (1GB), 2048 (2GB), 3072 (3GB), 4096 (4GB)
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
TaskRoleArn: !GetAtt EcsTaskRole.Arn
ContainerDefinitions:
- Name: !Sub "${AwsIamUserId}_django_celery"
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
Essential: true
#Cpu: 128
#Memory: 256
PortMappings:
- ContainerPort: 8000
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref DjangoAppLogGroup
awslogs-stream-prefix: DjangoAppContainer
EntryPoint:
- "/entrypoint"
Command:
- "/start"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
- Name: celery-default-worker
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
Essential: false
#Cpu: 128
#MemoryReservation: 256
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref DjangoAppLogGroup
awslogs-stream-prefix: CeleryAppContainer
EntryPoint:
- "/entrypoint"
Command:
- "/start-celery-default-worker"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
DjangoAppService:
Type: AWS::ECS::Service
DependsOn:
#- DjangoAppHttpsListener
- GeneralECSCluster
Properties:
ServiceName: !Ref DjangoAppServiceName
Cluster: !Ref GeneralECSCluster
TaskDefinition: !Ref DjangoAppTaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
DesiredCount: 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #!! Switch to DISABLED to use nat, if DISABLED uncomment the nat section above of this file
Subnets:
#- !Ref PrivateSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
#- !Ref PrivateSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
SecurityGroups:
- !Ref AppSecurityGroup
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
#################
# Celery Service#
#################
CeleryAppTaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- CeleryAppLogGroup
- DjangoCeleryRepository
Properties:
Family: !Join ['-', [!Ref AwsIamUserId, !Ref CeleryAppServiceName, TaskDefinition]]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 512
Memory: "1GB" #512 (0.5GB), 1024 (1GB), 2048 (2GB), 3072 (3GB), 4096 (4GB)
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
TaskRoleArn: !GetAtt EcsTaskRole.Arn
ContainerDefinitions:
- Name: celery-default-worker
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref CeleryAppLogGroup
awslogs-stream-prefix: Celery
EntryPoint:
- "/entrypoint"
Command:
- "/start-celery-default-worker"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
- Name: celery-long-running-worker
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref CeleryAppLogGroup
awslogs-stream-prefix: Celery
EntryPoint:
- "/entrypoint"
Command:
- "/start-celery-long-running-worker"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
CeleryAppService:
Type: AWS::ECS::Service
Properties:
ServiceName: !Ref CeleryAppServiceName
Cluster: !Ref GeneralECSCluster
TaskDefinition: !Ref CeleryAppTaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 0
MaximumPercent: 200
DesiredCount: 0 #! this scales down the service to 0 task, if you want 1 minimum task always runing change to 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #!! Switch to DISABLED to use nat, if DISABLED uncomment the nat section above of this file
Subnets:
#- !Ref PrivateSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
#- !Ref PrivateSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
SecurityGroups:
- !Ref AppSecurityGroup
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
#####################################################################################
# Service Discovery to enable communication between Django and Celery Service #
#####################################################################################
# Private Namespace for Service Discovery
# https://aws.amazon.com/premiumsupport/knowledge-center/cloudformation-ecs-service-discovery/
PrivateNamespace:
Type: AWS::ServiceDiscovery::PrivateDnsNamespace
Properties:
Name: !Sub "${AwsIamUserId}_local"
Vpc: !Ref VPC
DiscoveryService:
Type: AWS::ServiceDiscovery::Service
Properties:
Description: Discovery Service to enable communication between Django and Celery Service
DnsConfig:
RoutingPolicy: WEIGHTED #MULTIVALUE
DnsRecords:
- TTL: 60
Type: A
HealthCheckCustomConfig:
FailureThreshold: 1
Name: discovery-service
NamespaceId: !Ref PrivateNamespace
###########################
# Load balancer resources #
###########################
DjangoAppLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
- Key: 'idle_timeout.timeout_seconds'
Value: 4000
- Key: 'routing.http2.enabled'
Value: 'true'
- Key: 'access_logs.s3.enabled'
Value: 'false'
Scheme: internet-facing
SecurityGroups:
- !Ref ELBSecurityGroup
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
Type: application
DjangoAppHttpsListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
Certificates:
- CertificateArn: !FindInMap [ appConfigs, !Ref EnvStageName, loadbalancerCertArn ]
DefaultActions:
- TargetGroupArn: !Ref DjangoAppTargetGroup
Type: forward
LoadBalancerArn: !Ref DjangoAppLoadBalancer
Port: 443
Protocol: HTTPS
DjangoAppHttpListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- RedirectConfig:
Host: "#{host}"
Path: "/#{path}"
Port: 443
Protocol: "HTTPS"
Query: "#{query}"
StatusCode: HTTP_301
Type: redirect
LoadBalancerArn: !Ref DjangoAppLoadBalancer
Port: 80
Protocol: HTTP
DjangoAppTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Port: !Ref AppContainerPort
HealthCheckPath: '/health/'
HealthCheckProtocol: HTTP
HealthCheckIntervalSeconds: 90
HealthCheckTimeoutSeconds: 60
HealthyThresholdCount: 2
UnhealthyThresholdCount: 10
Matcher:
HttpCode: '200,301'
Protocol: HTTP
TargetType: ip
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 60
VpcId: !Ref VPC