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.txtapp.pyapp.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=0, 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 應用程式

建立好 Dockerfilerequirements.txtapp.py 三個檔案之後,我們就可以使用 docker 指令來自動建立 Docker 影像(image):

docker build -t friendlyhello .

這裡的 -t 參數是用來指定 Docker 影像(image)的名稱,這個名稱可以自己取一個好記的名字。

建立好的 Docker 影像(image)會儲存在 Docker 的影像庫中,我們可以使用 docker 來查詢目前可用的 Docker 影像(image):

docker images

可用的 Docker 影像

執行 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

Docker 程式測試網頁

在網頁中我們可以看到 Hello World! 的訊息,以及 Redis 的錯誤訊息。如果要停止這個 Docker 應用程式,就在命令列中按下 Ctrl + c 即可中止程式。

如果想要讓 Docker 程式以背景的方式執行,可以加上 -d 參數:

docker run -d -p 4000:80 friendlyhello
c5b69c3c387c497023a407ae99f0670789933dd618fbc8251bf7330d32fc4b5d

這樣 Docker 應用程式就會在背景執行,我們可以透過 dockerps 指令來查詢目前正在執行的 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 應用程式,則使用 dockerstop 指令,加上容器的 ID 即可:

docker stop c5b69c3c387c

接下來我們將介紹如何把打包好的 Docker 影像(image)拿到其他的地方執行,請繼續閱讀下一頁。

虛擬化

3 留言

  1. Hever

    File “app.py”, line 15
    except RedisError:

    您的教學不錯,但我執行到
    docker run -p 4000:80 friendlyhello
    遇到了此問題,不曉得如何解?

    • 这个示例会连接redis,你确定你的redis可用吗?

  2. sam

    講的真好
    我最近也在錄製docker教學影片,可以一起交流!
    https://www.youtube.com/watch?v=pa1Zao1Hy2c&list=PLVVMQF8vWNCJnlO0Y34AE_1AgCapldp38

Comments are Closed