0
  • 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
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
freelance
  • 1
  • 1
  • So you are confident that everything works, and your issue is only caused by misconfigured security groups? – Marcin Jun 17 '22 at 08:42
  • Everything worked fine when i had only two services : nginx, django, celery, pgbouncer in the same service and they were talking fine together. now when i split nginx and pgbouncer in one service, and django and celery in another, the communication works only in this way : django and celery talks to the service hosting pgbouncer, but nginx can't talk to django – freelance Jun 17 '22 at 09:04

0 Answers0