使用docker部署一个简单的streamlit应用

✅ 1. 准备工作

☑️ 1.1 streamlit脚本准备

把你的streamlit应用涉及到的东西都放到一个文件夹里,该封装的封装好,保证最后可以直接使用

streamlit run XXX.py

这个直接运行,不要有多余的步骤

streamlit页面优化

  1. 在sidebar添加logo(通过使用st.sidebar.image()实现):https://discuss.streamlit.io/t/inserting-image-at-side-bar/12114

  2. 在sidebar添加文字st.sidebar.text()实现:https://docs.streamlit.io/en/stable/api.html?highlight=sidebar#add-widgets-to-sidebar

  3. 修改sidebar颜色(自己通过浏览器控制台找到控制sidebar颜色的css类):https://discuss.streamlit.io/t/how-to-set-the-background-color-of-st-sidebar/4888

大致代码:

st.sidebar.image("XXXX/logo.png", use_column_width=True)
st.sidebar.text("联系我们:")
st.sidebar.text("https://XXXX/")
st.markdown(
    """
<style>
.css-17eq0hr{
    background-color: #182b45!important;
    color: white!important;
    background-image: none;
    
}
.css-j8zjtb{
    font-size:1.0rem;
}
</style>
""",
    unsafe_allow_html=True,
)

最后的效果大概是:
在这里插入图片描述

☑️ 1.2 Dockerfile准备

一般都是通过dockerfile文件来构建一个image镜像,然后通过镜像启动一个容器,启动容器的时候,在dockerfile里就配置了容器启动时要自动执行的命令,即streamlit run XXX.py

dockerfile文件的书写介绍如下

▶️ 1.2.1模板

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..

# 一开始要指明所基于的镜像名称
# Base image to use, this must be set as the first line
FROM ubuntu

# 说明维护者信息
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com

# 镜像操作指令,例如 RUN 指令,RUN 指令将对镜像执行跟随的命令。
# 每运行一条 RUN 指令,镜像添加新的一层,并提交。
# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# CMD 指令,来指定运行容器时的操作命令
# Commands when creating a new container
CMD /usr/sbin/nginx
  • Dockerfile 由一行行命令语句组成,并且支持以 # 开头的注释行。
  • 一般的,Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。

▶️ 1.2.2 我个人的


获取基础镜像
如果之前是使用docker构建的基本环境,然后在里面重新安装的stremalit,那么可以使用下面语句来查看自己的基础镜像

docker inspect IQA(容器的名称或者ID)
# 返回一个很长的json字符串,
> "Config":{
	"Image": "pytorch/pytorch:1.7.0-cuda11.0-cudnn8-runtime",
}

就可以看到基础镜像了(我的场景是需要用streamlit展示一个pytorch的CV相关模型,所以选择了pytorch作为基础镜像)

书写格式,看了几个例子,如下:

FROM ubuntu
FROM registry.baidubce.com/paddlepaddle/paddle:2.0.0-gpu-cuda10.1-cudnn7
FROM ddebby/ai:v0.1.0

除了获取基础镜像,还需要

  1. 把需要的文件复制到开启的容器中,
  2. 安装必要的包
  3. 设置容器开机运行的指令

我的项目比较简单,文件结构:

|--serving
	|-- dockerfile
	|-- streamlit.py

构建镜像的时候,肯定是切到这个目录下,所以dockerfile中移动文件的语句一般就是直接当前文件夹下的

在进行过上述步骤后,dockerfile文件大致如下:

# 基础镜像
FROM pytorch/pytorch:1.7.0-cuda11.0-cudnn8-runtime

# 镜像维护者
MAINTAINER user user@XXX.com

# 把需要使用的文件复制过去,如果是多个文件,就放一个文件夹
COPY streamlit.py ./streamlit.py

# 要在基础镜像里干点啥,
RUN apt-get update && apt-get install -y git
# 如果默认镜像里没有git,就要先安装git
RUN pip install git+http://0.0.0.7:8080/huangs/repo.git --user
RUN pip install streamlit -i https://pypi.tuna.tsinghua.edu.cn/simple
# 如果streamlit按的很慢,可以换个源

# 开放容器的8501端口,因为streamlit默认端口是8501
EXPOSE 8501

# 配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
ENTRYPOINT ["streamlit", "run"]
CMD ["streamlit.py"]
# 上面两个指令合到一起就是 stremlit run streamlit.py

简单解释下,参考 Dockerfile介绍

ENTRYPOINT有两种格式:
""
ENTRYPOINT ["executable", "param1", "param2"]
✅ENTRYPOINT command param1 param2(shell中执行)。

每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效。
""

CMD支持三种格式:

""
CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;
CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
✅CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;

指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。
如果指定了多条命令,只有最后一条会被执行。

如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。
""

✅ 2. pip安装私有包

