Docker实战——身份证识别引擎容器化

本人在一家人脸识别公司做Java后端开发,因工作需要,要将AI引擎进行容器化,方便以后部署,这其中包括人脸识别引擎、身份证识别引擎等。

这是一篇对引擎容器化的操作的记录和总结,本文不会过多涉及Docker的基本知识,以下示例针对身份证识别(OCR)引擎。

适用人群:机器学习初学者,转AI的开发人员。
容器化技术:Docker
操作系统:Centos7
显卡及驱动:CUDA 10.0

应用容器化步骤

参照 Docker应用容器化(将应用程序部署到容器中)
Docker实战——身份证识别引擎容器化_第1张图片
如上图:
完整的应用容器化过程主要分为以下几个步骤。

  1. 编写应用代码。
  2. 创建一个 Dockerfile,其中包括当前应用的描述、依赖以及该如何运行这个应用。
  3. 对该 Dockerfile 执行 docker image build 命令。
  4. 等待 Docker 将应用程序构建到 Docker 镜像中。

一旦应用容器化完成(即应用被打包为一个 Docker 镜像),就能以镜像的形式交付并以容器的方式运行了。
以下示例介绍到构建镜像并运行,并提供访问,后续的推送到仓库忽略。

以下

构建

构建java项目可以参照
创建自己的镜像并部署web项目

容器化ocr引擎比较复杂,以下将自己的步骤和遇到的问题贴出来,和大家一起学习:

创建目录并考入

宿主机建立/data/engines目录,根据引擎功能在下面建立相应目录,如身份证识别模块,建立/data/engines/idcard 目录,并拷入对应应用程序(引擎文件)idcard.tar.gz。

注意:由于应用程序由底层引擎方直接提供,这里直接拷入程序文件,这是一种方式,还有一种方式,在Dockefile中,将git仓库中的代码拉下来进行打包成应用程序,这里推荐第一种。

创建Dockerfile(核心)

Dockefile文件指导 Docker 完成应用的容器化(创建一个包含当前应用的镜像),Dockerfile 能实现开发和部署两个过程的无缝切换。应用容器化的核心在于Dockerfile。

/data/engines/idcard中创建Dockerfile文件,并写入:

#build image
# 由于需要显卡驱动,这里的基础镜像到dockerhub中去搜索,关键字cuda
FROM nvidia/cuda:10.0-base-centos7

#维护者信息
LABEL maintainer="[email protected]"

#如果是从git仓库中下载代码并打包成应用程序,可在下面写命令执行,这里不需要

# runtime image

#暴露访问端口
EXPOSE 20003

#设置操作路径为/app,后续命令默认在/app路径下执行
WORKDIR /app

COPY . /app

RUN tar xzvf idcard.tar.gz

