Programming in Container - Docker

這篇文章會分享我如何利用容器 (Container) 來依據產品或專案,分門別類的管理並隔離各種程式語言開發環境及其版本。

為了解決各種疑難雜症,我很常需要在各式各樣的開發環境來回切換,諸如 PythonGoPHPNodeJava 等,若再加上版本號,如 Python 2 / 3、Node 8 / 12、Java 1.8 / 11,則組合方式更為多元且複雜。這不僅容易弄亂生產環境,有時還會彼此發生衝突。

過去我會採用系統預載的版本,例如 Ubuntu 18.04 預設為 Python 2.7,然後再加上 Version Control 工具 (如 pyenv) 來新增 Python 3 環境,再用 virtualenv 來隔離模組。但到了容器化 (Containerization)時代,我們可以有更整潔的解決辦法。

1. 產品與專案目錄結構

容器化技術工具我選擇 Docker,每個目錄會依據各產品或專案來區隔。

Programming in Container - Dir

並建立各自獨立的容器。

Programming in Container - Image

2. 容器化環境的搭建

這是 docker/python-3.8-cli/Dockerfile 的範例,

FROM python:3.8-slim

COPY app/ /app
WORKDIR /app

RUN apt-get update -y && apt-get dist-upgrade -yq
RUN apt-get install -yq \
    git-core \
    vim-tiny

RUN pip install -r requirements.txt

ENTRYPOINT ["python3.8"]

其中 requirements.txt 的內容範例為,你可以依需求調整,

Flask>=1.1.1,<1.2
flask-restplus>=0.13,<0.14
Flask-SSLify>=0.1.5,<0.2
Flask-Admin>=1.5.3,<1.6
gunicorn>=19,<20

接著建構 Docker image,就可以在 docker images 看到 python-3.8-cli。

$ cd docker/python-3.8-cli
$ docker build -t python-3.8-cli .

Programming in Container - Image

3. 一行指令運行特定環境

當需要 Python 3.8 的環境時,之後只需一行指令即可在本機沒有 Python 3.8 環境下運行程式碼。

$ docker images
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
python-3.8-cli            latest              c21974826f58        3 hours ago         851MB

// 利用 python-3.8-cli 容器環境,執行本機 docker/python-3.8-cli/hello.py 的程式
$ cd docker/python-3.8-cli
$ docker run -it --rm -v $(PWD)/hello.py:/hello.py python-3.8-cli /hello.py

若專案為目錄結構,而非上述的單一檔案,也可以將整個本機目錄對應至容器環境執行。

// 利用 python-3.8-cli 容器環境,執行本機 docker/python-3.8-cli/app/app.py
$ cd docker/python-3.8-cli
$ docker run -it --rm -v $(PWD)/app:/app python-3.8-cli /app/app.py

Programming in Container - Python 3.8

4. 當需要運行較複雜的環境時,可以選擇啟動容器

當需要更複雜的開發環境時,可以選擇把工作區域掛載至容器內,接著在本機使用熟悉的 IDE 進行開發,異動/修改的程式碼也會同步至容器。

Programming in Container - Docker run

假設有個新專案 myproject python-3.8-cli 的開發環境,我可以基於此 Docker image 運行一個 instance。

// 專案目錄
$ cd project/myproject
$ docker run -d --name myproject -h myproject -v $(PWD)/app:/app:rw -t python-3.8-cli

// 繼續使用本機 IDE 編輯本機的程式碼
$ code $(PWD)/app

5. 開啟額外指示,讓容器存取硬體資源

若你的容器開發環境需要本機硬體資源時,例如攝影機,則需要開啟額外指示。此範例是我利用 Python + OpenCV + Camera 開發某個視覺化處理的產品,需要存取攝影機並將畫面投射至本機。

Programming in Container - Docker run with permissions

$ cd project/mypython
$ docker run -d --name python-3.8-opencv -h python-3.8-opencv -v $(PWD)/app:/app:rw -t python-3.8-cli \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -e DISPLAY=${DISPLAY} \
    --device /dev/video0:/dev/video0

// 繼續使用本機 IDE 編輯本機的程式碼
$ code $(PWD)/app

然而因為需要投射畫面,所以容器要安裝 X Window 相關所需的函式庫。

$ docker exec -it python-3.8-opencv bash
docker$ apt install libsm6 libxext6 libxrender-dev
$ exit

當程式碼編修完成,可以在本機 Host 直接運行,

// 允許其他 X 視窗的繪圖指令能連進本機
$ xhost +

// 本機運行 Instance 內的程式碼
// 因為 Dockerfile 有 WORKDIR 的關係,所以 Video.py 對應的是 Instance 裡的 WORKDIR/Video.py 路徑
$ docker exec python-3.8-opencv Video.py

接著就可以看到鏡頭拍攝的畫面,還有我試圖繪製的 Skeleton。

Python + OpenCV + Camera