最近在參加天池比賽,由於比賽需要使用阿里雲容器鏡像服務完成線上預測任務,所以花費了3-4天的時間了解並使用Docker完成相關鏡像操作,在此分享下我學習的內容,以下是本文的目錄結構:
- 介紹
- 鏡像
- 容器
- 倉庫
- 阿里鏡像服務使用流程
- 開通鏡像服務
- 構建鏡像
- 檢查鏡像
- 推送鏡像
- 其他
一、介紹
首先我們要知道為什么要使用Docker(可實現容器和鏡像)?Docker是一種虛擬化方式,它與虛擬機不同之處在於:Docker不像虛擬機要提前下載好一個操作系統(Operating System, OS)才能實現虛擬化,很方便讓用戶把一個用戶程序從一個平台直接遷移到另一個平台。舉個簡單的例子:你用一台電腦,從Github上fork的人臉識別項目代碼要求使用環境得在cuda10.1和Pytorch1.4,然后你又要搞另外一個有關垃圾分類的算法項目,但這個項目又要求你的環境配置是cuda10。不同項目要求不同環境,這就比較讓人為難,因此Docker的出現能讓你本機安裝環境不變的情況,虛擬化出不同的環境,且不同容器內放着不同代碼,這樣你能更好的進行項目遷移和擴展。雲服務提供商也是用這種辦法讓用戶更高效地調用他們平台資源。
那具體有哪些應用場景呢?例如:
- 公司要你同時進行多個不同環境配置下不同的項目。
- 算法比賽要對你代碼預測時間進行考評。
- 雲服務提供商要更好地利用服務器資源。
Docker包括三個概念:鏡像(Image),容器(Container)和倉庫(Repository)。
1. 鏡像(Image)
鏡像包含一個完整的ubuntu操作系統環境,相當於一個root文件系統,采用分層儲存法(前一層是后一層基礎,每一層的改變只會發生在自己這一層。基於基礎鏡像,自己還能定制化加入其它層)。
2. 容器(Container)
如果說鏡像是基礎層,那么容器就是它最上層的存儲層。熟悉類和實例的朋友,可以把鏡像看作類,容器看作實例。任何保存與容器存儲層的信息都會被容器刪除而丟失,因此不建議向其寫入任何數據,用數據卷Volume比較好,這樣數據不會隨容器消失而丟失。容器的好處就是,你有鏡像的環境,你再構建個容器關聯到該鏡像環境下,你就能把容器當作是一個簡易版的Linux環境,在其中放入你的代碼和數據,進入容器運行它們。
3. 倉庫(Repository)
倉庫就是集中存放鏡像文件的場所,一個倉庫可以包含同一軟件不同版本的鏡像,而標簽tag就對應不同版本,例如:[倉庫名] : [版本標簽], ubuntu:16.04和ubuntu:18.04就是個很好的例子。總的來說:倉庫注冊服務器上往往存放多個倉庫,每個倉庫又包含多個鏡像,每個鏡像有不同的標簽。 需要注意的是,如果我們使用官方Docker Hub的需要FQ,但像阿里雲就提供了一些基礎鏡像(存放在阿里的服務器上),因此阿里雲提供鏡像的倉庫也可以叫做是加速器,方便我們從國內下載合適的鏡像。
*以上部分內容摘自:《Docker - 從入門到實踐》
二、 阿里鏡像服務使用流程
阿里雖然官方提供了一個簡單的入門介紹 [1],但是有些簡略,這里我完整地分享下我參加天池比賽提交鏡像的使用流程。
1. 開通鏡像服務
首先我們要建立個私有倉庫去存放我們的構建的鏡像文件。打開阿里雲容器鏡像服務,在網頁右上角進入控制台,登錄后可免費開通鏡像托管,根據需求選擇倉庫地址,設置私有倉庫,之后將代碼源設為本地倉庫,靈活度大。
創建好倉庫后,一定要設置固定密碼(在左側菜單欄:默認實例 - 訪問憑證 - 設置固定密碼),用於拉取或上傳鏡像的憑證信息(避免其他人竊取你倉庫的鏡像)。
2. 構建鏡像
現在倉庫有了,我們需要構建符合我們項目代碼運行環境的鏡像了。
有兩種構建鏡像的方式,如下:
- 使用 docker pull 從倉庫拉取我們需要的基礎鏡像到本地,再在本地上使用該鏡像啟動容器( docker run ),進入到容器后便可安裝其他額外的依賴項或放入相關文件,最后使用 docker commit 提交更新后的鏡像即可。
- 利用Dockerfile來創建鏡像,其包含如何創建和修改基礎鏡像的指令。
個人比較偏好第二種,雖然第一種構建方式,對個人來說挺方便的,但構建完就容易忘記之前修改鏡像的整個過程。而第二種方法適合在團隊中有,有利於團隊中互相了解鏡像搭建過程,而且回頭修改之前構建鏡像過程也比較方便。我下面也講下如何使用第Dockerfile來創建鏡像。
首先,創建並進入到一個目錄,假設這里是/taobao/,那么該目錄下要有以下三類文件:
- Dockerfile:Docker鏡像配置文件,內含構建鏡像的各自指令。
- 項目代碼文件:存放了項目代碼,我這里就是上圖的mmdetection_ahf,其用於目標檢測任務。
- run.sh:運行代碼的腳本文件。簡單來說就是Linux Terminal下的要執行的命令集。
上面的解釋可能會有點難理解,通過了解下面舉的例子就能很好地明白上面三類文件的角色是什么了。首先我們看下Dockerfile的具體內容:
# 拉取阿里雲公開的鏡像,內含mmdetection文件 FROM registry.cn-shanghai.aliyuncs.com/tcc-public/mmdetection:pytorch1.3-cuda10.1-py3 # 加入本地文件 ADD . / # 到mmdetection下 WORKDIR /mmdetection_ahf # 編譯mmdetection ENV FORCE_CUDA="1" RUN pip install --no-cache-dir -e . # 回到根目錄 WORKDIR / # 運行sh CMD ["sh", "run.sh"]
上面這個例子是我要拉取阿里提供mmdetection的基礎鏡像(具體請見:阿里基礎鏡像地址集)。然后加入本地文件是指我把taobao/下的三類文件加入到鏡像里了,之后進入到mmdetection_ahf目錄下進行編譯,之后回到根目錄運行run.sh腳本文件。(注意:我的阿里提供的mmdetection鏡像內的根目錄下已經含有mmdetection的代碼,我這里的mmdetection_ahf是從它這個鏡像里復制到本地,然后修改的。關於怎么將鏡像中的文件復制到本地的方法,我將放到 "3. 其他" 這塊進行講解)。
run.sh如果不明白,看下我下面的例子就意會了,就是你要線上怎么去運行代碼得到最終的結果:
# UTF-8, 確保python的默認編碼為utf-8 export PYTHONIOENCODING=utf-8 # 回到根目錄 cd / # 1. 為目標檢測模型准備測試數據 python mmdetection_ahf/data/test_data_prepare.py # 2. 目標檢測模型模型預測結果 cd mmdetection_ahf ./tools/dist_test.sh configs/baseline_config.py mmdet/models/pretrained_models/epoch_100.pth 1 --json_out ./work_dirs/result.json
最后正式開始構建此鏡像:
# 一個倉庫包含一個工程不同版本的鏡像,例如ubuntu:16.04和ubuntu:18.04 sudo nvidia-docker build -t registry.cn-shenzhen.aliyuncs.com/[命名空間]/[倉庫名稱]:[版本號]
3. 檢查鏡像(可選)
檢查鏡像該環節是可選的,它主要目的是在線下確認你的之前構建的鏡像是否成功?是否能正常走完整個代碼項目的流程?
首先,先查看鏡像:
sudo docker images # 查看CPU鏡像 sudo nvidia-docker images # 查看GPU鏡像
如果鏡像存在,則說明鏡像構建成功了一半,那么接下來看看鏡像能不能成功運行run.sh的所有命令:
# 構建完成后可先驗證是否正常運行,正常運行后再進行推送。 # 需要注意的事,如果你本地工程代碼里需要用到雲服務器的數據例如tcdata/時,你直接驗證該代碼可能會報錯, # 因為你沒有雲服務器上的數據,所以你可以先在本地搞一個類似目錄結構的demo_data數據文件,使用``-v /demo_data:/tcdata``去將本地目錄掛載到容器上, # 簡單來說就是將本地數據demo_data文件,改成別名tcdata,掛載你之前構建的鏡像上的容器上面。 # 這樣就能實現線下測試,如果測試沒有報錯,就能放心推送鏡像到雲上。 # --shm-size 50G 是為了防止運行過程中嘗試的中間文件過大,導致現有容器裝不下 sudo nvidia-docker run --shm-size 50G -v /demo_data:/tcdata registry.cn-shenzhen.aliyuncs.com/[命名空間]/[倉庫名稱]:[版本號] sh run.sh
(注意:docker和nvidia-docker區別在於后者可以使用GPU。它們的安裝請參考:docker和nvidia-docker安裝教程。)
4. 推送鏡像
如果上面線下的鏡像檢查環節沒問題,就可以把鏡像推送到我們之前建的阿里雲私有倉庫里去了。
sudo nvidia-docker push registry.cn-shenzhen.aliyuncs.com/[命名空間]/[倉庫名稱]:[版本號] .
完成!可以到比賽頁面提交結果了!
三、其他
我們前兩個部分只是講了一些docker大概是怎么使用的,那么在這部分里,我整理了我在docker使用過程中,對我有幫助的一些操作命令。
1. 怎么創建空白的Dockerfile?
touch Dockerfile # 建立一個Dockerfile空配置文件
2. 怎么查看鏡像和容器?
sudo docker images # 查看鏡像 sudo docker ps -a # 查看容器
3. 怎么刪除指定/所有的鏡像和容器?
sudo docker rmi [image id] # 刪除指定鏡像 sudo docker rmi $(sudo docker images -q -f "dangling=true") # 刪除無用鏡像,即image id為<none>的鏡像 sudo docker rmi $(sudo docker images) # 刪除所有鏡像 sudo docker stop [container id] # 暫停指定容器才能實現刪除操作 sudo docker rm [container id] # 刪除指定容器 sudo docker rm $(sudo docker ps -a -q) # 刪除所有容器
4. 怎么創建,進入和退出容器?
# 在指定鏡像上創建容器,並進入容器交互界面,--shm-size指定容器大小 sudo nvidia-docker run --shm-size 80G -t -i [image id] /bin/bash # 在容器交互界面內退出容器 exit # 進入指定容器(在sudo docker ps -a下確保該指定容器已開啟) sudo nvidia-docker exec -it [container id] /bin/bash # 開啟容器 sudo docker start [container id] # 退出容器 sudo docker stop [container id] # 注意如果要刪除某個掛有容器的鏡像,一定要先確認該容器已退出並被刪除才能刪除鏡像。
5. 怎么在鏡像和本地之間進行上傳或復制操作?
# 本地上傳文件到容器上 sudo docker cp [本地路徑] [container id]:[容器中路徑] # 從容器復制文件到本地 sudo docker cp [container id]:[容器中路徑] [本地路徑]