RUN chmod 777 /app/*

#解压后/idCard下是应用程序启动文件start.sh
#容器运行时执行的脚步命令
ENTRYPOINT [ "sh","-c","cd /app/IdCard && sh start.sh"]

创建镜像

docker build -t engines/idcard:v1.0.0 .
不要省略了后面的 . 表示当前路径,少了会报错)

可能遇到的问题及解决

1.如果创建失败,会有提示,通过docker image ls查看所有镜像,并删除失败的镜像,找到对应的image id,执行:
docker rmi 8806d3001bfa(image id)
如果提示删除失败:Error response from daemon: conflict: unable to delete 8806d3001bfa (must be forced) - image is being used by stopped container 0ccf64707238,由于其容器没有删除,可以先执行:
docker rm 0ccf64707238(container id)
再执行:docker rmi 8806d3001bfa
其中通过docker ps -a查找image对应的container id

2.如果一直创建镜像,都没有成功,镜像本身又比较大,会有多个未创建成功的镜像,那么后续创建过程中,可能会No space left on device,根据如下解决(du -sh 查看文件大小):
https://blog.csdn.net/u014520797/article/details/80840214
或者是docker没有释放,将文件锁住

运行应用程序

docker run -d -p 20003:20003 enines/idcard:v1.0.0
(-d 表示后台运行)

docker命令后面参数:
-d: 后台运行容器,并返回容器ID;
-i: 以交互模式运行容器,通常与 -t 同时使用;
-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;

可能遇到的问题及解决

运行后通过命令docker ps -a 发现容器很快就变成exited状态,正常应该是up状态,原因如下:
Docker容器同时只能管理一个进程,如果这个进程退出那么容器也就退出了,但这不表示容器只能运行一个进程(其他进程可在后台运行),但是要使容器不退出必须有一个前台执行的进程。start.sh是后端运行(可以查看其脚本),容器中会退出,所以脚本中最后一个进程一定要用前台运行方式即在进程最后不加&(&表示后台运行)。

解决方法:
修改Dockerfile,在命令最后加上 & tail -f /etc/hosts

ENTRYPOINT [ "sh","-c","cd /app/IdCard && sh start.sh & tail -f /etc/hosts"]

访问

构建并运行成功后,通过浏览器或postman调用请求访问:
eg: ip:20003/index

如何调试?

写好Dockerfile,构建镜像过程中,可能会出现各种各样的问题,这些可以根据提示查看,但是有时候镜像构建成功并运行,但是发现并没有成功,需要调试,可以通过以下两种方式

1.屏蔽启动脚本,到镜像中运行并查看
(如果镜像中程序无法运行起来,可以先暂时修改Dockerfile,让其执行sleep time,然后再进去进行调试,如下让程序运行一小时)
er:ENTRYPOINT [ “sh”,"-c",“sleep 3600”]

然后查看容器,找到容器id:
docker ps -a
启动容器:
docker start d21690956562
进入容器:
sudo docker exec -it d21690956562 /bin/sh (前提,容器要先runing,其status是up,通过docker ps -a命令查看)
或者sudo docker exec -it d21690956562 /bin/bash

这时候就可以进入容器进行调试,相当管用,哈哈。

相应的 ,停止容器的运行:
docker container stop d21690956562(container id)

2.查看日志
查找对应的运行的镜像,找到其container id
sudo docker ps -a|grep idcard
查看其日志
docker logs -f c562(container id)

优化

基础镜像包

由于公司有自己的显卡驱动镜像包,优化的时候进行了替换

镜像大小

由于Dockerfile中的每一行都类似于一次提交操作,会在上一行命令上添加一个镜像层,而这个镜像层是占用空间的:
Docker实战——身份证识别引擎容器化_第2张图片
Docker容器大小,默认10G,基本已经够用,自己制作的镜像尽可能的减少空间占用,镜像大小基本等于基础镜像大小+运行程序大小,对于构建过程中的过程文件,要减少其对空间的占用

比如:基础镜像(后续会)是700M,tar包是200M,解压后是400M,那么经过优化前的镜像是2G多

针对之前的Dockerfile进行优化,如下几个点:

  1. 基础镜像替换为公司的
  2. 删除中间过程中的包
  3. chmod * (会造成修改很多文件)改为给指定文件授权
  4. 多条命令合为一条命令(程序包的获取、解压、删除在一行命令中)
  5. COPY . /app 放在构建镜像时的最后命令,在ENTRYPOINT之前

针对如上第四点,为了优化,决定将程序包放在gitlab上(内网下载会很快),构建镜像时进行下载,又会遇到两个问题:

  • 基础镜像中无wget命令
  • RUN wget idcard.tar.gz & tar xzvf idcard.tar.gz & rm idcard.tar.gz, 多条命令放在一起,由于下载是后端运行,会导致后面的命令先执行,则会报文件无法找到的错

为了解决上述两个问题,一是在Dockerfile中安装wget(1M左右,可忽略影响),二是通过管道方式直接进行wget的下载和解压,最后直接下载到解压后的文件

最终的Dockerfile文件如下:

#build image
# FROM nvidia/cuda:10.0-base-centos7
FROM xxx.xxx/centos76gpuukeynet:v1.1

#维护者信息
LABEL maintainer="[email protected]"

# runtime image
#暴露访问端口
EXPOSE 20003

#设置操作路径为/app,后续命令默认在/app路径下执行
WORKDIR /app

# 下载wget
RUN yum -y install wget
# 通过管道直接下载为解压后的文件
RUN wget idcard.tar.gz -O - | tar xzv  

#给运行脚本授权
RUN chmod 777 /app/IdCard/start.sh

COPY . /app

#解压后/idCard下是应用程序启动文件start.sh
#容器运行时执行的脚步命令
ENTRYPOINT [ "sh","-c","cd /app/FaceGo && sh start.sh && tail -f /etc/hosts"]
#ENTRYPOINT [ "sh","-c","sleep 1800"]

经过上述构建生成的镜像仅有1.1G左右。

总结

第一次写Dockerfile文件,本身对于linux命令不是很熟,所以折腾了很久,有很多不完善的地方,还希望大家提出宝贵意见。

进行应用容器化,核心还是写Dockerfile文件,其中很重要的一点就是Docker是一层一层镜像层进行叠加的,所以要构建简洁的镜像,需要自己不断努力,减少中间文件的产生、中间过程命令的提交,当然中间会遇到各种各样问题(比如后续会发现缺少各种.so文件,需要在宿主机找到或者下载,通过Dockefile传到镜像中),需要不断努力。

《Docker技术入门与实战》一书第八章的最后,有做一个很好的最佳实践总结:
Docker实战——身份证识别引擎容器化_第3张图片

最后附上指令说明:
Docker实战——身份证识别引擎容器化_第4张图片

你可能感兴趣的:(运维相关)