-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Implement nbthreads for haproxy router #19975
Conversation
PTAL @openshift/sig-networking |
/hold |
@@ -33,6 +33,11 @@ | |||
|
|||
global | |||
maxconn {{env "ROUTER_MAX_CONNECTIONS" "20000"}} | |||
{{- if isTrue (env "ROUTER_ENABLE_TRHEADS") }} | |||
nbthread {{env "ROUTER_THREADS" "4"}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just do it based on whether ROUTER_THREADS is defined and > 1, and then default to 1 thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, ROUTER_ENABLE_TRHEADS seems redundant.
pkg/oc/admin/router/router.go
Outdated
@@ -308,6 +311,7 @@ func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out, errout io. | |||
cmd.Flags().StringVar(&cfg.Ciphers, "ciphers", cfg.Ciphers, "Specifies the cipher suites to use. You can choose a predefined cipher set ('modern', 'intermediate', or 'old') or specify exact cipher suites by passing a : separated list. Not supported for F5.") | |||
cmd.Flags().BoolVar(&cfg.StrictSNI, "strict-sni", cfg.StrictSNI, "Use strict-sni bind processing (do not use default cert). Not supported for F5.") | |||
cmd.Flags().BoolVar(&cfg.Local, "local", cfg.Local, "If true, do not contact the apiserver") | |||
cmd.Flags().StringVar(&cfg.Threads, "threads", cfg.Threads, "Specifies the amount of threads for the haproxy router.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifies the number of threads... (number instead of amount)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use cmd.Flags().Int32Var() instead of cmd.Flags().StringVar() then you don't need to do integer validations for this param.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a max thread limit? (very high thread count does not seem practical)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i haven't tested the thread limit, i'll check that
{{- if ne (env "ROUTER_THREADS") "" }} | ||
nbthread {{env "ROUTER_THREADS"}} | ||
{{- end }} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be simplified by setting the default threads to 1 and removing the conditional entirely, assuming the controller code is the only consumer of this template
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not have the nbthreads word in the config file if it is not being used... the feature is beta in haproxy, so while it should be equivalent, who knows for sure?
But why not use 1 as the default for env and say gt 1?
{{- if gt (env "ROUTER_THREADS" 1) 1 }}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree re: the beta status, good point. Regarding the conditional, the way you suggest here makes more sense once the data type is corrected to be an int.
pkg/oc/admin/router/router.go
Outdated
@@ -228,6 +228,9 @@ type RouterConfig struct { | |||
// Strict SNI (do not use default cert) | |||
StrictSNI bool | |||
|
|||
// Number of threads to start per process | |||
Threads string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be an int
{{- if ne (env "ROUTER_THREADS") "" }} | ||
nbthread {{env "ROUTER_THREADS"}} | ||
{{- end }} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not have the nbthreads word in the config file if it is not being used... the feature is beta in haproxy, so while it should be equivalent, who knows for sure?
But why not use 1 as the default for env and say gt 1?
{{- if gt (env "ROUTER_THREADS" 1) 1 }}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also needs corresponding docs pr
@@ -33,6 +33,11 @@ | |||
|
|||
global | |||
maxconn {{env "ROUTER_MAX_CONNECTIONS" "20000"}} | |||
{{- if ne (env "ROUTER_THREADS") "" }} | |||
nbthread {{env "ROUTER_THREADS"}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we want this for ROUTER_THREADS > 1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cpu-map needs nbproc, this could be more complex , but it might be possible since we're already reading processor information.
we could have something like this
nbproc {{env "ROUTER_NUM_CPUS"}}
cpu-map auto:/all 0-{{env "ROUTER_NUM_CPUS" - 1}} this operation is not valid on the template is just as an example
nbthread {{env "ROUTER_THREADS"}}
contrib/completions/zsh/oc
Outdated
@@ -5949,6 +5949,8 @@ _oc_adm_router() | |||
local_nonpersistent_flags+=("--disable-namespace-ownership-check") | |||
flags+=("--dry-run") | |||
local_nonpersistent_flags+=("--dry-run") | |||
flags+=("--enable-threads") | |||
local_nonpersistent_flags+=("--enable-threads") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to re-generate bash completions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yeah i forgot to run the completions again
pkg/oc/admin/router/router.go
Outdated
@@ -308,6 +311,7 @@ func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out, errout io. | |||
cmd.Flags().StringVar(&cfg.Ciphers, "ciphers", cfg.Ciphers, "Specifies the cipher suites to use. You can choose a predefined cipher set ('modern', 'intermediate', or 'old') or specify exact cipher suites by passing a : separated list. Not supported for F5.") | |||
cmd.Flags().BoolVar(&cfg.StrictSNI, "strict-sni", cfg.StrictSNI, "Use strict-sni bind processing (do not use default cert). Not supported for F5.") | |||
cmd.Flags().BoolVar(&cfg.Local, "local", cfg.Local, "If true, do not contact the apiserver") | |||
cmd.Flags().StringVar(&cfg.Threads, "threads", cfg.Threads, "Specifies the amount of threads for the haproxy router.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use cmd.Flags().Int32Var() instead of cmd.Flags().StringVar() then you don't need to do integer validations for this param.
pkg/oc/admin/router/router.go
Outdated
@@ -308,6 +311,7 @@ func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out, errout io. | |||
cmd.Flags().StringVar(&cfg.Ciphers, "ciphers", cfg.Ciphers, "Specifies the cipher suites to use. You can choose a predefined cipher set ('modern', 'intermediate', or 'old') or specify exact cipher suites by passing a : separated list. Not supported for F5.") | |||
cmd.Flags().BoolVar(&cfg.StrictSNI, "strict-sni", cfg.StrictSNI, "Use strict-sni bind processing (do not use default cert). Not supported for F5.") | |||
cmd.Flags().BoolVar(&cfg.Local, "local", cfg.Local, "If true, do not contact the apiserver") | |||
cmd.Flags().StringVar(&cfg.Threads, "threads", cfg.Threads, "Specifies the amount of threads for the haproxy router.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a max thread limit? (very high thread count does not seem practical)
pkg/oc/admin/router/router.go
Outdated
@@ -228,6 +228,9 @@ type RouterConfig struct { | |||
// Strict SNI (do not use default cert) | |||
StrictSNI bool | |||
|
|||
// Number of threads to start per process | |||
Threads int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we wanted to use fixed width integers even for internal types, i.e. int32/64 over int?
Refer: #9366 and 865faaf
@knobunc @ironcladlou
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, although I don't think we're dealing with formal versioned API here... no objection to uint32
regardless
pkg/oc/admin/router/router.go
Outdated
@@ -308,6 +311,7 @@ func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out, errout io. | |||
cmd.Flags().StringVar(&cfg.Ciphers, "ciphers", cfg.Ciphers, "Specifies the cipher suites to use. You can choose a predefined cipher set ('modern', 'intermediate', or 'old') or specify exact cipher suites by passing a : separated list. Not supported for F5.") | |||
cmd.Flags().BoolVar(&cfg.StrictSNI, "strict-sni", cfg.StrictSNI, "Use strict-sni bind processing (do not use default cert). Not supported for F5.") | |||
cmd.Flags().BoolVar(&cfg.Local, "local", cfg.Local, "If true, do not contact the apiserver") | |||
cmd.Flags().IntVar(&cfg.Threads, "threads", cfg.Threads, "Specifies the amount of threads for the haproxy router.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why we would configure this - I think we want it to be autodetected by default. Probably based on the number of cores configured for this pod.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is a generally correct value we should be choosing, then we should have it autodetect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could set as default half or a quarter of the max number of threads per process (/proc/sys/kernel/threads-max) but i think that we should allow the sysadmin to tune this setting to suit his needs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Auto-detecting a default sounds great, but what I wonder is if we need to also provide a way to override the default; if so, then the defaulting logic could be a followup. If not an explicit value configuration, would we at least want an overall feature flag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add the --enable-threads flag, if no --threads flag is present a default is used for the nbthreads setting.
If the --threads flag is set just check that it's value is not higher than the one in /proc/sys/kernel/threads-max
Another option could be to only have the --threads flag, if no value is set use the default but i think this could be confusing
pkg/oc/admin/router/router.go
Outdated
@@ -655,6 +659,7 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write | |||
"STATS_PORT": strconv.Itoa(cfg.StatsPort), | |||
"STATS_USERNAME": cfg.StatsUsername, | |||
"STATS_PASSWORD": cfg.StatsPassword, | |||
"ROUTER_THREADS": strconv.Itoa(cfg.Threads), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would prefer we not set this here. I don't see why a user would set this flag, and not just expect us to do the right thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the right number for the threads? How do we know what else they want to use the machine for? Or are you suggesting we pick it up from the container limits? The other problem there is that the number of processes that are running will vary if they have long-running connections and have router reloads.
I really don't want to enable this by default yet, so we at least need a way to disable it. I'm fine with an "auto" setting for num threads, but I suspect we will need a way to tune it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 on auto setting the default num threads based on pod cpu limits and allow this value to be overridden via env var or some other mechanism.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without knowing how effective our auto-detected number is in practice, does it make any sense to add an explicitly unsupported parameter to give an escape hatch and allow us to test tunings without a new build?
When we have higher confidence our auto-detected algo is solid we could talk about removing the override. There is a bit of UX complexity there in explaining the mutual exclusivity or precedence order of the auto/explicit options, but if the override is considered unsupported/debugging-only maybe it isn't as big an issue?
pkg/oc/admin/router/router.go
Outdated
"ROUTER_ENABLE_THREADS": strconv.FormatBool(cfg.EnableThreads), | ||
} | ||
|
||
if cfg.EnableThreads { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should an error be returned if a number of threads is specified but threads are not enabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, an error or at least a warning that the --threads is ignored when --enable-threads is not set.
pkg/oc/admin/router/router.go
Outdated
"ROUTER_ENABLE_THREADS": strconv.FormatBool(cfg.EnableThreads), | ||
} | ||
|
||
if cfg.EnableThreads { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, an error or at least a warning that the --threads is ignored when --enable-threads is not set.
pkg/oc/admin/router/router.go
Outdated
} | ||
|
||
if cfg.EnableThreads { | ||
if cfg.Threads >= 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add validations for cfg.Threads (check >= minThreads and <= maxThreads)?
pkg/oc/admin/router/router.go
Outdated
cpuinfo := readProcFile("/proc/cpuinfo") | ||
siblings, _ := strconv.Atoi(cpuinfo["siblings"]) | ||
cores, _ := strconv.Atoi(cpuinfo["cpucores"]) | ||
threads := siblings * cores |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we want to consider CPU requests/limits specified by the router pod/container?
https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/#specify-a-cpu-request-and-a-cpu-limit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the router is running as a privileged container, /proc/cpuinfo will return container or host limits?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the router is running as a privileged container, /proc/cpuinfo will return container or host limits?
Worse than that, it will be /proc/cpuinfo
on the host where the administrator runs oc adm router
, which is probably not the node host. Calculating the default number of threads really needs to be done in pkg/router/template/router.go
, not pkg/oc/admin/router/router.go
.
@@ -257,6 +265,7 @@ var helperFunctions = template.FuncMap{ | |||
"env": env, //tries to get an environment variable, returns the first non-empty default value or "" on failure | |||
"matchPattern": matchPattern, //anchors provided regular expression and evaluates against given string | |||
"isInteger": isInteger, //determines if a given variable is an integer | |||
"toInt": toInt, //Convert string to integer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we call it toInteger
in the map for consistency with isInteger
?
func toInt(s string) int { | ||
ret, err := strconv.Atoi(s) | ||
if err != nil { | ||
return 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is becoming a part of the informal API for router templates, we should consider how it might be used or expected to work in the general case. Is returning 0 for invalid input a sensible default? Or should we return nil
? For example, we could do the following:
func toInt(s string) *int {
ret, err := strconv.Atoi(s)
if err != nil {
return nil
}
return &ret
}
Which could be used as follows:
{{- with $threads := toInt (env "ROUTER_THREADS") }}
{{- if gt $threads 0 }}
 nbthread {{ $threads }}
{{- end }}
{{- end }}
The part inside the with
block will get skipped entirely if strconv.Atoi
returns an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe document it as private (FWIW)?
I think this is another example of where a new field on an object passed to the template helps. There's no reason to do conversion inside the template itself: why would a user ever want to treat ROUTER_THREADS as anything but an int? If I were making a custom template and had to deal with this conversion manually I'd be annoyed
I'm not sure that we want to add all this complexity... I was good with the ENV as long as we changed gt to ne... @ironcladlou your call though. |
I'm fine with the |
I find the ne solution more complex than adding a conversion function which simplifies the logic on the template |
I agree, however using the |
Ok, i'll change the code for the ne solution |
@@ -33,6 +33,12 @@ | |||
|
|||
global | |||
maxconn {{env "ROUTER_MAX_CONNECTIONS" "20000"}} | |||
{{- $threads := env "ROUTER_THREADS" | |||
{{- if ne "" (firstMatch "[1-9][0-9]*" $threads) }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did the clearer alternative not work?
if ne "0" (env "ROUTER_THREADS" "0")
And then adding range validation in the Go code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, ^^ this looks clean to me.
@@ -33,6 +33,12 @@ | |||
|
|||
global | |||
maxconn {{env "ROUTER_MAX_CONNECTIONS" "20000"}} | |||
{{- $threads := env "ROUTER_THREADS" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing }}
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hopefully that was caught by an automated test...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm
/approve
@juanvallejo @enj @soltysh please approve the completions changes. |
/retest |
completion changes lgtm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/hold
You need to squash your commits.
@imcsk8 You preferred regex match over #19975 (comment) because once router dc is generated, user could edit the dc and can fiddle with 'ROUTER_THREADS' env which needs revalidation? |
@juanvallejo Can you say /approve please so the bot is happy. |
/approve |
Add threading support for router. Trello: https://trello.com/c/0thyZwBw/25-3-implement-nbthreads-for-haproxy-router-routerperformanceoperations Add completions Fix typo Simplify the threading options Change argument type Add enable threads flag Fix template condition Remove defaut threads setting Add proper integer condition for threads Change thread check condition Fix typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/hold cancel
/lgtm
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: imcsk8, juanvallejo, knobunc The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Add threading support for router.
Trello: https://trello.com/c/0thyZwBw/25-3-implement-nbthreads-for-haproxy-router-routerperformanceoperations