回覆列表
-
1 # 機器之心Pro
-
2 # whj711107
在RHEL6.5中整合對Docker的支援, 就連 Google 的 Compute Engine 也支援 docker 在其之上執行。這只是一款提供開源的應用容器引擎的軟體。如果想熟練運用。需要專業學習。但想成為科學家沒有捷徑。只有更努力學習。一分天才需九分努力。
在RHEL6.5中整合對Docker的支援, 就連 Google 的 Compute Engine 也支援 docker 在其之上執行。這只是一款提供開源的應用容器引擎的軟體。如果想熟練運用。需要專業學習。但想成為科學家沒有捷徑。只有更努力學習。一分天才需九分努力。
使用 Docker 容器來開發機器學習模型的好處有很多。近日,GitHub 的資深機器學習科學家 Hamel Husain 在 Towards Data Science 上發表了一篇入門級的 Docker 容器教程,文章從基本的概念談起,清楚明白地介紹了 Docker 容器的一些基本的操作方式和注意事項。機器之心對本文進行了編譯介紹。本文所涉及的所有相關程式碼請訪問:https://github.com/hamelsmu/Docker_Tutorial
過去五年來,Docker 容器已然成了一個熱門詞彙,似乎我的所有軟體工程師朋友都在使用它們來開發應用。我想搞清楚這種技術可以如何讓我更有效率,但我發現我在網上找到的教程要麼過於注重細節(解釋了一些我作為資料科學家絕不會使用的功能),要麼就過於淺顯(沒有足夠的資訊幫助我理解如何快速有效地使用 Docker)。
所以我寫了這篇快速入門,這樣你不必自己去網上篩選資訊就能學習到快速上手 Docker 所需要的一切。
Docker 是什麼?你可以把 Docker 看作是輕量級的虛擬機器——包含你執行應用所需要的一切。Docker 容器可以獲取你的系統的狀態的快照,這樣其他人就可以使用這個快照快速重建你的計算環境。對於本教程而言,這就是你需要了解的一切。更多詳細介紹可參閱:https://goo.gl/YzUwbc
為什麼要使用 Docker?1.重現性:作為專業的資料科學家,讓你的結果能夠重現是非常重要的。重現性不僅有助於同行評議,而且可以確保你建立的模型、應用或分析可以無障礙地執行,這能讓你交付的成果更穩健,更能經受時間的考驗。舉個例子,假如你用 Python 建立了一個模型,只是執行 pip freeze 並將結果得到的 requirements.txt 檔案傳送給你的同事是不夠的,因為其中只包含特定於 Python 的依賴條件——而實際上的依賴條件不只有 Python,還有作業系統、編譯器、驅動程式、配置檔案以及你的程式碼成功執行所需的其它資料。就算你只分享 Python 依賴條件也能成功,將所有東西都封裝到一個 Docker 容器中還是能減輕其他人重建你的環境的負擔,並讓他們能更輕鬆地訪問你的成果。
2.計算環境的可移植性:作為一位資料科學家,尤其是機器學習領域內的資料科學家,快速改變你的計算環境的能力能夠極大地影響你的生產力。資料科學的開始工作常常是原型設計、探索和研究——這些工作並不一定立即就需要特定的計算資源。這個工作往往是在膝上型電腦或個人計算機上完成的。但是在後面某個時候,你往往會需要不同的計算資源來顯著加速你的工作流程——比如使用更多 CPU 或強大的 GPU 來執行深度學習等任務。我看到很多資料科學家由於感受到了在遠端機器上重建他們的本地環境的困難,就將自己侷限在了本地計算環境內。而 Docker 能讓你的環境(你的所有庫和檔案等等)的移植非常簡單。在 Kaggle 競賽中,快速移植計算環境也是一個巨大的競爭優勢,因為你可以成本高效地利用 AWS 的寶貴計算資源。最後,建立 Docker 檔案讓你能移植很多你喜歡的本地環境配置——比如 bash 別名或 vim 外掛。
3.強化你的工程能力:熟練使用 Docker 讓你能將模型或分析部署成應用(比如用作提供預測的 REST API),從而讓其他人也能使用你的成果。此外,你在資料科學工作流程中可能需要與存在於 Docker 容器中的其它應用進行互動,比如資料庫。
Docker 術語在我們繼續深入之前,熟悉一下 Docker 的術語會很有幫助:
·映象(image):是你想要建立的東西的藍圖。比如:Ubuntu+TensorFlow,帶有英偉達驅動程式和一個執行的 Jupyter 伺服器。
·容器(container):是你實現的執行的映象的例項化。你可以運行同一個映象的多個副本。分清映象和容器之間的差異非常重要,因為這是新入門者常常混淆的兩個概念。如果你不清楚映象和容器的差別,停下來再讀一次。
·Dockerfile:用於建立映象的配方。Dockerfile 包含特殊的 Docker 語法。官方文件說:Dockerfile 是一個文字文件,其中包含了使用者可以在命令列呼叫的用來組裝成映象的所有命令。
·commit:和 git 類似,Docker 容器提供了版本控制。透過 commit 發生的改變,你在任何時間都可以將你的 Docker 容器的狀態儲存為一個新映象。
·DockerHub/Image Registry:人們可以釋出公開(或私人)Docker 映象的地方,用於促進合作與共享。
·層(layer):對已有映象的修改,由 Dockerfile 中的一個指令表示。層按次序應用到基礎映象上,以創建出最終的映象。
本文將使用這些術語,如果你在閱讀時忘記了,一定要回來檢視!這些術語很容易混淆,尤其是在映象和容器之間——所以你在閱讀時要保持警惕!
安裝 Docker你可以免費下載安裝 Docker 社群版(Docker Community Edition),地址:https://www.docker.com/community-edition
建立你的第一個 Docker 映象在建立 Docker 容器之前,建立一個將用於定義映象的 Dockerfile 會很有用。我們先慢慢解讀一下下面的 Dockerfile。你也可以在與本教程關聯的 GitHub 庫中找到這個檔案:https://goo.gl/iE4Bdr
# reference: https://hub.docker.com/_/ubuntu/
FROM ubuntu:16.04
# Adds metadata to the image as a key value pair example LABEL version="1.0"
LABEL maintainer="Hamel Husain <www.github.com/hamelsmu>"
##Set environment variables
ENV.UTF-8 LC_ALL=C.UTF-8
RUN apt-get update --fix-missing && apt-get install -y wget bzip2 ca-certificates \
build-essential \
byobu \
curl \
git-core \
htop \
pkg-config \
python3-dev \
python-pip \
python-setuptools \
python-virtualenv \
unzip \
&& \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN echo "export PATH=/opt/conda/bin:$PATH" > /etc/profile.d/conda.sh && \
wget --quiet https://repo.continuum.io/archive/Anaconda3-5.0.0.1-Linux-x86_64.sh -O ~/anaconda.sh && \
/bin/bash ~/anaconda.sh -b -p /opt/conda && \
rm ~/anaconda.sh
ENV PATH /opt/conda/bin:$PATH
RUN pip --no-cache-dir install --upgrade \
multiprocessing \
sklearn-pandas
# Open Ports for Jupyter
EXPOSE 7745
#Setup File System
RUN mkdir ds
ENV HOME=/ds
ENV SHELL=/bin/bash
VOLUME /ds
WORKDIR /ds
ADD run_jupyter.sh /ds/run_jupyter.sh
RUN chmod +x /ds/run_jupyter.sh
# Run the shell
CMD ["./run_jupyter.sh"]
FROM 語句
FROM ubuntu:16.04
FROM 語句包含了 Docker 最神奇的部分。這個語句指定了你想在上面進行建立的基礎映象。透過使用 FROM 指定一個基礎映象,Docker 將會在你的本地環境中尋找名為 ubuntu:16.04 的映象——如果它沒有找到,它就會搜尋你指定的 Docker Registry,預設是 DockerHub:https://hub.docker.com/explore/。如果你需要經常在你的 Ubuntu 等作業系統上安裝程式,那麼這種分層機制就非常方便。你不必費心從頭開始安裝 Ubuntu,而是可以直接在官方的 Ubuntu 映象上開發!DockerHub 上託管著種類繁多的映象,包括那些不只是提供了一個作業系統的映象,比如如果你想要一個已經安裝了 Anaconda 的容器,你可以選擇在官方的 Anaconda Docker 映象上開發,地址:https://hub.docker.com/r/continuumio/anaconda3/。最重要的是,你也可以隨時釋出你構建的映象,即使該映象是透過在其它映象上加層得到的!這有無盡的可能性。
在這個案例中,我們指定基礎映象為 ubuntu:16.04,它會搜尋名叫 ubuntu 的 DockerHub 庫(https://hub.docker.com/_/ubuntu/)。映象名之後的部分 16.04 是指定了你想要安裝的基礎映象的版本的標籤(tag)。如果你檢索一下 Ubuntu DockerHub 庫,你會注意到不同版本的 Ubuntu 對應於不同的 tag:
2017 年 12 月的官方 Ubuntu DockerHub 庫截圖
比如,ubuntu:16.04、ubuntu:xenial-20171201、ubuntu:xenial 和 ubuntu:latest 全都是指 16.04 版的 Ubuntu,它們全都是同一個映象的別名。此外,這裡提供的連結指向了對應的 Dockerfile,可用於構建每個版本的映象。有時候你無法在 DockerHub 中找到 Dockerfile,因為維護者可以自己選擇是否將關於這些映象的建立方式的 Dockerfile 包含進來。我個人覺得閱讀一些 Dockerfile 有助於更好地理解 Dockerfile。(但不要急,讀完這篇教程再說!)
你需要特別注意一個標籤,即 :latest 標籤。這也是你在不為 FROM 語句指定標籤時預設 pull 的映象。比如說如果你的 FROM 語句是這樣:
FROM ubuntu
然後你就將 pull ubuntu:16.04 映象。為什麼?——仔細看上面,你可以看到 :latest 關聯的是 16.04.
關於 Docker 映象最後需要注意的一點:在從 DockerHub pull 隨機的 Docker 映象時要做出明智的判斷。有惡意的人建立的映象有可能會包含惡意軟體。
LABEL 語句
這個語句會為你的映象新增元資料,而且是完全可選的。我增加這個語句的目的是為了讓別人知道可以聯絡誰,同時也方便我搜索我的 Docker 容器,尤其是在一個伺服器上同時執行著很多容器時。
LABEL maintainer="Hamel Husain <youremail>"
ENV 語句
ENV.UTF-8 LC_ALL=C.UTF-8
這讓你可以修改環境變數,而且相當直接,相關情況請參閱:https://docs.docker.com/engine/reference/builder/
RUN 語句
這通常是最需要花功夫的地方,給出了你構建該 Docker 映象所想要完成的任務。你可以執行 apt-get 和 pip install 等任意的 shell 命令來安裝你需要的軟體包和依賴包。
RUN apt-get update --fix-missing && apt-get install -y wget bzip2
build-essential \
ca-certificates \
git-core \
...
在這裡我安裝了一些我喜歡的實用工具,比如 curl、htop、byobu,然後安裝了 Anaconda,之後還安裝了一些基礎 Anaconda 中沒有的其它庫(你可以在完整的 Dockerfile 中檢視其它 RUN 語句)。
RUN 語句後的命令與 Docker 沒什麼關係,只是一些你在安裝這些軟體包時需要執行的正常 Linux 命令,所以就算你不熟悉這些軟體包或 Linux 命令也不要擔心。另外,再給一個建議:當我最早開始學習 Docker 時,我查看了 GitHub 或 DockerHub 上的其它 Dockerfile,然後將我需要的部分複製貼上到了我的 Dockerfile。
你可能注意到了 RUN 語句的格式。每個庫或軟體包都整齊地進行了縮排,而且為了可讀性還按字母進行了排序。這是 Dockerfile 的普遍慣例,所以我建議你也這樣做以便合作。
EXPOSE 語句
如果你想公開一個埠,這個語句會很有用——比如,如果你從該容器或某個網路服務內實施一個 Jupyter Notebook。Docker 的文件相當好地解釋了 EXPOSE 語句:
EXPOSE 指令實際上並沒有釋出該埠。它的功能是作為建立該映象的人和執行該容器的人之間的一類文件,內容是關於打算髮布的埠。要實際釋出該埠,就要在執行該容器時在 docker run 上使用 -p 標誌並且對映一個或多個埠,或者也可以使用 -P 標誌釋出所有埠並將它們對映到高階埠。
VOLUME 語句
VOLUME /ds
這個語句讓你可以在 Docker 容器和主機計算機之間共享資料。VOLUME 語句讓你可以安裝外部安裝的卷。主機目錄只有在容器執行時才宣告(因為你可能在不同的計算機上執行該容器),而不會在定義映象時宣告*。目前你只指定了 Docker 容器內你想與主機容器共享的資料夾的名稱。
Docker 使用者指南解釋說:
主機目錄是在容器執行時宣告的:主機目錄(掛載點)本質上取決於主機。這是為了保證映象的可移植性,因為一個給定的主機目錄無法保證在所有主機上都可用。由於這個原因,你不能在 Dockerfile 中掛載主機目錄。VOLUME 指令不支援指定 host-dir 引數。你必須在建立或執行容器時指定掛載點。
此外,這些卷的目的是將資料儲存到容器的檔案系統之外,當你要操作大量資料而且不希望你的映象膨脹得很大時,這會很有用。當你儲存一個 Docker 映象時,在這個 VOLUME 目錄中的任何資料都不會被儲存為該映象的一部分,但是在這個容器目錄之外的資料會被儲存。
WORKDIR 語句
WORKDIR /ds
這個語句設定了工作目錄,以便你在另一條命令中可以無需使用絕對路徑就能索引特定的檔案。例如這個 Dockerfile 中的最後一條語句是:
CMD [“./run_jupyter.sh”]
該語句就預設假設工作目錄是 /ds
ADD 語句
ADD run_jupyter.sh /ds/run_jupyter.sh
這條命令讓你可以在 Docker 容器執行時將檔案從主機計算機複製到該 Docker 容器。我使用這個命令來執行 bash 指令碼以及將 .bachrc 檔案等有用東西匯入到容器中。
在我執行這個容器時,run_jupyter.sh 正好在背景路徑的根目錄內,所以在該原始檔之前沒有路徑。
使用者指南中介紹說:
ADD <src>... <dest>
ADD 指令從 <src> 複製新檔案、目錄或遠端檔案 URL 並將它們新增到路徑 <dest> 的映象的檔案系統中。
CMD 語句
Docker 容器的設計思想是這些容器是短暫的,能保證執行完你想執行的應用就行了。但在資料科學方面,我們往往希望保持這些容器一直執行,即使它們之中並沒有主動地執行著什麼。很多人都透過執行 bash shell 來實現這一點(除非你終止它,否則它就不會停止)。
CMD [“./run_jupyter.sh”]
在上面的命令中,我運行了一個例項化一個 Jupyter Notebook 伺服器的 shell 指令碼。但是,如果你沒有什麼要執行的特定應用而只是想保持你的容器執行(而不退出),你可以直接執行 bash shell,只不過使用以下命令:
CMD ["/bin/bash"]
這種方法是有效的,因為除非你退出,否則 bash shell 就不會終止;因此該容器會一直保持執行。
使用者指南中介紹說:
在一個 Dockerfile 中只能有一個 CMD 指令。如果你列出了不止一個 CMD,那麼只有最後一個才有效。
CMD 的主要目的是為正在執行的容器提供預設配置。這些預設配置可能包含一個可執行檔案,或者也可以省略可執行檔案,在這種情況下你還必須指定一個 ENTRYPOINT 指令。
建立你的 Docker 映象Dockerfile 中的資訊可真夠多的。不要擔心,後面的內容就相對很簡單了。現在我們已經在 Dockerfile 中建立了我們的配方,是時候創造映象了。你可以透過以下命令完成:
GitHub 上也有:https://github.com/hamelsmu/Docker_Tutorial/blob/master/basic_tutorial/build_image.sh
這會建立一個 Docker 映象(而不是容器;如果你不記得這兩者之間的差異,請查閱文章前面的術語介紹),你可以在後面執行這個映象。
從你的 Docker 映象建立和執行容器現在你已經準備好讓這一切工作起來了!我們可以透過執行以下命令來調出環境:
同樣 GitHub 也有:https://github.com/hamelsmu/Docker_Tutorial/blob/master/basic_tutorial/run_container.sh
執行完這個命令之後,你的容器就執行起來了!Jupyter 伺服器也執行起來了,因為在該 Dockerfile 最後有這個命令:
CMD [“./run_jupyter.sh”]
現在你應該可以透過其使用的埠訪問你的 Jupyter Notebook 了——在這個案例中可透過 http://localhost:7745/ 訪問,密碼是 tutorial。如果你是透過遠端的方式執行這個 Docker 容器,你還必須設定本地埠轉發,這樣你才能透過你的瀏覽器訪問你的 Jupyter 伺服器。埠轉發介紹:https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding
與你的容器互動一旦容器設定完成並執行起來,下面這些命令就有用了:
·為容器附加一個新的終端會話。如果你需要安裝一些新軟體或使用 shell,這會很有用。
·將你的容器的狀態儲存為新映象。即使你一開始就在 Dockerfile 中配置了你想安裝的所有庫,隨著時間的推移,你也可能還是需要對容器的狀態進行很大的調整——透過互動來增加更多庫和軟體包。將你的容器的狀態儲存為映象是很有用的,你後面可以將其分享出去或在上面加層。你可以使用 docker commit CLI 命令將容器狀態儲存為新映象:
docker commit <container_name> new_image_name:tag_name(可選的)
比如說,如果我想將名為 container1 的容器的狀態儲存為名為 hamelsmu/tutorial:v2 的映象,我可以直接執行這個命令:
docker commit container_1 hamelsmu/tutorial:v2
你可能會疑惑映象名之前的 hamelsmu/ 是什麼——這只是為了讓之後將該容器推送到 DockerHub 的工作更輕鬆,因為 hamelsmu 是我的 DockerHub 使用者名稱(後面會再談這個問題)。如果你的工作要使用 Docker,那麼你的公司很可能有一個內部私有的 Docker 庫,你也可以將你的 Docker 推送到那裡。
·列出執行中的容器。當我忘記現在正在執行的容器的名稱時,我就常常使用這個命令:
docker ps -a -f status=running
如果你在使用該命令時沒有加上 status=running,那麼你就會看到你係統上的所有容器的列表(即使已經不再執行的容器也在)。這對查詢舊容器而言很有用。
·列出你在本地儲存的所有映象。
docker images
·將你的映象推送到 DockerHub(或其它地方)。如果你想與其他人分享你的工作或將映象儲存到雲上,這個命令就會很有用。注意你在做這件事時可不要分享任何私人資訊(DockerHub 上也有私有庫)。
首先建立一個 DockerHub 庫並給你的庫起一個適當的名稱,參考這裡:https://docs.docker.com/docker-hub/repos/。然後要執行 docker login 命令來連線到你在 DockerHub 或其它註冊位置的賬戶。比如,要推送一個映象到這個容器(https://hub.docker.com/r/hamelsmu/tutorial/),我首先必須將我的本地映象命令為 hamelsmu/tutorial(我可以選擇任意標籤名)。比如說,這個 CLI 命令就為:
docker push hamelsmu/tutorial:v2
將之前提到的 Docker 映象推送到這個庫,其標籤為 v2,參考:https://hub.docker.com/r/hamelsmu/tutorial/tags/。需要指出:如果你公開了你的映象,那麼其他人就可以直接在你的映象上加層,就像本教程中我們在 ubuntu 映象上加層一樣。對於想要重現或延展你的研究的其他人來說,這非常有用。
你已經掌握了現在你知道如何操作 Docker 了,你可以執行以下任務:
·與同事和朋友共享可重現的研究。
·透過將你的程式碼暫時遷移到所需的更大的計算環境中,無中斷地贏得 Kaggle 競賽。
·在你的膝上型電腦上的 Docker 容器內進行本地的原型開發,然後毫不費力地將同樣的計算過程無縫遷移到伺服器上,同時還能保留你喜歡的本地環境配置(你的別名、vim 外掛、bash 指令碼、自定義提示等)。
·使用 Nvidia-Docker 在 GPU 計算機上快速例項化執行 TensorFlow、PyTorch 或其它深度學習庫所需的所有依賴包。(如果你從頭開始做,這個過程將非常艱辛。)參閱後面的彩蛋。
·將你的模型作為應用釋出,比如用作從 Docker 容器提供預測的 REST API。當你的應用 Docker 化了以後,就可以按照需要輕鬆地隨意複製。
進階閱讀到這裡我們也只學到了 Docker 的一點皮毛,前面還有很多東西值得掌握。我很關注 Docker 領域,我認為資料科學家會常常遇到它,希望這篇文章能讓你有足夠的信心開始使用它。下面這些資源曾在我的 Docker 之旅中為我提供過幫助:
·有用的 Docker 命令:https://zaiste.net/posts/removing_docker_containers/
·更有用的 Docker 命令:https://www.digitalocean.com/community/tutorials/how-to-remove-docker-images-containers-and-volumes
·Dockerfile 參考:https://docs.docker.com/engine/reference/builder/
·如何建立和推送到 DockerHub 上的庫:https://docs.docker.com/docker-hub/repos/
彩蛋:Nvidia-Docker我學習 Docker 最早的原因是要在單個 GPU 上做深度學習模型的原型開發,然後在我需要更多計算資源時再遷移到 AWS 上。我當時也在學習 Jeremy Howard 的出色的 Fast.AI 課程(http://www.fast.ai/),並且希望與其他人分享我的原型設計。
但是,要將英偉達 GPU 的驅動程式等所有依賴包都包含以來,你不能使用 Docker,而是要用 Nvidia-Docker(https://github.com/NVIDIA/nvidia-docker)。這比使用 vanilla Docker 要多花一些功夫,但只要你理解了 Docker,做起來就很簡單。
我將我的 Nvidia-Docker 設定放在這裡:https://github.com/hamelsmu/Docker_Tutorial/tree/master/gpu_tutorial,你可以用這個來進行練習。