파이썬에서 오라클에 붙으려는데 두 갈래로 막힌다.
DPY-3010: connections to this database server version are not supported
by python-oracledb in thin mode
libaio.so.1: cannot open shared object file: No such file or directory
특히 컨테이너 기반 분석 환경(CML 세션 등)에서는 root 가 없어 yum install libaio 를 할 수 없어 cx_Oracle 설치 자체가 막힌다.
| 드라이버 | 오라클 클라이언트 라이브러리 | 상태 |
|---|---|---|
cx_Oracle |
필수 | 유지보수만. 신규 개발에는 쓰지 않는다 |
python-oracledb thin 모드 |
불필요 | 기본값. 순수 파이썬 |
python-oracledb thick 모드 |
필수 | 필요한 기능이 있을 때만 |
python-oracledb 는 cx_Oracle 의 후속이며 같은 개발팀이 만든다. API 도 거의 호환된다. 결정적인 차이는 thin 모드가 Instant Client 를 전혀 요구하지 않는다는 것이고, 그래서 libaio 문제도 함께 사라진다.
따라서 root 권한이 없는 컨테이너 환경이라면 답은 하나다. cx_Oracle 을 포기하고 python-oracledb thin 모드를 쓴다.
pip install oracledb
import oracledb, os
conn = oracledb.connect(
user="app",
password=${MASKED}"ORACLE_PASSWORD"],
dsn="dbhost.example.com:1521/ORCLPDB1",
)
with conn.cursor() as cur:
for row in cur.execute("SELECT sysdate FROM dual"):
print(row)
conn.close()
기존 코드가 import cx_Oracle 로 돼 있으면 별칭으로 바꿔 끼우는 것으로 대부분 돌아간다.
import oracledb as cx_Oracle
thin 모드는 Oracle Database 12.1 이상에만 붙는다. 11g 같은 옛 서버에 붙으려 하면 위 메시지가 나온다. 이때 선택지는 셋이다.
서버를 올린다. 이것이 정공법이지만 대개 불가능하다.
thick 모드로 바꾼다. Instant Client 가 필요하다.
JDBC 우회로 간다. JayDeBeApi 로 오라클 JDBC 드라이버를 쓰면 네이티브 라이브러리가 필요 없다. 자바 런타임은 필요하다.
흔히 잘못 알려진 것과 달리 oracledb.connect(..., mode=oracledb.THICK) 같은 매개변수는 없다. mode 는 SYSDBA 처럼 권한을 지정하는 자리다. thick 모드는 연결하기 전에 초기화 함수를 한 번 부르는 것으로 켠다.
import oracledb
oracledb.init_oracle_client(lib_dir="/opt/oracle/instantclient_21_13")
conn = oracledb.connect(user="app", password=..., dsn="...")
lib_dir 를 생략하면 LD_LIBRARY_PATH 와 시스템 라이브러리 경로에서 찾는다. 한 프로세스 안에서 thin 과 thick 을 섞을 수 없고, 첫 연결 이후에는 바꿀 수 없다.
Instant Client 를 zip 으로 받았다면 풀고 경로만 잡아 주면 된다. rpm 이 필수는 아니다.
mkdir -p /opt/oracle
unzip instantclient-basic-linux.x64-21.13.0.0.0dbru.zip -d /opt/oracle
export LD_LIBRARY_PATH=/opt/oracle/instantclient_21_13:$LD_LIBRARY_PATH
root 가 있다면 ld.so.conf.d 에 등록하는 편이 낫다.
echo /opt/oracle/instantclient_21_13 | sudo tee /etc/ld.so.conf.d/oracle-instantclient.conf
sudo ldconfig
libaio 는 비동기 I/O 라이브러리로 Instant Client 가 링크하고 있다. 배포판 이름이 다르다.
sudo dnf install libaio # RHEL 계열
sudo apt-get install libaio1 # 데비안 계열 (최근 릴리스에서는 libaio1t64)
root 가 없다면 패키지 파일만 받아 사용자 디렉터리에 풀고 LD_LIBRARY_PATH 에 넣는 우회가 가능하다. 다만 이 방식은 이미지가 바뀌면 다시 해야 하므로 임시 수단이다.
rpm2cpio libaio-*.rpm | cpio -idmv
export LD_LIBRARY_PATH=$PWD/usr/lib64:$LD_LIBRARY_PATH
근본 해결은 런타임 이미지에 libaio 를 넣어 다시 만드는 것이고, 그럴 수 없으면 thin 모드로 가는 것이다.
최신 Instant Client 계열에서는 libaio 의존이 제거됐다는 기록이 있으나 버전별로 다르므로 실제 받은 파일로 확인한다. (확인 필요)
ldd /opt/oracle/instantclient_21_13/libclntsh.so | grep -i aio
계속 cx_Oracle 을 써야 한다면 다음 셋을 본다.
export LD_LIBRARY_PATH=/opt/oracle/instantclient_21_13:$LD_LIBRARY_PATH
export TNS_ADMIN=/opt/oracle/network/admin # tnsnames.ora 를 쓸 때만
export NLS_LANG=KOREAN_KOREA.AL32UTF8 # 한글 깨짐 방지
TNS_ADMIN 은 tnsnames.ora 별칭으로 접속할 때만 필요하다. host:port/service 형태의 EZConnect 를 쓰면 없어도 된다.
python-oracledb 는 SQLAlchemy 2.0 계열에서 oracle+oracledb 방언으로 지원된다.
from sqlalchemy import create_engine
engine = create_engine("oracle+oracledb://app:${PASSWORD}@dbhost.example.com:1521/?service_name=ORCLPDB1")
이때도 thin 모드가 기본이므로 추가 환경변수는 필요 없다. thick 이 필요하면 엔진을 만들기 전에 init_oracle_client() 를 부른다.
thin 모드는 순수 파이썬 구현이라 일부 기능이 빠진다. Advanced Queuing, 일부 LOB 동작, Kerberos 를 포함한 외부 인증, 오라클 네트워크 암호화 설정 일부가 대표적이다. 이런 기능이 필요하면 thick 모드가 답이다.
접속 계정과 비밀번호는 소스에 적지 않고 환경변수나 자격증명 저장소에서 읽는다.