Skip to content

Commit d9b30ee

Browse files
authored
Allow using kube-apiserver cache for pod list requests (#1018)
* Allow using kube-apiserver cache for pod list requests * When UseAPIServerCacheToListPods is true, don't call kubectl's RunNodeDrain
1 parent b6e44c4 commit d9b30ee

File tree

3 files changed

+31
-7
lines changed

3 files changed

+31
-7
lines changed

pkg/config/config.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ const (
8888
uptimeFromFileDefault = ""
8989
workersConfigKey = "WORKERS"
9090
workersDefault = 10
91+
useAPIServerCache = "USE_APISERVER_CACHE"
9192
// prometheus
9293
enablePrometheusDefault = false
9394
enablePrometheusConfigKey = "ENABLE_PROMETHEUS_SERVER"
@@ -161,6 +162,7 @@ type Config struct {
161162
UseProviderId bool
162163
CompleteLifecycleActionDelaySeconds int
163164
DeleteSqsMsgIfNodeNotFound bool
165+
UseAPIServerCacheToListPods bool
164166
}
165167

166168
// ParseCliArgs parses cli arguments and uses environment variables as fallback values
@@ -223,6 +225,7 @@ func ParseCliArgs() (config Config, err error) {
223225
flag.BoolVar(&config.UseProviderId, "use-provider-id", getBoolEnv(useProviderIdConfigKey, useProviderIdDefault), "If true, fetch node name through Kubernetes node spec ProviderID instead of AWS event PrivateDnsHostname.")
224226
flag.IntVar(&config.CompleteLifecycleActionDelaySeconds, "complete-lifecycle-action-delay-seconds", getIntEnv(completeLifecycleActionDelaySecondsKey, -1), "Delay completing the Autoscaling lifecycle action after a node has been drained.")
225227
flag.BoolVar(&config.DeleteSqsMsgIfNodeNotFound, "delete-sqs-msg-if-node-not-found", getBoolEnv(deleteSqsMsgIfNodeNotFoundKey, false), "If true, delete SQS Messages from the SQS Queue if the targeted node(s) are not found.")
228+
flag.BoolVar(&config.UseAPIServerCacheToListPods, "use-apiserver-cache", getBoolEnv(useAPIServerCache, false), "If true, leverage the k8s apiserver's index on pod's spec.nodeName to list pods on a node, instead of doing an etcd quorum read.")
226229
flag.Parse()
227230

228231
if isConfigProvided("pod-termination-grace-period", podTerminationGracePeriodConfigKey) && isConfigProvided("grace-period", gracePeriodConfigKey) {
@@ -324,6 +327,7 @@ func (c Config) PrintJsonConfigArgs() {
324327
Bool("check_tag_before_draining", c.CheckTagBeforeDraining).
325328
Str("ManagedTag", c.ManagedTag).
326329
Bool("use_provider_id", c.UseProviderId).
330+
Bool("use_apiserver_cache", c.UseAPIServerCacheToListPods).
327331
Msg("aws-node-termination-handler arguments")
328332
}
329333

@@ -374,7 +378,8 @@ func (c Config) PrintHumanConfigArgs() {
374378
"\tcheck-tag-before-draining: %t,\n"+
375379
"\tmanaged-tag: %s,\n"+
376380
"\tuse-provider-id: %t,\n"+
377-
"\taws-endpoint: %s,\n",
381+
"\taws-endpoint: %s,\n"+
382+
"\tuse-apiserver-cache: %t,\n",
378383
c.DryRun,
379384
c.NodeName,
380385
c.PodName,
@@ -414,6 +419,7 @@ func (c Config) PrintHumanConfigArgs() {
414419
c.ManagedTag,
415420
c.UseProviderId,
416421
c.AWSEndpoint,
422+
c.UseAPIServerCacheToListPods,
417423
)
418424
}
419425

pkg/config/config_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func TestParseCliArgsEnvSuccess(t *testing.T) {
5252
t.Setenv("WEBHOOK_TEMPLATE", "WEBHOOK_TEMPLATE")
5353
t.Setenv("METADATA_TRIES", "100")
5454
t.Setenv("CORDON_ONLY", "false")
55+
t.Setenv("USE_APISERVER_CACHE", "true")
5556
nthConfig, err := config.ParseCliArgs()
5657
h.Ok(t, err)
5758

@@ -76,6 +77,7 @@ func TestParseCliArgsEnvSuccess(t *testing.T) {
7677
h.Equals(t, "WEBHOOK_TEMPLATE", nthConfig.WebhookTemplate)
7778
h.Equals(t, 100, nthConfig.MetadataTries)
7879
h.Equals(t, false, nthConfig.CordonOnly)
80+
h.Equals(t, true, nthConfig.UseAPIServerCacheToListPods)
7981

8082
// Check that env vars were set
8183
value, ok := os.LookupEnv("KUBERNETES_SERVICE_HOST")
@@ -111,6 +113,7 @@ func TestParseCliArgsSuccess(t *testing.T) {
111113
"--webhook-template=WEBHOOK_TEMPLATE",
112114
"--metadata-tries=100",
113115
"--cordon-only=false",
116+
"--use-apiserver-cache=true",
114117
}
115118
nthConfig, err := config.ParseCliArgs()
116119
h.Ok(t, err)
@@ -137,6 +140,7 @@ func TestParseCliArgsSuccess(t *testing.T) {
137140
h.Equals(t, 100, nthConfig.MetadataTries)
138141
h.Equals(t, false, nthConfig.CordonOnly)
139142
h.Equals(t, false, nthConfig.EnablePrometheus)
143+
h.Equals(t, true, nthConfig.UseAPIServerCacheToListPods)
140144

141145
// Check that env vars were set
142146
value, ok := os.LookupEnv("KUBERNETES_SERVICE_HOST")

pkg/node/node.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,18 @@ func (n Node) CordonAndDrain(nodeName string, reason string, recorder recorderIn
114114
if err != nil {
115115
return err
116116
}
117-
// Delete all pods on the node
118-
log.Info().Msg("Draining the node")
117+
// Be very careful here: in tests, nodeName and node.Name can be different, as
118+
// fetchKubernetesNode does some translation using the kubernetes.io/hostname label
119119
node, err := n.fetchKubernetesNode(nodeName)
120120
if err != nil {
121121
return err
122122
}
123+
var pods *corev1.PodList
124+
// Delete all pods on the node
125+
log.Info().Msg("Draining the node")
123126
// Emit events for all pods that will be evicted
124127
if recorder != nil {
125-
pods, err := n.fetchAllPods(nodeName)
128+
pods, err = n.fetchAllPods(node.Name)
126129
if err == nil {
127130
for _, pod := range pods.Items {
128131
podRef := &corev1.ObjectReference{
@@ -139,7 +142,14 @@ func (n Node) CordonAndDrain(nodeName string, reason string, recorder recorderIn
139142
}
140143
}
141144
}
142-
err = drain.RunNodeDrain(n.drainHelper, node.Name)
145+
if n.nthConfig.UseAPIServerCacheToListPods {
146+
if pods != nil {
147+
err = n.drainHelper.DeleteOrEvictPods(pods.Items)
148+
}
149+
} else {
150+
// RunNodeDrain does an etcd quorum-read to list all pods on this node
151+
err = drain.RunNodeDrain(n.drainHelper, node.Name)
152+
}
143153
if err != nil {
144154
return err
145155
}
@@ -628,9 +638,13 @@ func (n Node) fetchAllPods(nodeName string) (*corev1.PodList, error) {
628638
log.Info().Msgf("Would have retrieved running pod list on node %s, but dry-run flag was set", nodeName)
629639
return &corev1.PodList{}, nil
630640
}
631-
return n.drainHelper.Client.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{
641+
listOptions := metav1.ListOptions{
632642
FieldSelector: "spec.nodeName=" + nodeName,
633-
})
643+
}
644+
if n.nthConfig.UseAPIServerCacheToListPods {
645+
listOptions.ResourceVersion = "0"
646+
}
647+
return n.drainHelper.Client.CoreV1().Pods("").List(context.TODO(), listOptions)
634648
}
635649

636650
func getDrainHelper(nthConfig config.Config, clientset *kubernetes.Clientset) (*drain.Helper, error) {

0 commit comments

Comments
 (0)