
# 在Notebook中通过Dockerfile从0制作自定义镜像用于推理
#### 场景说明
针对ModelArts目前不支持的AI引擎，您可以通过自定义镜像的方式将编写的模型导入ModelArts，创建为模型。
本文详细介绍如何在ModelArts的开发环境Notebook中使用基础镜像构建一个新的推理镜像，并完成模型的创建，部署为在线服务。本案例仅适用于华为云北京四和上海一站点。
操作流程如下：
1. [步骤一：在Notebook中构建一个新镜像]：在ModelArts的开发环境Notebook中制作自定义镜像，镜像规范可参考[创建模型的自定义镜像规范](https://support.huaweicloud.com/usermanual-standard-modelarts/modelarts_23_0219.html)。
2. [步骤二：构建成功的镜像注册到镜像管理模块]：将构建成功的自定义镜像注册到ModelArts的镜像管理模块中，方便下一步调试。
3. [步骤三：在Notebook中变更镜像并调试]：在Notebook中调试镜像。
4. [步骤四：使用调试成功的镜像用于推理部署]：将调试完成的镜像导入ModelArts的模型管理中，并部署上线。
 
 #### 步骤一：在Notebook中构建一个新镜像
本章节以ModelArts提供的基础镜像tensorflow为例介绍如何在ModelArts的Notebook中构建一个新镜像并用于模型部署。
1. 登录[ModelArts管理控制台](https://console.huaweicloud.com/modelarts/)，在左侧导航栏中选择"全局配置"，检查是否配置了访问授权。如果未配置，请先配置访问授权。参考[快速配置ModelArts委托授权](https://support.huaweicloud.com/permission-modelarts/modelarts_24_0085.html)完成操作**。**
2. 在[ModelArts管理控制台](https://console.huaweicloud.com/modelarts/)左侧导航栏，按需选择以下操作。
   - 新版：选择"模型开发与训练 \> Notebook"，进入"Notebook"页面。
   
   - 旧版：选择"开发空间 \> Notebook"，进入"Notebook"页面。
    
3. 单击右上角"创建"，进入"创建Notebook"页面，请参见如下说明填写参数。
   1. 填写Notebook基本信息，包含名称、描述、是否自动停止。
   
   2. 填写Notebook详细参数，如选择镜像、资源规格等。
      - "选择镜像"：选择预置镜像下任意一个支持CPU类型的镜像，例如：**tensorflow2.1-cuda10.1-cudnn7-ubuntu18.04**
      
      - "资源池类型"：选择公共资源池或专属资源池，此处以公共资源池为例。
      
      - "实例规格"：推荐选择GP Tnt004规格，如果没有再选择其他规格。
       
    
4. 参数填写完成后，单击"立即创建"进行规格确认。参数确认无误后，单击"提交"，完成Notebook的创建操作。 进入Notebook列表，正在创建中的Notebook状态为"创建中"，创建过程需要几分钟，请耐心等待。当Notebook状态变为"运行中"时，表示Notebook已创建并启动完成。
   
5. 打开运行中的Notebook实例。具体操作，请参见[打开Notebook实例](https://support.huaweicloud.com/devtool-modelarts/devtool-modelarts_3495.html#section4)。
   图1打开Notebook实例   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556273196.png "点击放大") 
6. 通过![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556432900.png)功能，上传Dockerfile文件和模型包文件到Notebook中，默认工作目录/home/ma-user/work/。
   Dockerfile文件的具体内容请参见[附录1：Dockerfile模板]。模型包文件需要用户自己准备，样例内容请参见[附录2：模型包文件样例]。
   图2上传Dockerfile文件和模型包文件   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002586792925.png) 
7. 打开Terminal终端，解压model.zip，解压后删除zip文件。
   ```
   #解压命令
   unzip model.zip
   ```
   图3在Terminal终端中解压model.zip   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002586952911.png "点击放大") 
8. 打开一个新的.ipynb文件，启动构建脚本，在构建脚本中指定dockerfile文件和镜像的推送地址。构建脚本当前仅支持华为云北京四和上海一站点。
   图4启动构建脚本   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556273356.png "点击放大")
   构建脚本内容如下：
   ```
   from modelarts.image_builder import ImageBuilder
   from modelarts.session import Session
   session = Session()
   image = ImageBuilder(session=session,
      dockerfile_path="/home/ma-user/work/Dockerfile",
      image_url="custom_test/tensorflow2.1:1.0.0",#custom_test是组织名，tensorflow2.1是镜像名称，1.0.0是tag
      context="/home/ma-user/work")
   result = image.build_push()
   ```
   等待镜像构建完成。镜像构建完成后会自动推送到SWR中。
   图5等待镜像构建完成   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556433052.png "点击放大") 
 
 #### 步骤二：构建成功的镜像注册到镜像管理模块
