# Common (multistage) args ARG D_OS="rhel9.2" ARG D_ARCH="x86_64" ARG D_CONTAINER_VER="0" ARG D_DOCA_VERSION="2.9.1" ARG D_OFED_VERSION="24.10-1.1.4.0" ARG D_KERNEL_VER="5.14.0-284.32.1.el9_2.x86_64" ARG D_OFED_SRC_DOWNLOAD_PATH="/run/mellanox/src" ARG OFED_SRC_LOCAL_DIR=${D_OFED_SRC_DOWNLOAD_PATH}/MLNX_OFED_SRC-${D_OFED_VERSION} ARG STIG_COMPLIANT=false ARG D_ENABLE_DKMS=false # Final clean image of precompiled driver container ARG D_FINAL_BASE_IMAGE=registry.access.redhat.com/ubi9/ubi:latest # DTK base image (below example for specific kernel headers version) ARG D_BASE_IMAGE="registry.redhat.io/openshift4/driver-toolkit-rhel9:v4.13.0-202309112001.p0.gd719bdc.assembly.stream" # Standart: registry.access.redhat.com/ubi9:latest ARG D_PYTHON_VERSION="36" ARG D_PYTHON="python${D_PYTHON_VERSION}" ################################################################## # Stage: build go binary for entrypoint FROM golang:1.26 AS go_builder # Set GOPROXY if provided ARG GOPROXY ENV GOPROXY=$GOPROXY WORKDIR /workspace COPY entrypoint/go.mod go.mod COPY entrypoint/go.sum go.sum RUN go mod download COPY entrypoint/ . RUN TARGETARCH=${D_ARCH} TARGETOS=linux make build ################################################################## # Stage: Minimal base image update and install common requirements FROM $D_BASE_IMAGE AS base # Inherited global args ARG D_OS # https://redmine.mellanox.com/issues/3528150/issue_history#note-9 RUN if [[ -f /etc/rhsm/rhsm.conf ]] ; then \ sed -i 's#/etc/pki/entitlement#/etc/pki/entitlement-host#g' /etc/rhsm/rhsm.conf ;\ fi RUN set -x && \ # Perform distro update and install prerequirements MAJOR_VER=$(echo ${D_OS} | sed 's/rhel\([0-9]*\).*/\1/') && \ RELEASE_VER=$(echo ${D_OS} | sed 's/rhel//') && \ rpm --import https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-${MAJOR_VER} && \ curl -fsSL -o /tmp/epel-release.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-${MAJOR_VER}.noarch.rpm && \ rpm -ivh /tmp/epel-release.rpm && \ rm -f /tmp/epel-release.rpm && \ # UBI10 ships stale GPG keys (RHEL #7136991); bootstrap rpm first (old RSA sigs), # then import PQC release key 4 — old rpm cannot parse ML-DSA keys (import fails) if [[ "${MAJOR_VER}" -ge 10 ]] ; then \ dnf -y update rpm rpm-libs rpm-build-libs rpm-sign-libs rpm-sequoia \ python3-rpm \ crypto-policies crypto-policies-scripts openssl-libs gnutls \ --exclude=redhat-release --releasever=${RELEASE_VER} && \ rpm --import https://security.access.redhat.com/data/6afedf8f.txt && \ dnf clean all ; \ fi && \ dnf -y update --exclude=redhat-release --releasever=${RELEASE_VER} && \ dnf -y install perl \ # Container functional requirements jq iproute kmod procps-ng udev --releasever=${RELEASE_VER} # NFS userspace tools (mount.nfs) — copied to host at runtime when ENABLE_NFSRDMA=true. # nfs-utils is carried by the entitled RHEL repos only, not by the UBI ones, so bases that # resolve packages without a subscription (the hardened STIG base) cannot supply it. The # runtime copy is best effort, so keep the image buildable instead of requiring the package. RUN set -x && \ RELEASE_VER=$(echo ${D_OS} | sed 's/rhel//') && \ dnf -y install nfs-utils --releasever=${RELEASE_VER} || \ echo "WARNING: nfs-utils unavailable, mount.nfs will not be provisioned to the host when ENABLE_NFSRDMA=true" COPY --from=go_builder /workspace/build/entrypoint /root/entrypoint WORKDIR /root ADD ./entrypoint.sh /root/entrypoint.sh ADD ./loader.sh /root/loader.sh ENTRYPOINT ["/root/loader.sh"] ############################################################################################## # Stage: Download NVIDIA driver sources and install src driver container packages requirements FROM base AS driver-src # Inherited global args ARG D_DOCA_VERSION ARG D_OFED_VERSION ARG D_CONTAINER_VER ARG D_OFED_SRC_DOWNLOAD_PATH ARG STIG_COMPLIANT ARG D_OS # Stage args ARG D_OFED_BASE_URL="https://linux.mellanox.com/public/repo/doca/${D_DOCA_VERSION}/SOURCES/mlnx_ofed" ARG D_OFED_SRC_TYPE="" # Up to DOCA 3.5 the sources ship as MLNX_OFED_SRC-.tgz, which bundles # install.pl; from 3.6 on they ship as DOCA_BASE_SRC--.tar.gz # with no installer. This default composes the legacy name because the 3.6 build number cannot # be derived from D_DOCA_VERSION, so 3.6+ callers must pass the exact archive name or path via # D_OFED_URL_PATH. Both layouts are detected and handled below. ARG D_OFED_SRC_ARCHIVE="MLNX_OFED_SRC-${D_OFED_SRC_TYPE}${D_OFED_VERSION}.tgz" ARG D_OFED_URL_PATH="${D_OFED_BASE_URL}/${D_OFED_SRC_ARCHIVE}" # although argument name says URL, local compressed archives may also be used (intended for internal use) ENV NVIDIA_NIC_DRIVER_VER=${D_OFED_VERSION} ENV NVIDIA_NIC_CONTAINER_VER=${D_CONTAINER_VER} ENV NVIDIA_NIC_DRIVER_PATH="${D_OFED_SRC_DOWNLOAD_PATH}/MLNX_OFED_SRC-${D_OFED_VERSION}" WORKDIR /root # /etc/pki/entitlement-host is normally a symlink to # /run/secrets/etc-pki-entitlement; when no host entitlement secret is # mounted it's dangling, and buildah/crun's RUN --mount (unlike Docker's # BuildKit) refuses to bind-mount onto a dangling symlink. Swap in a real # directory for the mount and restore the symlink afterward (below). RUN if [ -L /etc/pki/entitlement-host ]; then mv /etc/pki/entitlement-host /etc/pki/entitlement-host.orig; fi && \ mkdir -p /etc/pki/entitlement-host # Mount RHEL subscription certificates so docker build can access subscription-gated repos. # Pass the host entitlement directory via: --build-context rhsm=/etc/pki/entitlement # (buildah inherits host entitlements automatically; docker requires explicit bind mount) RUN --mount=type=bind,from=rhsm,source=.,target=/etc/pki/entitlement-host \ set -x && \ RELEASE_VER=$(echo ${D_OS} | sed 's/rhel//') && \ # Install prerequirements dnf install -y --releasever=${RELEASE_VER} \ # Driver build requirements autoconf python3-devel ethtool automake pciutils libtool hostname dracut \ # Build tools needed by install.pl at runtime (avoids subscription-gated dnf at container start) elfutils-libelf-devel kernel-rpm-macros numactl-libs lsof rpm-build patch \ gcc make bison flex \ # DKMS is always installed in the dynamic (sources) image; install.pl is invoked at # container runtime and may build DKMS-enabled packages. Inherited by driver-builder too. dkms # Only restore if we actually swapped in a real directory above; leaves any # base image where entitlement-host was already a real directory untouched. RUN if [ -L /etc/pki/entitlement-host.orig ]; then \ rmdir /etc/pki/entitlement-host && mv /etc/pki/entitlement-host.orig /etc/pki/entitlement-host; \ fi # Download NVIDIA NIC driver RUN mkdir -p ${D_OFED_SRC_DOWNLOAD_PATH} WORKDIR ${D_OFED_SRC_DOWNLOAD_PATH} # Fixed destination name: ADD unpacks a local archive into a directory of this name, and # writes a plain file of this name when fetching a URL. The archive's own top-level # directory is discovered afterwards, so its exact version is not needed here. ADD ${D_OFED_URL_PATH} doca_base_src # Up to DOCA 3.5 the archive extracts to MLNX_OFED_SRC-/ complete with # install.pl, landing directly at NVIDIA_NIC_DRIVER_PATH with no further work needed. # DOCA 3.6 deprecated install.pl and ships a flat DOCA_BASE_SRC-* directory of SRPMs # instead, so that layout is reshaped into the tree the build expects and the vendored # scripts are staged into it. See vendor/mlnx_ofed/README.md. COPY vendor/mlnx_ofed /opt/mlnx_ofed RUN set -eux && \ if [ -d doca_base_src ]; then \ mv doca_base_src/* . && rmdir doca_base_src; \ else \ tar -xzf doca_base_src && rm -f doca_base_src; \ fi && \ src_dir=$(find . -maxdepth 1 -type d -name 'DOCA_BASE_SRC-*' | head -1) && \ if [ -n "${src_dir}" ]; then \ /opt/mlnx_ofed/stage_src_tree.sh "${src_dir}" "${NVIDIA_NIC_DRIVER_PATH}" rpm; \ elif [ -f "${NVIDIA_NIC_DRIVER_PATH}/install.pl" ]; then \ echo "Using legacy source tree with bundled install.pl: ${NVIDIA_NIC_DRIVER_PATH}"; \ else \ echo "error: ${D_OFED_URL_PATH} yielded neither a DOCA_BASE_SRC-* directory nor ${NVIDIA_NIC_DRIVER_PATH}/install.pl" >&2; \ exit 1; \ fi WORKDIR /root ADD ./entrypoint.sh /root/entrypoint.sh ADD ./dtk_nic_driver_build.sh /root/dtk_nic_driver_build.sh # dockerfile COPY instruction limitation requires adding a file - even if it's an empty/dummy file COPY stig-fixer.sh /tmp/stig-fixer.sh # run STIG fixer script for STIG complaint image builds RUN set -x && \ if [ "$STIG_COMPLIANT" = "true" ]; then \ chmod +x /tmp/stig-fixer.sh && \ /tmp/stig-fixer.sh ; \ fi && \ rm -f /tmp/stig-fixer.sh ENTRYPOINT ["/root/loader.sh"] CMD ["sources"] LABEL doca-version=${D_DOCA_VERSION} LABEL ofed-version=${D_OFED_VERSION} ##################### # Stage: Build driver FROM driver-src AS driver-builder # Inherited global args ARG D_OS ARG D_KERNEL_VER ARG OFED_SRC_LOCAL_DIR ARG D_ENABLE_DKMS ARG D_BUILD_EXTRA_ARGS # Build driver - conditionally enable DKMS (same pattern as Ubuntu_Dockerfile) # When D_ENABLE_DKMS=true, install.pl creates DKMS-enabled RPMs (source in /usr/src/, dkms.conf) # When D_ENABLE_DKMS=false, install.pl creates static kernel modules (--without-dkms) # Only the exact string "true" enables DKMS; reject other values so they cannot produce # non-DKMS packages while the runtime bool parser treats them as enabled. # Build toolchain (gcc, make, autoconf, rpm-build, dkms) is inherited from driver-src. RUN set -x && \ case "$D_ENABLE_DKMS" in \ true|false) ;; \ *) echo "error: D_ENABLE_DKMS must be exactly 'true' or 'false', got '$D_ENABLE_DKMS'" >&2; exit 1 ;; \ esac && \ if [ "$D_ENABLE_DKMS" = "true" ]; then \ ${OFED_SRC_LOCAL_DIR}/install.pl --without-depcheck --distro ${D_OS} --kernel ${D_KERNEL_VER} --kernel-sources /lib/modules/${D_KERNEL_VER}/build --kernel-only --build-only --without-iser --without-srp --without-isert --without-knem --without-xpmem --without-xpmem-modules --without-xpmem-dkms --with-mlnx-tools --copy-ifnames-udev ${D_BUILD_EXTRA_ARGS}; \ else \ ${OFED_SRC_LOCAL_DIR}/install.pl --without-depcheck --distro ${D_OS} --kernel ${D_KERNEL_VER} --kernel-sources /lib/modules/${D_KERNEL_VER}/build --kernel-only --build-only --without-iser --without-srp --without-isert --without-knem --without-xpmem --without-xpmem-modules --with-mlnx-tools --copy-ifnames-udev --without-dkms ${D_BUILD_EXTRA_ARGS}; \ fi ################################### # Stage: Install precompiled driver ARG D_FINAL_BASE_IMAGE FROM $D_FINAL_BASE_IMAGE AS precompiled # Inherited global args ARG D_OS ARG D_ARCH ARG D_KERNEL_VER ARG D_OFED_VERSION ARG D_CONTAINER_VER ARG OFED_SRC_LOCAL_DIR ARG D_ENABLE_DKMS ENV NVIDIA_NIC_DRIVER_VER=${D_OFED_VERSION} ENV NVIDIA_NIC_DRIVER_PATH="" ENV NVIDIA_NIC_CONTAINER_VER=${D_CONTAINER_VER} # Canonical true/false matching install.pl ([ "$D_ENABLE_DKMS" = "true" ]). # Reject TRUE/1/yes so Go cannot parse them as enabled against non-DKMS packages. RUN case "$D_ENABLE_DKMS" in \ true|false) ;; \ *) echo "error: D_ENABLE_DKMS must be exactly 'true' or 'false', got '$D_ENABLE_DKMS'" >&2; exit 1 ;; \ esac ENV NVIDIA_NIC_DRIVER_DKMS_ENABLED=${D_ENABLE_DKMS} COPY --from=driver-builder ${OFED_SRC_LOCAL_DIR}/RPMS/redhat-release-*/${D_ARCH}/*.rpm /root/ RUN rpm -ivh --nodeps /root/*.rpm RUN set -x && \ RELEASE_VER=$(echo ${D_OS} | sed 's/rhel//') && \ # MOFED functional requirements dnf install -y pciutils hostname udev ethtool \ # Container functional requirements jq iproute kmod procps-ng udev --releasever=${RELEASE_VER} && \ # DKMS and kernel module build tools when DKMS enabled (runtime dkms add/build/install) # This stage is FROM D_FINAL_BASE_IMAGE (e.g. ubi9/ubi), which ships full dnf, # not the microdnf that motivates the same curl+rpm swap in the base stage # above; kept identical here for consistency and because it is equally # correct under full dnf. if [ "$D_ENABLE_DKMS" = "true" ]; then \ MAJOR_VER=$(echo ${D_OS} | sed 's/rhel\([0-9]*\).*/\1/') && \ curl -fsSL -o /tmp/epel-release.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-${MAJOR_VER}.noarch.rpm && \ rpm -ivh /tmp/epel-release.rpm && \ rm -f /tmp/epel-release.rpm && \ dnf install -y dkms gcc make bison flex elfutils-libelf-devel --releasever=${RELEASE_VER}; \ fi # NFS userspace tools (mount.nfs) — copied to host at runtime when ENABLE_NFSRDMA=true. # Best effort for the same reason as in the base stage: only the entitled RHEL repos carry it. RUN set -x && \ RELEASE_VER=$(echo ${D_OS} | sed 's/rhel//') && \ dnf -y install nfs-utils --releasever=${RELEASE_VER} || \ echo "WARNING: nfs-utils unavailable, mount.nfs will not be provisioned to the host when ENABLE_NFSRDMA=true" # Prevent modprobe from giving a WARNING about missing files RUN touch /lib/modules/${D_KERNEL_VER}/modules.order /lib/modules/${D_KERNEL_VER}/modules.builtin && \ # Introduce installed kernel modules depmod ${D_KERNEL_VER} WORKDIR /root ADD ./entrypoint.sh /root/entrypoint.sh ADD ./loader.sh /root/loader.sh COPY --from=go_builder /workspace/build/entrypoint /root/entrypoint ADD ./dtk_nic_driver_build.sh /root/dtk_nic_driver_build.sh ENTRYPOINT ["/root/loader.sh"] CMD ["precompiled"]