2

I'm building a CloudFormation stack. I have

  • a web app in an ECS container which has PortMappings for ContainerPorts 9000 and 9002, mapped to HostPorts 80 and 443, and
  • an application load balancer (AWS::ElasticLoadBalancingV2::LoadBalancer) with Listeners and TargetGroups for HTTP on port 80 and HTTPS on port 443

When I define a Service, I can only specify one load balancer element; although LoadBalancers is plural, documentation says only one load balancer is allowed, and specifying two load balancer elements doesn't work. How, then, to map both ports?

Here's the service part of my CloudFormation JSON with only the HTTPS parts, which works. Can it be extended to route HTTP to the same container? If not, what's the best solution?

"Service": {
  "Type": "AWS::ECS::Service",
  "DependsOn": ["AutoScalingGroup", "HTTPSListener"],
  "Properties": {
    "Cluster": { "Ref": "Cluster" },
    "DesiredCount": { "Ref": "InstanceCount" },
    "LoadBalancers": [
      {
        "TargetGroupArn": { "Ref": "HTTPSTargetGroup" },
        "ContainerName": "nginx",
        "ContainerPort": "9002"
      }
    ],
    "Role": { "Ref": "ServiceRole" },
    "TaskDefinition": { "Ref": "TaskDefinition" }
  }
}

A CloudFormation solution would be ideal, but an API solution would also be of interest.

I could create a second Service for HTTP, with a separate load balancer and container instances, but that would be neither simple nor economical.

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
  • Are you using port 80 only for redirecting to HTTPS? – Andreas Oct 29 '16 at 07:20
  • So far, yes, so a way of doing the redirect in CloudFormation or the AWS API would be of interest. It would be best to keep the redirect in the container, though, since the container is used in other contexts. – Dave Schweisguth Oct 30 '16 at 13:42

2 Answers2

1

I would suggest one of these options:

a) Registering the task (container) at two different task definitions of the same load balancer as part of the container boot process instead of using the build in feature of the ECS service.

b) Defining another ECS service each of them connected with it's own target group. Both target groups linked with the same ALB.

Andreas
  • 844
  • 4
  • 10
  • Is there a way to do either of these that doesn't require a second instantiation of the container? – Dave Schweisguth Oct 29 '16 at 10:21
  • Yes, option a) does not require a second instantiation of the container. But you already answered the question yourself already. :) – Andreas Oct 31 '16 at 08:24
  • A solution which used only CloudFormation, automatically updated host registration with the target groups (without scripting on the host), or both would be better than mine. I did try a two-service solution before posting, but it started different containers for each target group. Can you provide detail on how not to do that? – Dave Schweisguth Oct 31 '16 at 11:34
0

A partial solution is to register the instances with the HTTP target group manually through the API after the stack is created:

autoscaling = boto3.client('autoscaling')
auto_scaling_groups = autoscaling.describe_auto_scaling_groups(AutoScalingGroupNames=[auto_scaling_group_name])
instances = auto_scaling_groups['AutoScalingGroups'][0]['Instances']

elbv2 = boto3.client('elbv2')
for instance in instances:
    elbv2.register_targets(
        TargetGroupArn=http_target_group_arn,
        Targets=[{'Id': instance['InstanceId'], 'Port': instance}]
    )

This isn't a fully acceptable answer since instances created by the autoscaling group in the future wouldn't automatically be registered with the HTTP target group. It ought to be possible to rig the instances up to register themselves; I'll look in to that.

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121