本篇是 Docker 容器的基本觀念與操作教學,敘述如何自行建立 Docker 影像檔。
相較於傳統的虛擬機器(例如 VirtualBox 與 VMWare 等),Docker 是一個輕量級的容器,只包含特定程式執行所需要的必要元件,不像虛擬機器要包含整個作業系統,所以大小會比較小,執行效能也會比較高。
以下是關於 Docker 的一些基本觀念,以及簡單的應用程式包裝與佈署步驟。
基本觀念
Docker 的影像(image)是一個可以獨立執行的輕量級套件,其包含所有執行程式所需要的函式庫、環境變數與設定檔等,而容器(container)則是一個影像(image)的執行實體,就是將影像(image)載入至記憶體中執行之後的環境。
預設的情況下,容器(container)是一個與 host 機器環境分開的獨立執行環境,但其程式卻可以在原生 host 機器的核心中運行,因此 Docker 的執行效能會比傳統虛擬機器更好。
以下兩張圖是傳統虛擬機器與 Docker 的比較,虛擬機器(VM)除了包含程式與函式庫之外,還要加上整個作業系統(Guest OS)。

在 Docker 容器的架構下,容器只需要包含程式與其所需要的函式庫,剩下的部份則由 Docker 容器來處理,所有的程式都共用同一個 host 系統核心。

安裝 Docker 環境
在開始使用 Docker 之前,請先在系統上安裝 Docker 的環境,大部分的作業系統 Docker 都有直接支援,這裡示範在 Ubuntu Linux 中的安裝步驟,若使用 CentOS Linux 的人可參考 CentOS Linux 安裝 Docker 的教學,或是 Docker 官方的文件。
首先移除舊版的 Docker 套件,官方的建議指令是:
sudo apt-get remove docker docker-engine
我自己的 Ubuntu Linux 中舊版的 Docker 套件是 docker.io:
sudo apt-get remove docker.io
安裝 linux-image-extra-* 套件,讓 Docker 可以使用 aufs 儲存驅動程式:
sudo apt-get install
linux-image-extra-$(uname -r)
linux-image-extra-virtual
安裝基本套件:
sudo apt-get install
apt-transport-https
ca-certificates
curl
software-properties-common
加入 Docker 的 GPG 金鑰:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
加入 Docker 套件庫:
sudo add-apt-repository
"deb [arch=amd64] https://download.docker.com/linux/ubuntu
$(lsb_release -cs)
stable"
更新套件庫:
sudo apt-get update
安裝 Docker CE(Community Edition):
sudo apt-get install docker-ce
檢查 Docker 是否可以正常運作:
sudo docker run hello-world
正常來說,執行之後就會輸出類似這樣的訊息:

