概要
本篇博客记录 Python 应用如何构建 Docker 镜像,主要两点内容
- 如何构造连接两个数据库的环境
- 如何尽可能减小制作出镜像的体积
目录结构说明
.
├── deps
│ ├── instantclient-basic-linux.x64-19.3.0.0.0dbru.zip
├── Dockerfile
├── Pipfile
├── Pipfile.lock
├── sources.list
├── test_oracle.py
└── test_postgresql.py
deps 是一个文件夹,里面的 oracle instanceclient 可以在这两个地方下载
Oracle 官网:https://www.oracle.com/database/technologies/instant-client/downloads.html
Github:https://github.com/epoweripione/oracle-instantclient-18
Pipfile 与 Pipfile.lock 是 pipenv 项目的依赖文件,跟 packages.json 一样的作用
[[source]]
name = "pypi"
url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
verify_ssl = true
[dev-packages]
[packages]
psycopg2 = "*"
cx-oracle = "*"
[requires]
python_version = "3.7"
以上是 Pipfile 内容,添加新软件时候,以往用 pip install flask
可以使用 pipenv install flask
,执行脚本的时候使用 pipenv run python3 1.py
关于 Pipenv,可以参考:https://github.com/pypa/pipenv
sources.list 是debian源文件,放在 /etc/apt/ 目录下
阿里源
deb http://mirrors.aliyun.com/debian/ buster main non-free contrib
deb http://mirrors.aliyun.com/debian-security buster/updates main
deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib
deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib
清华源
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free
test_oracle.py 与 test_postgresql.py 都是最基础的连接两个数据库的代码
# 可以获取 Oracle 版本号
conn.version
# 可以获取 PostgreSQL 版本号
cur = conn.cursor("public")
cur.execute("SELECT version();")
version = cur.fetchone()[0]
Python 连接数据库
借助 psycopg2 与 cx_oracle 模块,Python 可以连接到 PostgreSQL 与 Oracle
之前在 《Fedora 29 系统使用 Python3 连接Oracle 11.2数据库》 整理过Instance配置
构建镜像
普通的 Dockerfile
FROM python:3.7.4
WORKDIR /opt/myproj
# 一些文件复制到镜像
COPY Pipfile ./
COPY Pipfile.lock ./
COPY deps deps/
COPY sources.list ./
# 设置Oracle客户端环境变量
ENV TNS_ADMIN=/opt/oracle/instantclient_19_3
ENV LD_LIBRARY_PATH=/opt/oracle/instantclient_19_3
# * libaio libnsl: for cx_Oracle
# * libpq: required for psycopg2
ENV PACKAGES="\
unzip \
libaio1 \
libpq-dev \
"
ENV BUILD_PACKAGES="\
apt-utils \
build-essential \
"
# 修改Debian源为国内的地址
# 安装依赖并配置Oracle客户端
RUN echo \
&& mv /etc/apt/sources.list /etc/apt/sources.list.bak && mv /opt/myproj/sources.list /etc/apt/ \
&& apt-get update -y \
&& apt-get install -y --no-install-recommends $PACKAGES \
&& mkdir /opt/oracle && cd /opt/oracle \
&& mv /opt/myproj/deps/instantclient-basic-linux.x64-19.3.0.0.0dbru.zip /opt/oracle/client.zip \
&& unzip client.zip && rm client.zip \
&& cd /opt/oracle/instantclient_19_3 \
&& ln -s /lib/x86_64-linux-gnu/libnsl.so.1 /usr/lib/libnsl.so.1 \
&& rm -rf /var/lib/apt/lists/* \
&& echo
# 此处使用 pipenv 包来管理依赖
# 保证项目依赖的版本号稳定
RUN pip3 install --no-cache-dir pipenv -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN pipenv install
CMD echo "hello world!"
生成镜像
$ sudo docker build -t db-conn .
启动容器
$ sudo docker run --rm -it -v $PWD:/opt/myproj db-conn
hello world!
不带任何参数启动容器,那么会执行在 dockerfile 写的默认命令
使用镜像执行本地脚本
$ sudo docker run --rm -it -v $PWD:/opt/myproj db-conn-big pipenv run python3 test_oracle.py
PS:此处把当前目录映射到容器的 /opt/myproj 目录,使用容器里的 pipenv 执行脚本,执行完成后容器自动销毁
容器的好处之一就是可以把各种运行时做到镜像里,避免污染物理机的环境和物理机差异导致应用报错
镜像很大
上边制作的镜像已经集成了Python 使用 Oracle 和 PostgreSQL 的环境,还存在一个优化问题,即镜像应该在稳定并实现功能的前提下尽可能小,目前存在的问题的,制作出来的镜像很大,本身 python:3.7.4 就有 929MB
,加入了 228MB
(解压缩后的)的 instantclient_19_3,此时构建出来镜像有 1.29GB
大小
现在镜像导出后,压缩成 .tar.bz2
压缩包也有 495M
大小,接下来着手减小镜像体积
减小镜像体积
要减小镜像的体积,就要考虑哪些部分可以优化
- 基础镜像
- 构建使用的工具集(GCC等)
基础镜像部分,3.7.4-alpine3.10
看起来很吸引人,只有 98.7MB
,但是因为Alpine使用的是 musl c library 替代的 glibc,所以一些模块不能正常工作,我在测试的时候,在 Alpine 下使用 Oracle 客户端工具就一直报错
由于 Alpine 精简了不少内容,如果想要制作一个稳定且后续可能添加很多第三方模块的应用,那么它不是一个好的基础镜像选择
红帽系的镜像总是很大,相比之下,Debian系的小很多, 3.7.4-buster
镜像只有 179MB
一些模块在使用的时候,需要进行编译,那么系统就需要有GCC等一系列构建工具,这些工具在编译完模块后就不再需要了,那么可以把这些工具移除
可能很容易想到使用 remove 来移除这些软件,但是要移除安装这些软件时候依赖安装的软件好像不太容易
其实,构建和运行时是两个镜像就可以很好解决这个问题了,构建镜像用来安装编译工具,把模块都编译完成,然后把模块复制到新的镜像中,构建镜像就可以删除了,新的镜像只需要安装必要依赖就可以
两步构建的 dockerfile
FROM python:3.7-slim-buster as base
FROM base as builder
WORKDIR /opt/myproj
# Bundle APP files
COPY Pipfile ./
COPY Pipfile.lock ./
COPY sources.list ./
COPY pipenv-require.txt ./
COPY sources.list ./
# Change Mirrors
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && mv /opt/myproj/sources.list /etc/apt/
# * libaio libnsl: for cx_Oracle
# * libpq: required for psycopg2
ENV PACKAGES="\
libaio1 \
libpq-dev \
"
ENV BUILD_PACKAGES="\
apt-utils \
build-essential \
"
RUN echo \
&& apt-get update -y \
&& apt-get install -y --no-install-recommends $BUILD_PACKAGES \
&& apt-get install -y --no-install-recommends $PACKAGES \
&& rm -rf /var/lib/apt/lists/* \
&& echo
RUN pip3 install --no-cache-dir pipenv -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN pipenv run pip install -r pipenv-require.txt --target /packages
# === Final Image ===
FROM base
WORKDIR /opt/myproj
ENV TNS_ADMIN=/opt/oracle/instantclient_19_3
ENV LD_LIBRARY_PATH=/opt/oracle/instantclient_19_3
ENV PACKAGES="\
unzip \
libaio1 \
libpq-dev \
"
# 复制之前镜像中生成的包到最终镜像的包默认安装路径
COPY --from=builder /packages /usr/local/lib/python3.7/site-packages
# 将Oracle Instance复制到镜像
COPY deps deps/
COPY sources.list ./
RUN echo \
&& mv /etc/apt/sources.list /etc/apt/sources.list.bak && mv /opt/myproj/sources.list /etc/apt/ \
&& apt-get update -y \
&& apt-get install -y --no-install-recommends $PACKAGES \
&& mkdir /opt/oracle && cd /opt/oracle \
&& mv /opt/myproj/deps/instantclient-basic-linux.x64-19.3.0.0.0dbru.zip /opt/oracle/client.zip \
&& unzip client.zip && rm client.zip \
&& cd /opt/oracle/instantclient_19_3 \
&& ln -s /lib/x86_64-linux-gnu/libnsl.so.1 /usr/lib/libnsl.so.1 \
&& rm -rf /var/lib/apt/lists/* \
&& echo
CMD echo "hello world!"
生成镜像
$ pipenv lock -r > pipenv-require.txt
$ sudo docker build -t db-conn .
这里要说明的是,构建时使用 pipenv 是因为能保证每次安装包都能保证依赖相同
再最终的镜像中,直接使用 python3 运行就可以了,因为一个镜像已经是一个经过控制的运行时了
$ sudo docker run --rm -it -v $PWD:/opt/myproj db-conn python3 test_oracle.py
经过修改基础镜像并移除构建工具,现在导出后的.tar.bz2
压缩包有 200M
大小,比对优化之前的 495M
减小了百分之六十的大小