Instant Client 를 분명히 설치했는데도 실행 시점에 라이브러리를 못 찾는다.
DPI-1047: Cannot locate a 64-bit Oracle Client library:
"libclntsh.so: cannot open shared object file: No such file or directory"
파일은 /usr/lib/oracle/<버전>/client64/lib 에 있다. 파이썬 버전이나 드라이버 문제가 아니라, 동적 링커가 그 디렉터리를 검색 경로에 넣고 있지 않은 것이다.
링커가 무엇을 알고 있는지부터 본다.
ldconfig -p | grep clntsh
ls -l /usr/lib/oracle/*/client64/lib/libclntsh.so*
echo "$LD_LIBRARY_PATH"
ldconfig -p 에 아무것도 안 나오고 파일은 있다면 경로 등록 문제다. 파일 목록에 버전이 붙은 libclntsh.so.23.1 만 있고 확장자 없는 libclntsh.so 심볼릭 링크가 없다면 링크 문제다. 둘 다 흔하다.
서비스로 띄우는 환경에서는 이 방식이 맞다. 환경변수와 달리 실행 주체가 누구든 적용된다.
echo /usr/lib/oracle/23/client64/lib | sudo tee /etc/ld.so.conf.d/oracle-instantclient.conf
sudo ldconfig
ldconfig -p | grep clntsh
zip 으로 푼 Instant Client 에는 버전이 붙은 파일만 들어 있는 경우가 있다.
cd /usr/lib/oracle/23/client64/lib
sudo ln -sf libclntsh.so.23.1 libclntsh.so
sudo ldconfig
원인을 가려낼 때만 쓴다. 이것으로 되면 경로 문제가 맞다.
export LD_LIBRARY_PATH=/usr/lib/oracle/23/client64/lib:$LD_LIBRARY_PATH
LD_LIBRARY_PATH 는 셸 세션에만 남는다. systemd 유닛에서 쓰려면 Environment= 로 명시해야 하고, sudo 는 기본적으로 이 변수를 지운다.
/etc/ld.so.conf.d/ 에 파일을 넣고 ldconfig 를 돌렸는데 여전히 실패한다면 다음을 확인한다.
ldconfig 를 root 로 돌렸는가. 일반 사용자로 실행하면 캐시가 갱신되지 않는다.wrong ELF class 가 뜬다. file libclntsh.so.23.1 로 확인한다.ldd libclntsh.so 에 not found 가 있으면 대개 libaio 다. dnf install libaio 로 넣는다. Oracle Client 19c 까지는 libaio 가 필수다.restorecon -Rv <경로> 로 되돌린다.이미지 빌드 중 RUN 에서 export 한 환경변수는 다음 레이어로 넘어가지 않고 컨테이너 실행 시점에도 남지 않는다. ENV 로 쓰거나 ld.so.conf.d 방식을 쓴다. 후자가 낫다.
COPY instantclient-basiclite-linux.x64-23.9.0.0.0dbru.zip /tmp/
RUN set -eux; \
dnf install -y libaio unzip; \
mkdir -p /opt/oracle; \
unzip -q /tmp/instantclient-*.zip -d /opt/oracle; \
rm -f /tmp/instantclient-*.zip; \
echo /opt/oracle/instantclient_23_9 > /etc/ld.so.conf.d/oracle-instantclient.conf; \
ldconfig
멀티 스테이지 빌드를 쓴다면 최종 스테이지에도 ldconfig 설정을 다시 넣어야 한다. /etc/ld.so.cache 는 스테이지를 넘어가지 않는다.
python-oracledb 는 기본이 Thin 모드라 Instant Client 없이 붙는다. Thick 모드가 필요한 경우는 제한적이다 — 일부 고급 기능, 특정 인증 방식, cx_Oracle 호환 동작이다. Thin 으로 충분하다면 이 문제 자체가 사라진다.
Thick 을 꼭 써야 한다면 라이브러리 위치를 코드에서 직접 지정할 수도 있다. 환경변수보다 확실하다.
import oracledb
oracledb.init_oracle_client(lib_dir="/opt/oracle/instantclient_23_9")
cx_Oracle 은 항상 Thick 이므로 클라이언트가 반드시 있어야 한다. 신규 개발이라면 python-oracledb 로 옮기는 편이 낫다.