用 Docker 容器化你的 Django 專案
Let's Django!
這是 Django Tutorial 的第 11 篇,同時也是「Django 專案容器化」三部曲的第 1 篇。
範例程式碼可參考我的 GitHub 專案。
本文相關的程式碼改動,都集中在這個 PR。
系列:Django 專案容器化
「環境不一致」是軟體開發中的常見困境,你用 Windows、我用 Mac,在我的機器上順利運行的程式碼,換到另一個人的電腦可能就不對了。
而「容器化」正是處理這類環境設定議題的主要手段。
容器化技術不僅一定程度解決了「在我的電腦可以執行」的老問題,更在不同面向上,改變了軟體開發的方式。
不止是開發,從測試到部署,容器都佔據了重要角色。
透過標準化的容器環境,團隊成員可以確保程式碼在不同環境中的表現一致。
容器化開發
由此可見,「容器化」是現代開發的 ABC。
我更想強調是:哪怕只是在本機上運行,維持專案的容器化也是一個好習慣,方便日後遷移、分享,甚至協作。
本文是「Django 專案容器化」三部曲系列的第一篇,將帶你從零開始,將一個 Django 專案進行容器化。
下一篇則介紹如何將 Django 專案容器與的 db 容器整合,並使用 Docker Compose 建立多容器架構。
最後則是(拖稿許久的)Python 套件管理器——Poetry——的容器化設定,讓你正式告別requirements.txt
。
這些都是現代開發日常,值得我們一一了解並實踐。
本文主旨與目標讀者
本文會帶你手把手將一個 Django 專案進行容器化改造。
讓你在本地開發時,也透過 Docker 容器來運行專案 app。而不是常見的——在本機的 CLI 直接執行python manage.py runserver
指令。
這些改造並不難,但仍然需要讀者對 Docker 有基礎的了解。
此外,任何軟體專案都可以容器化,選擇 Django 只是為了讓例子更具體,而且它是我相當熟悉的工具。
如果你已經在工作中使用 Docker,那本文將會是一個實用的示範。
Docker 與容器
Docker 是一個開源的容器化平台,它讓開發者能夠將應用程式與其依賴(執行環境)打包成一個獨立的元件,確保在大部分環境中都能一致地運行。
關於 Docker 的學習指引,可參考這篇〈Docker 新手入門:書與線上課程推薦〉,本文主要關注「實作面」。
Docker 核心概念
使用 Docker,需要了解以下重要概念:
- Image:包含執行環境、作業系統和應用程式等等的定義,是容器的基礎。
- Container:根據 image 啟動起來的執行單位,本身是一個 process。其特性是在 image 之上再建立一個「讀寫層」。
- Dockerfile:定義如何建立 image 的檔案。(本文重點)
- Volume:容器的持久化儲存空間,可將資料獨立存放在主機上(而不是直接置於容器中),避免容器刪除時資料遺失。使用
-v
參數來設定。
這些概念環環相扣,形成了一個完整的容器生態系統。
接下來,讓我們實際動手,將這些概念應用在我們的 Django 專案中。
為 Django 專案建立 Dockerfile
想要將現有的 Django 專案容化器,就要從建立自己的 Docker Image 開始。
想要為專案建立專屬的 image,你需要自行定義 Dockerfile。
我們一樣以範例專案為例,試著在「專案根目錄」新增一個 Dockerfile。
Dockerfile
下面是一個基礎的 Dockerfile,使用了 Python 的官方 image:
1 | # 使用 Python Image |
非常簡單!
重點說明
FROM
:選擇基礎環境,這裡使用較輕量的python:3.12-slim
。WORKDIR
:設定工作目錄,確保相關檔案、資源都在同一處。COPY
:複製檔案至容器內部。這裡使用了兩次,主要是為了「快取最佳化」。RUN
:執行指令——使用 pip 安裝 Python 套件。CMD
:定義啟動容器時要執行的指令,這裡用來啟動 Django 開發伺服器。
在容器內執行 Python 專案的一大特色,就是不需要再建立一個專案虛擬環境——因為容器本身就已經是一個隔離環境。
容器內的 Python 及相關套件是專屬於這個容器的,不會與其他容器或系統產生衝突。這種環境隔離也是 Docker 容器化的核心優勢之一。
用 Dockerfile 建立 Image
Dockerfile 是自定義 image 的工具,或說設計圖。
有了 Dockerfile 後,我們在專案根目錄下使用 build 指令來建立 image:
1 | docker build -t my-django-app . |
這個指令會根據 Dockerfile 的設定,建立一個名為my-django-app
的 image。當然,這裡的名稱是自訂的。
執行後我得到了錯誤訊息,才發現我竟然還沒有為本專案建立requirements.txt
😅
參考〈Python 套件管理器——Poetry 完全入門指南〉中的這段來將 Poetry 虛擬環境內容輸出為requirements.txt
。
或直接使用下列指令:
1 | poetry export -f requirements.txt -o requirements.txt --without-hashes |
貼心提醒:我們會在第三篇將 Poetry 一併容器化。至此之後,專案中就不再需要
requirements.txt
了。
建立 Image 並確認
新增requirements.txt
後重新執行指令,得到下列成功結果:
1 | ... |
保險起見,還是先用指令docker image ls
確認一下 image 真的存在。
1 | ❯ docker image ls |
至此,image 的建立大功告成。接下來我們要把容器 run 起來。
運行 Docker 容器
使用下面 Docker 指令運行容器,並將容器內部的 8000 port 對應到主機:
1 | docker run -v $(pwd)/db.sqlite3:/app/db.sqlite3 -p 8000:8000 my-django-app |
除了 port mapping,我們還用了-v
參數進行「bind mounts」,確保本機的 SQLite 資料庫檔案可以直接 mount 到容器中。
執行成功後,在瀏覽器中輸入 http://localhost:8000/hello/
,就可以看到專案已經正常執行:
1 | ❯ docker run -v $(pwd)/db.sqlite3:/app/db.sqlite3 -p 8000:8000 my-django-app |
PS:目前專案只有一個端點可以使用XD,那就是/hello/
,回應如下:
1 | // http://127.0.0.1:8000/hello/ |
目前的不足之處
這樣算是完成初步的容器化了,但說真的,如果只是做到這步,你可能會覺得這簡直比之前還不便!
不便之處有下。
問題一:修改專案程式碼後無法即時更新
在本機運行時,程式碼只要一改,服務就會自動更新(使用測試模式),最多也只要重啟就可以看到新的變動。
但容器中的程式碼並不會「自動同步」。
簡單暴力的方式,是將「整個專案目錄」內容都透過 bind mount 掛載到容器中:
1 | docker run -p 8000:8000 -v $(pwd):/app my-django-app |
而不僅僅是掛載db.sqlite3
這個檔案。
問題二:每次都要執行「Docker 指令 + 參數」,好麻煩!
落落長的指令加參數,真的讓人很排斥,遠不如原來的python manage.py runserver
指令簡潔。
如果每次重置環境都要輸入這些內容,會讓人動力大減。
放心,以上兩個問題都會在下一篇中改善——畢竟應該沒有人是這樣開發的吧?😅
小結與下一步
本文完成了對 Django 專案的基礎容器化,從建立 Dockerfile 到建立 Image,再到運行容器,一步步帶你體驗 Docker 的基本操作。
還處理了 SQLite 資料庫檔案的持久化問題。
不過,這些都只是「暫時」的做法。
在下一篇文章中,我們將更進一步:
- 探討如何替換 SQLite 為生產級資料庫(如 PostgreSQL)。
- 使用 Docker Compose 將 Django 與資料庫容器整合,建立一個多容器架構。
透過 Docker,為我們打造更流暢的現代開發體驗,敬請期待。