docker镜像alpine中安装oracle客户端

1.背景

有项目需使用python连接oracle数据库,然后查询一些数据进行分析。在安装oracle客户端驱动过程中遇到了一些问题,在此记录下来分享读者。
一点限制:

  • oracle数据库与本应用程序不在同一台机器上,数据连接为远程访问方式,针对同一台机器的应用访问,网上有很多;
  • 本应用支行在docker容器中,镜像基于alpine:3.7版本编译,针对在ubuntu中的安装网上有很多;
  • 基础信息:alping3.7 + python3.6.5 + cx_Oracle7.0.0 + instantclient-basic-linux.x64-11.2.0.4.0

搭建目标:

  • 使用python -c "import cx_Oracle as ora; ora.connect('xxx')"可正常连接oracle数据库

2.下载instant_client程序包

可直接从oracle 官网下载,本应用下载了【instantclient-basic-linux.x64-11.2.0.4.0.zip】包。原因为最好与oracle数据库版本对应。
该压缩包中有instanclient_11_2目录,其目录结构:

./instantclient_11_2:
|---BASIC_README
|---adrci
|---genezi
|---libclntsh.so.11.1
|---libnnz11.so
|---libocci.so.11.1
|---libociei.so
|---libocijdbc11.so
|---ojdbc5.jar
|---ojdbc6.jar
|---uidrvci
|---xstreeams.jar

即包中为oracle客户端连接数据库的所需的库。

3.Dockerfile

直接先上代码然后再说明其中的关键点:

FROM alpine:3.7
ENV ALPINE_VERSION=3.7

#### packages from https://pkgs.alpinelinux.org/packages
# These are always installed. Notes:
#   * dumb-init: a proper init system for containers, to reap zombie children
#   * bash: For entrypoint, and debugging
#   * ca-certificates: for SSL verification during Pip and easy_install
#   * python: the binaries themselves
#   * openblas: required for numpy.
#   * libaio libnsl: for cx_Oracle
ENV PACKAGES="\
  dumb-init \
  bash vim tini \
  ca-certificates \
  python3==3.6.5-r0 \
  openblas \
  libaio libnsl \
"

# These packages are not installed immediately, but are added at runtime or ONBUILD to shrink the image as much as possible. Notes:
#   * build-base: used so we include the basic development packages (gcc)
#   * linux-headers: commonly needed, and an unusual package name from Alpine.
ENV BUILD_PACKAGES="\
  build-base \
  linux-headers \
"

## for install oracle instant client
## from https://oracle.github.io/odpi/doc/installation.html#linux
ENV TNS_ADMIN=/oracle_client/instantclient_11_2
ENV NLS_LANG=SIMPLIFTED_CHINESE_CHINA_ZHS16GBK
ENV LD_LIBRARY_PATH=/oracle_client/instantclient_11_2

RUN echo \
  # 1.install oracle client and create soft link
  && mkdir /oracle_client && cd /oracle_client \
  && wget -O client.zip "https://raw.githubusercontent.com/tianxiawuzhe/alpine37-py365-django21-ai/master/instantclient-basic-linux.x64-11.2.0.4.0.zip" \
  && unzip client.zip && rm client.zip \
  && cd /oracle_client/instantclient_11_2 \
  && ln -s libclntsh.so.11.1  libclntsh.so \
  && ln -s /usr/lib/libnsl.so.2.0.0  /usr/lib/libnsl.so.1 \

  # 2.replacing default repositories with edge ones
  && echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
  && echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \
  && echo "http://dl-cdn.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \

 # 3.Add the build packages, and then will be deleted
  && apk add --no-cache --virtual=.build-deps $BUILD_PACKAGES \
 
 # 4.Add the packages, with a CDN-breakage fallback if needed
  && apk add --no-cache $PACKAGES || \
    (sed -i -e 's/dl-cdn/dl-4/g' /etc/apk/repositories && apk add --no-cache $PACKAGES) \

  # 5.make some useful symlinks that are expected to exist
  && cd /usr/bin \
  && { [[ -e idle ]] || ln -s idle3 idle; } \
  && { [[ -e pydoc ]] || ln -s pydoc3 pydoc; } \
  && { [[ -e python ]] || ln -sf python3.6 python; } \
  && { [[ -e python-config ]] || ln -sf python3-config python-config; } \
  && { [[ -e pip ]] || ln -sf pip3 pip; } \
  && ls -l idle pydoc python* pip* \
  && python -m pip install --upgrade --no-cache-dir pip \
  && ls -l idle pydoc python* pip* \
  
  # 6.install my app software
  && pip install --no-cache-dir cx_Oracle \
  
  # 7.End
  && apk del .build-deps \
  && ls -l idle pydoc python* pip* \
  && echo

