Docker實戰——身份證識別引擎容器化

本人在一家人臉識別公司做Java後端開發,因工作需要,要將AI引擎進行容器化,方便以後部署,這其中包括人臉識別引擎、身份證識別引擎等。

這是一篇對引擎容器化的操作的記錄和總結,本文不會過多涉及Docker的基本知識,以下示例針對身份證識別(OCR)引擎。

適用人羣:機器學習初學者,轉AI的開發人員。
容器化技術:Docker
操作系統:Centos7
顯卡及驅動:CUDA 10.0

應用容器化步驟

參照 Docker應用容器化(將應用程序部署到容器中)
在這裏插入圖片描述
如上圖:
完整的應用容器化過程主要分爲以下幾個步驟。

  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容器大小,默認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技術入門與實戰》一書第八章的最後,有做一個很好的最佳實踐總結:
在這裏插入圖片描述

最後附上指令說明:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章