menu Chancel's blog
rss_feed
Chancel's blog
秋雨一何碧,山色倚晴空。

Dockerfile构建Python3.9 Flask镜像

warning 这篇文章距离上次更新于122天前,文中部分信息可能已失效,请自行甄别无效内容。

1. 前言

Docker兴起已经非常长的一段时间了,现在Docker已经非常成熟了,作为实践本文将尝试打包一个使用Flask框架的Web服务应用

  • https://github.com/chancelyg/syncmemo

Tips:本文默认已简单了解Docker的一些基础知识,如仓库、镜像、容器之间的关系以及基础的Docker操作如拉取镜像、运行容器等

2. 准备

要学习Docker项目打包,首先要安装Docker的环境,这里以 Ubuntu1804 为例子进行环境安装部署,官方已支持大部分Linux发行版直接安装,如下

curl -fsSL https://get.docker.com -o get-docker.sh
bash get-docker.sh

# 如果get-docker.sh下载不顺利,可从阿里云镜像站点下载
bash get-docker.sh --mirror Aliyun

拉取Hello World镜像测试安装是否顺利

docker run hello-world

# 输出如下
Unable to find image 'hello-world:latest' locally
...
For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Docker的环境安装完成

2.1. Dockerfile文件

Docker可以采用commit的方式打包容器,但缺点也很明显,由于分层存储的概念,在容器做任何简单的操作都将改动大量的底层文件,此时Commit导致镜像臃肿,此外由于缺乏记录工具,从容器Commit更改而来的镜像将不具备复用性可维护性

Dockerfile便能避免这一点,顾名思义Dockerfile是一个定义Docker的文本文件,内容由一条条指令构成,描述镜像该如何生成

2.2. Dockerfile 关键字

Docker的关键字不少,但基础入门需要了解的不多,定制一个镜像无非主要就2点

  • 运行环境
  • 运行指令

以SyncMemo为例,我们需要

  1. Linux操作环境
  2. Python3.9.1

