forked from GoogleCloudDataproc/initialization-actions
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspark-rapids.sh
More file actions
925 lines (780 loc) · 35.2 KB
/
spark-rapids.sh
File metadata and controls
925 lines (780 loc) · 35.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
#!/bin/bash
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS-IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script installs NVIDIA GPU drivers (version 535.104.05) along with CUDA 12.2.
# However, Cuda 12.1.1 - Driver v530.30.02 is used for Ubuntu 18 only.
# For Ubuntu 24.04 with kernel 6.14+, this script uses repository installation to get the latest CUDA toolkit and NVIDIA driver 570+ for compatibility.
# Additionally, it installs the RAPIDS Spark plugin, configures Spark and YARN, and is compatible with Debian, Ubuntu, and Rocky Linux distributions.
# Note that the script is designed to work when secure boot is disabled during cluster creation.
# It also creates a Systemd Service for maintaining up-to-date Kernel Headers on Debian and Ubuntu.
set -euxo pipefail
function os_id() {
grep '^ID=' /etc/os-release | cut -d= -f2 | xargs
}
function os_version() {
grep '^VERSION_ID=' /etc/os-release | cut -d= -f2 | xargs
}
function is_debian() {
[[ "$(os_id)" == 'debian' ]]
}
function is_debian10() {
is_debian && [[ "$(os_version)" == '10'* ]]
}
function is_debian11() {
is_debian && [[ "$(os_version)" == '11'* ]]
}
function is_debian12() {
is_debian && [[ "$(os_version)" == '12'* ]]
}
function is_ubuntu() {
[[ "$(os_id)" == 'ubuntu' ]]
}
function is_ubuntu18() {
is_ubuntu && [[ "$(os_version)" == '18.04'* ]]
}
function is_ubuntu20() {
is_ubuntu && [[ "$(os_version)" == '20.04'* ]]
}
function is_ubuntu22() {
is_ubuntu && [[ "$(os_version)" == '22.04'* ]]
}
function is_ubuntu24() {
is_ubuntu && [[ "$(os_version)" == '24.04'* ]]
}
function is_rocky() {
[[ "$(os_id)" == 'rocky' ]]
}
function is_rocky8() {
is_rocky && [[ "$(os_version)" == '8'* ]]
}
function is_rocky9() {
is_rocky && [[ "$(os_version)" == '9'* ]]
}
function os_vercat() {
if is_ubuntu ; then
os_version | sed -e 's/[^0-9]//g'
elif is_rocky ; then
os_version | sed -e 's/[^0-9].*$//g'
else
os_version
fi
}
function get_metadata_attribute() {
local -r attribute_name=$1
local -r default_value="${2:-}"
/usr/share/google/get_metadata_value "attributes/${attribute_name}" || echo -n "${default_value}"
}
CA_TMPDIR="$(mktemp -u -d -p /run/tmp -t ca_dir-XXXX)"
PSN="$(get_metadata_attribute private_secret_name)"
readonly PSN
function configure_dkms_certs() {
if [[ -z "${PSN}" ]]; then
echo "No signing secret provided. skipping";
return 0
fi
mkdir -p "${CA_TMPDIR}"
# If the private key exists, verify it
if [[ -f "${CA_TMPDIR}/db.rsa" ]]; then
echo "Private key material exists"
local expected_modulus_md5sum
expected_modulus_md5sum=$(get_metadata_attribute cert_modulus_md5sum)
if [[ -n "${expected_modulus_md5sum}" ]]; then
modulus_md5sum="${expected_modulus_md5sum}"
else
modulus_md5sum="bd40cf5905c7bba4225d330136fdbfd3"
fi
# Verify that cert md5sum matches expected md5sum
if [[ "${modulus_md5sum}" != "$(openssl rsa -noout -modulus -in \"${CA_TMPDIR}/db.rsa\" | openssl md5 | awk '{print $2}')" ]]; then
echo "unmatched rsa key modulus"
fi
ln -sf "${CA_TMPDIR}/db.rsa" /var/lib/dkms/mok.key
# Verify that key md5sum matches expected md5sum
if [[ "${modulus_md5sum}" != "$(openssl x509 -noout -modulus -in /var/lib/dkms/mok.pub | openssl md5 | awk '{print $2}')" ]]; then
echo "unmatched x509 cert modulus"
fi
return
fi
# Retrieve cloud secrets keys
local sig_priv_secret_name
sig_priv_secret_name="${PSN}"
local sig_pub_secret_name
sig_pub_secret_name="$(get_metadata_attribute public_secret_name)"
local sig_secret_project
sig_secret_project="$(get_metadata_attribute secret_project)"
local sig_secret_version
sig_secret_version="$(get_metadata_attribute secret_version)"
# If metadata values are not set, do not write mok keys
if [[ -z "${sig_priv_secret_name}" ]]; then return 0 ; fi
# Write private material to volatile storage
gcloud secrets versions access "${sig_secret_version}" \
--project="${sig_secret_project}" \
--secret="${sig_priv_secret_name}" \
| dd status=none of="${CA_TMPDIR}/db.rsa"
# Write public material to volatile storage
gcloud secrets versions access "${sig_secret_version}" \
--project="${sig_secret_project}" \
--secret="${sig_pub_secret_name}" \
| base64 --decode \
| dd status=none of="${CA_TMPDIR}/db.der"
# symlink private key and copy public cert from volatile storage for DKMS
if is_ubuntu ; then
mkdir -p /var/lib/shim-signed/mok
ln -sf "${CA_TMPDIR}/db.rsa" /var/lib/shim-signed/mok/MOK.priv
cp -f "${CA_TMPDIR}/db.der" /var/lib/shim-signed/mok/MOK.der
else
mkdir -p /var/lib/dkms/
ln -sf "${CA_TMPDIR}/db.rsa" /var/lib/dkms/mok.key
cp -f "${CA_TMPDIR}/db.der" /var/lib/dkms/mok.pub
fi
}
function clear_dkms_key {
if [[ -z "${PSN}" ]]; then
echo "No signing secret provided. skipping" >2
return 0
fi
echo "WARN -- PURGING SIGNING MATERIAL -- WARN" >2
echo "future dkms runs will not use correct signing key" >2
rm -rf "${CA_TMPDIR}" /var/lib/dkms/mok.key /var/lib/shim-signed/mok/MOK.priv
}
function add_contrib_components() {
if ! is_debian ; then
return
fi
if is_debian12 ; then
# Include in sources file components on which nvidia-open-kernel-dkms depends
local -r debian_sources="/etc/apt/sources.list.d/debian.sources"
local components="main contrib"
sed -i -e "s/Components: .*$/Components: ${components}/" "${debian_sources}"
elif is_debian ; then
sed -i -e 's/ main$/ main contrib/' /etc/apt/sources.list
fi
}
# Short name for nvidia urls
if is_rocky ; then
shortname="$(os_id | sed -e 's/rocky/rhel/')$(os_vercat)"
else
shortname="$(os_id)$(os_vercat)"
fi
readonly shortname
# Detect dataproc image version from its various names
if (! test -v DATAPROC_IMAGE_VERSION) && test -v DATAPROC_VERSION; then
DATAPROC_IMAGE_VERSION="${DATAPROC_VERSION}"
fi
# Fetch Linux Family distro and Dataproc Image version
readonly OS_NAME=$(lsb_release -is | tr '[:upper:]' '[:lower:]')
# Fetch SPARK config
readonly SPARK_VERSION_ENV=$(spark-submit --version 2>&1 | sed -n 's/.*version[[:blank:]]\+\([0-9]\+\.[0-9]\).*/\1/p' | head -n1)
if [[ "${SPARK_VERSION_ENV}" == "3"* ]]; then
readonly DEFAULT_XGBOOST_VERSION="1.7.6"
readonly SPARK_VERSION="3.0"
readonly SCALA_VERSION="2.12"
elif [[ "${SPARK_VERSION_ENV}" == "4"* ]]; then
readonly DEFAULT_XGBOOST_VERSION="2.1.4"
readonly SPARK_VERSION="4.0"
readonly SCALA_VERSION="2.13"
else
echo "Error: Your Spark version is not supported. Please upgrade Spark to one of the supported versions."
exit 1
fi
# Update SPARK RAPIDS config
readonly DEFAULT_SPARK_RAPIDS_VERSION="25.12.0"
readonly SPARK_RAPIDS_VERSION=$(get_metadata_attribute 'spark-rapids-version' ${DEFAULT_SPARK_RAPIDS_VERSION})
readonly XGBOOST_VERSION=$(get_metadata_attribute 'xgboost-version' ${DEFAULT_XGBOOST_VERSION})
# Fetch instance roles and runtime
readonly ROLE=$(/usr/share/google/get_metadata_value attributes/dataproc-role)
readonly MASTER=$(/usr/share/google/get_metadata_value attributes/dataproc-master)
readonly RUNTIME=$(get_metadata_attribute 'rapids-runtime' 'SPARK')
# CUDA version and Driver version config
CUDA_VERSION=$(get_metadata_attribute 'cuda-version' '12.4.1') #12.2.2
NVIDIA_DRIVER_VERSION=$(get_metadata_attribute 'driver-version' '550.54.15') #535.104.05
CUDA_VERSION_MAJOR="${CUDA_VERSION%.*}" #12.2
# EXCEPTIONS
# Change CUDA version for Ubuntu 18 (Cuda 12.1.1 - Driver v530.30.02 is the latest version supported by Ubuntu 18)
# Change CUDA version for Ubuntu 24 (Cuda 12.4.1 is not available, use 12.6.0)
if [[ "${OS_NAME}" == "ubuntu" ]]; then
if is_ubuntu18 ; then
CUDA_VERSION=$(get_metadata_attribute 'cuda-version' '12.1.1') #12.1.1
NVIDIA_DRIVER_VERSION=$(get_metadata_attribute 'driver-version' '530.30.02') #530.30.02
CUDA_VERSION_MAJOR="${CUDA_VERSION%.*}" #12.1
elif is_ubuntu24 ; then
# CUDA 12.4.1 is not available for Ubuntu 24.04, use 12.6.0 instead
# For kernel 6.14+, use NVIDIA driver 570 for compatibility
KERNEL_VERSION=$(uname -r | cut -d'-' -f1)
KERNEL_MAJOR=$(echo "$KERNEL_VERSION" | cut -d'.' -f1)
KERNEL_MINOR=$(echo "$KERNEL_VERSION" | cut -d'.' -f2)
if [[ "$KERNEL_MAJOR" -eq 6 && "$KERNEL_MINOR" -ge 14 ]]; then
# For kernel 6.14+ (dataproc 3), use repository installation to get latest CUDA and compatible drivers
CUDA_VERSION=$(get_metadata_attribute 'cuda-version' 'latest') #latest from repo
NVIDIA_DRIVER_VERSION=$(get_metadata_attribute 'driver-version' '570') #570 series
CUDA_VERSION_MAJOR="12" #Will be determined from repository
USE_REPO_INSTALL="true"
else
# Use CUDA 12.6.0 local installer for older kernels
CUDA_VERSION=$(get_metadata_attribute 'cuda-version' '12.6.0') #12.6.0
NVIDIA_DRIVER_VERSION=$(get_metadata_attribute 'driver-version' '560.28.03') #560.28.03
CUDA_VERSION_MAJOR="${CUDA_VERSION%.*}" #12.6
USE_REPO_INSTALL="false"
fi
fi
fi
# Verify Secure boot
SECURE_BOOT="disabled"
SECURE_BOOT=$(mokutil --sb-state|awk '{print $2}')
# Stackdriver GPU agent parameters
# Whether to install GPU monitoring agent that sends GPU metrics to Stackdriver
INSTALL_GPU_AGENT=$(get_metadata_attribute 'install-gpu-agent' 'false')
readonly INSTALL_GPU_AGENT
# Dataproc configurations
readonly HADOOP_CONF_DIR='/etc/hadoop/conf'
readonly HIVE_CONF_DIR='/etc/hive/conf'
readonly SPARK_CONF_DIR='/etc/spark/conf'
NVIDIA_SMI_PATH='/usr/bin'
MIG_MAJOR_CAPS=0
IS_MIG_ENABLED=0
function execute_with_retries() {
local -r cmd=$1
for ((i = 0; i < 10; i++)); do
if time eval "$cmd"; then
return 0
fi
sleep 5
done
return 1
}
function install_spark_rapids() {
local -r nvidia_repo_url='https://repo1.maven.org/maven2/com/nvidia'
local -r dmlc_repo_url='https://repo.maven.apache.org/maven2/ml/dmlc'
# For Spark 4.0 with Scala 2.13, use the cuda12 variant and Scala 2.13 XGBoost JARs
if [[ "${SPARK_VERSION}" == "4.0" ]]; then
wget -nv --timeout=30 --tries=5 --retry-connrefused \
"${nvidia_repo_url}/rapids-4-spark_${SCALA_VERSION}/${SPARK_RAPIDS_VERSION}/rapids-4-spark_${SCALA_VERSION}-${SPARK_RAPIDS_VERSION}-cuda12.jar" \
-P /usr/lib/spark/jars/
# Download XGBoost JARs for Scala 2.13 (Spark 4.0)
wget -nv --timeout=30 --tries=5 --retry-connrefused \
"${dmlc_repo_url}/xgboost4j-spark-gpu_${SCALA_VERSION}/${XGBOOST_VERSION}/xgboost4j-spark-gpu_${SCALA_VERSION}-${XGBOOST_VERSION}.jar" \
-P /usr/lib/spark/jars/
wget -nv --timeout=30 --tries=5 --retry-connrefused \
"${dmlc_repo_url}/xgboost4j-gpu_${SCALA_VERSION}/${XGBOOST_VERSION}/xgboost4j-gpu_${SCALA_VERSION}-${XGBOOST_VERSION}.jar" \
-P /usr/lib/spark/jars/
else
# For Spark 3.0 with Scala 2.12
wget -nv --timeout=30 --tries=5 --retry-connrefused \
"${nvidia_repo_url}/rapids-4-spark_${SCALA_VERSION}/${SPARK_RAPIDS_VERSION}/rapids-4-spark_${SCALA_VERSION}-${SPARK_RAPIDS_VERSION}.jar" \
-P /usr/lib/spark/jars/
wget -nv --timeout=30 --tries=5 --retry-connrefused \
"${dmlc_repo_url}/xgboost4j-spark-gpu_${SCALA_VERSION}/${XGBOOST_VERSION}/xgboost4j-spark-gpu_${SCALA_VERSION}-${XGBOOST_VERSION}.jar" \
-P /usr/lib/spark/jars/
wget -nv --timeout=30 --tries=5 --retry-connrefused \
"${dmlc_repo_url}/xgboost4j-gpu_${SCALA_VERSION}/${XGBOOST_VERSION}/xgboost4j-gpu_${SCALA_VERSION}-${XGBOOST_VERSION}.jar" \
-P /usr/lib/spark/jars/
fi
}
function configure_spark() {
if [[ "${SPARK_VERSION}" == "3"* ]] || [[ "${SPARK_VERSION}" == "4"* ]]; then
cat >>${SPARK_CONF_DIR}/spark-defaults.conf <<EOF
###### BEGIN : RAPIDS properties for Spark ${SPARK_VERSION} ######
# Rapids Accelerator for Spark can utilize AQE, but when the plan is not finalized,
# query explain output won't show GPU operator, if user have doubt
# they can uncomment the line before seeing the GPU plan explain, but AQE on gives user the best performance.
spark.executor.resource.gpu.amount=1
spark.plugins=com.nvidia.spark.SQLPlugin
spark.executor.resource.gpu.discoveryScript=/usr/lib/spark/scripts/gpu/getGpusResources.sh
spark.dynamicAllocation.enabled=false
spark.sql.autoBroadcastJoinThreshold=10m
spark.sql.files.maxPartitionBytes=512m
# For Spark SQL, we want the scheduler to use the number of CPU cores as the
# limiting resource (the number of tasks we can run in parallel is the number of cores).
# We therefore set the per task GPU amount to a small number, telling the scheduler
# to ignore the GPU when limiting parallel tasks, so we should see "number of cores" tasks
# in parallel able to submit work to the GPU.
spark.task.resource.gpu.amount=0.00001
###### END : RAPIDS properties for Spark ${SPARK_VERSION} ######
EOF
else
cat >>${SPARK_CONF_DIR}/spark-defaults.conf <<EOF
###### BEGIN : RAPIDS properties for Spark ${SPARK_VERSION} ######
spark.submit.pyFiles=/usr/lib/spark/jars/xgboost4j-spark_${SPARK_VERSION}-${XGBOOST_VERSION}-${XGBOOST_GPU_SUB_VERSION}.jar
###### END : RAPIDS properties for Spark ${SPARK_VERSION} ######
EOF
fi
}
# Enables a systemd service on bootup to install new headers.
# This service recompiles kernel modules for Ubuntu and Debian, which are necessary for the functioning of nvidia-smi.
function setup_systemd_update_headers() {
cat <<EOF >/lib/systemd/system/install-headers.service
[Unit]
Description=Install Linux headers for the current kernel
After=network-online.target
[Service]
ExecStart=/bin/bash -c 'count=0; while [ \$count -lt 3 ]; do /usr/bin/apt-get install -y -q linux-headers-\$(/bin/uname -r) && break; count=\$((count+1)); sleep 5; done'
Type=oneshot
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd to recognize the new unit file
systemctl daemon-reload
# Enable and start the service
systemctl enable --now install-headers.service
}
readonly NVIDIA_BASE_DL_URL='https://developer.download.nvidia.com/compute'
readonly NVIDIA_REPO_URL="${NVIDIA_BASE_DL_URL}/cuda/repos/${shortname}/x86_64"
# Hold all NVIDIA-related packages from upgrading unintenionally or services like unattended-upgrades
# Users should run apt-mark unhold before they wish to upgrade these packages
function hold_nvidia_packages() {
apt-mark hold nvidia-* > /dev/null 2>&1
apt-mark hold libnvidia-* > /dev/null 2>&1
if dpkg -l | grep -q "xserver-xorg-video-nvidia"; then
apt-mark hold xserver-xorg-video-nvidia* > /dev/null 2>&1
fi
}
function unhold_nvidia_packages() {
apt-mark unhold nvidia-* > /dev/null 2>&1
apt-mark unhold libnvidia-* > /dev/null 2>&1
apt-mark unhold xserver-xorg-video-nvidia* > /dev/null 2>&1
}
# Install NVIDIA GPU driver provided by NVIDIA
function install_nvidia_gpu_driver() {
## common steps for all linux family distros
readonly NVIDIA_DRIVER_VERSION_PREFIX=${NVIDIA_DRIVER_VERSION%%.*}
## For Debian & Ubuntu
# For driver 570, use the original CUDA installer with driver 560, then upgrade driver separately
if [[ "${NVIDIA_DRIVER_VERSION_PREFIX}" == "570" ]]; then
readonly LOCAL_INSTALLER_DEB="cuda-repo-${shortname}-${CUDA_VERSION_MAJOR//./-}-local_${CUDA_VERSION}-560.28.03-1_amd64.deb"
readonly LOCAL_DEB_URL="${NVIDIA_BASE_DL_URL}/cuda/${CUDA_VERSION}/local_installers/${LOCAL_INSTALLER_DEB}"
else
readonly LOCAL_INSTALLER_DEB="cuda-repo-${shortname}-${CUDA_VERSION_MAJOR//./-}-local_${CUDA_VERSION}-${NVIDIA_DRIVER_VERSION}-1_amd64.deb"
readonly LOCAL_DEB_URL="${NVIDIA_BASE_DL_URL}/cuda/${CUDA_VERSION}/local_installers/${LOCAL_INSTALLER_DEB}"
fi
readonly DIST_KEYRING_DIR="/var/cuda-repo-${shortname}-${CUDA_VERSION_MAJOR//./-}-local"
## installation steps based OS
if is_debian ; then
export DEBIAN_FRONTEND=noninteractive
execute_with_retries "apt-get install -y -q 'linux-headers-$(uname -r)'"
curl -fsSL --retry-connrefused --retry 3 --retry-max-time 5 \
"${LOCAL_DEB_URL}" -o /tmp/local-installer.deb
dpkg -i /tmp/local-installer.deb
rm /tmp/local-installer.deb
cp ${DIST_KEYRING_DIR}/cuda-*-keyring.gpg /usr/share/keyrings/
add_contrib_components
execute_with_retries "apt-get update"
## EXCEPTION
if is_debian10 ; then
apt-get remove -y libglvnd0
apt-get install -y ca-certificates-java
fi
configure_dkms_certs
execute_with_retries "apt-get install -y -q nvidia-kernel-open-dkms"
clear_dkms_key
execute_with_retries \
"apt-get install -y -q --no-install-recommends cuda-drivers-${NVIDIA_DRIVER_VERSION_PREFIX}"
execute_with_retries \
"apt-get install -y -q --no-install-recommends cuda-toolkit-${CUDA_VERSION_MAJOR//./-}"
modprobe nvidia
# enable a systemd service that updates kernel headers after reboot
setup_systemd_update_headers
# prevent auto upgrading nvidia packages
hold_nvidia_packages
elif is_ubuntu ; then
# Unhold NVIDIA packages to allow upgrades (see issue #1321)
unhold_nvidia_packages
execute_with_retries "apt-get install -y -q 'linux-headers-$(uname -r)'"
# Ubuntu 18.04 is not supported by new style NV debs; install from .run files + github
if is_ubuntu18 ; then
# fetch .run file
curl -o driver.run \
"https://download.nvidia.com/XFree86/Linux-x86_64/${NVIDIA_DRIVER_VERSION}/NVIDIA-Linux-x86_64-${NVIDIA_DRIVER_VERSION}.run"
# Install all but kernel driver
bash driver.run --no-kernel-modules --silent --install-libglvnd
rm driver.run
WORKDIR=/opt/install-nvidia-driver
mkdir -p "${WORKDIR}"
pushd $_
# Fetch open souce kernel module with corresponding tag
test -d open-gpu-kernel-modules || \
git clone https://github.com/NVIDIA/open-gpu-kernel-modules.git \
--branch "${NVIDIA_DRIVER_VERSION}" --single-branch
cd ${WORKDIR}/open-gpu-kernel-modules
#
# build kernel modules
#
make -j$(nproc) modules \
> /var/log/open-gpu-kernel-modules-build.log \
2> /var/log/open-gpu-kernel-modules-build_error.log
configure_dkms_certs
# sign
for module in $(find kernel-open -name '*.ko'); do
/lib/modules/$(uname -r)/build/scripts/sign-file sha256 \
"${CA_TMPDIR}/db.rsa" \
"${CA_TMPDIR}/db.der" \
"${module}"
done
clear_dkms_key
# install
make modules_install \
>> /var/log/open-gpu-kernel-modules-build.log \
2>> /var/log/open-gpu-kernel-modules-build_error.log
depmod -a
modprobe nvidia
popd
#
# Install CUDA
#
cuda_runfile="cuda_${CUDA_VERSION}_${NVIDIA_DRIVER_VERSION}_linux.run"
curl -fsSL --retry-connrefused --retry 10 --retry-max-time 30 \
"https://developer.download.nvidia.com/compute/cuda/${CUDA_VERSION}/local_installers/${cuda_runfile}" \
-o cuda.run
time bash cuda.run --silent --toolkit --no-opengl-libs
rm cuda.run
elif [[ "${USE_REPO_INSTALL:-false}" == "true" ]]; then
# Repository-based installation for latest CUDA and kernel 6.14+ compatibility
# Install CUDA keyring for repository access
execute_with_retries "wget https://developer.download.nvidia.com/compute/cuda/repos/${shortname}/x86_64/cuda-keyring_1.1-1_all.deb"
execute_with_retries "dpkg -i cuda-keyring_1.1-1_all.deb"
rm -f cuda-keyring_1.1-1_all.deb
# Add graphics-drivers PPA for latest NVIDIA drivers
execute_with_retries "apt-get install -y -q software-properties-common"
execute_with_retries "add-apt-repository -y ppa:graphics-drivers/ppa"
execute_with_retries "apt-get update"
execute_with_retries "apt-get install -y -q --no-install-recommends dkms"
configure_dkms_certs
# Install latest CUDA toolkit and compatible NVIDIA driver
execute_with_retries "apt-get install -y -q --no-install-recommends cuda-toolkit"
execute_with_retries "apt-get install -y -q --no-install-recommends nvidia-driver-${NVIDIA_DRIVER_VERSION_PREFIX}-open"
clear_dkms_key
modprobe nvidia
else
# Install from repo provided by NV
readonly UBUNTU_REPO_CUDA_PIN="${NVIDIA_REPO_URL}/cuda-${shortname}.pin"
curl -fsSL --retry-connrefused --retry 3 --retry-max-time 5 \
"${UBUNTU_REPO_CUDA_PIN}" -o /etc/apt/preferences.d/cuda-repository-pin-600
curl -fsSL --retry-connrefused --retry 3 --retry-max-time 5 \
"${LOCAL_DEB_URL}" -o /tmp/local-installer.deb
dpkg -i /tmp/local-installer.deb
rm /tmp/local-installer.deb
cp ${DIST_KEYRING_DIR}/cuda-*-keyring.gpg /usr/share/keyrings/
execute_with_retries "apt-get update"
execute_with_retries "apt-get install -y -q --no-install-recommends dkms"
configure_dkms_certs
# Special handling for driver 570 which may not be in local CUDA repo
if [[ "${NVIDIA_DRIVER_VERSION_PREFIX}" == "570" ]]; then
# First install CUDA toolkit from local repo (this will install driver 560)
execute_with_retries "apt-get install -y -q --no-install-recommends cuda-toolkit-${CUDA_VERSION_MAJOR//./-}"
# Then upgrade to driver 570 from graphics-drivers PPA
execute_with_retries "apt-get install -y -q --no-install-recommends software-properties-common"
execute_with_retries "add-apt-repository -y ppa:graphics-drivers/ppa"
execute_with_retries "apt-get update"
execute_with_retries "apt-get install -y -q --no-install-recommends nvidia-driver-${NVIDIA_DRIVER_VERSION_PREFIX}-open"
else
# Standard installation from local CUDA repo
for pkg in "nvidia-driver-${NVIDIA_DRIVER_VERSION_PREFIX}-open" \
"cuda-drivers-${NVIDIA_DRIVER_VERSION_PREFIX}" \
"cuda-toolkit-${CUDA_VERSION_MAJOR//./-}" ; do
execute_with_retries "apt-get install -y -q --no-install-recommends ${pkg}"
done
fi
clear_dkms_key
modprobe nvidia
fi
# enable a systemd service that updates kernel headers after reboot
setup_systemd_update_headers
# prevent auto upgrading nvidia packages
hold_nvidia_packages
elif is_rocky ; then
# Install kernel development packages
execute_with_retries "dnf install -y kernel-devel-$(uname -r) kernel-headers-$(uname -r)"
# Download the CUDA installer run file
curl -fsSL --retry-connrefused --retry 3 --retry-max-time 30 -o driver.run \
"https://developer.download.nvidia.com/compute/cuda/${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${NVIDIA_DRIVER_VERSION}_linux.run"
# Run the installer in silent mode
execute_with_retries "bash driver.run --silent --driver --toolkit --no-opengl-libs"
# Remove the installer file after installation to clean up
rm driver.run
# Load the NVIDIA kernel module
modprobe nvidia
else
echo "Unsupported OS: '${OS_NAME}'"
exit 1
fi
ldconfig
echo "NVIDIA GPU driver provided by NVIDIA was installed successfully"
}
# Collects 'gpu_utilization' and 'gpu_memory_utilization' metrics
function install_gpu_agent() {
download_agent
install_agent_dependency
start_agent_service
}
function download_agent(){
if [[ ${OS_NAME} == rocky ]]; then
execute_with_retries "dnf -y -q install git"
else
execute_with_retries "apt-get install git -y"
fi
mkdir -p /opt/google
chmod 777 /opt/google
cd /opt/google
test -d compute-gpu-monitoring || \
execute_with_retries "git clone https://github.com/GoogleCloudPlatform/compute-gpu-monitoring.git"
}
function install_agent_dependency(){
cd /opt/google/compute-gpu-monitoring/linux
python3 -m venv venv
venv/bin/pip install wheel
venv/bin/pip install -Ur requirements.txt
}
function start_agent_service(){
cp /opt/google/compute-gpu-monitoring/linux/systemd/google_gpu_monitoring_agent_venv.service /lib/systemd/system
systemctl daemon-reload
systemctl --no-reload --now enable /lib/systemd/system/google_gpu_monitoring_agent_venv.service
}
function set_hadoop_property() {
local -r config_file=$1
local -r property=$2
local -r value=$3
/usr/local/bin/bdconfig set_property \
--configuration_file "${HADOOP_CONF_DIR}/${config_file}" \
--name "${property}" --value "${value}" \
--clobber
}
function configure_yarn() {
if [[ ! -f ${HADOOP_CONF_DIR}/resource-types.xml ]]; then
printf '<?xml version="1.0" ?>\n<configuration/>' >"${HADOOP_CONF_DIR}/resource-types.xml"
fi
set_hadoop_property 'resource-types.xml' 'yarn.resource-types' 'yarn.io/gpu'
set_hadoop_property 'capacity-scheduler.xml' \
'yarn.scheduler.capacity.resource-calculator' \
'org.apache.hadoop.yarn.util.resource.DominantResourceCalculator'
set_hadoop_property 'yarn-site.xml' 'yarn.resource-types' 'yarn.io/gpu'
}
# This configuration should be applied only if GPU is attached to the node
function configure_yarn_nodemanager() {
set_hadoop_property 'yarn-site.xml' 'yarn.nodemanager.resource-plugins' 'yarn.io/gpu'
set_hadoop_property 'yarn-site.xml' \
'yarn.nodemanager.resource-plugins.gpu.allowed-gpu-devices' 'auto'
set_hadoop_property 'yarn-site.xml' \
'yarn.nodemanager.resource-plugins.gpu.path-to-discovery-executables' $NVIDIA_SMI_PATH
set_hadoop_property 'yarn-site.xml' \
'yarn.nodemanager.linux-container-executor.cgroups.mount' 'true'
set_hadoop_property 'yarn-site.xml' \
'yarn.nodemanager.linux-container-executor.cgroups.mount-path' '/sys/fs/cgroup'
set_hadoop_property 'yarn-site.xml' \
'yarn.nodemanager.linux-container-executor.cgroups.hierarchy' 'yarn'
set_hadoop_property 'yarn-site.xml' \
'yarn.nodemanager.container-executor.class' \
'org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor'
set_hadoop_property 'yarn-site.xml' 'yarn.nodemanager.linux-container-executor.group' 'yarn'
}
function configure_gpu_exclusive_mode() {
# check if running spark 3 or 4, if not, enable GPU exclusive mode
local spark_version
spark_version=$(spark-submit --version 2>&1 | sed -n 's/.*version[[:blank:]]\+\([0-9]\+\.[0-9]\).*/\1/p' | head -n1)
if [[ ${spark_version} != 3.* ]] && [[ ${spark_version} != 4.* ]]; then
# include exclusive mode on GPU
nvidia-smi -c EXCLUSIVE_PROCESS
fi
}
function fetch_mig_scripts() {
mkdir -p /usr/local/yarn-mig-scripts
chmod 755 /usr/local/yarn-mig-scripts
wget -P /usr/local/yarn-mig-scripts/ https://raw.githubusercontent.com/NVIDIA/spark-rapids-examples/branch-22.10/examples/MIG-Support/yarn-unpatched/scripts/nvidia-smi
wget -P /usr/local/yarn-mig-scripts/ https://raw.githubusercontent.com/NVIDIA/spark-rapids-examples/branch-22.10/examples/MIG-Support/yarn-unpatched/scripts/mig2gpu.sh
chmod 755 /usr/local/yarn-mig-scripts/*
}
function configure_gpu_script() {
# Download GPU discovery script
local -r spark_gpu_script_dir='/usr/lib/spark/scripts/gpu'
mkdir -p ${spark_gpu_script_dir}
# need to update the getGpusResources.sh script to look for MIG devices since if multiple GPUs nvidia-smi still
# lists those because we only disable the specific GIs via CGROUPs. Here we just create it based off of:
# https://raw.githubusercontent.com/apache/spark/master/examples/src/main/scripts/getGpusResources.sh
echo '
#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
NUM_MIG_DEVICES=$(nvidia-smi -L | grep MIG | wc -l)
ADDRS=$(nvidia-smi --query-gpu=index --format=csv,noheader | sed -e '\'':a'\'' -e '\''N'\'' -e'\''$!ba'\'' -e '\''s/\n/","/g'\'')
if [ $NUM_MIG_DEVICES -gt 0 ]; then
MIG_INDEX=$(( $NUM_MIG_DEVICES - 1 ))
ADDRS=$(seq -s '\''","'\'' 0 $MIG_INDEX)
fi
echo {\"name\": \"gpu\", \"addresses\":[\"$ADDRS\"]}
' > ${spark_gpu_script_dir}/getGpusResources.sh
chmod a+rwx -R ${spark_gpu_script_dir}
}
function configure_gpu_isolation() {
# enable GPU isolation
sed -i "s/yarn\.nodemanager\.linux\-container\-executor\.group\=.*$/yarn\.nodemanager\.linux\-container\-executor\.group\=yarn/g" "${HADOOP_CONF_DIR}/container-executor.cfg"
if [[ $IS_MIG_ENABLED -ne 0 ]]; then
# configure the container-executor.cfg to have major caps
printf '\n[gpu]\nmodule.enabled=true\ngpu.major-device-number=%s\n\n[cgroups]\nroot=/sys/fs/cgroup\nyarn-hierarchy=yarn\n' $MIG_MAJOR_CAPS >> "${HADOOP_CONF_DIR}/container-executor.cfg"
printf 'export MIG_AS_GPU_ENABLED=1\n' >> "${HADOOP_CONF_DIR}/yarn-env.sh"
printf 'export ENABLE_MIG_GPUS_FOR_CGROUPS=1\n' >> "${HADOOP_CONF_DIR}/yarn-env.sh"
else
printf '\n[gpu]\nmodule.enabled=true\n[cgroups]\nroot=/sys/fs/cgroup\nyarn-hierarchy=yarn\n' >> "${HADOOP_CONF_DIR}/container-executor.cfg"
fi
# Configure a systemd unit to ensure that permissions are set on restart
cat >/etc/systemd/system/dataproc-cgroup-device-permissions.service<<EOF
[Unit]
Description=Set permissions to allow YARN to access device directories
[Service]
ExecStart=/bin/bash -c "chmod a+rwx -R /sys/fs/cgroup/cpu,cpuacct; chmod a+rwx -R /sys/fs/cgroup/devices"
[Install]
WantedBy=multi-user.target
EOF
systemctl enable dataproc-cgroup-device-permissions
systemctl start dataproc-cgroup-device-permissions
}
function setup_gpu_yarn() {
if [[ ${OS_NAME} == debian ]] || [[ ${OS_NAME} == ubuntu ]]; then
export DEBIAN_FRONTEND=noninteractive
execute_with_retries "apt-get --allow-releaseinfo-change update"
execute_with_retries "apt-get install -y -q pciutils"
elif [[ ${OS_NAME} == rocky ]] ; then
execute_with_retries "dnf -y -q install pciutils"
else
echo "Unsupported OS: '${OS_NAME}'"
exit 1
fi
# This configuration should be run on all nodes
# regardless if they have attached GPUs
configure_yarn
# Detect NVIDIA GPU
if (lspci | grep -q NVIDIA); then
# if this is called without the MIG script then the drivers are not installed
nv_smi="/usr/bin/nvidia-smi"
if (test -f "${nv_smi}" && "${nv_smi}" --query-gpu=mig.mode.current --format=csv,noheader | uniq | wc -l); then
NUM_MIG_GPUS="$($nv_smi --query-gpu=mig.mode.current --format=csv,noheader | uniq | wc -l)"
if [[ $NUM_MIG_GPUS -eq 1 ]]; then
if (/usr/bin/nvidia-smi --query-gpu=mig.mode.current --format=csv,noheader | grep Enabled); then
IS_MIG_ENABLED=1
NVIDIA_SMI_PATH='/usr/local/yarn-mig-scripts/'
MIG_MAJOR_CAPS=`grep nvidia-caps /proc/devices | cut -d ' ' -f 1`
fetch_mig_scripts
fi
fi
fi
if is_debian || is_ubuntu ; then
execute_with_retries "apt-get install -y -q 'linux-headers-$(uname -r)'"
elif is_rocky ; then
echo "kernel devel and headers not required on rocky. installing from binary"
fi
# if mig is enabled drivers would have already been installed
if [[ $IS_MIG_ENABLED -eq 0 ]]; then
install_nvidia_gpu_driver
#Install GPU metrics collection in Stackdriver if needed
if [[ ${INSTALL_GPU_AGENT} == true ]]; then
install_gpu_agent
echo 'GPU metrics agent successfully deployed.'
else
echo 'GPU metrics agent will not be installed.'
fi
configure_gpu_exclusive_mode
fi
configure_yarn_nodemanager
configure_gpu_script
configure_gpu_isolation
elif [[ "${ROLE}" == "Master" ]]; then
configure_yarn_nodemanager
configure_gpu_script
fi
# Restart YARN services if they are running already
for svc in resourcemanager nodemanager; do
if [[ $(systemctl show hadoop-yarn-${svc}.service -p SubState --value) == 'running' ]]; then
systemctl restart hadoop-yarn-${svc}.service
fi
done
}
# Verify if compatible linux distros and secure boot options are used
function check_os_and_secure_boot() {
if is_debian ; then
if ! is_debian10 && ! is_debian11 && ! is_debian12 ; then
echo "Error: The Debian version ($(os_version)) is not supported. Please use a compatible Debian version."
exit 1
fi
elif is_ubuntu ; then
if ! is_ubuntu18 && ! is_ubuntu20 && ! is_ubuntu22 && ! is_ubuntu24 ; then
echo "Error: The Ubuntu version ($(os_version)) is not supported. Please use a compatible Ubuntu version."
exit 1
fi
elif is_rocky ; then
if ! is_rocky8 && ! is_rocky9 ; then
echo "Error: The Rocky Linux version ($(os_version)) is not supported. Please use a compatible Rocky Linux version."
exit 1
fi
fi
if [[ "${SECURE_BOOT}" == "enabled" && $(echo "${DATAPROC_IMAGE_VERSION} <= 2.1" | bc -l) == 1 ]]; then
echo "Error: Secure Boot is not supported before image 2.2. Please disable Secure Boot while creating the cluster."
exit 1
elif [[ "${SECURE_BOOT}" == "enabled" ]] && [[ -z "${PSN}" ]]; then
echo "Secure boot is enabled, but no signing material provided."
echo "Please either disable secure boot or provide signing material as per"
echo "https://github.com/GoogleCloudDataproc/custom-images/tree/master/examples/secure-boot"
return 1
fi
}
function remove_old_backports {
# This script uses 'apt-get update' and is therefore potentially dependent on
# backports repositories which have been archived. In order to mitigate this
# problem, we will remove any reference to backports repos older than oldstable
# https://github.com/GoogleCloudDataproc/initialization-actions/issues/1157
oldoldstable=$(curl -s https://deb.debian.org/debian/dists/oldoldstable/Release | awk '/^Codename/ {print $2}');
oldstable=$(curl -s https://deb.debian.org/debian/dists/oldstable/Release | awk '/^Codename/ {print $2}');
stable=$(curl -s https://deb.debian.org/debian/dists/stable/Release | awk '/^Codename/ {print $2}');
matched_files=( $(test -d /etc/apt && grep -rsil '\-backports' /etc/apt/sources.list*||:) )
if [[ -n "$matched_files" ]]; then
for filename in "${matched_files[@]}"; do
# Fetch from archive.debian.org for ${oldoldstable}-backports
perl -pi -e "s{^(deb[^\s]*) https?://[^/]+/debian ${oldoldstable}-backports }
{\$1 https://archive.debian.org/debian ${oldoldstable}-backports }g" "${filename}"
done
fi
}
function main() {
if is_debian && [[ $(echo "${DATAPROC_IMAGE_VERSION} <= 2.1" | bc -l) == 1 ]]; then
remove_old_backports
fi
check_os_and_secure_boot
setup_gpu_yarn
if [[ "${RUNTIME}" == "SPARK" ]]; then
install_spark_rapids
configure_spark
echo "RAPIDS initialized with Spark runtime"
else
echo "Unsupported RAPIDS Runtime: ${RUNTIME}"
exit 1
fi
for svc in resourcemanager nodemanager; do
if [[ $(systemctl show hadoop-yarn-${svc}.service -p SubState --value) == 'running' ]]; then
systemctl restart hadoop-yarn-${svc}.service
fi
done
if is_debian || is_ubuntu ; then
apt-get clean
fi
}
main