当前位置:   article > 正文

【踩坑记录】基于 wsl2 环境部署 fastapi 封装的服务_fastapi打包部署到windows

fastapi打包部署到windows

【踩坑记录】基于 wsl2 环境部署 fastapi 封装的服务

背景:要求封装一个图片上色的api接口,接口的输入为一张黑白图,返回的结果是一张对灰度图片进行染色后的图片

现在很多开源项目可能都只针对linux系统做了适配,但是对于大多数时候都只有一台windows笔记本 or 比较依赖windows环境的学习者来说,使用wsl2 在windows上搭建一个ubuntu 系统就可以以屁眼插火箭的速度提升了部署效率,大大节省了配置环境的时间成本!

本博客用以记录封装 api 的整个流程。

使用工具:

  • pytorch docker 包(本实验要求cuda版本最低为11.8)
  • wsl2(ubuntu)
  • fastapi
  • postman

启动 wsl2

首先就是在windows上安装ubuntu啦,其实整个过程还是比较容易的,网上教程很多,不做赘述。

安装完成之后就可以从 cmd 中使用ubuntu了,界面如下:

image-20231204210059796

值得一提的是,wsl 相较于原生ubuntu有个天然优势,就是在wsl下依旧可以访问windows下的目录,可以使用linux的操作逻辑去管理windows下的文件,非常方便。相较于把文件上传到服务器,再用ssh去连接服务器只能突出一个丝滑和优雅 :)

共享文件夹的path为:/mnt (可以看到目录下的C D E盘)

image-20231204210512125

拉取 docker 环境

===============================================================================================
这一部分主要是为了聊docker,如果无兴趣且环境已经解决(例如本机conda有torch)可直接跳到本机部署部分
===============================================================================================
  • 1
  • 2
  • 3

安装部分

首先是安装docker,其实之前写过一个和安装docker相关的博客,在这里()。不过由于之前初学理解不深,且步骤不全、不够简化。现在重新对 docker安装 进行阐述:

1.首先是更新库(这一步如果之前更新过库的话,这一步可以跳过)

sudo apt-get update
  • 1

2.卸载旧版本docker(如果之前没装过,这一步也可以跳过)

sudo apt-get remove docker docker-engine docker.io
  • 1

3.安装docker(可能截止到目前为止最有用的一步,笑死)

sudo apt install docker.io
  • 1

其实到这一步就已经安装完docker了,对,就已经可以正常使用了。。。。(为什么之前官方那个那么复杂,所以这才是官方鸭 hhhh)

为了每次服务器(主机)关机重启后能自动运行docker,而不是手动去让docker运行,那么可以将docker设置为开机自启(没错,就是微信默认设置的那玩意==)

命令如下:

4.设置自启

sudo systemctl start docker
sudo systemctl enable docker
  • 1
  • 2

可以输入 docker --version 查看是否安装成功

image-20231204211707160

docker安装完之后,有时候会出现用不了gpu的情况,例如你明明拉的是cuda版本的torch,但是就是用不了cuda,那是为啥呢,其实很有可能是因为你没有安装nvidia-docker,安装步骤也很简单,如下:

1.老步骤,首先更新库,然后安装nvidia-docker2包,指令如下:

sudo apt-get update
sudo apt-get install -y nvidia-docker2
  • 1
  • 2

2.重启docker

sudo systemctl restart docker
  • 1

3.可以测试下:

sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi
  • 1

如果返回显卡信息,就说明安装成功啦!

至此,docker 安装完毕。


创建容器

完成docker安装后,下一步就是去拉取pytorch镜像。步骤很简单,就是先去下载一个镜像,然后把镜像转成一个容器

1.拉取镜像

docker pull pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel
  • 1

这个是从docker hub获得的,网址如下:https://hub.docker.com/r/pytorch/pytorch/tags。解释下,docker pull 是拉取指令,后面的是具体版本,指的是pytorch2.1.0 版本,cuda是11.8,cudnn为8。

devel是版本,版本还有一种是runtime,大概意思就是devel版本中的库更全,而runtime只包含基本库,类比conda和miniconda,不过空间够的话就拉devel版本吧,有的包好像还包含base版本,反正都是库全不全的区别!(目前理解,很有可能不对哈哈哈哈哈)