为了满足这2个条件,我们简单了解以下操作指令

  • FROM指令

    • 导入基础的镜像,通常是Linux发行版或者应用环境(如Docker官方直接提供的Python3环境)
  • RUN命令

    • 通常用来执行命令行
    • RUN的层数将决定镜像的大小
    • RUN之间没有任何联系,RUN cd /app是错误的,要用WORKDIR来定义目录
  • COPY/ADD指令

    • COPY复制文件
    • ADD是支持解压缩、自动下载的高级COPY指令(如无特殊情况一般建议使用COPY指令
  • EXPOSE指令

    • 对外服务端口(Docker run -p 4704:3700... 3700即是对外服务端口)
  • WORKDIR/USER指令

    • RUN之间是没有联系的,所以如需定义Docker的工作目录(即每一个RUN命令执行时的初始路径)则采用WORKDIR
    • 同理,USER指令用于指定每一个RUN的执行者(在使用USER前必须先用RUN指令创建执行者)

掌握了以上的命令就已足够构建一个简单的镜像

3. 构建

SyncMemo基于Python3.9.1,由于没有合适的基础镜像,为了方便起见,则以Ubuntu1804为基础镜像

此次定制Dockerfile大致步骤如下

  1. 导入Ubuntu1804
  2. 编译Python3.9.1
  3. 克隆SyncMemo并安装项目依赖
  4. 运行程序

3.1. Dockerfile撰写

第一步是导入Ubuntu1804并安装编译工具,如下

# Compile python3.9.1
FROM ubuntu:18.04 AS compile-python  
WORKDIR /app
RUN apt-get update && \
apt-get install -y git libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev zlib1g-dev make gcc

基础工具安装后,我们需要编译一个Python3.9.1并克隆SyncMemo仓库安装相关的库依赖,如下

ADD https://www.python.org/ftp/python/3.9.1/Python-3.9.1.tgz ./
RUN tar -zxvf Python-3.9.1.tgz && \
./Python-3.9.1/configure --prefix=/usr/local/python3.9.1 && \
make && make install && \
git clone https://github.com/chancelyg/syncmemo.git && \
/usr/local/python3.9.1/bin/pip3 install -r syncmemo/requirements.txt -i https://pypi.doubanio.com/simple

然后合并一下上面的片段并添加执行命令和端口,整个Dockerfile文件,如下

FROM ubuntu:18.04
WORKDIR /app
RUN apt-get update && \
apt-get install -y git wget libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev zlib1g-dev make gcc 
RUN wget https://www.python.org/ftp/python/3.9.1/Python-3.9.1.tgz && \
tar -zxvf Python-3.9.1.tgz && \
./Python-3.9.1/configure --prefix=/usr/local/python3.9.1 && \
make && make install && \
rm -f ./Python-3.9.1.taz && rm -rf Python-3.9.1 && \
git clone https://github.com/chancelyg/syncmemo.git && \
/usr/local/python3.9.1/bin/pip3.9 install -r syncmemo/requirements.txt -i https://pypi.doubanio.com/simple

EXPOSE 7900

CMD ["/usr/local/python3.9.1/bin/python3","/app/src/main/py","--port=7900","--host=0.0.0.0"]

最后利用docker打包Dockerfile生成镜像

docker build -t syncmemo:v0.1 . --no-cache

# 此时位于/home/chancel/docker/syncmemo文件夹下,文件夹中只有Dockerfile一个文件

查看生成的镜像

docker images

# 输出如下
REPOSITORY         TAG                IMAGE ID       CREATED              SIZE
syncmemo           v0.1               098681d3e5e3   About a minute ago   761MB
...

打包成功!

3.2. 体积优化

虽然是打包成功了,但761Mb的镜像很显然太大了

一个简单的Python Flask应用不应该占用如此大的空间

优化的思路一般如下

  • 选择更小的基础镜像
  • 删减不必要的RUN指令减少分层
  • 采用多阶段构建
  • 其他第三方的Docker压缩工具来压缩镜像大小

Tips:APT/YUM/APK等系统仓库包安装工具时可以添加“非必须依赖”参数来减少安装的软件数量

alipine就比Ubuntu小了20倍左右,但实测alipine对Python编译不太友好,遂放弃

检查我们的Dockerfile文件,仅使用了一次RUN,分层这一点无法再优化

第三方工具情况有些复杂,这里不做测试

最后是考虑用多阶段构建来减少镜像大小

多阶段构建即放弃安装了编译Python的Ubuntu镜像,在编译Python后直接迁移新的二进制Python程序到全新的Ubuntu镜像中

多阶段构建Dockerfile如下,可自行参考

# Compile python3.9.1
FROM ubuntu:18.04 AS compile-python  
WORKDIR /app
RUN apt-get update && \
apt-get install -y git wget libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev zlib1g-dev make gcc wget

RUN wget https://www.python.org/ftp/python/3.9.1/Python-3.9.1.tgz --no-check-certificate && \
tar -zxvf Python-3.9.1.tgz && \
./Python-3.9.1/configure --prefix=/usr/local/python3.9.1 && \
make && make install && \
git clone https://github.com/chancelyg/syncmemo.git && \
/usr/local/python3.9.1/bin/pip3 install -r syncmemo/requirements.txt -i https://pypi.doubanio.com/simple

FROM ubuntu:18.04
WORKDIR /app/
COPY --from=compile-python /root/ /root/
COPY --from=compile-python /usr/local/python3.9.1 /usr/local/python3.9.1
COPY --from=compile-python /app/syncmemo /app/syncmemo
COPY app.conf /app/syncmemo/conf/app.conf
EXPOSE 7900
CMD ["/usr/local/python3.9.1/bin/python3","/app/syncmemo/src/main.py","--config=/app/syncmemo/conf/app.conf"]

在改用多阶段构建之后占用大小如下,可以看到明显减少了一半的体积

docker images

# 输出如下
REPOSITORY      TAG                IMAGE ID       CREATED          SIZE
syncmemo        latest             894cdf854d7d   29 seconds ago   325MB

4. 结束

参考资料

  • https://yeasy.gitbook.io/docker_practice/image/dockerfile/shell
  • http://dockone.io/article/8163
博文目录

[[replyMessage== null?"发表评论":"@" + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]