aws / aws/aws-cdk

AWS ECS: UDP-only service not allowed to attach to NetworkTargetGroup

Open
#23,026 4 comments 3 reactions 0 assignees View on GitHub
@aws-cdk/aws-ecs bug effort/medium p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
2d 3h
Merged PRs (30d)
83

Description

### Describe the bug

I cannot attach a UDP-only EC2 Service to a NetworkTargetGroup via the function `attachToNetworkTargetGroup`.

When I define a UDP and TCP container, defining the UDP port mapping first, I cannot attach the EC2 Service to a NetworkTargetGroup.

### Expected Behavior

I am able to attach a UDP-only Ec2Service to a NetworkTargetGroup.

### Current Behavior

CDK fails with the following error:
```
Error: Container 'HelloCdkStack/TaskDefinition/Container' has no mapping for port undefined and protocol tcp. Did you call "container.addPortMappings()"?
```

### Reproduction Steps

Using the sample application from the cdk CLI:

1. Create the sample application
```
mkdir hello-cdk
cd hello-cdk
cdk init sample-app --language=typescript
```

2. Replace the code in `lib/hello-cdk-stack.ts` with the code below
3. `npm run build && cdk synth`, you should expect to see an error. Please test with various other use cases commented out in the code.
```typescript
import { aws_ecs, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NetworkTargetGroup, Protocol, TargetType } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import { Cluster, Compatibility, ContainerImage, Ec2Service, NetworkMode, TaskDefinition } from 'aws-cdk-lib/aws-ecs';
import { Repository } from 'aws-cdk-lib/aws-ecr';
import { InstanceClass, InstanceSize, InstanceType, Vpc } from 'aws-cdk-lib/aws-ec2';

export class HelloCdkStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);

const vpc = new Vpc(this, 'Vpc', {})

const tg = new NetworkTargetGroup(this, 'TargetGroup', {
port: 8125,
protocol: Protocol.UDP,
vpc: vpc,
targetType: TargetType.INSTANCE,
})

const taskDefinition = new TaskDefinition(this, 'TaskDefinition', {
compatibility: Compatibility.EC2,
networkMode: NetworkMode.BRIDGE,
})

const cluster = new Cluster(this, 'Cluster', {})
cluster.addCapacity('Cap', {
instanceType: InstanceType.of(InstanceClass.M5, InstanceSize.SMALL)

})

const image = ContainerImage.fromEcrRepository(Repository.fromRepositoryName(this, 'Rep', 'Foobar'), 'Foobar')

/* This task definition is our "base case": a TaskDefinition container that does not use UDP and passes `cdk synth` */
// taskDefinition.addContainer('Container', {
// portMappings: [
// {
// containerPort: 8125,
// protocol: aws_ecs.Protocol.TCP,
// }
// ],
// memoryReservationMiB: 123,
// image: image
// })

/*
This task definition container produces the following error:

Error: Container 'HelloCdkStack/TaskDefinition/Container' has no mapping for port undefined and protocol tcp. Did you call "container.addPortMappings()"?
*/
taskDefinition.addContainer('Container', {
portMappings: [
{
containerPort: 8125,
protocol: aws_ecs.Protocol.UDP,
}
],
memoryReservationMiB: 123,
image: image
})

/*
* This TaskDefinition defines both UDP and TCP port mappings, but with the UDP port defined first. This fails for the same
* reason above.
*/
// taskDefinition.addContainer('Container', {
// portMappings: [
// {
// containerPort: 8125,
// protocol: aws_ecs.Protocol.UDP,
// },
// {
// containerPort: 8080,
// protocol: aws_ecs.Protocol.TCP
// }
// ],
// memoryReservationMiB: 123,
// image: image
// })

/* This TaskDefinition defines both UDP and TCP port mappings, but with the TCP port defined first. This passes `cdk synth` */
// taskDefinition.addContainer('Container', {
// portMappings: [
// {
// containerPort: 8080,
// protocol: aws_ecs.Protocol.TCP
// },
// {
// containerPort: 8125,
// protocol: aws_ecs.Protocol.UDP,
// }
// ],
// memoryReservationMiB: 123,
// image: image
// })

const service = new Ec2Service(this, 'Service', {
cluster: cluster, taskDefinition: taskDefinition
})

service.attachToNetworkTargetGroup(tg)
}
}
```

### Possible Solution

_No response_

### Additional Information/Context

There is a workaround for this.

```typescript
const lbTarget = service.loadBalancerTarget({
containerPort: 8125,
protocol: Protocol.UDP,
containerName: 'Container',
});

tg.addTarget(lbTarget);
```

### CDK CLI Version

2.51.1 (build 3d30cdb)

### Framework Version

_No response_

### Node.js Version

v18.11.0

### OS

MacOS Monterey

### Language

Typescript

### Language Version

Typescript 3.9.7

### Other information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with the reproduction in lib/hello-cdk-stack.ts and run npm run build && cdk synth. Compare service.attachToNetworkTargetGroup(tg) with the working loadBalancerTarget workaround, then verify that UDP-only and UDP-first mappings synthesize successfully without the port-mapping error.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, typescript
Domain
cloud, infrastructure
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.