@@ -11,6 +11,7 @@ import (
1111"k8s.io/apimachinery/pkg/util/cache"
1212"k8s.io/apimachinery/pkg/util/sets"
1313"net"
14+ "sigs.k8s.io/aws-load-balancer-controller/pkg/algorithm"
1415"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
1516"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
1617"sigs.k8s.io/controller-runtime/pkg/client"
@@ -20,6 +21,10 @@ import (
2021
2122const (
2223defaultPodENIInfoCacheTTL = 10 * time .Minute
24+ // EC2:DescribeNetworkInterface supports up to 200 filters per call.
25+ describeNetworkInterfacesFiltersLimit = 200
26+
27+ labelEKSComputeType = "eks.amazonaws.com/compute-type"
2328)
2429
2530// PodENIInfoResolver is responsible for resolve the AWS VPC ENI that supports pod network.
@@ -29,15 +34,17 @@ type PodENIInfoResolver interface {
2934}
3035
3136// NewDefaultPodENIInfoResolver constructs new defaultPodENIInfoResolver.
32- func NewDefaultPodENIInfoResolver (k8sClient client.Client , ec2Client services.EC2 , nodeInfoProvider NodeInfoProvider , logger logr.Logger ) * defaultPodENIInfoResolver {
37+ func NewDefaultPodENIInfoResolver (k8sClient client.Client , ec2Client services.EC2 , nodeInfoProvider NodeInfoProvider , vpcID string , logger logr.Logger ) * defaultPodENIInfoResolver {
3338return & defaultPodENIInfoResolver {
34- k8sClient : k8sClient ,
35- ec2Client : ec2Client ,
36- nodeInfoProvider : nodeInfoProvider ,
37- logger : logger ,
38- podENIInfoCache : cache .NewExpiring (),
39- podENIInfoCacheMutex : sync.RWMutex {},
40- podENIInfoCacheTTL : defaultPodENIInfoCacheTTL ,
39+ k8sClient : k8sClient ,
40+ ec2Client : ec2Client ,
41+ nodeInfoProvider : nodeInfoProvider ,
42+ vpcID : vpcID ,
43+ logger : logger ,
44+ podENIInfoCache : cache .NewExpiring (),
45+ podENIInfoCacheMutex : sync.RWMutex {},
46+ podENIInfoCacheTTL : defaultPodENIInfoCacheTTL ,
47+ describeNetworkInterfacesIPChunkSize : describeNetworkInterfacesFiltersLimit - 1 , // we used 1 filter for VPC.
4148}
4249}
4350
@@ -51,6 +58,8 @@ type defaultPodENIInfoResolver struct {
5158ec2Client services.EC2
5259// nodeInfoProvider
5360nodeInfoProvider NodeInfoProvider
61+ // vpcID
62+ vpcID string
5463// logger
5564logger logr.Logger
5665
@@ -62,6 +71,9 @@ type defaultPodENIInfoResolver struct {
6271// TTL for each cache entries.
6372// Note: we assume pod's ENI information(e.g. securityGroups) haven't changed per podENICacheTTL.
6473podENIInfoCacheTTL time.Duration
74+
75+ // chunkSize when describe network interface with IPAddress filter.
76+ describeNetworkInterfacesIPChunkSize int
6577}
6678
6779func (r * defaultPodENIInfoResolver ) Resolve (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
@@ -129,7 +141,8 @@ func (r *defaultPodENIInfoResolver) saveENIInfosToCache(pods []k8s.PodInfo, eniI
129141func (r * defaultPodENIInfoResolver ) resolveViaCascadedLookup (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
130142resolveFuncs := []func (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ){
131143r .resolveViaPodENIAnnotation ,
132- r .resolveViaVPCIPAddress ,
144+ r .resolveViaNodeENIs ,
145+ r .resolveViaVPCENIs ,
133146// TODO, add support for kubenet CNI plugin(kops) by resolve via routeTable.
134147}
135148
@@ -151,7 +164,8 @@ func (r *defaultPodENIInfoResolver) resolveViaCascadedLookup(ctx context.Context
151164return eniInfoByPodKey , nil
152165}
153166
154- // resolveViaPodENIAnnotation tries to resolve a pod ENI via the branch ENI annotation.
167+ // resolveViaPodENIAnnotation tries to resolve pod ENI by lookup pod's ENIInfo annotation.
168+ // with aws-vpc-cni CNI plugin's SecurityGroups for pods feature, podIP is supported by branchENI, whose information is exposed as pod annotation.
155169func (r * defaultPodENIInfoResolver ) resolveViaPodENIAnnotation (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
156170podKeysByENIID := make (map [string ][]types.NamespacedName )
157171for _ , pod := range pods {
@@ -191,8 +205,9 @@ func (r *defaultPodENIInfoResolver) resolveViaPodENIAnnotation(ctx context.Conte
191205return eniInfoByPodKey , nil
192206}
193207
194- // resolveViaVPCIPAddress tries to resolve Pod ENI through the Pod IPAddress within VPC.
195- func (r * defaultPodENIInfoResolver ) resolveViaVPCIPAddress (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
208+ // resolveViaNodeENIs tries to resolve Pod ENI by matching podIP against ENIs on EC2 node's ENIs.
209+ // with aws-vpc-cni CNI plugin, podIP can be supported by either IPv4Addresses or IPv4Prefixes on ENI.
210+ func (r * defaultPodENIInfoResolver ) resolveViaNodeENIs (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
196211nodeKeysSet := make (map [types.NamespacedName ]sets.Empty )
197212for _ , pod := range pods {
198213nodeKey := types.NamespacedName {Name : pod .NodeName }
@@ -204,13 +219,20 @@ func (r *defaultPodENIInfoResolver) resolveViaVPCIPAddress(ctx context.Context,
204219if err := r .k8sClient .Get (ctx , nodeKey , node ); err != nil {
205220return nil , err
206221}
222+ // Fargate based nodes are not EC2 instances
223+ if node .Labels [labelEKSComputeType ] == "fargate" {
224+ continue
225+ }
207226nodes = append (nodes , node )
208227}
228+ if len (nodes ) == 0 {
229+ return nil , nil
230+ }
231+
209232nodeInstanceByNodeKey , err := r .nodeInfoProvider .FetchNodeInstances (ctx , nodes )
210233if err != nil {
211234return nil , err
212235}
213-
214236eniInfoByPodKey := make (map [types.NamespacedName ]ENIInfo , len (pods ))
215237for _ , pod := range pods {
216238nodeKey := types.NamespacedName {Name : pod .NodeName }
@@ -226,6 +248,56 @@ func (r *defaultPodENIInfoResolver) resolveViaVPCIPAddress(ctx context.Context,
226248return eniInfoByPodKey , nil
227249}
228250
251+ // resolveViaVPCENIs tries to resolve pod ENI by matching podIP against ENIs in vpc.
252+ // with EKS fargate pods, podIP is supported by an ENI in vpc.
253+ func (r * defaultPodENIInfoResolver ) resolveViaVPCENIs (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
254+ podKeysByIP := make (map [string ][]types.NamespacedName , len (pods ))
255+ for _ , pod := range pods {
256+ podKeysByIP [pod .PodIP ] = append (podKeysByIP [pod .PodIP ], pod .Key )
257+ }
258+ if len (podKeysByIP ) == 0 {
259+ return nil , nil
260+ }
261+
262+ podIPs := sets .StringKeySet (podKeysByIP ).List ()
263+ podIPChunks := algorithm .ChunkStrings (podIPs , r .describeNetworkInterfacesIPChunkSize )
264+ eniByID := make (map [string ]* ec2sdk.NetworkInterface )
265+ for _ , podIPChunk := range podIPChunks {
266+ req := & ec2sdk.DescribeNetworkInterfacesInput {
267+ Filters : []* ec2sdk.Filter {
268+ {
269+ Name : awssdk .String ("vpc-id" ),
270+ Values : awssdk .StringSlice ([]string {r .vpcID }),
271+ },
272+ {
273+ Name : awssdk .String ("addresses.private-ip-address" ),
274+ Values : awssdk .StringSlice (podIPChunk ),
275+ },
276+ },
277+ }
278+ enis , err := r .ec2Client .DescribeNetworkInterfacesAsList (ctx , req )
279+ if err != nil {
280+ return nil , err
281+ }
282+ for _ , eni := range enis {
283+ eniID := awssdk .StringValue (eni .NetworkInterfaceId )
284+ eniByID [eniID ] = eni
285+ }
286+ }
287+
288+ eniInfoByPodKey := make (map [types.NamespacedName ]ENIInfo )
289+ for _ , eni := range eniByID {
290+ eniInfo := buildENIInfoViaENI (eni )
291+ for _ , addr := range eni .PrivateIpAddresses {
292+ eniIP := awssdk .StringValue (addr .PrivateIpAddress )
293+ for _ , podKey := range podKeysByIP [eniIP ] {
294+ eniInfoByPodKey [podKey ] = eniInfo
295+ }
296+ }
297+ }
298+ return eniInfoByPodKey , nil
299+ }
300+
229301// isPodSupportedByNodeENI checks whether pod is supported by specific nodeENI.
230302func (r * defaultPodENIInfoResolver ) isPodSupportedByNodeENI (pod k8s.PodInfo , nodeENI * ec2sdk.InstanceNetworkInterface ) bool {
231303for _ , ipv4Address := range nodeENI .PrivateIpAddresses {
0 commit comments