2.构建容器

详细命令如下:

docker run -it --gpus all -v [本地路径]:[容器内部路径] --name [容器名字] -p [本地端口]:[容器端口] [镜像名]:[镜像tag] /bin/bash
  • 1

详细说明下:

  • -it 指的是前台运行,会直接进入到容器内部
  • –gpus 指的是容器可以使用所有的显卡
  • -v 指的是挂载,将本地的某个路径与容器的某个路径挂载下。(为什么要这样呢,因为如果不挂载,可以想象一下,你要在容器内部去运行某个项目,然后你要把你本地的文件传到容器里面去,那你要先看容器名,然后使用各种命令把文件复制到容器文件夹中去。但如果你挂载了之后,挂在后的文件夹会一直保持一致,也就是只要你的本地文件夹内容变化了,容器内的内容也会跟着变化,会少很多麻烦!)
  • –name 指定名字
  • -p 是指端口映射,这一步也相当重要!举个例子,那这个项目来说,假设我的服务部署在docker容器中(项目在docker容器里面运行),设置项目监听8000端口。如果其他用户想使用我的接口,那他们就需要从8000端口把图片输入进来,他们只能使用我宿主机的ip+端口,但是我的服务是部署在docker容器里的鸭,因此需要做个映射,把别人访问本机8000端口的请求送到访问容器的请求里面去。

后面不谈了。假设针对我的镜像:

image-20231204220606769

我创建容器的指令就应该是:

docker run -it --gpus all -v /mnt/e/colorize/code:/workspace --name colorize -p 8000:8000 pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel /bin/bash
  • 1

ok ,这样你应该就进入容器了,然后ctrl + p + q 可以返回宿主机。docker exec -it [容器ID] /bin/bash 又可以重新进入容器。

至此,容器部分结束!

之后在容器中运行 pip install requirements.txt -i https://mirrors.aliyun.com/pypi/simple 环境部分其实就解决了。


但是,docker还没有结束!这次部署学了更多新东西,想一并记录,不仅希望有助于提升下次工作效率,更希望能帮到其它有需要的朋友!

【踩坑记录】docker 打包

首先,是docker容器打包,其实之前一直有个疑问,就是 conda 完全能解决环境的问题了,大不了就是多花点时间,那为什么还要花那么多的时间去搭建docker容器呢,其实docker容器有几个天然优势,就是容器可以离线打包成压缩文件,这个文件包含了所有的环境,用户只需要获得你的tar压缩文件,对文件进行解压,获取镜像,然后这个镜像构建的容器就可以直接运行项目了,全程可离线,不会有任何库相关的问题(如果容器打包完整的话),真正的即插即用!

打包的具体步骤如下:
1.提交容器为镜像

docker commit container_name your_new_image_name
  • 1

container_name 是容器的名称或 ID,your_new_image_name 是新创建的镜像指定的名称。这个命令会将容器的当前状态(包括所有更改)保存为一个新的镜像。

2.保存镜像为 tar 文件
创建新镜像后,可以使用 docker save 命令将其保存为一个 tar 文件:

docker save your_new_image_name > your_new_image_name.tar
  • 1

也可以使用下述指令指定打包路径:

docker save -o /your/path/new_image_name.tar new_image_name
  • 1

这样就完成了打包的操作!

3.其他地方加载 tar 文件为镜像
在一台新的机器上,可使用下述命令,将打包好的镜像解压出来以便使用!

docker load -i your_new_image_name.tar
  • 1

至此,打包和解压步骤全部结束!
(另外因为pip打包之前都出问题,这里一并将可用的方法进行阐述)

【踩坑记录】pip 打包

首先进入conda环境

pip freeze > requirements.txt
  • 1

本地部署项目

部署项目,进入 github 主页,将项目下载到本地 == over

使用 fastapi 封装 api

1. 安装

首先是需要安装fastapi,安装指令如下:

pip install "fastapi[all]"
  • 1

2. 构建接口

因为本项目仅仅只考虑上传文件部分,因此不考虑其它接口的构建形式,官网可看详情