将[步骤一：在Notebook中构建一个新镜像]中构建成功的自定义镜像注册到镜像管理中，方便后续使用。
1. 登录[ModelArts管理控制台](https://console.huaweicloud.com/modelarts/)，在左侧导航栏按需选择以下操作。
   - 新版：选择"镜像"，在"已注册镜像"页签单击"注册镜像"，进入注册镜像页面。
   
   - 旧版：选择"镜像管理"，在"已注册镜像"页签单击"注册镜像"，进入注册镜像页面。
    
2. 选择镜像源地址、架构和类型后，单击"立即注册"。
   - "镜像源"：单击![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002586793013.png)图标，在"选择镜像"面板，选择目标镜像，地址为swr.cn-north-4-myhuaweicloud.com/custom_test/tensorflow2.1:1.0.0。其中custom_test/tensorflow2.1:1.0.0为[8]镜像构建脚本中设置的镜像地址。
   
   - "架构"：选择x86。
   
   - "类型"：选择CPU。
   
   - "规格"：按需选择。
    
3. 注册完成后，可以在镜像管理页面查看到注册成功的镜像。
 
 #### 步骤三：在Notebook中变更镜像并调试
使用制作完成的自定义镜像进行推理服务调试，调试成功后再导入到ModelArts的模型中并部署为在线服务。
1. 登录[ModelArts管理控制台](https://console.huaweicloud.com/modelarts/)，在左侧导航栏按需选择以下操作。
   - 新版：选择"模型开发与训练 \> Notebook"，进入"Notebook"页面。
   
   - 旧版：选择"开发空间 \> Notebook"，进入"Notebook"页面。
    
2. 停止[步骤一：在Notebook中构建一个新镜像]中创建的Notebook。
3. 在目标Notebook的"操作"列，单击"更多 \> 变更镜像"，在"变更镜像"页面，选择"自定义镜像"，将当前镜像变更为[步骤二]注册的镜像，勾选"我已阅读并同意《镜像免责声明》《云商店服务协议》"，单击"确定"。
4. 启动变更后的Notebook，并打开。进入Terminal运行界面，在工作目录，运行启动脚本run.sh，并预测模型。基础镜像中默认提供了run.sh作为启动脚本。
   图6运行启动脚本   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556273440.png "点击放大") 
5. 上传一张预测图片（手写数字图片）到Notebook中。
   图7手写数字图片   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556433106.png)
   图8上传预测图片   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002586793091.png) 
6. 重新打开一个新的Terminal终端，执行如下命令进行预测。
   ```
   curl -kv -F 'images=@/home/ma-user/work/test.png' -X POST http://127.0.0.1:8080/
   ```
   图9预测   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002586953057.png "点击放大")
   在调试过程中，如果有修改模型文件或者推理脚本文件，需要重启run.sh脚本。执行如下命令先停止nginx服务，再运行run.sh脚本。
   ```
   #查询nginx进程
   ps -ef |grep nginx 
   #关闭所有nginx相关进程
   kill -9 {进程ID}  
   #运行run.sh脚本
   sh run.sh
   ```
   也可以执行pkill nginx命令直接关闭所有nginx进程。
   ```
   #关闭所有nginx进程
   pkill nginx
   #运行run.sh脚本
   sh run.sh
   ```
   图10重启run.sh脚本   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556273512.png "点击放大") 
 
 #### 步骤四：使用调试成功的镜像用于推理部署
