Skip to content

Commit 9cf8eb6

Browse files
authored
RHOAIENG-31715: add s390x Support for TrustyAI Notebook (opendatahub-io#2568)
* Enabled TrustyAI Notebook for s390x Signed-off-by: Nishan Acharya <Nishan.Acharya@ibm.com> * Address Comments Signed-off-by: Nishan Acharya <Nishan.Acharya@ibm.com> * Removed EPEL from output stage Signed-off-by: Nishan Acharya <Nishan.Acharya@ibm.com> * Add s390x label for konflux Signed-off-by: Nishan Acharya <Nishan.Acharya@ibm.com> --------- Signed-off-by: Nishan Acharya <Nishan.Acharya@ibm.com>
1 parent 46bbdf6 commit 9cf8eb6

File tree

5 files changed

+205
-96
lines changed

5 files changed

+205
-96
lines changed

.tekton/odh-workbench-jupyter-trustyai-cpu-py312-ubi9-pull-request.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ spec:
3838
- linux/x86_64
3939
- linux-m2xlarge/arm64
4040
- linux/ppc64le
41+
- linux/s390x
4142
- name: dockerfile
4243
value: jupyter/trustyai/ubi9-python-3.12/Dockerfile.cpu
4344
- name: path-context

jupyter/trustyai/ubi9-python-3.12/Dockerfile.cpu

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ RUN curl -Lo mongodb-cli-mongocli-v${MONGOCLI_VERSION}.zip https://github.com/mo
1818
RUN unzip ./mongodb-cli-mongocli-v${MONGOCLI_VERSION}.zip
1919
RUN cd ./mongodb-cli-mongocli-v${MONGOCLI_VERSION}/ && \
2020
CGO_ENABLED=1 GOOS=linux go build -a -tags strictfipsruntime -o /tmp/mongocli ./cmd/mongocli/
21+
2122
####################
2223
# wheel-cache-base #
2324
####################
@@ -35,7 +36,7 @@ COPY ${TRUSTYAI_SOURCE_CODE}/devel_env_setup.sh .
3536

3637
RUN --mount=type=cache,target=/root/.cache/uv \
3738
pip install --no-cache-dir uv && \
38-
# the devel script is ppc64le specific - sets up build-time dependencies
39+
# the devel script is ppc64le and s390x specific - sets up build-time dependencies
3940
source ./devel_env_setup.sh && \
4041
# This may have to download and compile some dependencies, and as we don't lock requirements from `build-system.requires`,
4142
# we often don't know the correct hashes and `--require-hashes` would therefore fail on non amd64, where building is common.
@@ -121,7 +122,7 @@ WORKDIR /opt/app-root/bin
121122
USER root
122123

123124
# Install useful OS packages
124-
RUN dnf install -y jq unixODBC postgresql git-lfs libsndfile libxcrypt-compat && dnf clean all && rm -rf /var/cache/yum
125+
RUN dnf install -y jq unixODBC unixODBC-devel postgresql git-lfs libsndfile libxcrypt-compat && dnf clean all && rm -rf /var/cache/yum
125126

126127
# Copy dynamically-linked mongocli built in earlier build stage
127128
COPY --from=mongocli-builder /tmp/mongocli /opt/app-root/bin/
@@ -142,7 +143,7 @@ FROM jupyter-datascience AS jupyter-trustyai
142143
ARG DATASCIENCE_SOURCE_CODE=jupyter/datascience/ubi9-python-3.12
143144
ARG TRUSTYAI_SOURCE_CODE=jupyter/trustyai/ubi9-python-3.12
144145

145-
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib
146+
ENV LD_LIBRARY_PATH=/usr/lib:/usr/lib64${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
146147

147148
LABEL name="odh-notebook-jupyter-trustyai-ubi9-python-3.12" \
148149
summary="Jupyter trustyai notebook image for ODH notebooks" \
@@ -157,28 +158,40 @@ LABEL name="odh-notebook-jupyter-trustyai-ubi9-python-3.12" \
157158
USER 0
158159

159160
# Install jre that is needed to run the trustyai library
161+
# Also install runtime libraries for s390x/ppc64le
160162
RUN INSTALL_PKGS="java-17-openjdk" && \
163+
ARCH=$(uname -m) && \
164+
if [ "$ARCH" = "s390x" ] || [ "$ARCH" = "ppc64le" ]; then \
165+
# Add runtime libraries needed for s390x/ppc64le (OpenBLAS for PyTorch/NumPy)
166+
INSTALL_PKGS="$INSTALL_PKGS openblas openblas-threads"; \
167+
fi && \
161168
dnf install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \
162-
dnf -y clean all --enablerepo='*'
169+
dnf -y clean all --enablerepo='*' && \
170+
# Create symlink for compatibility (openblas package provides libopenblasp.so.0 but PyTorch looks for libopenblas.so.0)
171+
if [ "$ARCH" = "s390x" ] || [ "$ARCH" = "ppc64le" ]; then \
172+
ln -sf /usr/lib64/libopenblasp.so.0 /usr/lib64/libopenblas.so.0 && \
173+
ldconfig; \
174+
fi
163175

164176
# Install Python packages and Jupyterlab extensions from requirements.txt
165177
COPY ${TRUSTYAI_SOURCE_CODE}/pylock.toml ./
166178

167179
# install openblas for ppc64le
168180
RUN --mount=type=cache,from=whl-cache,source=/root/OpenBLAS/,target=/OpenBlas/,rw \
169-
bash -c ' \
170-
if [[ $(uname -m) == "ppc64le" ]]; then \
181+
bash -c 'ARCH=$(uname -m); \
182+
if [ "$ARCH" = "ppc64le" ]; then \
171183
PREFIX=/usr/ make install -C /OpenBlas; \
172-
fi '
184+
fi'
173185

174186
# Install packages and cleanup
175187
# install packages as USER 0 (this will allow us to consume uv cache)
176188
RUN --mount=type=cache,from=whl-cache,source=/wheelsdir/,target=/wheelsdir/,rw \
177189
--mount=type=cache,target=/root/.cache/uv \
178-
bash -c ' \
179-
if [[ $(uname -m) == "ppc64le" ]]; then \
190+
bash -c 'ARCH=$(uname -m); \
191+
if [ "$ARCH" = "ppc64le" ] || [ "$ARCH" = "s390x" ]; then \
180192
UV_LINK_MODE=copy uv pip install /wheelsdir/*.whl accelerate --cache-dir /root/.cache/uv; \
181-
fi '
193+
fi'
194+
182195
RUN --mount=type=cache,target=/root/.cache/uv \
183196
echo "Installing softwares and packages" && \
184197
# we can ensure wheels are consumed from the cache only by restricting internet access for uv install with '--offline' flag

jupyter/trustyai/ubi9-python-3.12/devel_env_setup.sh

Lines changed: 143 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,25 @@
22
set -eoux pipefail
33

44
#####################################################################################################
5-
# This script is expected to be run on ppc64le hosts as `root` #
5+
# This script is expected to be run on ppc64le and s390x hosts as `root` #
66
# It installs the required build-time dependencies for python wheels #
77
# OpenBlas is built from source (instead of distro provided) with recommended flags for performance #
88
#####################################################################################################
9+
10+
# Initialize environment variables with default values
11+
if [[ $(uname -m) == "s390x" ]]; then
12+
export GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=1
13+
export CFLAGS="-O3"
14+
export CXXFLAGS="-O3"
15+
else
16+
# For other architectures, set custom library paths
17+
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-/usr/local/lib64:/usr/local/lib}
18+
export PKG_CONFIG_PATH=${PKG_CONFIG_PATH:-/usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig}
19+
fi
20+
921
WHEELS_DIR=/wheelsdir
1022
mkdir -p ${WHEELS_DIR}
11-
if [[ $(uname -m) == "ppc64le" ]]; then
23+
if [[ $(uname -m) == "ppc64le" ]] || [[ $(uname -m) == "s390x" ]]; then
1224
CURDIR=$(pwd)
1325

1426
# install development packages
@@ -17,31 +29,64 @@ if [[ $(uname -m) == "ppc64le" ]]; then
1729
dnf install -y fribidi-devel gcc-toolset-13 lcms2-devel libimagequant-devel patchelf \
1830
libraqm-devel openjpeg2-devel tcl-devel tk-devel unixODBC-devel
1931

20-
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
32+
# Install build tools and libraries needed for compiling PyTorch/PyArrow
33+
if [[ $(uname -m) == "s390x" ]]; then
34+
dnf install -y gcc gcc-gfortran gcc-c++ make cmake ninja-build \
35+
autoconf automake libtool pkg-config \
36+
python3.12-devel python3-devel pybind11-devel \
37+
openssl-devel openblas-devel \
38+
libjpeg-devel zlib-devel libtiff-devel freetype-devel \
39+
lcms2-devel libwebp-devel \
40+
fribidi-devel openjpeg2-devel libraqm-devel libimagequant-devel \
41+
tcl-devel tk-devel unixODBC-devel \
42+
git tar wget unzip
43+
else
44+
# ppc64le packages
45+
dnf install -y fribidi-devel lcms2-devel libimagequant-devel \
46+
libraqm-devel openjpeg2-devel tcl-devel tk-devel unixODBC-devel
47+
fi
48+
49+
# Install Rust for both ppc64le and s390x
50+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
51+
source $HOME/.cargo/env
2152

22-
source /opt/rh/gcc-toolset-13/enable
23-
source $HOME/.cargo/env
24-
25-
uv pip install cmake
53+
# Install cmake via pip (already available via dnf for s390x, but this ensures it's in PATH)
54+
uv pip install cmake
55+
56+
# Set python alternatives for s390x
57+
if [[ $(uname -m) == "s390x" ]]; then
58+
alternatives --install /usr/bin/python python /usr/bin/python3.12 1
59+
alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1
60+
alternatives --install /usr/bin/python3-config python3-config /usr/bin/python3.12-config 1
61+
alternatives --install /usr/bin/python3-devel python3-devel /usr/bin/python3.12-devel 1
62+
python --version && python3 --version
63+
fi
2664

2765
export MAX_JOBS=${MAX_JOBS:-$(nproc)}
28-
export OPENBLAS_VERSION=${OPENBLAS_VERSION:-0.3.30}
29-
30-
# Install OpenBlas
31-
# IMPORTANT: Ensure Openblas is installed in the final image
32-
cd /root
33-
curl -L https://github.com/OpenMathLib/OpenBLAS/releases/download/v${OPENBLAS_VERSION}/OpenBLAS-${OPENBLAS_VERSION}.tar.gz | tar xz
34-
# rename directory for mounting (without knowing version numbers) in multistage builds
35-
mv OpenBLAS-${OPENBLAS_VERSION}/ OpenBLAS/
36-
cd OpenBLAS/
37-
make -j${MAX_JOBS} TARGET=POWER9 BINARY=64 USE_OPENMP=1 USE_THREAD=1 NUM_THREADS=120 DYNAMIC_ARCH=1 INTERFACE64=0
38-
make install
39-
cd ..
40-
41-
# set path for openblas
42-
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/OpenBLAS/lib/:/usr/local/lib64:/usr/local/lib
43-
export PKG_CONFIG_PATH=$(find / -type d -name "pkgconfig" 2>/dev/null | tr '\n' ':')
44-
export CMAKE_ARGS="-DPython3_EXECUTABLE=python"
66+
67+
# For s390x, we use the system openblas-devel package
68+
# Only build OpenBLAS from source for ppc64le
69+
if [[ $(uname -m) == "ppc64le" ]]; then
70+
export OPENBLAS_VERSION=${OPENBLAS_VERSION:-0.3.30}
71+
cd /root
72+
curl -L https://github.com/OpenMathLib/OpenBLAS/releases/download/v${OPENBLAS_VERSION}/OpenBLAS-${OPENBLAS_VERSION}.tar.gz | tar xz
73+
mv OpenBLAS-${OPENBLAS_VERSION}/ OpenBLAS/
74+
cd OpenBLAS/
75+
make -j${MAX_JOBS} TARGET=POWER9 BINARY=64 USE_OPENMP=1 USE_THREAD=1 NUM_THREADS=120 DYNAMIC_ARCH=1 INTERFACE64=0
76+
make PREFIX=/usr/local install NO_STATIC=1
77+
cd ..
78+
else
79+
# Create empty OpenBLAS directory for s390x (for Docker mount compatibility)
80+
mkdir -p /root/OpenBLAS/
81+
fi
82+
83+
# Verify OpenBLAS is found by pkg-config for s390x
84+
if [[ $(uname -m) == "s390x" ]]; then
85+
echo "Checking OpenBLAS pkg-config..."
86+
pkg-config --exists openblas || echo "Warning: openblas.pc not found"
87+
fi
88+
89+
export CMAKE_ARGS="-DPython3_EXECUTABLE=python -DCMAKE_PREFIX_PATH=/usr/local"
4590
export CMAKE_POLICY_VERSION_MINIMUM=3.5
4691

4792
TMP=$(mktemp -d)
@@ -50,36 +95,82 @@ if [[ $(uname -m) == "ppc64le" ]]; then
5095
cd ${CURDIR}
5196
TORCH_VERSION=$(grep -A1 '"torch"' pylock.toml | grep -Eo '\b[0-9\.]+\b')
5297
cd ${TMP}
53-
git clone --recursive https://github.com/pytorch/pytorch.git -b v${TORCH_VERSION}
54-
cd pytorch
55-
uv pip install -r requirements.txt
56-
python setup.py develop
57-
rm -f dist/torch*+git*whl
58-
MAX_JOBS=${MAX_JOBS:-$(nproc)} \
59-
PYTORCH_BUILD_VERSION=${TORCH_VERSION} PYTORCH_BUILD_NUMBER=1 uv build --wheel --out-dir ${WHEELS_DIR}
98+
if [[ $(uname -m) == "s390x" ]]; then
99+
echo "Building PyTorch for s390x"
100+
export CMAKE_C_FLAGS="-fPIC -O2"
101+
export CMAKE_CXX_FLAGS="-fPIC -O2"
102+
export CFLAGS="-O2 -pipe"
103+
export CXXFLAGS="-O2 -pipe"
104+
git clone --depth 1 --branch "v${TORCH_VERSION}" --recurse-submodules --shallow-submodules https://github.com/pytorch/pytorch.git
105+
cd pytorch
106+
pip install --no-cache-dir -r requirements.txt
107+
python setup.py develop
108+
rm -f dist/torch*+git*whl
109+
MAX_JOBS=${MAX_JOBS} PYTORCH_BUILD_VERSION=${TORCH_VERSION} PYTORCH_BUILD_NUMBER=1 uv build --wheel --out-dir ${WHEELS_DIR}
110+
echo "PyTorch build completed successfully"
111+
else
112+
git clone --depth 1 --branch "v${TORCH_VERSION}" --recurse-submodules --shallow-submodules https://github.com/pytorch/pytorch.git
113+
cd pytorch
114+
uv pip install -r requirements.txt
115+
python setup.py develop
116+
rm -f dist/torch*+git*whl
117+
MAX_JOBS=${MAX_JOBS:-$(nproc)} \
118+
PYTORCH_BUILD_VERSION=${TORCH_VERSION} PYTORCH_BUILD_NUMBER=1 uv build --wheel --out-dir ${WHEELS_DIR}
119+
fi
60120

61121
cd ${CURDIR}
62122
# Pyarrow
63123
PYARROW_VERSION=$(grep -A1 '"pyarrow"' pylock.toml | grep -Eo '\b[0-9\.]+\b')
64124
cd ${TMP}
65-
git clone --recursive https://github.com/apache/arrow.git -b apache-arrow-${PYARROW_VERSION}
125+
git clone --depth 1 --branch "apache-arrow-${PYARROW_VERSION}" --recurse-submodules --shallow-submodules https://github.com/apache/arrow.git
66126
cd arrow/cpp
67127
mkdir build && cd build && \
68-
cmake -DCMAKE_BUILD_TYPE=release \
69-
-DCMAKE_INSTALL_PREFIX=/usr/local \
70-
-DARROW_PYTHON=ON \
71-
-DARROW_BUILD_TESTS=OFF \
72-
-DARROW_JEMALLOC=ON \
73-
-DARROW_BUILD_STATIC="OFF" \
74-
-DARROW_PARQUET=ON \
75-
.. && \
76-
make install -j ${MAX_JOBS:-$(nproc)} && \
128+
# Set architecture-specific CMake flags
129+
if [[ $(uname -m) == "s390x" ]]; then
130+
ARROW_CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=Release \
131+
-DCMAKE_INSTALL_PREFIX=/usr/local \
132+
-DARROW_PYTHON=ON \
133+
-DARROW_PARQUET=ON \
134+
-DARROW_ORC=ON \
135+
-DARROW_FILESYSTEM=ON \
136+
-DARROW_JSON=ON \
137+
-DARROW_CSV=ON \
138+
-DARROW_DATASET=ON \
139+
-DARROW_DEPENDENCY_SOURCE=BUNDLED \
140+
-DARROW_WITH_LZ4=OFF \
141+
-DARROW_WITH_ZSTD=OFF \
142+
-DARROW_WITH_SNAPPY=OFF \
143+
-DARROW_BUILD_TESTS=OFF \
144+
-DARROW_BUILD_BENCHMARKS=OFF"
145+
else
146+
ARROW_CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=release \
147+
-DCMAKE_INSTALL_PREFIX=/usr/local \
148+
-DARROW_PYTHON=ON \
149+
-DARROW_BUILD_TESTS=OFF \
150+
-DARROW_JEMALLOC=ON \
151+
-DARROW_BUILD_STATIC=OFF \
152+
-DARROW_PARQUET=ON"
153+
fi && \
154+
cmake ${ARROW_CMAKE_FLAGS} .. && \
155+
make -j${MAX_JOBS} VERBOSE=1 && \
156+
make install -j${MAX_JOBS} && \
77157
cd ../../python/ && \
78-
uv pip install -v -r requirements-wheel-build.txt && \
79-
PYARROW_PARALLEL=${PYARROW_PARALLEL:-$(nproc)} \
80-
python setup.py build_ext \
81-
--build-type=release --bundle-arrow-cpp \
82-
bdist_wheel --dist-dir ${WHEELS_DIR}
158+
uv pip install -v -r requirements-build.txt && \
159+
if [[ $(uname -m) == "s390x" ]]; then
160+
PYARROW_WITH_PARQUET=1 \
161+
PYARROW_WITH_DATASET=1 \
162+
PYARROW_WITH_FILESYSTEM=1 \
163+
PYARROW_WITH_JSON=1 \
164+
PYARROW_WITH_CSV=1 \
165+
PYARROW_PARALLEL=${MAX_JOBS} \
166+
python setup.py build_ext --build-type=release --bundle-arrow-cpp bdist_wheel
167+
else
168+
PYARROW_PARALLEL=${PYARROW_PARALLEL:-$(nproc)} \
169+
python setup.py build_ext \
170+
--build-type=release --bundle-arrow-cpp \
171+
bdist_wheel
172+
fi && \
173+
mkdir -p /wheelsdir && cp dist/pyarrow-*.whl /wheelsdir/ && cp dist/pyarrow-*.whl ${WHEELS_DIR}/
83174

84175
# Pillow (use auditwheel repaired wheel to avoid pulling runtime libs from EPEL)
85176
cd ${CURDIR}
@@ -97,11 +188,15 @@ if [[ $(uname -m) == "ppc64le" ]]; then
97188
ls -ltr ${WHEELS_DIR}
98189

99190
cd ${CURDIR}
100-
uv pip install --refresh ${WHEELS_DIR}/*.whl accelerate==$(grep -A1 '"accelerate"' pylock.toml | grep -Eo '\b[0-9\.]+\b')
191+
# Install wheels for s390x and ppc64le
192+
if [[ $(uname -m) == "ppc64le" ]] || [[ $(uname -m) == "s390x" ]]; then
193+
pip install --no-cache-dir "${WHEELS_DIR}"/*.whl
194+
uv pip install --refresh ${WHEELS_DIR}/*.whl accelerate==$(grep -A1 '"accelerate"' pylock.toml | grep -Eo '\b[0-9\.]+\b')
195+
fi
101196

102197
uv pip list
103198
cd ${CURDIR}
104199
else
105-
# only for mounting on non-ppc64le
200+
# only for mounting on non-ppc64le and non-s390x
106201
mkdir -p /root/OpenBLAS/
107202
fi

0 commit comments

Comments
 (0)