Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve WARM_IP_TARGET handling #461

Merged
merged 11 commits into from
May 15, 2019
4 changes: 1 addition & 3 deletions config/v1.3/aws-k8s-cni.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ spec:
tolerations:
- operator: Exists
containers:
- image: 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni:v1.3.3
- image: 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni:v1.3.4
imagePullPolicy: Always
ports:
- containerPort: 61678
Expand Down Expand Up @@ -126,5 +126,3 @@ spec:
plural: eniconfigs
singular: eniconfig
kind: ENIConfig


11 changes: 8 additions & 3 deletions config/v1.4/calico.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ spec:
# Set Felix endpoint to host default action to ACCEPT.
- name: FELIX_DEFAULTENDPOINTTOHOSTACTION
value: "ACCEPT"
# This will make Felix honor AWS VPC CNI's mangle table
# rules.
- name: FELIX_IPTABLESMANGLEALLOWACTION
value: Return
# Disable IPV6 on Kubernetes.
- name: FELIX_IPV6SUPPORT
value: "false"
Expand All @@ -86,9 +90,6 @@ spec:
value: "true"
securityContext:
privileged: true
resources:
requests:
cpu: 250m
livenessProbe:
httpGet:
path: /liveness
Expand Down Expand Up @@ -451,6 +452,10 @@ spec:
value: "1"
- name: TYPHA_HEALTHENABLED
value: "true"
# This will make Felix honor AWS VPC CNI's mangle table
# rules.
- name: FELIX_IPTABLESMANGLEALLOWACTION
value: Return
livenessProbe:
exec:
command:
Expand Down
85 changes: 0 additions & 85 deletions config/v1.4/cni_metrics_helper.yaml

This file was deleted.

48 changes: 42 additions & 6 deletions ipamd/datastore/data_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,21 +319,42 @@ func (ds *DataStore) GetStats() (int, int) {
return ds.total, ds.assigned
}

func (ds *DataStore) getDeletableENI() *ENIIPPool {
// IsRequiredForWarmIPTarget determines if this ENI has warm IPs that are required to fulfill whatever WARM_IP_TARGET is
// set to.
func (ds *DataStore) isRequiredForWarmIPTarget(warmIPTarget int, eni *ENIIPPool) bool {
otherWarmIPs := 0
for _, other := range ds.eniIPPools {
if other.ID != eni.ID {
otherWarmIPs += len(other.IPv4Addresses) - other.AssignedIPv4Addresses
}
}
return otherWarmIPs < warmIPTarget
}

func (ds *DataStore) getDeletableENI(warmIPTarget int) *ENIIPPool {
for _, eni := range ds.eniIPPools {
if eni.IsPrimary {
log.Debugf("ENI %s cannot be deleted because it is primary", eni.ID)
continue
}

if eni.isTooYoung() {
log.Debugf("ENI %s cannot be deleted because it is too young", eni.ID)
continue
}

if time.Now().Sub(eni.createTime) < minLifeTime {
if eni.hasIPInCooling() {
log.Debugf("ENI %s cannot be deleted because has IPs in cooling", eni.ID)
continue
}

if time.Now().Sub(eni.lastUnassignedTime) < addressENICoolingPeriod {
if eni.hasPods() {
log.Debugf("ENI %s cannot be deleted because it has pods assigned", eni.ID)
continue
}

if eni.AssignedIPv4Addresses != 0 {
if warmIPTarget != 0 && ds.isRequiredForWarmIPTarget(warmIPTarget, eni) {
log.Debugf("ENI %s cannot be deleted because it is required for WARM_IP_TARGET: %d", eni.ID, warmIPTarget)
continue
}

Expand All @@ -343,6 +364,21 @@ func (ds *DataStore) getDeletableENI() *ENIIPPool {
return nil
}

// IsTooYoung returns true if the ENI hasn't been around long enough to be deleted.
func (e *ENIIPPool) isTooYoung() bool {
return time.Now().Sub(e.createTime) < minLifeTime
}

// HasIPInCooling returns true if an IP address was unassigned recently.
func (e *ENIIPPool) hasIPInCooling() bool {
return time.Now().Sub(e.lastUnassignedTime) < addressENICoolingPeriod
}

// HasPods returns true if the ENI has pods assigned to it.
func (e *ENIIPPool) hasPods() bool {
return e.AssignedIPv4Addresses != 0
}

// GetENINeedsIP finds an ENI in the datastore that needs more IP addresses allocated
func (ds *DataStore) GetENINeedsIP(maxIPperENI int, skipPrimary bool) *ENIIPPool {
for _, eni := range ds.eniIPPools {
Expand All @@ -362,11 +398,11 @@ func (ds *DataStore) GetENINeedsIP(maxIPperENI int, skipPrimary bool) *ENIIPPool
// RemoveUnusedENIFromStore removes a deletable ENI from the data store.
// It returns the name of the ENI which has been removed from the data store and needs to be deleted,
// or empty string if no ENI could be removed.
func (ds *DataStore) RemoveUnusedENIFromStore() string {
func (ds *DataStore) RemoveUnusedENIFromStore(warmIPTarget int) string {
ds.lock.Lock()
defer ds.lock.Unlock()

deletableENI := ds.getDeletableENI()
deletableENI := ds.getDeletableENI(warmIPTarget)
if deletableENI == nil {
log.Debugf("No ENI can be deleted at this time")
return ""
Expand Down
6 changes: 4 additions & 2 deletions ipamd/datastore/data_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,13 +287,15 @@ func TestPodIPv4Address(t *testing.T) {
assert.Equal(t, len(ds.eniIPPools["eni-2"].IPv4Addresses), 1)
assert.Equal(t, ds.eniIPPools["eni-2"].AssignedIPv4Addresses, 0)

noWarmIPTarget := 0

// should not able to free this eni
eni := ds.RemoveUnusedENIFromStore()
eni := ds.RemoveUnusedENIFromStore(noWarmIPTarget)
assert.True(t, eni == "")

ds.eniIPPools["eni-2"].createTime = time.Time{}
ds.eniIPPools["eni-2"].lastUnassignedTime = time.Time{}
eni = ds.RemoveUnusedENIFromStore()
eni = ds.RemoveUnusedENIFromStore(noWarmIPTarget)
assert.Equal(t, eni, "eni-2")

assert.Equal(t, ds.total, 2)
Expand Down
3 changes: 1 addition & 2 deletions ipamd/introspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ func (c *IPAMContext) ServeIntrospection() {
}

func (c *IPAMContext) setupIntrospectionServer() *http.Server {
// If enabled, add introspection endpoints
serverFunctions := map[string]func(w http.ResponseWriter, r *http.Request){
"/v1/enis": eniV1RequestHandler(c),
"/v1/eni-configs": eniConfigRequestHandler(c),
Expand All @@ -87,7 +86,7 @@ func (c *IPAMContext) setupIntrospectionServer() *http.Server {
availableCommandResponse, err := json.Marshal(&availableCommands)

if err != nil {
log.Error("Failed to marshal: %v", err)
log.Errorf("Failed to marshal: %v", err)
}

defaultHandler := func(w http.ResponseWriter, r *http.Request) {
Expand Down
Loading