EXPOSE 8080

ENTRYPOINT tail -f /dev/null
CMD ["/bin/bash"]

说明:
01)使用环境变量PACKAGES和BUILD_PACKAGES来区分应用运行时和编译安装时所需要的库,在RUN命令最后删除了BUILD_PACKAGES的依赖库,从而保证镜像尽可能的小;
02)PACKAGES中含有libaio和libnsl两个依赖库,此2个依赖库为cx_Oracle程序包所需的库(更具体的是instant_client驱动包中libclntsh.so.11.1库文件依赖这两个包);
03)LD_LIBRARY_PATH环境变量保证在python程序运行时能够找到instant_client的驱动包位置,本文中是将驱动包解压至容器的/oracle_client/目录下(解压过程见后面的RUN里的unzip);
04)RUN中第1步,是从github上下载上面的驱动包。原因:本应用编译时是使用公共云平台进行编译,在编译过程中绑定了github,从github上读取Dockerfile和*.zip文件进行编译。若是在本地编译镜像,可以使用COPY或ADD来添加文件,但这样可能会让镜像的大小增加,理由是COPY和ADD不能自动解压缩zip,即使在RUN的后面写上rm *.zip,也无法降低镜像的大小,更多信息请查询镜像层的原理。
05)RUN第1步,【ln -s libclntsh.so.11.1 libclntsh.so】是建立驱动包中软连接,这样cx_Oracle在寻找动态连接库时就能找到了,如果不软连接或重命名,那么cx_Oracle将会找不到该库;【ln -s /usr/lib/libnsl.so.2.0.0 /usr/lib/libnsl.so.1】建立libnsl库的软连接,当libclntsh库在运行时会查找此库;
06)RUN第2步,向/etc/apk/repositories文件中添加一些alpine特有的库地址,后面在安装时就能自动下载并安装了;
07)RUN第3步,安装编译过程中所需的依赖包;
08)RUN第4步,安装运行过程中所需的依赖包,此2步可调换顺序,但个人觉得应该这个顺序,因为如果编译的依赖包覆盖了运行所需的包,那么在删除BUILD_PACKAGES后,程序运行时就会出现问题;
09)RUN第5步,建立一些python中简单的软连接,方便后面使用;同时更新了pip的版本;
10)RUN第6步,使用pip安装cx_Oracle程序包;
11)RUN第7步,清理BUILD_PACKAGES这些依赖包;
12)最后就是暴露的端口和启动脚本了;

4.遇到的问题

以下遇到的问题,均是通过执行【python -c "import cx_Oracle as o; o.connect('xxx')"】来验证是否客户端安装成功。

4.1.找不到libclntsh.so动态连接库

详细信息:

cx_Oracle.DatabaseError: 
DPI-1047: 64-bit Oracle Client library cannot be loaded: 
"Error loading shared library libclntsh.so: No such file or directory". 
See https://oracle.github.io/odpi/doc/installation.html#linux for help

原因有2:

  • 未正确设置LD_LIBRARY_PATH环境变量,导致python在加载应用时,操作系统未设置正确的库路径;
  • 未设置软连接libclntsh.so指向libclntsh.so.11.1,也会导致无法加载;

诊断方法:
在python命令行里,手工【from ctypes import find_library as f, CDLL】,尝试使用find_library来查找动态连接库,如果能用此命令找到,比如f('libclntsh.so.11.1')可以找到,但f('libclntsh.so')找不到,说明LD_LIBRARY_PATH设置正确了,只是没有软连接。然后再用CDLL尝试加载动态连接库,看中间加载是否会出问题。

4.2.找不到libaio.so.1动态连接库

请确认libaio是否安装成功,安装成功后,应该在/usr/lib/libaio.so.1.0.1文件,同时会存在libaio.so.1的软连接。

4.3.找不到libnsl.so.1动态连接库

由于当前libnsl的版本已经是libns.so.2.0.0,因此在安装libnsl后会自动存在libnsl.so.2的软连接,而本应用中oracle的驱动版本是较老的,因此直接手工建立了软连接【ln -s /usr/lib/libnsl.so.2.0.0 /usr/lib/libnsl.so.1】,试验成功!

4.完成

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容