在上述dockerfile写好,build时候报错了。因为dockerfile属于一个不可中断的脚本执行过程,所以中间使用gitlab上的项目时,没有办法输入用户名和密码,所以无法访问。

这就涉及到pip安装私有包,查看gitlab的文档Installing Private Python Packages

正确的形式应该是:

git+https://${GITLAB_TOKEN_USER}:${GITLAB_TOKEN}@gitlab.com/user/project.git@{version}

写出来类似:

git+http://huangs:Hdjfyhwi7KznYT7hpoSsGJ@0.0.0.7:8080/huangs/repo.git

对于gitlab,需要根据Creating a Deploy token设置一个该项目的部署token(在里面可以设置username(huangs))

参考:


✅ 3. docker build

☑️ 3.1 构建镜像

docker build -t streamlitapp:latest .

使用docker build来构建镜像,然后会弹出一大堆info,类似

Step 1/9 : FROM pytorch/pytorch:1.7.0-cuda11.0-cudnn8-runtime
Step 2/9 : MAINTAINER huangs huangs@shaiic.com
Step 3/9 : COPY streamlit_demo.py ./streamlit_demo.py
Step 4/9 : RUN apt-get update && apt-get install -y git
Step 5/9 : RUN pip install git+http://huangs:XXXXXXX@0.0.0.7:8080/huangs/repo.git

# 简单来说,就是你的dockerfile里有多少句 这里就有多少个阶段。。
Step 6/9 : RUN pip install streamlit

# 这步开始安装streamlit(8.0MB)和pandas(11.7MB)等库开始变慢了。。还有pyarrow(23.6MB) 慢慢等着吧
# 挨过这几个大的按起来就快了

# 最后提示:
Step 9/9 : CMD ["streamlit.py"]
 ---> Running in 6c740d85f7c0
Removing intermediate container 6c740d85f7c0
 ---> 6a43abdd557e
Successfully built 6a43abdd557e
Successfully tagged streamlitapp:latest

你的dockerfile里有多少句,这里就有多少step,最后执行完大概会提示以上信息。然后就可以在这个构建的镜像上运行容器了

☑️ 3.2 运行容器

docker run -p 8502:8501 streamlitapp:latest

这里的8502:8501,后者表示镜像的端口,前者表示本地的端口,将镜像的8501端口映射到本地的8502(使用8502是因为本地的8501端口被其他容器占用了。。。)

如果希望容器退出后自动关闭(删除),可以使用--rm参数

# 使用--rm参数 容器退出时自动删除容器
docker run --rm -p 8502:8501 streamlitapp:latest

不然每次退出容器后,这个容器都会以Exited状态存在在系统中,占资源

☑️ 3.3 删除容器和镜像

如果不幸镜像构建成功,但是执行入口脚本时报错,导致容器失败,需要删除镜像,重新构建一次,那么需要先删除对应的容器,再去删除镜像,不然删不干净。
参考:Docker中如何删除image(镜像)

# 1. 查看镜像ID
> docker image ls
6a43abdd557e

REPOSITORY     TAG     IMAGE ID      CREATED         SIZE
streamlitapp latest  6a43abdd557e   2 minutes ago   5.15GB

# 查看当前运行的容器
> docker ps -a|grep streamlit
# 大概看到了两个
> docker ps -a
CONTAINER ID   IMAGE       COMMAND                  CREATED       STATUS  PORTS  NAMES
1291a058c6db streamlitapp "streamlit run strea…" 10 minutes ago Exited XX brave_wescoff
974a98b2c3a7 streamlitapp:latest "streamlit run strea…"   11 minutes ago Created allant_goldstine

# 使用docker stop()或者kill关闭容器
# stop给与一定的关闭时间交由容器自己保存状态,kill直接关闭容器
#docker stop 容器ID或容器名 参数 -t:关闭容器的限时,如果超时未能关闭则用kill强制关闭,默认值10s,这个时间用于容器的自己保存状态 docker stop -t=60 容器ID或容器名
docker stop 1291a058c6db

# 关了之后删除容器  
docker rm 1291a058c6db
docker rm 974a98b2c3a7

# 删了容器之后才可以删除镜像
docker rmi 6a43abdd557e

rm Remove one or more containers
rmi Remove one or more images

# 删除之后再看镜像列表,可以看到已经没有了。
docker image ls

在这里插入图片描述

☑️ 3.4 后台运行容器/后台调回前面

参考:docker run的–rm选项详解

执行docker run命令带–rm命令选项,等价于在容器退出后,执行docker rm -v。
Docker version 20.10.8,zookeeper 3.6.3,–rm选项可以与-d同时使用

# 使用-d参数 调到后台运行
# 使用--rm参数,在容器退出后,删除容器
 docker run -d --rm -p 8502:8501 streamlitapp 

# 获取容器id
docker ps -a|grep streamlit

# 使用attach参数来把后台运行的容器调到前面
docker attach XXXX(容器ID)

另外,可以参考: