Menu

今天我們來聊聊如何將 Docker 應用在資料科學領域裡頭吧!

全文共分上下 2 篇。在這篇裡頭,我們將透過一些簡單的比喻來直觀地理解何謂 Docker,並讓你能在閱讀本文後馬上利用 Docker 來加速你的開發效率;在下篇的內容當中,我則會分享一個資料科學家(Data Scientist:DS)為了解決一些數據問題而時常碰到的 3 種 Docker 使用方式。

不管是哪一篇,我們都不會深入探討 Docker 本身是以什麼技術被實現的。反之,我們將會以 DS 的角度,專注在「應用」層面:如何把 Docker 實際應用在資料科學以及資料工程領域裡頭。

這系列文章適合 2 種讀者:

  • 對 Docker 完全沒有概念,但想讓自己的 Workflow 更有效率的資料科學家
  • 熟悉 Docker,但好奇其在資料科學領域如何被應用的工程師

讓我們開始吧!

雲端運算 & Docker

在解釋何謂 Docker 之前,讓我把你已經非常熟悉的雲端運算(Cloud Computing)老朋友叫出來。

Amazon Web Service(AWS)Google 雲端平台(GCP) 以及 Microsoft Azure 大概是大家最耳熟能詳的幾家雲端計算 / 服務平台了。隨著時代的演進,這些平台提供越來越多樣的機器學習 API,讓開發人員不需做複雜的開發,透過一個 HTTP 要求就能直接使用各種酷炫的服務,比方說:

儘管如此,很多時候只使用這些現成的 API 並不能滿足我們這些 DS 以及企業的野心。

比起使用現成 API,如何運用雲端運算來 scale 各種數據處理工作是一個 DS / DE 更常問的問題

除了直接用各家雲端平台提供的 API 以外,一個 DS 可能更常需要利用雲端上的計算資源來完成以下的工作:

  1. 部署一些新的分析工具來嘗試提升自己及分析團隊的效率
  2. 開發、訓練、部署並規模化(scale)自己的機器學習模型
  3. 對大量數據做批次處理,將結果儲存後顯示在儀表板上

事實上,這就是本系列文章最想要跟你分享的 3 件 DS 可以活用 Docker 來最大化產出的案例。

當我們透過這篇文章(上篇)熟悉了 Docker 的基本概念以及操作以後,就能在下篇裡頭深入地探討它們。因此在這篇先讓我們專注在學習 Docker 的基礎知識吧!

雖然我們現在不會細談,但如果你再看一次上面的 3 個工作的話,會發現裡頭可不只包含資料科學(Data Science)。除了建置儀表板以及設計 ML 演算法以外,這裡頭還包含了不少軟體工程、資料工程甚至 DevOps 成分。當然資料工程師(Data Engineer)很樂意幫助你,但如果你想要快速地自己兜出一些方法呢?你該用什麼工具?


你可能覺得一個 DS 要在各種 deadlines 內完成以上所有的事情是不可能的。不過後面我們會慢慢發現,活用 Docker 能讓這些工作變得簡單許多。

接著就讓我們以 DS 的角度了解 Docker 到底是什麼技術。我相信閱讀接下來的文章,對你之後開發效率的提升是一個非常好的投資。

Docker:可愛的大鯨魚

首先看看以下這張 Docker 示意圖:


有什麼感覺嗎?注意到上圖包含了 3 個要素:

  • 海洋
  • 鯨魚
  • 貨櫃

現在讓我們發揮點想像力。

如果你把雲端運算的平台想像成一個充滿運算資源的大海的話,Docker 就是如圖中在裡頭悠遊的大鯨魚。這隻鯨魚將上述所有 DS 想要做的數據處理工作、執行的 App,一個個封裝成彼此獨立的貨櫃,並載著它們在這大海上運行。

Docker 提供的抽象化讓我們能輕鬆地運行任何想使用的資料科學工具、軟體而不需花費過多時間在建置底層環境。

我知道你可能還是沒什麼感覺,讓我們看下去。

鯨魚背上的貨櫃:Docker 容器

實際上這一個個假想的貨櫃就代表著 Docker 術語裡頭的容器(Container)。

「容器」顧名思義,是一個「容納」了某些東西的「器具」。

一般而言,一個容器裡通常會包含了一個完整的 App。這邊的 App 不是手機上的 App,而是指廣義的應用程式(Application)。

DS 常用的 App 可以是:

  • 一個包含 TensorFlow 函式庫的 Jupyter Notebook 伺服器
  • 一個 ML 產品,如透過已訓練的模型來判斷圖片裡頭有沒有貓咪的 Flask App
  • 一個 SQL 查詢以及資料視覺化的工具,如 Superset
  • 一個簡單的 Python Script,針對輸入的大量數據做處理

要從頭建構這些 App 的環境不是不可能,但除了基本的 pip install 以外你還需要花不少工夫;更令人困擾的是,很多時候你在 Mac、Windows 上安裝環境的步驟,到了雲端上的 Linux 機器上就完全行不通了。

如果這時候有人先幫我們把一個在哪邊都能跑的 App 環境建好,我們不是就能馬上開始使用各種分析工具,進行各種有趣的分析,而不用煩惱底層如不同 OS 的差異了嗎?

Docker 的容器就是這樣的一個概念,幫你事先將一個 App 所需要的所有環境,包含作業系統都「容納」在一起。

Docker 將一個 App 會使用到的程式語言函式庫(JAVA、Python、R)、資料庫、甚至作業系統(OS)都包在一個自給自足的容器(CONTAINER)裡頭。想使用某個 App 的 DS 不用從頭建置環境,只需利用 Docker 啟動該容器即可開始工作

容器裡頭不只包含 App 自己本身的程式碼,也涵蓋了所有能讓這個 App 順利執行的必要環境:

  • App 需要的各種 Python 函式庫,如特定版本的 TensorFlow、Pandas 及 Jupyter Notebook
  • MySQL、MongoDB 等 App 會用到的資料庫
  • App 會用到的各種 metadata、資料集
  • 各種 OS 限定的驅動程式(drivers)、依賴函式庫
  • (把所有你想得到的東西填進來)

包羅萬象。

因此只要我們能利用 Docker 把一個 App 需要執行的環境全部包在一個容器裡頭,我們就能在任何有 Docker 的地方啟動並運行該容器。不再需要每次重新建置環境,也不用考慮不同機器上的安裝問題。

而這正是 Docker 最強大的地方:

Docker - Build, Ship, and Run Any App, Anywhere

因為連 OS 都被包起來了,實際上每個容器(container)的執行環境都是自給自足的(self-contained)。

你可以把它想像成非常輕量的虛擬機器,其執行結果不會因為啟動該容器的「計算環境」不同而受到影響,在任何地方(Anywhere)都能順利被執行,且執行的結果都是一樣的。

以我們前面的比喻來說的話,每個貨櫃(容器 / App)都是我們想要 Docker 幫我們運送(執行)的東西,而不管 Docker 這隻鯨魚(或大船)現在在哪個海洋(計算環境)裡頭,它都能使命必達。

Docker 就像艘大船,幫我們在任何海洋(計算環境)上運送我們的貨櫃(容器)

有一點值得澄清的是,就算 Docker 幫我們抽象化建置一個 App 環境的工作,在執行一個容器的時候,我們還是需要實際的計算資源來跑這些容器。

因此前面所謂的「計算環境」指的是一個擁有計算資源(CPU、GPU、記憶體 etc)且我們實際運行 Docker 的地方。這計算環境可以是任何一家雲端服務平台上的機器,如 AWS 的某台 EC2 機器GCP 上一個包含數千台機器的群集(Cluster),或是你現在用來看本文的筆電。只要 Docker 能在該計算環境下運行,它就能幫我們在該環境「之上」執行任何容器。

簡單來說:

Docker 幫我們抽象化在任何 OS 上建置環境的工作。只要給 Docker 一個容器,它就能在任何地方啟動該容器以供你使用。

現在你對 Docker 以及容器概念有個高層次的理解了,讓我們來看看這些 Docker 容器實際上是怎麼來的吧!

貨櫃(Docker 容器)從哪來

在了解 Docker 這隻大鯨魚能幫我們運行任意的容器 / App 以後,你腦中浮現的第一個問題應該是:

  • 這些容器(貨櫃)最初是怎麼被產生的?

非常好的一個問題。

事實上,要產生一個新的 Docker 容器,Docker 需要一份「環境安裝步驟書」來讓它幫我們自動地建置容器內的環境,比方說使用什麼 OS,用什麼版本的 TensorFlow 等等。這份步驟書在 Docker 的世界裡被稱作 Dockerfile

舉個例子,以下是 Tensorflow 官方釋出的一個 Dockerfile(截錄重要部分):

FROM ubuntu:16.04

...

RUN pip --no-cache-dir install \
        ipykernel \
        jupyter \
        numpy \
        pandas \
        sklearn \
        && \
    python -m ipykernel.kernelspec

...

# Install TensorFlow CPU version from central repo
RUN pip --no-cache-dir install \
    http://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.0.0-cp27-none-linux_x86_64.whl

...

CMD ["/run_jupyter.sh", "--allow-root"]

除了 RUNCMD 等 Docker 專屬的關鍵字以後,你會發現這份 Dockfile 裡頭的指令其實跟你平常在本地開發時也會使用的指令如 pip install 沒有相差太多。差別在於透過第一行的 FROM ubuntu:16.04 指令,我們要求 Docker 在這個容器裡頭建置一個 Ubuntu OS 後,在其之上安裝這些函式庫。

追求規模性:Docker 映像檔的誕生

聽完以上的解釋,你可能會覺得在我們每次要啟動一個新的容器的時候,Docker 就得拿出 Dockerfile,一步步建置該容器的環境。

這樣的實作也不是不行,但很沒有效率。為什麼?


其中一個考量是可擴展性(Scalability)。

有時你會想要用同一份 Dockerfile 在短時間內迅速地產生好幾個一模一樣的容器(s):

  • 用多個相同的機器學習模型,同時對大量的新數據做批次預測
  • 使用多個相同的 Python Script 來處理大量數據

這時候與其在每次要啟動新的容器時才拿出 Dockerfile 建置環境,Docker 可以事先用這個 Dockerfile 把建置環境所需的步驟先做好一遍,然後把該環境「拍張照」,存成一個 Docker 映像檔(image)後等待之後的使用。

等你決定要開始使用容器的時候,因為我們已經有一個環境的快照(Snapshot),Docker 就能利用該映像檔,快速地啟動 1 個(或 100 個)相同的容器給你。

