iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0
Kubernetes

探討k8s部署方式系列 第 14 篇

把FastAPI包成Docker image[Day14]

  • 分享至 

  • xImage
  •  

實作四:把 FastAPI 做成 Docker Image

前面我們已經完成:

Ubuntu Server
    │
    ├── Nginx
    │
    └── systemd
          │
          ▼
       FastAPI

目前 FastAPI 的執行環境是直接安裝在 Server 裡面。

例如:

Ubuntu
├── Python 3.12
├── virtualenv
├── FastAPI
├── Uvicorn
└── Application Code

啟動則交給:

systemd

這種方式本身沒有問題。

但接下來我們想把:

Python
Dependencies
Application Code
Startup Command

包成一個可以重複使用的:

Docker Image

一、目前專案結構

前面的 FastAPI 專案:

fastapi-deployment-lab/
│
├── app/
│   ├── __init__.py
│   └── main.py
│
├── requirements.txt
└── .venv/

其中 app/main.py:

from fastapi import FastAPI

app = FastAPI(
    title="FastAPI Deployment Lab",
)


@app.get("/")
async def root():
    return {
        "message": "Hello FastAPI"
    }


@app.get("/health")
async def health():
    return {
        "status": "ok"
    }

requirements.txt 至少包含:

fastapi
uvicorn

二、建立 Dockerfile

在專案根目錄建立:

Dockerfile

專案變成:

fastapi-deployment-lab/
│
├── app/
│   ├── __init__.py
│   └── main.py
│
├── requirements.txt
└── Dockerfile

Dockerfile:

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install \
    --no-cache-dir \
    -r requirements.txt

COPY app ./app

CMD [
    "uvicorn",
    "app.main:app",
    "--host",
    "0.0.0.0",
    "--port",
    "8000"
]

接下來逐段理解這份 Dockerfile。


三、FROM:決定執行環境

FROM python:3.12-slim

意思是:

這個 Application 的基礎環境使用 Python 3.12。

以前我們要在 Server 上手動:

sudo apt install python3

現在 Python Runtime 已經包含在:

python:3.12-slim

這個 Base Image 裡。

因此:

Server A
Server B
Server C

只要跑同一份 Image,就不需要分別確認:

Server A Python 3.12?

Server B Python 3.11?

Server C Python 3.10?

Application 的 Runtime 已經被固定下來。


四、WORKDIR

WORKDIR /app

代表:

Container 裡面的工作目錄
=
/app

後面的:

COPY
RUN
CMD

主要都以:

/app

作為工作位置。


五、COPY requirements.txt

COPY requirements.txt .

會把本機:

requirements.txt

複製進 Container Image:

/app/requirements.txt

此時 Image 內概念上:

/app/
└── requirements.txt

六、RUN pip install

接著:

RUN pip install \
    --no-cache-dir \
    -r requirements.txt

會在 Build Image 的過程安裝:

FastAPI
Uvicorn
其他 Python dependencies

所以以前 Server 上需要:

python3 -m venv .venv

source .venv/bin/activate

pip install -r requirements.txt

現在這件事情在:

docker build

時完成。


七、為什麼 Container 裡通常不需要 virtualenv?

傳統 Server:

Ubuntu
│
├── System Python
│
└── .venv
     ├── FastAPI
     └── Uvicorn

使用 virtualenv 是為了避免:

Application A 的 Package

跟

Application B 的 Package

互相干擾。

但是 Container 本身就已經提供了一層隔離:

Container A
├── Python
└── Packages

Container B
├── Python
└── Packages

所以很多 Docker Image 會直接:

pip install

而不另外建立:

.venv

這並不是說 virtualenv 不能放在 Docker 裡,而是通常沒有必要再增加一層隔離。


八、COPY Application

COPY app ./app

把 FastAPI 程式:

app/

複製進 Image。

Image 內容現在大約:

/app/
│
├── requirements.txt
│
└── app/
    ├── __init__.py
    └── main.py

九、CMD:Container 啟動後要執行什麼?

CMD [
    "uvicorn",
    "app.main:app",
    "--host",
    "0.0.0.0",
    "--port",
    "8000"
]

意思是:

Container 啟動時,自動啟動 FastAPI。

以前 systemd:

ExecStart=/var/www/.../.venv/bin/uvicorn \
    app.main:app \
    --host 127.0.0.1 \
    --port 8000

現在啟動指令被放進:

Docker Image

裡。

所以 Container 啟動時就知道:

我要跑什麼程式

十、為什麼 Docker 裡要綁 0.0.0.0?

這裡跟前面的 systemd 版本有一個重要差別。

之前:

FastAPI
127.0.0.1:8000

因為 Nginx 和 FastAPI 都在同一台 Host。

但 Container 有自己的 Network Namespace。

如果 FastAPI 在 Container 裡只綁:

127.0.0.1

那通常只代表:

Container 自己的 localhost

Host 很可能無法透過 Container Port 正常存取。

因此 Container 裡通常使用:

0.0.0.0:8000

表示:

監聽 Container 裡所有 Network Interface

再由 Docker 決定要不要把這個 Port 暴露到 Host。


十一、建立 .dockerignore

建議同時建立:

.dockerignore

例如:

.venv
__pycache__
*.pyc
.git
.env
.idea
.DS_Store

避免:

virtualenv
Git History
IDE 設定
環境變數檔

全部被送進 Docker Build Context。

專案現在:

fastapi-deployment-lab/
│
├── app/
├── requirements.txt
├── Dockerfile
└── .dockerignore

十二、Build Docker Image

執行:

docker build \
    -t fastapi-deployment-lab:v1 .

其中:

docker build

代表:

根據 Dockerfile 建立 Image

-t:

幫 Image 命名

所以最後得到:

fastapi-deployment-lab:v1

可以查看:

docker images

可能看到:

REPOSITORY                TAG
fastapi-deployment-lab    v1

這個 Image 現在包含:

Python 3.12
+
Python Packages
+
FastAPI Code
+
Startup Command

十三、啟動 Container

接著:

docker run \
    --name fastapi-demo \
    -p 8000:8000 \
    fastapi-deployment-lab:v1

現在:

Docker Host
   │
   │ :8000
   ▼
Container
   │
   │ :8000
   ▼
Uvicorn
   │
   ▼
FastAPI

測試:

curl http://127.0.0.1:8000/

應該看到:

{
  "message": "Hello FastAPI"
}

Health Check:

curl http://127.0.0.1:8000/health

得到:

{
  "status": "ok"
}

十四、-p 8000:8000 到底是什麼?

這一段:

-p 8000:8000

可以理解成:

Host Port
8000

↓

Container Port
8000

所以:

127.0.0.1:8000

會被 Docker 導向:

Container:8000

如果寫:

-p 9000:8000

則:

Host
9000

↓

Container
8000

此時要:

curl http://127.0.0.1:9000

但 FastAPI 在 Container 裡仍然是:

8000

十五、Background 執行 Container

剛才:

docker run ...

會佔著 Terminal。

正式使用時可以:

docker run \
    -d \
    --name fastapi-demo \
    -p 8000:8000 \
    fastapi-deployment-lab:v1

-d:

Detached Mode

也就是背景執行。

查看:

docker ps

Log:

docker logs fastapi-demo

即時 Log:

docker logs -f fastapi-demo

上一篇
從零開始實作部署[Day13]
下一篇
把 FastAPI 做成 Docker Image(續)[Day15]
系列文
探討k8s部署方式 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言