from typing import Annotated
from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这里就构建了两种post可访问的路由(地址)-- files 和 uploadfile,

构建完之后首先运行一次该文件,假设文件名为main.py,运行指令为:

uvicorn main:app --host 0.0.0.0 --port 8000
  • 1

这里就是将服务绑定到端口8000,设置主机为0.0.0.0,之所以设置为0.0.0.0是为了所有的连接都能够访问,不管是局域网还是宿主机都可以,默认一般设置为 127.0.0.1,就会导致只有本机可以使用了,其他的机器都用不了这个服务!

运行成功后截图如下

image-20231204224308708

此外,为了避免每次运行服务都需要额外的输入指令,可以设置一个函数,用于运行后直接启动。结合本项目,main.py文件的内容为:

from colorization_pipline import ImageColorizationPipeline
from fastapi.responses import StreamingResponse
from fastapi import FastAPI, File, UploadFile
from typing import Union
import numpy as np
import cv2
import io

model_path = 'modelscope/damo/cv_ddcolor_image-colorization/pytorch_model.pt'	#模型权重
colorizer = ImageColorizationPipeline(model_path=model_path)

app = FastAPI()

@app.post("/api/photo_process/colorize")
async def create_upload_file(file: Union[UploadFile, None] = None):
    contents = await file.read()	#读取输入的内容
    nparr = np.fromstring(contents, np.uint8)	
    image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)	#解码获取到的图片输入
    color_image = colorizer.process(image)		#上色函数

    # 将处理后的图像编码为JPEG格式
    _, encoded_image = cv2.imencode('.jpg', color_image)
    encoded_image_bytes = encoded_image.tobytes()

    # 创建一个字节流以便发送
    stream = io.BytesIO(encoded_image_bytes)
    
    # 返回图像
    return StreamingResponse(stream, media_type="image/jpeg")

if __name__=="__main__":
  import uvicorn
  uvicorn.run(app, host="0.0.0.0", port=8000)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

这个是结合本项目背景进行修改后的main文件。至此,接口封装完毕!

可以使用postman去测试端口是否可以使用

结果展示

输入及postman测试输出样例如下:

首先是人物图片测试:

image-20231205103329680

image-20231205103436226

其次是动漫场景测试:

image-20231205103823969

image-20231205103834876

最后是大场景测试:

image-20231205104547645

image-20231205104618214

至此,测试完毕!

【报错原因】

设置windows防火墙

首先,如果服务本地使用postman 访问localhost可以,但是其他机器或者使用其他ip不可以,那极有可能是因为防火墙问题。

解决方法就是新建出站规则和入站规则,具体如下:

选择 控制面板 -> 系统与安全 -> windows defender 防火墙 -> 高级设置

选择新建入站规则

image-20231205101143114

选择端口 —> 将开放的端口填入(本实验为8000)

image-20231205101333824

然后一直下一步,取个名字就可以了!

之后在出站规则中重复一次上述步骤。

至此,防火墙设置完毕!

设置端口转发(wsl2 下跑不了很可能是因为该原因)

原因是因为wsl2 会呈现出一个单独的机器,会有自己的一个独立的IP,但是由于依然只有宿主机的网卡,因此外部链接访问的话依然只能访问物理机,所以要先将请求转发到wsl2上,之后才能检测到该请求!

使用下述指令可以将访问宿主机 ip 的请求转发至 wsl 对应的地址

netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=8000 connectaddress=WSL的IP地址 connectport=8000
  • 1

至此,部署完成!

环境:

docker包 后续发布到docker hub上,工程文件后续发布到github上

硬件环境(可根据实际调整,要求较低)

  • AMD Ryzen 9 5950X 16-Core Processor
  • 单卡 RTX 3090(24G)

参考资料:

  • https://github.com/piddnad/DDColor(阿里团队的达摩院开发,在目前测试过的与colorize相关的project中取得了SOTA的结果)

  • https://fastapi.tiangolo.com/

  • https://www.zhihu.com/question/484035804/answer/3218940256

  • https://zhuanlan.zhihu.com/p/361934132

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/849222
推荐阅读
相关标签
  

闽ICP备14008679号