如果要以一般的使用者的權限執行 Docker,要先在系統中加入 docker 群組:
sudo groupadd docker
在將要執行 Docker 的使用者加入至 docker 群組內:
sudo usermod -aG docker $USER
這樣就完成了。如果沒有把使用者加入 docker 群組的話,使用時就會出現類似這樣的錯誤訊息:
docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.27/containers/create: dial unix /var/run/docker.sock: connect: permission denied. See 'docker run --help'.
如果要設定 Docker 的服務是否要在開機時自動啟動,可透過 systemd 的控制介面:
sudo systemctl enable docker # 啟動 Docker 服務
sudo systemctl disable docker # 關閉 Docker 服務
最後檢查一下 Docker 的版本。
docker --version
Docker version 17.03.1-ce, build c6d412e
接下來的教學內容適用於 1.13 或更新版的 Docker 環境。
將 Docker 的執行環境準備好之後,接下來要介紹如何在 Docker 環境中開發與佈署應用程式,請繼續閱讀下一頁。
Docker 容器
傳統上的應用成是在開發與佈署時,都會需要注意執行環境是否一致的問題,以 Python 來說,可能需要注意直譯器的版本是否相同,所需要的函式庫是否有安裝等,而在 Docker 環境之下,這個問題可以簡化許多,在 Docker 中開發應用程式時,我們就會將所有程式需要的 Python 直譯器、函式庫等元件都放進 Docker 影像(image)中,讓所有的必要元件與應用程式都放在一起,在佈署時也隨同應用程式一起放進目標的執行環境中。
Dockerfile
Docker 容器內部的各種系統資源(例如儲存、網路等)都是抽象的,我們需要自行定義內部抽象資源與外部實體資源的對應關係,這些設定都是寫在 Dockerfile 這個 Docker 容器的設定檔中,以下示範如何使用 Dockerfile 建立一個 Docker 應用程式。
首先在一個空的目錄中,建立一個檔名為 Dockerfile 的文字檔,內容如下:
# 使用官方的 Python 執行環境作為基本的 Docker 影像
FROM python:2.7-slim
# 設定工作目錄為 /app
WORKDIR /app
# 複製目前目錄下的內容,放進 Docker 容器中的 /app
ADD . /app
# 安裝 requirements.txt 中所列的必要套件
RUN pip install -r requirements.txt
# 讓 80 連接埠可以從 Docker 容器外部存取
EXPOSE 80
# 定義環境變數
ENV NAME World
# 當 Docker 容器啟動時,自動執行 app.py
CMD ["python", "app.py"]
Dockerfile 需要用到另外兩個檔案,分別為 requirements.txt 與 app.py,app.py 就是我們自己撰寫的 Python 應用程式,而 requirements.txt 則是相依套件的列表,請在同一個目錄中建立這兩個檔案。requirements.txt 的內容為:
Flask
Redis
app.py 是自己開發的應用程式,這裡我們實做一個簡單的網頁伺服器,並連接到內部的 Redis 資料庫,其完整的內容如下:
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis
redis = Redis(host="redis", db=, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>"
"<b>Hostname:</b> {hostname}<br/>"
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
建立 Docker 應用程式
建立好 Dockerfile、requirements.txt 與 app.py 三個檔案之後,我們就可以使用 docker 指令來自動建立 Docker 影像(image):
docker build -t friendlyhello .
這裡的 -t 參數是用來指定 Docker 影像(image)的名稱,這個名稱可以自己取一個好記的名字。
建立好的 Docker 影像(image)會儲存在 Docker 的影像庫中,我們可以使用 docker 來查詢目前可用的 Docker 影像(image):
docker images

執行 Docker 應用程式
建立好 Docker 影像(image)之後,接著就可以直接執行了:
docker run -p 4000:80 friendlyhello
這裡我們使用 -p 參數將 Docker 內部應用程式的 80 連接埠對應到 host 機器上的 4000 連接埠。執行之後,會出現這樣的輸出訊息:
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
在 Docker 中的程式是開啟 80 這個連接埠,透過 Docker 對應到外部
host 機器上的 4000 連接埠,而這時候我們就可以開啟瀏覽器,打開本機的 4000 連接埠,連線至 Docker 中的應用程式:
http://localhost:4000

在網頁中我們可以看到 Hello World! 的訊息,以及 Redis 的錯誤訊息。如果要停止這個 Docker 應用程式,就在命令列中按下 Ctrl + c 即可中止程式。
如果想要讓 Docker 程式以背景的方式執行,可以加上 -d 參數:
docker run -d -p 4000:80 friendlyhello
c5b69c3c387c497023a407ae99f0670789933dd618fbc8251bf7330d32fc4b5d
這樣 Docker 應用程式就會在背景執行,我們可以透過 docker 的 ps 指令來查詢目前正在執行的 Docker 應用程式:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c5b69c3c387c friendlyhello "python app.py" About a minute ago Up About a minute 0.0.0.0:4000->80/tcp unruffled_kowalevski
若要停止指定的 Docker 應用程式,則使用 docker 的 stop 指令,加上容器的 ID 即可:
docker stop c5b69c3c387c
接下來我們將介紹如何把打包好的 Docker 影像(image)拿到其他的地方執行,請繼續閱讀下一頁。
分享 Docker 影像
Docker Cloud 網站有提供一個註冊處(registry)功能,可以讓使用者上傳自己製作的 Docker 影像(image),分享給其他人下載(或是自己使用),看起來有點類似 GitHub,只是它裡面放的東西都是已經建立好的 Docker 影像(images)。
在註冊處(registry)中會包含許多個套件庫(repositories),而每個套件庫又包含了許多的 Docker 影像(images),我們可以在帳號的註冊處(registry)中建立好多個套件庫(repositories),方便管理不同類型的 Docker 影像(images)。
首先請到 Docker Cloud 網站註冊一個帳號(它是免費的),然後在自己的系統上使用 docker 指令登入:
docker login
Docker 預設就會使用 Docker Cloud 網站的註冊處(registry),所以執行這行指令之後,就可以直接輸入 Docker Cloud 帳號與密碼來登入。
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username: guozhaowang Password: Login Succeeded
登入成功之後,就可以準備上傳自己的 Docker 影像了。
在上傳影像之前,我們要先把自己製作的 Docker 影像(image)掛上比較正式的名稱,習慣上 Docker 的影像(image)名稱會以這樣的格式命名:
username/repository:tag
username 就是自己的帳號名稱,而 repository 與 tag 則是自己命名的套件庫與標籤名稱,建議使用比較有意義的字眼來命名,例如 guozhaowang/gtwang-demo:part1。
決定好名稱之後,使用 docker 的 tag 指令將 Docker 影像(image)標上指定的名稱:
docker tag friendlyhello guozhaowang/gtwang-demo:part1
接著再看一下目前的 Docker 影像(images)列表:
docker images

從這裡就可以看到 Docker 影像列表多了一筆新的 guozhaowang/gtwang-demo,接著上傳這一個 Docker 影像:
docker push guozhaowang/gtwang-demo:part1

將 Docker 影像(image)放上 Docker Cloud 之後,我們就可以在其他台電腦中直接使用這個 Docker 影像(image):
docker run -p 4000:80 guozhaowang/gtwang-demo:part1
執行時 Docker 會自動從套件庫中下載需要的 Docker 影像(image)來執行。

不管使用者在哪裡執行這個 Docker 影像(image),Docker 都會自動下載應用程式所需要的執行環境(例如 Python)與 requirements.txt 所列的套件,使用者只需要準備基本的 Docker 環境即可。
