TargetGroupBinding¶
TargetGroupBinding is a custom resource (CR) that can expose your pods using an existing ALB TargetGroup or NLB TargetGroup.
This allows you to provision the load balancer infrastructure completely outside of Kubernetes but still manage the targets with Kubernetes Service.
Usage in multi-tenant clusters
TargetGroupBinding allows users to reference any TargetGroup in the AWS account where the EKS cluster resides. In multi-tenant environments, this means tenants can potentially import arbitrary TargetGroups from the account into their namespace, subject to the controller's IAM permissions.
Security risk: Tenants could route traffic to TargetGroups belonging to other workloads or services causing unintended traffic routing.
Recommendation: Cluster administrators should restrict TargetGroupBinding creation using Kubernetes RBAC: Use Kubernetes RBAC to deny create and update permissions on TargetGroupBinding resources for untrusted users.
usage to support Ingress and Service
The AWS LoadBalancer controller internally uses TargetGroupBinding to support the functionality for Ingress, Service, and Gateway resources as well. It automatically creates TargetGroupBinding in the same namespace of the Service used.
You can view all TargetGroupBindings in a namespace by `kubectl get targetgroupbindings -n <your-namespace> -o wide`
Do not manually delete controller-generated TargetGroupBindings
Deleting a TargetGroupBinding that the controller generated for an Ingress, Service, or Gateway resource:
- deregisters every target from the associated AWS target group, and
- is not immediately reconciled by the controller.
The controller watches the Ingress, Service, and Gateway resources that own the binding, but it does not watch the generated TargetGroupBinding itself. As a result, a deleted binding is only recreated when the owning resource is updated (for example, editing any annotation) or during the periodic resync (--sync-period, which defaults to 10h). Until then, the load balancer forwards to zero registered targets.
If you delete a controller-generated binding while troubleshooting, force an immediate reconcile by editing any annotation on the owning Ingress, Service, or Gateway resource, and verify that the binding and its targets are restored.
EKS Auto Mode users
If you are using EKS Auto Mode, please see the EKS Auto Mode documentation for key differences between the load balancing capability of EKS Auto Mode and the open source load balancer controller.
TargetType¶
TargetGroupBinding CR supports TargetGroups of either instance or ip TargetType.
If TargetType is not explicitly specified, a mutating webhook will automatically call AWS API to find the TargetType for your TargetGroup and set it to the correct value.
Choosing the Target Group¶
You can use either targetGroupARN or targetGroupName to identify a TargetGroup. Although both are unique and immutable in an AWS region, you only have control of the targetGroupName, as targetGroupARN is generated by AWS and contains random characters.
If you provide both targetGroupARN and targetGroupName, the targetGroupARN takes precedence.
Sample YAML¶
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb
spec:
serviceRef:
name: awesome-service # route traffic to the awesome-service
port: 80
targetGroupName: <name-of-the-targetGroup>
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb
spec:
serviceRef:
name: awesome-service # route traffic to the awesome-service
port: 80
targetGroupARN: <arn-to-targetGroup>
VpcID¶
TargetGroupBinding CR supports the explicit definition of the Virtual Private Cloud (VPC) of your TargetGroup.
If the VpcID is not explicitly specified, a mutating webhook will automatically call AWS API to find the VpcID for your TargetGroup and set it to the correct value.
Sample YAML with VpcID¶
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb
spec:
serviceRef:
name: awesome-service # route traffic to the awesome-service
port: 80
targetGroupARN: <arn-to-targetGroup>
vpcID: <vpcID>
NodeSelector¶
Default Node Selector¶
For TargetType: instance, all nodes of a cluster that match the following
selector are added to the TargetGroup by default:
matchExpressions:
- key: node-role.kubernetes.io/master
operator: DoesNotExist
- key: node.kubernetes.io/exclude-from-external-load-balancers
operator: DoesNotExist
- key: alpha.service-controller.kubernetes.io/exclude-balancer
operator: DoesNotExist
- key: eks.amazonaws.com/compute-type
operator: NotIn
values: ["fargate"]
Custom Node Selector¶
TargetGroupBinding CR supports NodeSelector which is a
LabelSelector. This will select nodes to attach to the
instance TargetType TargetGroup and is merged with the default node
selector above.
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb
spec:
nodeSelector:
matchLabels:
foo: bar
...
AssumeRole (Cross-Account TargetGroups)¶
Use this feature when you need to manage TargetGroups in a different AWS account than where your EKS cluster runs. This is common in multi-account architectures where load balancers are centrally managed.
Terminology: - Cluster Owner (CO): The AWS account where your EKS cluster and AWS Load Balancer Controller run - TargetGroup Owner (TGO): The AWS account where the TargetGroup exists
Spec fields:
* iamRoleArnToAssume: The ARN of the role in the TGO account that the controller will assume
* assumeRoleExternalId: External ID for the assume role operation (optional but recommended to prevent the confused deputy problem)
* registerTargetsWithPodAvailabilityZone: Register cross-account IP targets with the availability zone of the pod's node instead of all. See Availability zones for cross-account targets. Requires iamRoleArnToAssume.
Sample YAML¶
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: peered-tg
namespace: nlb-game-2048-1
spec:
assumeRoleExternalId: very-secret-string-2
iamRoleArnToAssume: arn:aws:iam::155642222660:role/tg-management-role
networking:
ingress:
- from:
- securityGroup:
groupID: sg-0b6a41a2fd959623f
ports:
- port: 80
protocol: TCP
serviceRef:
name: service-2048
port: 80
targetGroupARN: arn:aws:elasticloadbalancing:us-west-2:155642222660:targetgroup/peered-tg/6a4ecf7bfae473c1
Setup Instructions¶
Step 1: Create IAM role in the TargetGroup Owner (TGO) account
Create a role that allows the AWS Load Balancer Controller role from the Cluster Owner (CO) account to assume it.
Trust policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::565768096483:role/eksctl-awslbc-loadtest-addon-iamserviceaccoun-Role1-13RdJCMqV6p2"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "very-secret-string"
}
}
}
]
}
Step 2: Add permissions to the role in the TGO account
Attach the following policy to the role created in Step 1:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:DeregisterTargets"
],
"Resource": [
"arn:aws:elasticloadbalancing:us-west-2:155642222660:targetgroup/tg1/*",
"arn:aws:elasticloadbalancing:us-west-2:155642222660:targetgroup/tg2/*"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetHealth"
],
"Resource": "*"
}
]
}
Step 3: Add AssumeRole permission in the Cluster Owner (CO) account
Add the following permission to the AWS Load Balancer Controller's IAM role. This is not included in the standard IAM policy. For improved security, scope the permission to specific role ARNs:
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": "*"
}
Availability zones for cross-account targets¶
By default, cross-account IP targets are registered with AvailabilityZone: all. Every zonal node of
the load balancer can then forward to every target, so turning off cross-zone load balancing has no
effect and inter-AZ data transfer charges are unavoidable.
Set registerTargetsWithPodAvailabilityZone: true to register each target with the availability zone
of the node its pod runs on. With cross-zone load balancing disabled, a zonal load balancer node then
only forwards to targets in its own zone.
Availability zone names are randomized per AWS account — us-west-2a in the CO account is usually a
different physical zone than us-west-2a in the TGO account. Availability zone IDs (for example
usw2-az1) are stable, so the controller resolves the pod's zone name to a zone ID in the CO account
and then back to the matching zone name in the TGO account. Both lookups use
ec2:DescribeAvailabilityZones, so the TGO role from Step 2 needs it:
{
"Effect": "Allow",
"Action": [
"ec2:DescribeAvailabilityZones"
],
"Resource": "*"
}
The zone must be one the load balancer has a subnet in. If a cluster runs nodes in a zone the load balancer does not use, registration is rejected and the TargetGroupBinding reports an error.
If the zone cannot be translated — for instance when the TGO role is missing
ec2:DescribeAvailabilityZones — the controller logs the failure and falls back to
AvailabilityZone: all rather than failing registration.
The setting only affects targets registered after it is set. Targets that are already registered
keep their current availability zone (all or a zone name) until they are deregistered and
registered again, for example when the pod is replaced. This avoids disrupting live traffic when
the setting is toggled on an existing TargetGroupBinding.
MultiCluster TargetGroup¶
TargetGroupBinding CR supports sharing the same TargetGroup ARN among multiple TargetGroupBindings. Setting this flag allows TargetGroup ARNs among multiple clusters or services within the same cluster.
The default value is false, meaning that the controller assumes full control over the TargetGroup ARN and will deregister any targets that are not found within the cluster.
To set this flag for TGBs managed by the controller use either:
ALB: alb.ingress.kubernetes.io/multi-cluster-target-group: "true"
NLB: service.beta.kubernetes.io/aws-load-balancer-multi-cluster-target-group: "true"
Gateway: Set enableMultiCluster in the TargetGroupConfiguration attached to your service.
It is not recommended to change this value after TGB creation. Changing between shared / not shared might lead to leaked targets.
Only use this flag if you intend to share the TargetGroup ARN in multiple clusters. This flag will slow down reconciles and put a small additional load on the Kubernetes control plane.
Sample YAML with MultiCluster¶
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb
spec:
serviceRef:
name: awesome-service # route traffic to the awesome-service
port: 80
targetGroupARN: <arn-to-targetGroup>
multiClusterTargetGroup: true
Reference¶
See the reference for TargetGroupBinding CR