@@ -11,6 +11,7 @@ import (
11
11
"k8s.io/apimachinery/pkg/util/cache"
12
12
"k8s.io/apimachinery/pkg/util/sets"
13
13
"net"
14
+ "sigs.k8s.io/aws-load-balancer-controller/pkg/algorithm"
14
15
"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
15
16
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
16
17
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -20,6 +21,10 @@ import (
20
21
21
22
const (
22
23
defaultPodENIInfoCacheTTL = 10 * time .Minute
24
+ // EC2:DescribeNetworkInterface supports up to 200 filters per call.
25
+ describeNetworkInterfacesFiltersLimit = 200
26
+
27
+ labelEKSComputeType = "eks.amazonaws.com/compute-type"
23
28
)
24
29
25
30
// PodENIInfoResolver is responsible for resolve the AWS VPC ENI that supports pod network.
@@ -29,15 +34,17 @@ type PodENIInfoResolver interface {
29
34
}
30
35
31
36
// 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 {
33
38
return & 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.
41
48
}
42
49
}
43
50
@@ -51,6 +58,8 @@ type defaultPodENIInfoResolver struct {
51
58
ec2Client services.EC2
52
59
// nodeInfoProvider
53
60
nodeInfoProvider NodeInfoProvider
61
+ // vpcID
62
+ vpcID string
54
63
// logger
55
64
logger logr.Logger
56
65
@@ -62,6 +71,9 @@ type defaultPodENIInfoResolver struct {
62
71
// TTL for each cache entries.
63
72
// Note: we assume pod's ENI information(e.g. securityGroups) haven't changed per podENICacheTTL.
64
73
podENIInfoCacheTTL time.Duration
74
+
75
+ // chunkSize when describe network interface with IPAddress filter.
76
+ describeNetworkInterfacesIPChunkSize int
65
77
}
66
78
67
79
func (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
129
141
func (r * defaultPodENIInfoResolver ) resolveViaCascadedLookup (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
130
142
resolveFuncs := []func (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ){
131
143
r .resolveViaPodENIAnnotation ,
132
- r .resolveViaVPCIPAddress ,
144
+ r .resolveViaNodeENIs ,
145
+ r .resolveViaVPCENIs ,
133
146
// TODO, add support for kubenet CNI plugin(kops) by resolve via routeTable.
134
147
}
135
148
@@ -151,7 +164,8 @@ func (r *defaultPodENIInfoResolver) resolveViaCascadedLookup(ctx context.Context
151
164
return eniInfoByPodKey , nil
152
165
}
153
166
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.
155
169
func (r * defaultPodENIInfoResolver ) resolveViaPodENIAnnotation (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
156
170
podKeysByENIID := make (map [string ][]types.NamespacedName )
157
171
for _ , pod := range pods {
@@ -191,8 +205,9 @@ func (r *defaultPodENIInfoResolver) resolveViaPodENIAnnotation(ctx context.Conte
191
205
return eniInfoByPodKey , nil
192
206
}
193
207
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 ) {
196
211
nodeKeysSet := make (map [types.NamespacedName ]sets.Empty )
197
212
for _ , pod := range pods {
198
213
nodeKey := types.NamespacedName {Name : pod .NodeName }
@@ -204,13 +219,20 @@ func (r *defaultPodENIInfoResolver) resolveViaVPCIPAddress(ctx context.Context,
204
219
if err := r .k8sClient .Get (ctx , nodeKey , node ); err != nil {
205
220
return nil , err
206
221
}
222
+ // Fargate based nodes are not EC2 instances
223
+ if node .Labels [labelEKSComputeType ] == "fargate" {
224
+ continue
225
+ }
207
226
nodes = append (nodes , node )
208
227
}
228
+ if len (nodes ) == 0 {
229
+ return nil , nil
230
+ }
231
+
209
232
nodeInstanceByNodeKey , err := r .nodeInfoProvider .FetchNodeInstances (ctx , nodes )
210
233
if err != nil {
211
234
return nil , err
212
235
}
213
-
214
236
eniInfoByPodKey := make (map [types.NamespacedName ]ENIInfo , len (pods ))
215
237
for _ , pod := range pods {
216
238
nodeKey := types.NamespacedName {Name : pod .NodeName }
@@ -226,6 +248,56 @@ func (r *defaultPodENIInfoResolver) resolveViaVPCIPAddress(ctx context.Context,
226
248
return eniInfoByPodKey , nil
227
249
}
228
250
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
+
229
301
// isPodSupportedByNodeENI checks whether pod is supported by specific nodeENI.
230
302
func (r * defaultPodENIInfoResolver ) isPodSupportedByNodeENI (pod k8s.PodInfo , nodeENI * ec2sdk.InstanceNetworkInterface ) bool {
231
303
for _ , ipv4Address := range nodeENI .PrivateIpAddresses {
0 commit comments