Docker 三元素:Dockerfile、Docker 映像檔以及 Docker 容器 (圖片來源

到了這邊,我們已經了解 Docker 最基本也是最重要的概念:

Docker 利用 Dockerfile 預先建置好一個 Docker 映像檔。在使用者想要使用容器的時候,以該映像檔為基礎,運行一個對應的 Docker 容器

坐而言不如起而行。

在掌握了這些概念以後,我相信你也迫不及待地想要開始使用 Docker 了,接下來就讓我們實際操作 Docker 來體會一下它的威力。

Docker 映像檔:法式千層酥

不管是 Windows 或是 Mac 用戶,你都可以很輕鬆地在官方網站下載 Docker 並安裝。


下載完以後啟動 Docker,大鯨魚就會在你的筆電上開始閒晃,等待你的指示。一般而言,我們會在 terminal 使用各種 docker 指令來跟大鯨魚溝通。

當 Docker 就緒以後,依照我們前面的所學,你會需要一個 Dockerfile 或是 Docker 映像檔來產生一個 Docker 容器。就像 Github 是一個被大家拿來分享程式碼的地方,Dockerhub 則被用來分享 Dockerfile 以及 Docker 映像檔。

假設我們現在要開始一個新的 TensorFlow 專案,並且想透過 Jupyter Notebook 進行開發,最省力的方式就是從 Dockerhub 下載一個 TensorFlow 官方幫我們弄好的 Docker 映像檔。

讓我們打開一個 terminal 並輸入 docker pull 指令:

docker pull tensorflow/tensorflow

第 1 個 tensorflow 代表 Tensorflow 的官方 Dockerhub repository,就跟 Github repository 的概念相同;第 2 個則是容器名稱。

你會看到當 Docker 在下載映像檔的時候,同時也在建置環境,而其環境會分成一層一層(Layer)的:

$ docker pull tensorflow/tensorflow
Using default tag: latest
latest: Pulling from tensorflow/tensorflow
3b37166ec614: Already exists
ba077e1ddb3a: Already exists
34c83d2bc656: Already exists
84b69b6e4743: Already exists
0f72e97e1f61: Already exists
6086c6484ab2: Pull complete
25817b9e5842: Pull complete
5252e5633f1c: Pull complete
8de57ae4ad7d: Pull complete
4b7717108c3b: Pull complete
b65e9e47e80a: Pull complete
006d31e013ea: Pull complete
700521cc53f3: Pull complete
Digest: sha256:f45d87bd473bf999241afe444748a2d3a9be24f8d736a808277b4f3e32159566
Status: Downloaded newer image for tensorflow/tensorflow:latest

我們不會細談 Docker 實作細節,但你可以想像 Docker 映像檔是一個法式千層酥(Mille Feuille)。

這時候的 Docker 是一名蛋糕師傅,利用 Dockerfile 作為食譜,逐行執行裡頭的指令以建立一層層的環境。每做出一層新的環境,就把它加在目前所有環境的上面,最後成為一個 Docker 映像檔。

這樣做有 2 個好處:

  • 當你對 Dockerfile 做變動的時候,Docker 可以只針對被改變的那一層環境做修改,而不用重建每一層,減少建置環境所需要的時間
  • 有利用到一樣環境的不同映像檔可以分享部分結果(如上面的 Already exists
一個 Docker 映像檔就像是蛋糕師傅利用 Dockerfile 食譜做出來的法式千層酥(誠摯地希望你不是晚上看本文,餓了)

依照你的網路速度,下載映像檔所需的時間可能有所不同。

在下載完成以後,輸入 docker images 指令可以顯示所有目前本地端擁有的 Docker 映像檔:

$ docker images tensorflow/tensorflow
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
tensorflow/tensorflow   latest              76fb62c3cb89        2 weeks ago         1.23GB

這邊因為我的環境裡已經有一大堆的映像檔,我在 docker images 後面加入額外的篩選器來告訴 Docker 只顯示 tensorflow repository 裡頭的 tensorflow 容器。

有了映像檔以後,最令人期待的時刻終於來臨了!

我們現在要呼叫 Docker 幫我們從這個映像檔產生並執行(run)一個新的 Docker 容器:

docker run -it -p 1234:8888 tensorflow/tensorflow

短短一行指令,包含了 3 個你不可不知的重要概念:

  • 利用 docker run 來告訴 Docker 我們要利用 tensorflow/tensorflow 映像檔來運行一個容器。實際上 Docker 容器就是在 Docker 映像檔的環境之上再加 1 層可執行的環境供你使用(貫徹千層酥的理念)
  • 利用 -it 參數來告訴 Docker 我們同時要建立一個互動式的 TTY 連線,讓容器內的結果直接顯示在我們的 terminal 裡頭,彷彿我們在本地環境下執行該 App 一樣。我們之後還可以直接在 terminal 使用 Ctrl + C 或 Command + C 來終止容器
  • 利用 -p 1234:8888 告訴 Docker 我們將會透過本地端的 1234 port 來連到容器裡頭的 8888 port

你可以透過

docker run --help

來查看所有 docker run 可以使用的參數。

另外,一個 DS 應該都知道,8888 是 Jupyter Notebook 預設的 port。因此我們的企圖就跟司馬昭之心一樣,打算透過本地端的 1234 port 連到在容器裡頭跑的 Jupyter Notebook。

現在打開你的瀏覽器並輸入 localhost:1234,應該就能連到容器內部的 Jupyter Notebook 伺服器:

容器內的 Juypter Notebook 畫面,所有環境包含 TensorFlow 都已經幫你設置好,只要輸入你在啟動容器的 terminal 裡看到的 token 就能通過認證

對你沒看錯,你已經用 Docker 建置了一個完整的資料科學環境,裡頭有 TensorFlow 以及 Jupyter Notebook。

而你只需要 2 個指令:

docker pull tensorflow/tensorflow
docker run -it -p 1234:8888 tensorflow/tensorflow

建置環境什麼的交給 Docker 吧,你已經能馬上開始實作機器學習模型了。

有些 DS 可能會覺得他的 Anaconda 或者是 pip 功能爐火純青,不需要用到 Docker 也能自己在本地建出這樣的環境。其實沒錯,如果你只是開發個人專案,說真的不學 Docker 也沒關係(喂!)

但就如我們在下篇會看到的,當你在開發企業等級的數據處理工作、機器學習模型的時候,你可不能永遠躲在你的本地環境裡頭。當你習慣於在不透過 Docker 的情況下在本機建置環境,等到要在各種雲端平台上的機器重現你的結果的時候,你就會發現不妙了。

利用 Docker 分享你的成果

為了加強你使用 Docker 的動機,讓我再給個例子。

有持續關注我文章的讀者會發現,我在資料科學文摘 Vol.3 Pandas、Docker 以及數據時代的反思裡頭有提到,Docker 除了讓我們免除建置環境的痛苦以外,也能讓我們與他人簡單地分享開發結果。

Cat Recognizer 是我用 TensorFlow 以及 Flask 實作的一個非常 naive 的貓咪辨識 App。

如同我們前面所說的,我事先將所有此 App 需要的環境用一個 Dockerfile 定義、全部包在一個 Docker 映像檔後分享在 Docker Hub 上面。

任何想要使用此 App 的人,只需要利用 Docker 輸入兩行指令:

docker pull leemeng/cat
docker run -it -p 2468:5000 leemeng/cat

接著他們就能在瀏覽器輸入 localhost:2468 來看到我的 App:

Docker 讓你與其他人分享成果,不須額外做一大堆環境設定

當然這個 ML App 在預測能力以及 UI 上都不完美,但這邊重點是你能利用 Docker 與他人快速地分享成果。如果你有想到其他利用 Docker 封裝好的 ML App 例子(或者是你接下來打算做一個自己的),非常歡迎留言讓我知道它們的存在:)

總結

呼!看完本文以後,相信你現在應該對 Docker 有個非常清楚的認識了:

  • Docker 是一個能幫我們在各種不同 OS 上建置開發環境的工具
  • Docker 三元素包含 Dockerfile、Docker 映像檔(Image)以及 Docker 容器(Container)
  • Docker 利用 Dockerfile 預先建置好一個 Docker 映像檔。在使用者想要使用容器的時候,以該映像檔為基礎,運行一個對應的 Docker 容器
  • Docker Hub 上有各式各樣可以直接供使用的映像檔
  • 你只需要 docker pulldocker run 就能開始一個分析專案

給自己鼓鼓掌!

現在這張 Docker 的示意圖在你眼裡應該變得平易近人許多

正因為我們是資料科學家,利用 Docker能幫我們抽象化很多不必要的環境建置工作,加速我們的開發效率。

在本系列文章的下篇出爐之前,我鼓勵你先下載 Docker,並開始在 Docker Hub 或者 Google 搜尋一些你感興趣的映像檔,甚至自己寫一個 Dockerfile 將你目前的專案打包起來跟別人分享。

雖然我們這篇因為篇幅關係沒有細講,但只要有一個 Dockerfile,你就能使用 docker build 來輕鬆建立一個自給自足的 Docker 映像檔。一個 Dockerfile 也不難寫,像是上面貓咪的 App 的 Dockerfile 也不過就如此幾行:

FROM python:3.6.3
MAINTAINER Meng Lee "b98705001@gmail.com"
COPY ./requirements.txt /app/requirements.txt
WORKDIR /app
RUN pip install -r requirements.txt
COPY . /app
ENTRYPOINT [ "python3" ]
CMD ["app.py"]

在本篇裡頭我們都是在自己的機器上使用 Docker。在下篇,我們將利用本篇學到的 Docker 知識,將其運用在浩瀚無垠的雲端平台之上,去最大化我們的影響力。

在那之前你可以先熟悉熟悉 Docker,下次遇到你的 DS 同事時,可以問問他/她:

嘿!你的 Docker Image 呢?

跟資料科學相關的最新文章直接送到家。
只要加入訂閱名單,當新文章出爐時,
你將能馬上收到通知