Skip to content

Commit ec22d70

Browse files
feat: Investigate and fix issue with wrong CPU count for containers (#623)
So, I found that `nproc` always shows how many CPUs available is. K8s "limits" and docker `--cpus` are throttling mechanisms, which do not hide the visibility of all cores. There are a few workarounds, but IMO, it is better to implement checks for that than do them >Workaround for docker - set `--cpuset-cpus` >Workaraund for K8s - somehow deal with [kubelet static CPU management policy](https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/#cpu-management-policies), as [recommend in Reddit](https://news.ycombinator.com/item?id=25224714) * Send all "colorify" logs through stderr, as make able to add user-facing-logs in functions that also need to return same value to the function-caller. Needed for `common::get_cpu_num` err_msg show up --------- Co-authored-by: George L. Yermulnik <[email protected]>
1 parent 598d16b commit ec22d70

File tree

2 files changed

+87
-9
lines changed

2 files changed

+87
-9
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,16 @@ If you'd like to set parallelism value relative to number of CPU logical cores -
393393
>
394394
> </details>
395395

396+
397+
398+
```yaml
399+
args:
400+
- --hook-config=--parallelism-cpu-cores=N
401+
```
402+
403+
If you don't see code above in your `pre-commit-config.yaml` or logs - you don't need it.
404+
`--parallelism-cpu-cores` used only in edge cases. Check-out it usage in [hooks/_common.sh](hooks/_common.sh)
405+
396406
### checkov (deprecated) and terraform_checkov
397407

398408
> `checkov` hook is deprecated, please use `terraform_checkov`.

hooks/_common.sh

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,68 @@ function common::is_hook_run_on_whole_repo {
170170
fi
171171
}
172172

173+
#######################################################################
174+
# Get the number of CPU logical cores available for pre-commit to use
175+
# Arguments:
176+
# parallelism_cpu_cores (string) Used in edge cases when number of
177+
# CPU cores can't be derived automatically
178+
# Outputs:
179+
# Returns number of CPU logical cores, rounded down to nearest integer
180+
#######################################################################
181+
function common::get_cpu_num {
182+
local -r parallelism_cpu_cores=$1
183+
184+
if [[ -n $parallelism_cpu_cores ]]; then
185+
# 22 EINVAL Invalid argument. Some invalid argument was supplied.
186+
[[ $parallelism_cpu_cores =~ ^[[:digit:]]+$ ]] || return 22
187+
188+
echo "$parallelism_cpu_cores"
189+
return
190+
fi
191+
192+
local millicpu
193+
194+
if [[ -f /sys/fs/cgroup/cpu/cpu.cfs_quota_us ]]; then
195+
# Inside K8s pod or DInD in K8s
196+
millicpu=$(< /sys/fs/cgroup/cpu/cpu.cfs_quota_us)
197+
198+
if [[ $millicpu -eq -1 ]]; then
199+
# K8s no limits or in DinD
200+
common::colorify "yellow" "Unable to derive number of available CPU cores.\n" \
201+
"Running inside K8s pod without limits or inside DinD without limits propagation.\n" \
202+
"To avoid possible harm, parallelism is disabled.\n" \
203+
"To re-enable it, set corresponding limits, or set the following for the current hook:\n" \
204+
" args:\n" \
205+
" - --hook-config=--parallelism-ci-cpu-cores=N\n" \
206+
"where N is the number of CPU cores to allocate to pre-commit."
207+
208+
echo 1
209+
return
210+
fi
211+
212+
echo $((millicpu / 1000))
213+
return
214+
fi
215+
216+
if [[ -f /sys/fs/cgroup/cpu.max ]]; then
217+
# Inside Linux (Docker?) container
218+
millicpu=$(cut -d' ' -f1 /sys/fs/cgroup/cpu.max)
219+
220+
if [[ $millicpu == max ]]; then
221+
# No limits
222+
nproc 2> /dev/null || echo 1
223+
return
224+
fi
225+
226+
echo $((millicpu / 1000))
227+
return
228+
fi
229+
230+
# On host machine or any other case
231+
# `nproc` - linux, `sysctl -n hw.ncpu` - macOS, `echo 1` - fallback
232+
nproc 2> /dev/null || sysctl -n hw.ncpu 2> /dev/null || echo 1
233+
}
234+
173235
#######################################################################
174236
# Hook execution boilerplate logic which is common to hooks, that run
175237
# on per dir basis.
@@ -219,13 +281,8 @@ function common::per_dir_hook {
219281

220282
# Lookup hook-config for modifiers that impact common behavior
221283
local change_dir_in_unique_part=false
222-
# Limit the number of parallel processes to the number of CPU cores -1
223-
# `nproc` - linux, `sysctl -n hw.ncpu` - macOS, `echo 1` - fallback
224-
local CPU
225-
CPU=$(nproc 2> /dev/null || sysctl -n hw.ncpu 2> /dev/null || echo 1)
226-
local parallelism_limit
227-
local parallelism_disabled=false
228284

285+
local parallelism_limit
229286
IFS=";" read -r -a configs <<< "${HOOK_CONFIG[*]}"
230287
for c in "${configs[@]}"; do
231288
IFS="=" read -r -a config <<< "$c"
@@ -246,11 +303,21 @@ function common::per_dir_hook {
246303
;;
247304
--parallelism-limit)
248305
# this flag will limit the number of parallel processes
249-
parallelism_limit=$((value))
306+
parallelism_limit="$value"
307+
;;
308+
--parallelism-cpu-cores)
309+
# Used in edge cases when number of CPU cores can't be derived automatically
310+
parallelism_cpu_cores="$value"
250311
;;
251312
esac
252313
done
253314

315+
CPU=$(common::get_cpu_num "$parallelism_cpu_cores")
316+
317+
# parallelism_limit can include reference to 'CPU' variable
318+
parallelism_limit=$((parallelism_limit))
319+
local parallelism_disabled=false
320+
254321
if [[ ! $parallelism_limit ]]; then
255322
parallelism_limit=$((CPU - 1))
256323
elif [[ $parallelism_limit -le 1 ]]; then
@@ -321,14 +388,15 @@ function common::colorify {
321388

322389
# Params start #
323390
local COLOR="${!1}"
324-
local -r TEXT=$2
391+
shift
392+
local -r TEXT="$*"
325393
# Params end #
326394

327395
if [ "$PRE_COMMIT_COLOR" = "never" ]; then
328396
COLOR=$RESET
329397
fi
330398

331-
echo -e "${COLOR}${TEXT}${RESET}"
399+
echo -e "${COLOR}${TEXT}${RESET}" >&2
332400
}
333401

334402
#######################################################################

0 commit comments

Comments
 (0)