Skip to content

Commit 3998f6d

Browse files
mumoshuLink-
andauthored
Make EphemeralRunnerController MaxConcurrentReconciles configurable (#3832)
Co-authored-by: Bassem Dghaidi <568794+Link-@users.noreply.github.com>
1 parent 835bc2a commit 3998f6d

File tree

5 files changed

+78
-7
lines changed

5 files changed

+78
-7
lines changed

charts/gha-runner-scale-set-controller/templates/deployment.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ spec:
6565
{{- with .Values.flags.watchSingleNamespace }}
6666
- "--watch-single-namespace={{ . }}"
6767
{{- end }}
68+
{{- with .Values.runnerMaxConcurrentReconciles }}
69+
- "--runner-max-concurrent-reconciles={{ . }}"
70+
{{- end }}
6871
{{- with .Values.flags.updateStrategy }}
6972
- "--update-strategy={{ . }}"
7073
{{- end }}

charts/gha-runner-scale-set-controller/values.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ flags:
106106
## Defaults to watch all namespaces when unset.
107107
# watchSingleNamespace: ""
108108

109+
## The maximum number of concurrent reconciles which can be run by the EphemeralRunner controller.
110+
# Increase this value to improve the throughput of the controller.
111+
# It may also increase the load on the API server and the external service (e.g. GitHub API).
112+
runnerMaxConcurrentReconciles: 2
113+
109114
## Defines how the controller should handle upgrades while having running jobs.
110115
##
111116
## The strategies available are:

controllers/actions.github.com/ephemeralrunner_controller.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -823,12 +823,14 @@ func (r *EphemeralRunnerReconciler) deleteRunnerFromService(ctx context.Context,
823823
}
824824

825825
// SetupWithManager sets up the controller with the Manager.
826-
func (r *EphemeralRunnerReconciler) SetupWithManager(mgr ctrl.Manager) error {
827-
return ctrl.NewControllerManagedBy(mgr).
828-
For(&v1alpha1.EphemeralRunner{}).
829-
Owns(&corev1.Pod{}).
830-
WithEventFilter(predicate.ResourceVersionChangedPredicate{}).
831-
Complete(r)
826+
func (r *EphemeralRunnerReconciler) SetupWithManager(mgr ctrl.Manager, opts ...Option) error {
827+
return builderWithOptions(
828+
ctrl.NewControllerManagedBy(mgr).
829+
For(&v1alpha1.EphemeralRunner{}).
830+
Owns(&corev1.Pod{}).
831+
WithEventFilter(predicate.ResourceVersionChangedPredicate{}),
832+
opts,
833+
).Complete(r)
832834
}
833835

834836
func runnerContainerStatus(pod *corev1.Pod) *corev1.ContainerStatus {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package actionsgithubcom
2+
3+
import (
4+
"sigs.k8s.io/controller-runtime/pkg/builder"
5+
"sigs.k8s.io/controller-runtime/pkg/controller"
6+
)
7+
8+
// Options is the optional configuration for the controllers, which can be
9+
// set via command-line flags or environment variables.
10+
type Options struct {
11+
// RunnerMaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run
12+
// by the EphemeralRunnerController.
13+
RunnerMaxConcurrentReconciles int
14+
}
15+
16+
// OptionsWithDefault returns the default options.
17+
// This is here to maintain the options and their default values in one place,
18+
// rather than having to correlate those in multiple places.
19+
func OptionsWithDefault() Options {
20+
return Options{
21+
RunnerMaxConcurrentReconciles: 2,
22+
}
23+
}
24+
25+
type Option func(*controller.Options)
26+
27+
// WithMaxConcurrentReconciles sets the maximum number of concurrent Reconciles which can be run.
28+
//
29+
// This is useful to improve the throughput of the controller, but it may also increase the load on the API server and
30+
// the external service (e.g. GitHub API). The default value is 1, as defined by the controller-runtime.
31+
//
32+
// See https://github.com/actions/actions-runner-controller/issues/3021 for more information
33+
// on real-world use cases and the potential impact of this option.
34+
func WithMaxConcurrentReconciles(n int) Option {
35+
return func(b *controller.Options) {
36+
b.MaxConcurrentReconciles = n
37+
}
38+
}
39+
40+
// builderWithOptions applies the given options to the provided builder, if any.
41+
// This is a helper function to avoid the need to import the controller-runtime package in every reconciler source file
42+
// and the command package that creates the controller.
43+
// This is also useful for reducing code duplication around setting controller options in
44+
// multiple reconcilers.
45+
func builderWithOptions(b *builder.Builder, opts []Option) *builder.Builder {
46+
if len(opts) == 0 {
47+
return b
48+
}
49+
50+
var controllerOpts controller.Options
51+
for _, opt := range opts {
52+
opt(&controllerOpts)
53+
}
54+
55+
return b.WithOptions(controllerOpts)
56+
}

main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ func main() {
102102

103103
autoScalerImagePullSecrets stringSlice
104104

105+
opts = actionsgithubcom.OptionsWithDefault()
106+
105107
commonRunnerLabels commaSeparatedStringSlice
106108
)
107109
var c github.Config
@@ -136,6 +138,7 @@ func main() {
136138
flag.DurationVar(&defaultScaleDownDelay, "default-scale-down-delay", actionssummerwindnet.DefaultScaleDownDelay, "The approximate delay for a scale down followed by a scale up, used to prevent flapping (down->up->down->... loop)")
137139
flag.IntVar(&port, "port", 9443, "The port to which the admission webhook endpoint should bind")
138140
flag.DurationVar(&syncPeriod, "sync-period", 1*time.Minute, "Determines the minimum frequency at which K8s resources managed by this controller are reconciled.")
141+
flag.IntVar(&opts.RunnerMaxConcurrentReconciles, "runner-max-concurrent-reconciles", opts.RunnerMaxConcurrentReconciles, "The maximum number of concurrent reconciles which can be run by the EphemeralRunner controller. Increase this value to improve the throughput of the controller, but it may also increase the load on the API server and the external service (e.g. GitHub API).")
139142
flag.Var(&commonRunnerLabels, "common-runner-labels", "Runner labels in the K1=V1,K2=V2,... format that are inherited all the runners created by the controller. See https://github.com/actions/actions-runner-controller/issues/321 for more information")
140143
flag.StringVar(&namespace, "watch-namespace", "", "The namespace to watch for custom resources. Set to empty for letting it watch for all namespaces.")
141144
flag.StringVar(&watchSingleNamespace, "watch-single-namespace", "", "Restrict to watch for custom resources in a single namespace.")
@@ -156,6 +159,8 @@ func main() {
156159
}
157160
c.Log = &log
158161

162+
log.Info("Using options", "runner-max-concurrent-reconciles", opts.RunnerMaxConcurrentReconciles)
163+
159164
if !autoScalingRunnerSetOnly {
160165
ghClient, err = c.NewClient()
161166
if err != nil {
@@ -285,7 +290,7 @@ func main() {
285290
Scheme: mgr.GetScheme(),
286291
ActionsClient: actionsMultiClient,
287292
ResourceBuilder: rb,
288-
}).SetupWithManager(mgr); err != nil {
293+
}).SetupWithManager(mgr, actionsgithubcom.WithMaxConcurrentReconciles(opts.RunnerMaxConcurrentReconciles)); err != nil {
289294
log.Error(err, "unable to create controller", "controller", "EphemeralRunner")
290295
os.Exit(1)
291296
}

0 commit comments

Comments
 (0)