NVIDIA Triton Inference Server - version `GLIBCXX_3.4.32' not found (required by /opt/tritonserver/backends/python/triton_python_backend_stub)
triton 서버에 파이썬 백엔드를 사용하는 경우, 이런 오류가 나온다면?
I0523 04:29:23.975315 1 stub_launcher.cc:385] "Starting Python backend stub: source /tmp/python_env_UM09Ut/0/bin/activate && exec env LD_LIBRARY_PATH=/tmp/python_env_UM09Ut/0/lib:$LD_LIBRARY_PATH /opt/tritonserver/backends/python/triton_python_backend_stub /model_dir/preprocessing/1/model.py triton_python_backend_shm_region_afffbc98-a153-4285-9cd5-7a0746a084a4 1048576 1048576 1 /opt/tritonserver/backends/python 336 preprocessing DEFAULT"
/opt/tritonserver/backends/python/triton_python_backend_stub: /tmp/python_env_UM09Ut/0/lib/libstdc++.so.6: version `GLIBCXX_3.4.32' not found (required by /opt/tritonserver/backends/python/triton_python_backend_stub)
/opt/tritonserver/backends/python/triton_python_backend_stub: /tmp/python_env_UM09Ut/0/lib/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by /opt/tritonserver/backends/python/triton_python_backend_stub)
예전 경험도 있고 하니,
리눅스에서 공유 라이브러리가 로드되지 않는다면?
; https://www.sysnet.pe.kr/2/0/11973
이번엔 제 스스로 원인 분석이 될 듯도 합니다. ^^ 우선, 저 상황에서 로그에 나온 "/tmp/python_env_UM09Ut/0/lib/libstdc++.so.6" 파일을 보면,
# ls /tmp/python_env_UM09Ut/0/lib/libstdc++.so -l
lrwxrwxrwx 1 root root 19 May 23 01:35 /tmp/python_env_UM09Ut/0/lib/libstdc++.so -> libstdc++.so.6.0.29
저렇게 29 버전으로 연결돼 있고 따라서 "GLIBCXX_3.4.29" 버전의 so 파일이 존재하는 상황입니다. 하지만, 실행 주체인 (역시 로그에서 확인할 수 있는) /opt/tritonserver/backends/python/triton_python_backend_stub은 6.0.32 버전 이후로 갖는 함수를 사용하고 있어,
# strings /opt/tritonserver/backends/python/triton_python_backend_stub | grep GLIBCXX_3.4.32
GLIBCXX_3.4.32
_ZSt21ios_base_library_initv@GLIBCXX_3.4.32
실행이 안 되는 것입니다. 아니, 그런데 왜? triton 파이썬 백엔드가 요구하는 3.4.32가 아닌 3.4.29 버전의 so 파일을 로딩하려는 것일까요? 이유를 찾기 위해 오류 메시지가 발생한 시점의 로그를 살펴보면, 다음과 같은 선행 메시지가 보입니다.
I0523 04:29:23.974814 1 pb_env.cc:292] "Extracting Python execution env /model_dir/postprocessing/post_env.tar.gz"
그러니까, 아마도 conda가 6.0.29 버전의 libstdc++.so.6 파일을 패키징 처리한 것 같은데요, 실제로 post_env.tar.gz 압축을 풀어 보면, /lib 디렉터리에 libstdc++.so.6.0.29 파일이 존재합니다. 결국 conda 가상 환경 구성 자체에 이미 6.0.29 버전이 위치하고 있었던 것입니다.
// conda 가상 환경의 이름이 "test_env"라고 가정
$ ls ~/miniconda3/envs/test_env/lib/libstdc++.so
libstdc++.so libstdc++.so.6 libstdc++.so.6.0.29
그리고 그 경로의 라이브러리를 사용하라고 triton_python_backend_stub 실행 시 LD_LIBRARY_PATH 환경 변수를 통해 전달하는 것을 로그에서 확인할 수 있습니다.
I0528 07:03:35.351471 1 stub_launcher.cc:385] "Starting Python backend stub: source /model_dir/preprocessing/python_env/bin/activate && exec env LD_LIBRARY_PATH=/model_dir/preprocessing/python_env/lib:$LD_LIBRARY_PATH /opt/tritonserver/backends/python/triton_python_backend_stub /model_dir/preprocessing/1/model.py triton_python_backend_shm_region_b0fc2220-a884-4074-b0e9-50111be4a834 1048576 1048576 1 /opt/tritonserver/backends/python 336 preprocessing DEFAULT"
자, 그럼 저걸 업데이트해야 하는데요, 이에 대해 검색해 보면,
Add a note on the GLIBCXX_3.4.30 not found issue when using custom ex… #280
; https://github.com/triton-inference-server/python_backend/pull/280/commits/2a41ca9f3fff80fb40ecfd103f14e88c214e4c9a
conda 환경에
conda-forge 채널로부터 libstdcxx-ng의 버전을 명시해 설치하는 내용이 나옵니다.
conda install -c conda-forge libstdcxx-ng=12 -y
실제로 12로 지정하면 6.0.30 버전으로 덮어써지는데요,
$ ls ~/miniconda3/envs/test_env/lib/libstdc++.so -l
lrwxrwxrwx 1 testusr testusr 19 May 23 14:58 /home/testusr/miniconda3/envs/test_env/lib/libstdc++.so -> libstdc++.so.6.0.30
해보니까, 13으로 지정해야 6.0.32 버전이 나옵니다.
$ conda install -c conda-forge libstdcxx-ng=13 -y
$ ls ~/miniconda3/envs/test_env/lib/libstdc++.so -l
lrwxrwxrwx 1 testusr testusr 19 May 23 15:02 /home/testusr/miniconda3/envs/test_env/lib/libstdc++.so -> libstdc++.so.6.0.32
참고로, 파이썬 백엔드는 docker 컨테이너의 경우 "/opt/tritonserver/backends/python/triton_python_backend_stub" 경로에 있으므로, 다음과 같이 의존성을 확인할 수도 있습니다.
# ldd /opt/tritonserver/backends/python/triton_python_backend_stub
linux-vdso.so.1 (0x00007ffeac5c4000)
libcudart.so.12 => /usr/local/cuda/targets/x86_64-linux/lib/libcudart.so.12 (0x00007f91bc800000)
libpython3.12.so.1.0 => /usr/lib/x86_64-linux-gnu/libpython3.12.so.1.0 (0x00007f91bbf59000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f91bbcdb000)
libgcc_s.so.1 => /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f91bcbb2000)
libc.so.6 => /usr/lib/x86_64-linux-gnu/libc.so.6 (0x00007f91bbac9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f91bccfa000)
libdl.so.2 => /usr/lib/x86_64-linux-gnu/libdl.so.2 (0x00007f91bcbab000)
libpthread.so.0 => /usr/lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f91bcba6000)
librt.so.1 => /usr/lib/x86_64-linux-gnu/librt.so.1 (0x00007f91bcba1000)
libm.so.6 => /usr/lib/x86_64-linux-gnu/libm.so.6 (0x00007f91bcab8000)
libz.so.1 => /usr/lib/x86_64-linux-gnu/libz.so.1 (0x00007f91bbaad000)
libexpat.so.1 => /usr/lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f91bba81000)
# ls /usr/lib/x86_64-linux-gnu/libstdc++.so.6 -l
lrwxrwxrwx 1 root root 19 Sep 9 2024 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 -> libstdc++.so.6.0.33
위와 같은 경우 6.0.33 버전에 의존성이 있으므로 14 버전으로 구성해야 합니다.
$ conda install -c conda-forge libstdcxx-ng=14 -y
혹은 이런 오류 메시지가 발생한다면?
I0527 05:01:23.630588 1 pb_env.cc:292] "Extracting Python execution env /model_dir/preprocessing/pre_env.tar.gz"
I0527 05:01:23.631184 1 stub_launcher.cc:385] "Starting Python backend stub: source /tmp/python_env_WtprbR/0/bin/activate && exec env LD_LIBRARY_PATH=/tmp/python_env_WtprbR/0/lib:$LD_LIBRARY_PATH /opt/tritonserver/backends/python/triton_python_backend_stub /model_dir/postprocessing/1/model.py triton_python_backend_shm_region_c30a0918-191c-49c9-bc42-da9567c6e646 1048576 1048576 1 /opt/tritonserver/backends/python 336 postprocessing DEFAULT"
I0527 05:01:27.312170 143 pb_stub.cc:320] Failed to initialize Python stub for auto-complete: ImportError: libGL.so.1: cannot open shared object file: No such file or directory
At:
/tmp/python_env_WtprbR/0/lib/python3.12/importlib/__init__.py(90): import_module
/tmp/python_env_WtprbR/0/lib/python3.12/site-packages/cv2/__init__.py(153): bootstrap
/tmp/python_env_WtprbR/0/lib/python3.12/site-packages/cv2/__init__.py(181): <module>
<frozen importlib._bootstrap>(488): _call_with_frames_removed
<frozen importlib._bootstrap_external>(999): exec_module
<frozen importlib._bootstrap>(950): _load_unlocked
<frozen importlib._bootstrap>(1333): _find_and_load_unlocked
<frozen importlib._bootstrap>(1360): _find_and_load
/model_dir/postprocessing/1/model.py(5): <module>
<frozen importlib._bootstrap>(488): _call_with_frames_removed
<frozen importlib._bootstrap_external>(999): exec_module
<frozen importlib._bootstrap>(950): _load_unlocked
<frozen importlib._bootstrap>(1334): _find_and_load_unlocked
<frozen importlib._bootstrap>(1360): _find_and_load
I0527 05:01:27.575096 1 python_be.cc:2266] "TRITONBACKEND_ModelFinalize: delete model state"
E0527 05:01:27.575174 1 model_lifecycle.cc:654] "failed to load 'postprocessing' version 1: Internal: ImportError: libGL.so.1: cannot open shared object file: No such file or directory\n\nAt:\n /tmp/python_env_WtprbR/0/lib/python3.12/importlib/__init__.py(90): import_module\n /tmp/python_env_WtprbR/0/lib/python3.12/site-packages/cv2/__init__.py(153): bootstrap\n /tmp/python_env_WtprbR/0/lib/python3.12/site-packages/cv2/__init__.py(181): <module>\n <frozen importlib._bootstrap>(488): _call_with_frames_removed\n <frozen importlib._bootstrap_external>(999): exec_module\n <frozen importlib._bootstrap>(950): _load_unlocked\n <frozen importlib._bootstrap>(1333): _find_and_load_unlocked\n <frozen importlib._bootstrap>(1360): _find_and_load\n /model_dir/postprocessing/1/model.py(5): <module>\n <frozen importlib._bootstrap>(488): _call_with_frames_removed\n <frozen importlib._bootstrap_external>(999): exec_module\n <frozen importlib._bootstrap>(950): _load_unlocked\n <frozen importlib._bootstrap>(1334): _find_and_load_unlocked\n <frozen importlib._bootstrap>(1360): _find_and_load\n"
I0527 05:01:27.575246 1 model_lifecycle.cc:789] "failed to load 'postprocessing'"
libGL.so 파일의 로딩에 실패한 것인데요, 그런 경우라면 dockerfile에 미리 설치해 두는 식으로 해결할 수 있습니다.
FROM nvcr.io/nvidia/tritonserver:25.04-py3
RUN apt-get update && apt-get install libgl1 -y
SHELL ["/bin/bash", "-c"]
WORKDIR /home/ubuntu
비록 동작은 하지만 다음과 같은 경고가 거슬린다면?
I0527 05:11:05.790675 1 stub_launcher.cc:385] "Starting Python backend stub: source /tmp/python_env_PP7fff/0/bin/activate && exec env LD_LIBRARY_PATH=/tmp/python_env_PP7fff/0/lib:$LD_LIBRARY_PATH /opt/tritonserver/backends/python/triton_python_backend_stub /model_dir/preprocessing/1/model.py triton_python_backend_shm_region_970603fa-20f8-4bb4-a4ed-cbead6cf17ae 1048576 1048576 1 /opt/tritonserver/backends/python 336 preprocessing DEFAULT"
/tmp/python_env_PP7fff/0/lib/python3.12/site-packages/albumentations/check_version.py:147: UserWarning: Error fetching version info <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1010)>
data = fetch_version_info()
아래의 글에 힌트가 나오는데요,
SSL: CERTIFICATE_VERIFY_FAILED certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)')))
; https://stackoverflow.com/questions/77442172/ssl-certificate-verify-failed-certificate-verify-failed-unable-to-get-local-is
SSL_CERT_DIR 환경 변수를 설정하는 것으로 해결할 수 있습니다.
export SSL_CERT_DIR=/etc/ssl/certs/
triton 컨테이너를 구동하는 경우에는 -e 옵션으로 전달해도 됩니다.
docker run --gpus='"device=0"' -it --rm --shm-size=8g -e SSL_CERT_DIR=/etc/ssl/certs/ ...[생략]...
마지막으로 문서에 보면,
Creating Custom Execution Environments
; https://github.com/triton-inference-server/python_backend?tab=readme-ov-file#creating-custom-execution-environments
conda 가상 환경을 pack/unpack한 것뿐만 아니라 애당초 conda create -p 옵션으로 디렉터리를 지정해 만든 가상 환경도 지원한다고 나옵니다. 그런데, 실제로 그런 식으로 구성한 후,
$ export PYTHONNOUSERSITE=True
$ conda create -p /home/testusr/test/triton_server_example/triton/preprocessing/python_env python=3.12 -y
$ conda activate /home/testusr/test/triton_server_example/triton/preprocessing/python_env
$ conda install -c conda-forge libstdcxx-ng=14 -y
$ conda install pip -y
$ conda install conda-pack -y
$ pip install -r pre_requirements.txt
$ conda deactivate
$ conda create -p /home/testusr/test/triton_server_example/triton/postprocessing/python_env python=3.12 -y
$ conda activate /home/testusr/test/triton_server_example/triton/postprocessing/python_env
$ conda install -c conda-forge libstdcxx-ng=14 -y
$ conda install pip -y
$ conda install conda-pack -y
$ pip install -r post_requirements.txt
$ conda deactivate
config.pbtxt 파일의 경로도 맞춰주고,
parameters: {
key: "EXECUTION_ENV_PATH",
value: {string_value: "$$TRITON_MODEL_DIRECTORY/python_env"}
}
실행해 보면 이런 오류가 발생합니다.
...[생략]...
I0528 07:26:05.285089 1 python_be.cc:1849] "Using Python execution env /model_dir/postprocessing/python_env"
I0528 07:26:05.285850 1 pb_env.cc:267] "Returning canonical path since EXECUTION_ENV_PATH does not contain compressed path. Path: /model_dir/postprocessing/python_env"
I0528 07:26:05.285923 1 python_be.cc:2266] "TRITONBACKEND_ModelFinalize: delete model state"
E0528 07:26:05.285951 1 model_lifecycle.cc:654] "failed to load 'postprocessing' version 1: Internal: Path /model_dir/postprocessing/python_env/bin/activate does not exist. The Python environment should contain an 'activate' script."
I0528 07:26:05.285967 1 model_lifecycle.cc:789] "failed to load 'postprocessing'"
...[생략]...
물론, conda unpack으로 한 경우에는 저 경로에 activate 스크립트가 존재하는데요,
# cat /model_dir/postprocessing/python_env/bin/activate
_conda_pack_activate() {
local _CONDA_SHELL_FLAVOR
if [ -n "${BASH_VERSION:+x}" ]; then
_CONDA_SHELL_FLAVOR=bash
elif [ -n "${ZSH_VERSION:+x}" ]; then
_CONDA_SHELL_FLAVOR=zsh
elif [ -n "${KSH_VERSION:+x}" ]; then
_CONDA_SHELL_FLAVOR=ksh
elif [ -n "${POSH_VERSION:+x}" ]; then
_CONDA_SHELL_FLAVOR=posh
else
# https://unix.stackexchange.com/a/120138/92065
local _q="$(ps -p$$ -o cmd="",comm="",fname="" 2>/dev/null | sed 's/^-//' | grep -oE '\w+' | head -n1)"
if [ "$_q" = dash ]; then
_CONDA_SHELL_FLAVOR=dash
else
(>&2 echo "Unrecognized shell.")
return 1
fi
fi
# https://unix.stackexchange.com/questions/4650/determining-path-to-sourced-shell-script/
local script_dir
case "$_CONDA_SHELL_FLAVOR" in
bash) script_dir="$(dirname "${BASH_SOURCE[0]}")";;
zsh) script_dir="$(dirname "${(%):-%x}")";; # http://stackoverflow.com/a/28336473/2127762
dash) x=$(lsof -p $$ -Fn0 | tail -1); script_dir="$(dirname "${x#*n}")";;
*) script_dir="$(cd "$(dirname "$_")" && echo "$PWD")";;
esac
local full_path_script_dir="$(cd "${script_dir}" > /dev/null && pwd)"
local full_path_env="$(dirname "$full_path_script_dir")"
local env_name="$(basename "$full_path_env")"
# If there's already a source env
if [ -n "$CONDA_PREFIX" ]; then
# If the source env differs from this env
if [ "$CONDA_PREFIX" != "$full_path_env" ]; then
# Check whether deactivate is a function or executable
type deactivate >/dev/null 2>/dev/null
if [ $? -eq 0 ]; then
. deactivate >/dev/null 2>/dev/null
fi
else
return 0 # nothing to do
fi
fi
export CONDA_PREFIX="$full_path_env"
export _CONDA_PACK_OLD_PS1="$PS1"
PATH="$full_path_env/bin:$PATH"
PS1="($env_name) $PS1"
case "$_CONDA_SHELL_FLAVOR" in
zsh) rehash;;
posh) ;;
*) hash -r;;
esac
# Run the activate scripts
local _script_dir="${full_path_env}/etc/conda/activate.d"
if [ -d "$_script_dir" ] && [ -n "$(ls -A "$_script_dir")" ]; then
local _path
for _path in "$_script_dir"/*.sh; do
. "$_path"
done
fi
}
_conda_pack_activate
딱히 어떤 식으로 더 맞춰줘야 하는 것인지 잘 모르겠습니다. 이에 대해 혹시 아시는 분은 덧글 부탁드립니다. ^^
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]