14
14
package ipamd
15
15
16
16
import (
17
+ "fmt"
17
18
"net"
18
19
"os"
19
20
"strconv"
@@ -31,6 +32,7 @@ import (
31
32
"github.com/aws/amazon-vpc-cni-k8s/ipamd/datastore"
32
33
"github.com/aws/amazon-vpc-cni-k8s/pkg/awsutils"
33
34
"github.com/aws/amazon-vpc-cni-k8s/pkg/docker"
35
+ "github.com/aws/amazon-vpc-cni-k8s/pkg/eniconfig"
34
36
"github.com/aws/amazon-vpc-cni-k8s/pkg/k8sapi"
35
37
"github.com/aws/amazon-vpc-cni-k8s/pkg/networkutils"
36
38
)
@@ -43,11 +45,40 @@ const (
43
45
ipPoolMonitorInterval = 5 * time .Second
44
46
maxRetryCheckENI = 5
45
47
eniAttachTime = 10 * time .Second
46
- defaultWarmENITarget = 1
47
48
nodeIPPoolReconcileInterval = 60 * time .Second
48
49
maxK8SRetries = 12
49
50
retryK8SInterval = 5 * time .Second
50
- noWarmIPTarget = 0
51
+
52
+ // This environment is used to specify the desired number of free IPs always available in "warm pool"
53
+ // When it is not set, ipamD defaut to use the number IPs per ENI for that instance.
54
+ // For example, for a m4.4xlarge node,
55
+ // if WARM-IP-TARGET is set to 1, and there are 9 pods running on the node, ipamD will try
56
+ // to make "warm pool" to have 10 IP address with 9 being assigned to Pod and 1 free IP.
57
+ //
58
+ // if "WARM-IP-TARGET is not set, it will be defaulted to 30 (which the number of IPs per ENI). If there are 9 pods
59
+ // running on the node, ipamD will try to make "warm pool" to have 39 IPs with 9 being assigned to Pod and 30 free IPs.
60
+ envWarmIPTarget = "WARM_IP_TARGET"
61
+ noWarmIPTarget = 0
62
+
63
+ // This environment is used to specify the desired number of free ENIs along with all of its IP addresses
64
+ // always available in "warm pool".
65
+ // When it is not set, it is default to 1.
66
+ //
67
+ // when "WARM-IP-TARGET" is defined, ipamD will use behavior defined for "WARM-IP-TARGET".
68
+ //
69
+ // For example, for a m4.4xlarget node
70
+ // if WARM_ENI_TARGET is set to 2, and there are 9 pods running on the node, ipamD will try to
71
+ // make "warm pool" to have 2 extra ENIs and its IP addresses, in another word, 90 IP addresses with 9 IPs assigne to Pod
72
+ // and 81 free IPs.
73
+ //
74
+ // if "WARM_ENI_TARGET" is not set, it is default to 1, if there 9 pods running on the node, ipamD will try to
75
+ // make "warm pool" to have 1 extra ENI, in aother word 60 IPs with 9 being assigned to Pod and 51 free IPs.
76
+ envWarmENITarget = "WARM_ENI_TARGET"
77
+ defaultWarmENITarget = 1
78
+
79
+ // This environment is used to specify whether Pods need to use securitygroup and subnet defined in ENIConfig CRD
80
+ // When it is NOT set or set to false, ipamD will use primary interface security group and subnet for Pod network.
81
+ envCustomNetworkCfg = "AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG"
51
82
)
52
83
53
84
var (
@@ -105,6 +136,7 @@ type IPAMContext struct {
105
136
awsClient awsutils.APIs
106
137
dataStore * datastore.DataStore
107
138
k8sClient k8sapi.K8SAPIs
139
+ eniConfig eniconfig.ENIConfig
108
140
dockerClient docker.APIs
109
141
networkClient networkutils.NetworkAPIs
110
142
@@ -133,13 +165,14 @@ func prometheusRegister() {
133
165
134
166
// New retrieves IP address usage information from Instance MetaData service and Kubelet
135
167
// then initializes IP address pool data store
136
- func New (k8sapiClient k8sapi.K8SAPIs ) (* IPAMContext , error ) {
168
+ func New (k8sapiClient k8sapi.K8SAPIs , eniConfig * eniconfig. ENIConfigController ) (* IPAMContext , error ) {
137
169
prometheusRegister ()
138
170
c := & IPAMContext {}
139
171
140
172
c .k8sClient = k8sapiClient
141
173
c .networkClient = networkutils .New ()
142
174
c .dockerClient = docker .New ()
175
+ c .eniConfig = eniConfig
143
176
144
177
client , err := awsutils .New ()
145
178
if err != nil {
@@ -314,7 +347,7 @@ func (c *IPAMContext) retryAllocENIIP() {
314
347
log .Infof ("Failed to retrieve ENI IP limit: %v" , err )
315
348
return
316
349
}
317
- eni := c .dataStore .GetENINeedsIP (maxIPLimit )
350
+ eni := c .dataStore .GetENINeedsIP (maxIPLimit , useCustomNetworkCfg () )
318
351
if eni != nil {
319
352
log .Debugf ("Attempt again to allocate IP address for eni :%s" , eni .ID )
320
353
var err error
@@ -392,7 +425,30 @@ func (c *IPAMContext) increaseIPPool() {
392
425
log .Debugf ("Skipping increase IPPOOL due to max ENI already attached to the instance : %d" , c .maxENI )
393
426
return
394
427
}
395
- eni , err := c .awsClient .AllocENI ()
428
+
429
+ var securityGroups []* string
430
+ var subnet string
431
+ customNetworkCfg := useCustomNetworkCfg ()
432
+
433
+ if customNetworkCfg {
434
+ eniCfg , err := c .eniConfig .MyENIConfig ()
435
+
436
+ if err != nil {
437
+ log .Errorf ("Failed to get pod ENI config" )
438
+ return
439
+ }
440
+
441
+ log .Infof ("ipamd: using custom network config: %v, %s" , eniCfg .SecurityGroups , eniCfg .Subnet )
442
+
443
+ for _ , sgID := range eniCfg .SecurityGroups {
444
+ log .Debugf ("Found security-group id: %s" , sgID )
445
+ securityGroups = append (securityGroups , aws .String (sgID ))
446
+ }
447
+
448
+ subnet = eniCfg .Subnet
449
+ }
450
+
451
+ eni , err := c .awsClient .AllocENI (customNetworkCfg , securityGroups , subnet )
396
452
if err != nil {
397
453
log .Errorf ("Failed to increase pool size due to not able to allocate ENI %v" , err )
398
454
@@ -546,7 +602,7 @@ func (c *IPAMContext) waitENIAttached(eni string) (awsutils.ENIMetadata, error)
546
602
}
547
603
548
604
func getWarmENITarget () int {
549
- inputStr , found := os .LookupEnv ("WARM_ENI_TARGET" )
605
+ inputStr , found := os .LookupEnv (envWarmENITarget )
550
606
551
607
if ! found {
552
608
return defaultWarmENITarget
@@ -722,8 +778,21 @@ func (c *IPAMContext) eniIPPoolReconcile(ipPool map[string]*datastore.AddressInf
722
778
723
779
}
724
780
781
+ func useCustomNetworkCfg () bool {
782
+ defaultValue := false
783
+ if strValue := os .Getenv (envCustomNetworkCfg ); strValue != "" {
784
+ parsedValue , err := strconv .ParseBool (strValue )
785
+ if err != nil {
786
+ log .Error ("Failed to parse " + envCustomNetworkCfg + "; using default: " + fmt .Sprint (defaultValue ), err .Error ())
787
+ return defaultValue
788
+ }
789
+ return parsedValue
790
+ }
791
+ return defaultValue
792
+ }
793
+
725
794
func getWarmIPTarget () int {
726
- inputStr , found := os .LookupEnv ("WARM_IP_TARGET" )
795
+ inputStr , found := os .LookupEnv (envWarmIPTarget )
727
796
728
797
if ! found {
729
798
return noWarmIPTarget
@@ -753,3 +822,12 @@ func (c *IPAMContext) getCurWarmIPTarget() (int, bool) {
753
822
754
823
return curTarget , true
755
824
}
825
+
826
+ // GetConfigForDebug returns the active values of the configuration env vars (for debugging purposes).
827
+ func GetConfigForDebug () map [string ]interface {} {
828
+ return map [string ]interface {}{
829
+ envWarmIPTarget : getWarmIPTarget (),
830
+ envWarmENITarget : getWarmENITarget (),
831
+ envCustomNetworkCfg : useCustomNetworkCfg (),
832
+ }
833
+ }
0 commit comments