将[步骤三：在Notebook中变更镜像并调试]中调试成功的自定义镜像导入到模型中，并部署为在线服务。
1. 登录[ModelArts管理控制台](https://console.huaweicloud.com/modelarts/)，在左侧导航栏中选择"模型管理"，单击"创建"，进入模型管理。
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/public_sys-resources/note_3.0-zh-cn.png)
   如果您使用的是新版控制台，请在左侧导航栏最下方单击"返回旧版"，然后在"资产管理 \> 模型管理"页面创建模型。
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556433172.png)
   
2. 设置模型的参数，如[图11]所示。
   - 元模型来源：从容器镜像中选择。
   
   - 容器镜像所在的路径：单击![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002586793133.png)选择前面创建的镜像。
   
   - 容器调用接口：选择HTTPS。
   
   - host：设置为8443。
   
   - 部署类型：选择在线部署。
   
   
   图11设置模型参数   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002586953095.png "点击放大") 
3. 填写apis定义，单击"保存"生效。apis定义中指定输入为文件，具体内容参见下面代码样例。
   图12填写apis定义   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556273588.png "点击放大")
   apis定义具体内容如下：
   ```
   [{
   "url": "/",
   "method": "post",
   "request": {
   "Content-type": "multipart/form-data",
   "data": {
   "type": "object",
   "properties": {
   "images": {
   "type": "file"
   }
   }
   }
   },
   "response": {
   "Content-type": "application/json",
   "data": {
   "type": "object",
   "properties": {
   "result": {
   "type": "integer"
   }
   }
   }
   }
   }]
   ```
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/public_sys-resources/notice_3.0-zh-cn.png)
   apis定义提供模型对外Restfull api数据定义，用于定义模型的输入、输出格式。
   - 创建模型填写apis。在创建的模型部署服务成功后，进行预测时，会自动识别预测类型。
   
   - 创建模型时不填写apis。在创建的模型部署服务成功后，进行预测，需选择"请求类型"。"请求类型"可选择"application/json"或"multipart/form-data"。请根据元模型，选择合适的类型。
     - 选择"application/json"时，直接填写"预测代码"进行文本预测。
     
     - 选择"multipart/form-data"时，需填写"请求参数"，请求参数取值等同于[方式一：使用图形界面的软件进行预测（以Postman为例）](https://support.huaweicloud.com/inference-modelarts/inference2.0-modelarts-0007.html#section2)Body页签中填写的"KEY"的取值，也等同于[方式二：使用curl命令发送预测请求](https://support.huaweicloud.com/inference-modelarts/inference2.0-modelarts-0007.html#section3)上传数据的参数名。
      
     
4. 设置完成后，单击"立即创建"，等待模型状态变为"正常"。
5. 单击新建的模型名称左侧的小三角形，展开模型的版本列表。在操作列单击"部署 \> 在线服务"，跳转至在线服务的部署页面。
6. 在部署页面，参考如下说明填写关键参数。 "名称"：按照界面提示规则自定义一个在线服务的名称，也可以使用默认值。
   "资源池"：选择"公共资源池"。
   "模型来源"和"选择模型及版本"：会自动选择模型和版本号。
   "计算节点规格"：在下拉框中选择"限时免费"资源，勾选并阅读免费规格说明。
   其他参数可使用默认值。
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/public_sys-resources/note_3.0-zh-cn.png)
   如果限时免费资源售罄，建议选择收费CPU资源部署。当选择收费CPU资源部署在线服务时会收取少量资源费用，具体费用以界面信息为准。
   
7. 参数配置完成后，单击"下一步"，确认规格参数后，单击"提交"启动在线服务的部署。
8. 进入"部署上线 \> 在线服务"页面，等待服务状态变为"运行中"时，表示服务部署成功。单击操作列的"预测"，进入服务详情页的"预测"页面。上传图片，预测结果。
   图13预测   
   ![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002556433310.png "点击放大") 
 
 #### 附录1：Dockerfile模板
