AWS ECS: UDP-only service not allowed to attach to NetworkTargetGroup
- 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
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