Skip to content
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

Refactor image builds to put all of the hardware and platform selection logic into one place. #720

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions container-images/asahi/Containerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM fedora:41
FROM quay.io/fedora/fedora:41

COPY ../scripts /scripts
RUN chmod +x /scripts/*.sh && \
/scripts/build_llama_and_whisper.sh "asahi"
COPY build_llama_and_whisper.sh /

RUN chmod +x /build_llama_and_whisper.sh ; \
/build_llama_and_whisper.sh asahi
113 changes: 113 additions & 0 deletions container-images/build_llama_and_whisper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env bash

# Bash does not easily pass arrays as a single arg to a function. So, make this a global var in the script.
Copy link
Collaborator

@ericcurtin ericcurtin Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to pass local variables as parameters in bash, they propagate to called functions, they just don't have global scope if that makes sense.

CMAKE_FLAGS=""
LLAMA_CPP_CMAKE_FLAGS=""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we need a global variables, could we put local variables in main instead? Trying to keep everything scoped.

Copy link
Collaborator

@ericcurtin ericcurtin Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The styling changes aren't a blocker for merge, but why change it?

We don't put "function" in front of the functions in any of the other shell scripts in the project, we don't use camelCase in any of the other shell scripts either.

Now this is the odd shell script in terms of consistency 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't want to be the oddball...

WHISPER_CPP_CMAKE_FLAGS=""

function cmakeCheckWarnings() {
awk -v rc=0 '/CMake Warning:/ { rc=1 } 1; END {exit rc}'
}

function cloneAndBuild() {
local git_repo=${1}
local git_sha=${2}
local install_prefix=${3}
local work_dir=$(mktemp -d)

git clone ${git_repo} ${work_dir}
cd ${work_dir}
git submodule update --init --recursive
git reset --hard ${git_sha}
cmake -B build ${CMAKE_FLAGS[@]} 2>&1 | cmakeCheckWarnings
cmake --build build --config Release -j$(nproc) -v 2>&1 | cmakeCheckWarnings
cmake --install build --prefix ${install_prefix} 2>&1 | cmakeCheckWarnings
cd -
rm -rf ${work_dir}
}

function dnfPrepUbi() {

local url="https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm"
local uname_m=$(uname -m)

dnf install -y ${url}
crb enable # this is in epel-release, can only install epel-release via url
dnf copr enable -y slp/mesa-krunkit epel-9-${uname_m}
url="https://mirror.stream.centos.org/9-stream/AppStream/${uname_m}/os/"
dnf config-manager --add-repo ${url}
url="http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-Official"
curl --retry 8 --retry-all-errors -o /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Official ${url}
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Official
}

function main() {
set -ex

local container_image=${1}
local install_prefix=/usr
local package_list
local whisper_cpp_sha=${WHISPER_CPP_SHA:-8a9ad7844d6e2a10cddf4b92de4089d7ac2b14a9}
local llama_cpp_sha=${LLAMA_CPP_SHA:-aa6fb1321333fae8853d0cdc26bcb5d438e650a1}
local common_rpms=("python3" "python3-pip" "python3-argcomplete" "python3-dnf-plugin-versionlock" "gcc-c++" "cmake" "vim" "procps-ng" "git" "dnf-plugins-core" "libcurl-devel")
local vulkan_rpms=("vulkan-headers" "vulkan-loader-devel" "vulkan-tools" "spirv-tools" "glslc" "glslang")
local intel_rpms=("intel-oneapi-mkl-sycl-devel" "intel-oneapi-dnnl-devel" "intel-oneapi-compiler-dpcpp-cpp" "intel-level-zero" "oneapi-level-zero" "oneapi-level-zero-devel" "intel-compute-runtime")

LLAMA_CPP_CMAKE_FLAGS+=("-DGGML_CCACHE=OFF" "-DGGML_NATIVE=OFF" "-DBUILD_SHARED_LIBS=NO" "-DLLAMA_CURL=ON")
Copy link
Collaborator

@ericcurtin ericcurtin Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't right, we want BUILD_SHARE_LIBS for llama.cpp but not whisper.cpp

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think -DBUILD_SHARED_LIBS breaks for the intel-gpu build. I'll double check that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I bet that's why the rocm image got really big... -DBUILD_SHARED_LIBS=NO. That resulted in some huge executables in /usr/bin...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think you got it, -DBUILD_SHARED_LIBS=NO end up in the libraries being duplicated in every single binary produced.

The reason we do it for whisper.cpp is it uses some of the same libraries, with the same names, but with different versions sometimes, so one install will replace the library of the other, breaking whichever was installed first.

whisper.cpp is a small project so we statically link that one to fix the issue.

WHISPER_CPP_CMAKE_FLAGS+=("-DGGML_CCACHE=OFF" "-DGGML_NATIVE=OFF" "-DBUILD_SHARED_LIBS=NO")

case ${container_image} in
ramalama)
dnf --enablerepo=ubi-9-appstream-rpms install -y "${common_rpms[@]}"
dnfPrepUbi
dnf --enablerepo=ubi-9-appstream-rpms install -y mesa-vulkan-drivers "${vulkan_rpms[@]}"
LLAMA_CPP_CMAKE_FLAGS+=("-DGGML_KOMPUTE=ON" "-DKOMPUTE_OPT_DISABLE_VULKAN_VERSION_CHECK=ON")
;;
rocm)
dnf --enablerepo=ubi-9-appstream-rpms install -y "${common_rpms[@]}"
dnfPrepUbi
dnf install -y "${vulkan_rpms[@]}" rocm-dev hipblas-devel rocblas-devel
LLAMA_CPP_CMAKE_FLAGS+=("-DGGML_HIP=ON" "-DAMDGPU_TARGETS=${AMDGPU_TARGETS:-gfx1010,gfx1030,gfx1032,gfx1100,gfx1101,gfx1102}")
WHISPER_CPP_CMAKE_FLAGS+=("-DGGML_HIP=ON" "-DAMDGPU_TARGETS=${AMDGPU_TARGETS:-gfx1010,gfx1030,gfx1032,gfx1100,gfx1101,gfx1102}")
;;
cuda)
dnf install -y "${common_rpms[@]}" gcc-toolset-12
. /opt/rh/gcc-toolset-12/enable
LLAMA_CPP_CMAKE_FLAGS+=("-DGGML_CUDA=ON" "-DCMAKE_EXE_LINKER_FLAGS=-Wl,--allow-shlib-undefined")
WHISPER_CPP_CMAKE_FLAGS+=("-DGGML_CUDA=ON" "-DCMAKE_EXE_LINKER_FLAGS=-Wl,--allow-shlib-undefined")
install_prefix=/llama-cpp
;;
vulkan)
dnf --enablerepo=ubi-9-appstream-rpms install -y "${common_rpms[@]}"
dnfPrepUbi
dnf --enablerepo=ubi-9-appstream-rpms install -y mesa-vulkan-drivers "${vulkan_rpms[@]}"
LLAMA_CPP_CMAKE_FLAGS+=("-DGGML_VULKAN=1")
WHISPER_CPP_CMAKE_FLAGS+=("-DGGML_VULKAN=1")
;;
asahi)
dnf copr enable -y @asahi/fedora-remix-branding
dnf install -y asahi-repos
dnf install -y mesa-vulkan-drivers "${vulkan_rpms[@]}" "${common_rpms[@]}"
LLAMA_CPP_CMAKE_FLAGS+=("-DGGML_VULKAN=1")
WHISPER_CPP_CMAKE_FLAGS+=("-DGGML_VULKAN=1")
;;
intel-gpu)
dnf install -y ${common_rpms[@]} ${intel_rpms[@]}
LLAMA_CPP_CMAKE_FLAGS+=("-DGGML_SYCL=ON" "-DCMAKE_C_COMPILER=icx" "-DCMAKE_CXX_COMPILER=icpx")
WHISPER_CPP_CMAKE_FLAGS+=("-DGGML_SYCL=ON" "-DCMAKE_C_COMPILER=icx" "-DCMAKE_CXX_COMPILER=icpx")
install_prefix=/llama-cpp
source /opt/intel/oneapi/setvars.sh
;;
esac

CMAKE_FLAGS+=("${WHISPER_CPP_CMAKE_FLAGS[@]}")
cloneAndBuild https://github.com/ggerganov/whisper.cpp ${whisper_cpp_sha} ${install_prefix}
CMAKE_FLAGS+=(${LLAMA_CPP_CMAKE_FLAGS[@]})
cloneAndBuild https://github.com/ggerganov/llama.cpp ${llama_cpp_sha} ${install_prefix}
dnf -y clean all
rm -rf /var/cache/*dnf* /opt/rocm-*/lib/*/library/*gfx9*
Copy link
Collaborator

@ericcurtin ericcurtin Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was where the bulk of the trimming was done for rocm in the past. Not sure why it's ballooned again.

I'd revert this shell script back to it's original state and introduce just enough changes to get "intel-gpu" working with this script. There's no need to rewrite the whole script, it's asking for breakages.

It's not making a reviewers job easy re-writing the whole thing 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. It was a wild hair 🤓

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intent is to see if we can prevent the build script from becoming a Rube Goldberg machine. The more logic that we have to drop into places based on different builds may get unmanageable. IMO, it's already a bit challenging to read. Natural entropy of code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure my attempt is much easier to read either though...

ldconfig # needed for libraries

}

main "$@"
10 changes: 5 additions & 5 deletions container-images/cuda/Containerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Base image with CUDA for compilation
FROM docker.io/nvidia/cuda:12.8.0-devel-ubi9 AS builder

COPY ../scripts /scripts
RUN chmod +x /scripts/*.sh && \
/scripts/build_llama_and_whisper.sh "cuda"
COPY build_llama_and_whisper.sh /

RUN chmod +x /build_llama_and_whisper.sh ; \
/build_llama_and_whisper.sh cuda

# Final runtime image
FROM docker.io/nvidia/cuda:12.8.0-runtime-ubi9
Expand All @@ -12,5 +13,4 @@ RUN dnf install -y python3 && \
dnf clean all && rm -rf /var/cache/*dnf*

# Copy the entire installation directory from the builder
COPY --from=builder /tmp/install /usr

COPY --from=builder /llama-cpp /usr
31 changes: 11 additions & 20 deletions container-images/intel-gpu/Containerfile
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
FROM quay.io/fedora/fedora:41 as builder

COPY intel-gpu/oneAPI.repo /etc/yum.repos.d/
COPY build_llama_and_whisper.sh /

RUN dnf install -y intel-opencl g++ cmake git tar libcurl-devel intel-oneapi-mkl-sycl-devel intel-oneapi-dnnl-devel intel-oneapi-compiler-dpcpp-cpp ; \
git clone https://github.com/ggerganov/llama.cpp.git -b b4523 ; \
cd llama.cpp ; \
mkdir -p build ; \
cd build ; \
source /opt/intel/oneapi/setvars.sh ; \
cmake .. -DGGML_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DLLAMA_CURL=ON -DGGML_CCACHE=OFF -DGGML_NATIVE=OFF ; \
cmake --build . --config Release -j -v ; \
cmake --install . --prefix /llama-cpp
RUN chmod +x /build_llama_and_whisper.sh ; \
/build_llama_and_whisper.sh intel-gpu

FROM quay.io/fedora/fedora:41

ENV LD_LIBRARY_PATH="/usr/local/lib64:/usr/local/lib/:/opt/intel/oneapi/mkl/2025.0/lib:/opt/intel/oneapi/compiler/2025.0/opt/compiler/lib:/opt/intel/oneapi/compiler/2025.0/lib/clang/19/lib:/opt/intel/oneapi/compiler/2025.0/lib:/opt/intel/oneapi/umf/0.9/lib:/opt/intel/oneapi/tbb/2022.0/lib:/opt/intel/oneapi/tcm/1.2/lib:/opt/intel/oneapi/redist/opt/compiler/lib:/opt/intel/oneapi/redist/lib/clang/19/lib:/opt/intel/oneapi/redist/lib:/opt/intel/oneapi/mkl/2025.0/lib:/opt/intel/oneapi/compiler/2025.0/opt/compiler/lib:/opt/intel/oneapi/compiler/2025.0/lib/clang/19/lib:/opt/intel/oneapi/compiler/2025.0/lib:/opt/intel/oneapi/umf/0.9/lib:/opt/intel/oneapi/tbb/2022.0/lib:/opt/intel/oneapi/tcm/1.2/lib:/opt/intel/oneapi/redist/opt/compiler/lib:/opt/intel/oneapi/redist/lib/clang/19/lib:/opt/intel/oneapi/redist/lib"

COPY --from=builder /llama-cpp/bin/ /usr/local/bin/
COPY --from=builder /llama-cpp/lib/ /usr/local/lib/
COPY --from=builder /llama-cpp/lib64/ /usr/local/lib64/
COPY --from=builder /llama-cpp/include/ /usr/local/include/
COPY --from=builder /llama-cpp/ /usr/
COPY intel-gpu/oneAPI.repo /etc/yum.repos.d/
COPY --chown=0:0 intel-gpu/entrypoint.sh /

RUN dnf install -y intel-opencl libcurl lspci clinfo intel-oneapi-runtime-compilers intel-oneapi-mkl-core intel-oneapi-mkl-sycl-blas intel-oneapi-runtime-dnnl ; \
RUN dnf install -y procps-ng python3 python3-pip python3-devel intel-level-zero oneapi-level-zero intel-compute-runtime libcurl lspci clinfo intel-oneapi-runtime-compilers intel-oneapi-mkl-core intel-oneapi-mkl-sycl-blas intel-oneapi-runtime-dnnl ; \
chown 0:0 /etc/passwd ; \
chown 0:0 /etc/group ; \
chmod g=u /etc/passwd /etc/group ; \
useradd -u 1000 -g render -G video -s /bin/bash -d /home/llama-user llama-user
chmod g=u /etc/passwd /etc/group /home ; \
chmod +x /entrypoint.sh

USER 10000

USER 1000
WORKDIR /home/llama-user
ENTRYPOINT ["/entrypoint.sh"]
46 changes: 46 additions & 0 deletions container-images/intel-gpu/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash

if [ -z ${HOME} ]
then
export HOME=/home/llama-user
fi

# Create Home directory
if [ ! -d "${HOME}" ]
then
mkdir -p "${HOME}"
fi

# Create User ID
if ! whoami &> /dev/null
then
if [ -w /etc/passwd ] && [ -w /etc/group ]
then
echo "${USER_NAME:-llama-user}:x:$(id -u):0:${USER_NAME:-llama-user} user:${HOME}:/bin/bash" >> /etc/passwd
echo "${USER_NAME:-llama-user}:x:$(id -u):" >> /etc/group
render_group="$(cat /etc/group | grep 'render:x')"
video_group="$(cat /etc/group | grep 'video:x')"
render_group_new="${render_group}${USER_NAME:-llama-user}"
video_group_new="${video_group}${USER_NAME:-llama-user}"
sed "s|${render_group}|${render_group_new}|g" /etc/group > /tmp/group
cat /tmp/group > /etc/group
sed "s|${video_group}|${video_group_new}|g" /etc/group > /tmp/group
cat /tmp/group > /etc/group
fi
fi

# Configure Z shell
if [ ! -f ${HOME}/.zshrc ]
then
(echo "source /opt/intel/oneapi/setvars.sh") > ${HOME}/.zshrc
fi

# Configure Bash shell
if [ ! -f ${HOME}/.bashrc ]
then
(echo "source /opt/intel/oneapi/setvars.sh") > ${HOME}/.bashrc
fi

source /opt/intel/oneapi/setvars.sh

exec "$@"
7 changes: 4 additions & 3 deletions container-images/ramalama/Containerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
FROM registry.access.redhat.com/ubi9/ubi:9.5-1736404036

COPY ../scripts /scripts
RUN chmod +x /scripts/*.sh && \
/scripts/build_llama_and_whisper.sh "ramalama"
COPY build_llama_and_whisper.sh /

RUN chmod +x /build_llama_and_whisper.sh ; \
/build_llama_and_whisper.sh ramalama
12 changes: 7 additions & 5 deletions container-images/rocm/Containerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
FROM registry.access.redhat.com/ubi9/ubi:9.5-1736404036
ARG AMDGPU_TARGETS=
ENV AMDGPU_TARGETS=${AMDGPU_TARGETS}

COPY rocm/amdgpu.repo /etc/yum.repos.d/
COPY rocm/rocm.repo /etc/yum.repos.d/
COPY scripts /scripts

ARG AMDGPU_TARGETS=
ENV AMDGPU_TARGETS=${AMDGPU_TARGETS}
RUN chmod +x /scripts/*.sh && \
/scripts/build_llama_and_whisper.sh "rocm"
COPY build_llama_and_whisper.sh /

RUN chmod +x /build_llama_and_whisper.sh ; \
/build_llama_and_whisper.sh rocm

127 changes: 0 additions & 127 deletions container-images/scripts/build_llama_and_whisper.sh

This file was deleted.

Loading
Loading