Dockerfile样例，此样例可以直接另存为一个Dockerfile文件使用。此处可以使用的基础镜像列表请参见[ModelArts统一镜像](https://support.huaweicloud.com/usermanual-standard-modelarts/docker-modelarts_6022.html)。
```
FROM swr.cn-north-4.myhuaweicloud.com/atelier/tensorflow_2_1:tensorflow_2.1.0-cuda_10.1-py_3.7-ubuntu_18.04-x86_64-20221121111529-d65d817
# here create a soft link from '/home/ma-user/anaconda/lib/python3.7/site-packages/model_service' to '/home/ma-user/infer/model_service'. It’s the build-in inference framework code dir
# if the installed python version of this base image is python3.8, you should create a soft link from '/home/ma-user/anaconda/lib/python3.8/site-packages/model_service' to '/home/ma-user/infer/model_service'.
USER root
RUN ln -s /home/ma-user/anaconda/lib/python3.7/site-packages/model_service  /home/ma-user/infer/model_service
USER ma-user
# here we supply a demo, you can change it to your own model files
ADD model/  /home/ma-user/infer/model/1
USER root
RUN chown -R ma-user:ma-group  /home/ma-user/infer/model/1
USER ma-user
# default MODELARTS_SSL_CLIENT_VERIFY switch is "true". In order to debug, we set it to be "false"
ENV MODELARTS_SSL_CLIENT_VERIFY="false"
# change your port and protocol here, default is 8443 and https
# ENV MODELARTS_SERVICE_PORT=8080
# ENV MODELARTS_SSL_ENABLED="false"
# add pip install here
# RUN pip install numpy==1.16.4
# RUN pip install -r requirements.txt
# default cmd, you can chage it here
# CMD sh /home/ma-user/infer/run.sh
```
 #### 附录2：模型包文件样例
模型包文件model.zip中需要用户自己准备模型文件，此处仅是举例示意说明，以一个手写数字识别模型为例。
Model目录下必须要包含推理脚本文件customize_service.py，目的是为开发者提供模型预处理和后处理的逻辑。
图14推理模型model目录示意图（需要用户自己准备模型文件）   
![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000002586793343.png "点击放大")
推理脚本customize_service.py的具体写法要求可以参考[模型推理代码编写说明](https://support.huaweicloud.com/usermanual-standard-modelarts/inference-modelarts-0057.html)。
本案例中提供的customize_service.py文件具体内容如下：
```
import logging
import threading
import numpy as np
import tensorflow as tf
from PIL import Image
from model_service.tfserving_model_service import TfServingBaseService
class mnist_service(TfServingBaseService):
    def __init__(self, model_name, model_path):
        self.model_name = model_name
        self.model_path = model_path
        self.model = None
        self.predict = None
        # 非阻塞方式加载saved_model模型，防止阻塞超时
        thread = threading.Thread(target=self.load_model)
        thread.start()
    def load_model(self):
        # load saved_model 格式的模型
        self.model = tf.saved_model.load(self.model_path)
        signature_defs = self.model.signatures.keys()
        signature = []
        # only one signature allowed
        for signature_def in signature_defs:
            signature.append(signature_def)
        if len(signature) == 1:
            model_signature = signature[0]
        else:
            logging.warning("signatures more than one, use serving_default signature from %s", signature)
            model_signature = tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY
        self.predict = self.model.signatures[model_signature]
    def _preprocess(self, data):
        images = []
        for k, v in data.items():
            for file_name, file_content in v.items():
                image1 = Image.open(file_content)
                image1 = np.array(image1, dtype=np.float32)
                image1.resize((28, 28, 1))
                images.append(image1)
        images = tf.convert_to_tensor(images, dtype=tf.dtypes.float32)
        preprocessed_data = images
        return preprocessed_data
    def _inference(self, data):
        return self.predict(data)
    def _postprocess(self, data):
        return {
            "result": int(data["output"].numpy()[0].argmax())
        }
```
