อัปเดตล่าสุด Dec. 1, 2024
บทความนี้ ผมจะมาแนะนำขั้นตอนในการสร้าง RESTful API โดยใช้ FastAPI และทำการ
deploy ด้วย Docker Compose เพื่อให้สามารถง่ายต่อการเรียกใช้งาน API และสามารถนำไปรันได้ทุกที่กันเลยครับ
Note: สำหรับเพื่อน ๆ ที่ยังไม่แม่น API ก็สามารถอ่านบทความนี้ได้เพิ่มเติมเลยครับ API คืออะไร ?
FastAPI เป็น Web Framework ของภาษา Python ที่ใช้สำหรับสร้าง API โดยเน้นที่ความเร็ว ประสิทธิภาพ และง่ายต่อการเขียน มันถูกสร้างขึ้นบนพื้นฐานของ ASGI (Asynchronous Server Gateway Interface) ซึ่งทำให้สามารถรองรับ Asynchronous Programming ได้ อีกทั้งยังมาพร้อมกับเครื่องมือที่ช่วยให้การสร้าง API เป็นเรื่องง่าย เช่น Automatic Documentation, Type Hints, Data Validation เป็นต้น
ทำให้ FastAPI เป็นตัวเลือกยอดนิยมสำหรับนักพัฒนาที่ต้องการสร้าง API ที่รวดเร็ว ปลอดภัย และเชื่อถือได้ในเวลาอันสั้น นอกจากนี้ FastAPI ยังสามารถทำงานร่วมกับเฟรมเวิร์คและไลบรารีอื่น ๆ ของ Python ได้เป็นอย่างดี จึงมีความยืดหยุ่นสูงในการพัฒนา
Pydantic เป็น Library ใน Python สำหรับการจัดการและ Validate ข้อมูลที่มีโครงสร้าง (Structured Data) โดยอาศัย Type Hints ซึ่งช่วยให้โค้ดอ่านง่ายและลดข้อผิดพลาด มีคุณสมบัติหลักคือ Data Validation, Data Serialization และ Data Documentation ที่ช่วยให้จัดการข้อมูล input และ output ได้อย่างมีประสิทธิภาพ โดย FastAPI เองก็ใช้ Pydantic ในการจัดการ Request และ Response Model นั่นเองครับ
FastAPI Performance (Image Source: ChistopherGS)
แนะนำ Docker คืออะไร
สำหรับบทความนี้เราจะพาสร้าง Todo App กันครับเริ่มต้นด้วยสเต็ปด้านล่างนี้ได้เลย
เปิด Terminal ขึ้นมาแล้วสร้างโฟลเดอร์ที่ชื่อว่า todo-app
จากนั้นเข้าไปที่โฟลเดอร์
สร้างไฟล์ที่ชื่อว่า main.py
เตรียมสร้าง Virtual Environment สำหรับโปรเจคท์ todo-api ด้วยการ พิมพ์คำสั่ง
$ mkdir todo-app$ cd todo-app$ python -m venv env$ source env/bin/activate(env) $ pip install fastapi uvicorn
บทความแนะนำเพิ่มเติม: Python Virtual Environment ที่ Python Dev ทุกคนต้องรู้
โครงสร้างของโปรเจค
└── todo-api├── main.py└── env
สำหรับบทความนี้เราจะ setup ทุกอย่างไว้ที่ไฟล์นี้ ไม่ได้ทำการแยก module แต่อย่างใด
from fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModelfrom typing import List, Optionalapp = FastAPI()class Item(BaseModel):name: strcompleted: bool# In-memory storage of to-do itemsto_do_list: List[Item] = []@app.get("/todos/", response_model=List[Item])async def read_todos():return to_do_list@app.post("/todos/", response_model=Item)async def create_todo(item: Item):to_do_list.append(item)return item@app.put("/todos/{item_id}", response_model=Item)async def update_todo(item_id: int, item: Item):if item_id >= len(to_do_list):raise HTTPException(status_code=404, detail="Item not found")to_do_list[item_id] = itemreturn item@app.delete("/todos/{item_id}")async def delete_todo(item_id: int):if item_id >= len(to_do_list):raise HTTPException(status_code=404, detail="Item not found")to_do_list.pop(item_id)return {"detail": "Item deleted"}
ให้ทำการเปิด Terminal หรือ Power Shell ในโปรแกรม Editor จากนั้นพิมพ์คำสั่งเพื่อรัน Server
$ uvicorn main:app --reload
ซึ่งในส่วนของ FastAPI จะมี Swagger UI มาให้เราพร้อมใช้เลยครับ
Dockerize FastAPI App
$ pip freeze > requirements.txt
คำสั่งด้านบนจะเป็นการ Export Libraries ที่เราทำการติดตั้งไปตั้งแต่ตอนต้น แล้วจัดเก็บไปที่ไฟล์ requirements.txt
ประโยชน์ก็เพื่อให้เพื่อน คนในทีม หรือ เวลาเรา build docker มี Libraries ชุดเดียวกัน ตรงกัน หรือเหมือนกันกับเรา
เพื่อไม่ให้เกิดปัญหา Libraries conflict นั่นเอง หลังจากที่เรา พิมพ์คำสั่งเรียบร้อยก็จะได้ไฟล์เพิ่มขึ้นมา
└── todo-api├── main.py├── env└── requirements.txt
จากนั้นเราก็สร้างไฟล์ Dockerfile
ไว้ในโฟลเดอร์เดียวกันได้เลยครับ
#DockerfileFROM python:3.9-alpine3.18. #1WORKDIR /code #2COPY requirements.txt . #3RUN pip install --no-cache-dir --upgrade -r requirements.txt #4COPY . . #5CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] #6
เรามาดูในส่วนของ Dockerfile
กันนิดนึงครับ
#1 นี่คือ Python base Image ที่เรา pull มาจาก Docker Hub เพื่อที่เราจะใช้เป็น base ของ Dockerfile อันนี้ครับ
#2 เป็นการกำหนด Working Directory หรือโฟลเดอร์ใน container ของเรา
#3 เราให้ทำการ copy ไฟล์ requirements.txt
จากโปรเจคท์ในเครื่องของเรา เข้าไปที่ โฟลเดอร์ใน Docker Container
#4 ตรงนี้เป็นการสั่งให้ติดตั้ง libraries ทั้งหมดจากไฟล์ requirements.txt
ของเรา
#5 จากนั้นใช้คำสั่ง COPY
เพื่อคัดลอกไฟล์หมด . .
จากโปรเจคท์ของเราเข้าไปไว้ที่ โฟลเดอร์ใน container
#6 CMD
คือคำสั่งให้ Docker Image รัน command นี้ ตอนสตาร์ท Docker Container ครับ
หลังจากที่สร้างไฟล์ Dockerfile
กันเรียบร้อยก็ถึงเวลาใช้งานกันครับ อันนี้เป็นโครงสร้างโปรเจคท์ปัจจุบันของเรานะครับ
└── todo-api├── main.py├── env├── Dockerfile└── requirements.txt
เปิด Terminal หรือ PowerShell และให้เข้ามาที่โปรเจคท์ของเราได้เลยครับ
จากนั้นทำการพิมพ์คำสั่ง
$ docker build -t todo-api:v1 .
คำสั่งตรงนี้ เป็นการสั่งให้ build Dockerfile ของเราที่โปรเจคท์นี้นะครับ ด้วยการบอก ที่อยู่ของ Dockerfile ของเราด้วย จุด . (dot) นั้นเองครับ
และให้สร้างด้วยชื่อ tod-api
ซึ่งมี -t
เป็น v1
ครับ เพื่อให้แน่ใจว่าเราทำการ build docker image เสร็จเรียบร้อยให้พิมพ์คำสั่ง
$ docker image ls
เพื่อดูว่ามี docker image ชื่อ todo-api
ที่เราเพิ่ง build ไป
$ docker images lsREPOSITORY TAG IMAGE ID CREATED SIZEtodo-api v1 513251c9bdac 9 minutes ago 106 MB
docker run เป็นการนำ docker image ที่เราได้ทำการ build มาใช้งานซึ่งจะสร้างเป็น docker container และเราจะเรียกใช้ Aญ ของเราผ่าน docker container กันครับ
$ docker run -p 8000:80 --name my-first-api todo-api:v1
จากคำสั่งเราก็ทำการบอกให้ docker สร้าง container จาก docker image ที่ชื่อ todo-api ที่มี tag ชื่อว่า v1 และให้เข้าถึง container ด้วย port 8000 ซึ่ง port 8000 จาก host (เครื่องของเรา)
ก็จะเข้าถึง ตัว API ของเราที่อยู่ใน docker container ที่รันด้วย port 80 นั่นเองครับ และทำการตั้งชื่อ container ว่า my-first-api
ลองทดสอบเรียก API ของเราที่ Browser ได้เลยครับ ด้วย URL นี้ localhost:8000/docs
ลองทดสอบด้วยการเพิ่มข้อมูลด้วย http POST
ลองทดสอบด้วยการเรียกดูข้อมูลด้วย http GET
ทดสอบแล้ว API สามารถทำงานได้ปกติแสดงว่าการรันด้วย Container สำเร็จเรียบร้อยครับ
ทีนี้กลับมาที่ตัว Terminal ของเรากันครับ จะเห็นได้ว่า Terminal ของเราจะแสดงเป็น Log ของ API ถ้าเกิดเรากด Ctrl+c ก็จะเป็นการ Terminate หรือออกจากการรั้น container ครับ
ถ้าหากอยากให้ docker container รันแบบ background หรือรันอยู่เบื้องหลังเราต้องเพิ่มคำสั่งเข้าไปครับ ให้ทำการกด Ctrl+c เพื่อออกจาก container ก่อนครับ
จากนั้นทำการลบ container ที่ชื่อ my-first-api
ก่อน (ไม่เช่นนั้นแล้วเราจะไม่สามารถใช่ซื่อเดิมได้ครับ)
$ docker rm -f my-first-api
หลังจากพิมพ์คำสั่งแล้วกด Enter เพื่อลบ container ที่ชื่อ my-first-api เรียบร้อยแล้วให้ทำการพิมพ์ด้วยคำสั่งนี้ต่อครับ
$ docker run -d -p 8000:80 --name my-first-api todo-api:v1
-d
หรือ --detach
หมายถึงให้ Docker container รันแบบ background นั่นเองครับ
ทีนี้เราจะไม่เห็นในส่วนของ output แบบก่อนหน้าแล้ว (ตอนที่ยังไม่ใส่ Flag -d)
เนื่องจากเรารัน Docker container แบบ background ถ้าหากเราต้องการดู Logs ของ API เราจะทำได้ยัง?
ง่ายมากครับ ให้ใช้คำสั่ง
$ docker logs <CONTAINER_NAME>
ในกรณี my-first-api ของเราให้พิมพ์คำสั่งนี้ครับ
$ docker logs my-first-api -f
-f
หรือ follow เป็นการให้ Logs แสดงข้อมูลแบบ Real time นั่นเองครับ
Docker Compose เป็นเครื่องมือที่เอาไว้จัดการ container แบบหลายๆ ตัว สำหรับ Docker Compose เราจะใช้ ไฟล์ YAML เป็นตัว config app ของเรานั่นเองครับ ทำให้ง่ายต่อการจัดการ Docker container คือเราไม่ต้องมานั่งพิมพ์คำสั่งให้ยาวเหมือนด้านบนครับ แต่เราประกาศไว้ใน ไฟล์ docker-compose.yaml แทน
Use case?
อยาก Develop ตัว API ด้วย Dockerโดยที่ไม่ต้องมาคอย build docker image ใหม่ตลอด
ตรงนี้เราต้องปรับในส่วนของ Dockerfile นิดหน่อยครับ
FROM python:3.9-alpine3.18WORKDIR /codeCOPY requirements.txt .RUN pip install --no-cache-dir --upgrade -r requirements.txtCOPY . .
ให้ตัดในส่วนของ CMD ออกไปเพราะว่า ในส่วนของ Docker image ให้ทำแค่
ติดตั้ง libraries หรือ dependencies ต่าง ๆ ที่ต้องใช้ในโปรเจคท์
Copy ซอร์สโค้ดไปไว้ที่โฟลเดอร์ใน container
จากนั้นทำการสร้างไฟล์ docker-compose.yaml ในโปรเจคท์เดียวกันได้เลยครับ
└── todo-api├── main.py├── env├── Dockerfile├── docker-compose.yaml└── requirements.txt
ทำการแปะ config file ชุดนี้ลงไปที่ไฟล์ docker-compose.yaml ได้เลยครับ
version: '3.8' #1services: #2my-first-api: #3build: . #4command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80", "--reload"] #5volumes: #6- .:/code #7ports: #8- "80:80"
อธิบายในส่วนของ docker-compose.yaml กันนิดนึงครับ
#1 version
เป็นการกำหนดเวอร์ชั่นของ Docker Compose สามารถดูได้จาก compose-versioning
#2 services
ประกาศ services ไว้สำหรับ จัดการกับ container
#3 ชื่อ Service หรือ Container.
#4. build
เราสั่งให้ Compose ทำการ build image จาก Dockerfile ของเรา
#5 command
ให้ Compose run คำสั่ง เวลารัน Container
#6 volumes
กำหนดที่จัดเก็บไฟล์ของ container
#7 สั่งให้ทำการ เชื่อมต่อ โฟล์เดอร์ปัจจุบันของเรา ไปที่ โฟลเดอร์ code ใน container
#8 ports
คือการกำหนด port ให้เข้าถึง container ผ่าน port 80
จาก Compose config เราจะเห็นได้ว่า การจัดการ Docker container ดูง่ายขึ้นกว่าเดิมเยอะเลยครับ
ถึงขั้นตอนในการรัน Docker Compose กันแล้วครับ ในส่วนของการรันก็จะมีสองแบบได้แก่
รันแบบปกติ คือให้แสดง Logs ทาง terminal เลยถ้ากด Ctrl+c ก็จะออก
รันแบบ Background หรือ ก็คือแบบ detach นั่นเองครับ
สำหรับการรันแบบปกติ ให้ใช้คำสั่ง
$ docker-compose up
สำหรับการรันแบบ Background ให้ใช้คำสั่ง
$ docker-compose up -d
จะได้ผลลัพธ์ตามรูป
ให้ใช้คำสั่งนี้ในกรณีที่เรารันแบบ Background ถ้าหากเราต้องการเช็คว่า Docker Compose สามารถรันได้หรือไม่
$ docker-compose psNAME IMAGE COMMAND SERVICE CREATED STATUS PORTStodo-api-my-first-api-1 todo-api-my-first-api "uvicorn main:app --host 0.0.0.0 --port 80 --reload" my-first-api 5 hours ago Up 5 hours 0.0.0.0:80->80/tcp
หรือใช้ docker ps ก็ได้เช่นกัน
ให้ทำการเช็ค API ของเราว่าสามารถทำงานได้ปกติโดยเข้าไปที่ localhost:80/docs
ลองทำการแก้ไข ชื่อ route ใน POST จาก /todos/
ให้เป็น /todo/
@app.post("/todo/", response_model=Item)async def create_todo(item: Item): to_do_list.append(item) return item
จากนั้นทำการ Save ไฟล์และ refresh browser อีกครั้ง
เป็นยังไงกันบ้างครับกับ สำหรับการสร้าง FastAPI และ ใช้ Docker เข้ามาช่วยจากการลงมือทำข้างต้น หวังว่าเพื่อนจะได้ความรู้ในส่วนของการใช้ FastAPI สร้าง API ขึ้นมา และได้รู้ถึงขั้นตอนการ Containerization ด้วย Docker และต่อยอดไปถึง Docker Compose กันเลยทีเดียวครับ หวังว่าเพื่อนๆ สามารถนำความรู้จากบทความนี้ไปประยุกต์ใช้กันได้ครับ
ศึกษาเพิ่มเติมเกี่ยวกับ FastAPI ได้ที่ FastAPI Official Website
เปิดโลกการเขียนโปรแกรมและ Software Development ด้วย online courses ที่จะพาคุณอัพสกิลและพัฒนาสู่การเป็นมืออาชีพ เรียนออนไลน์ เรียนจากที่ไหนก็ได้ พร้อมซัพพอร์ตหลังเรียน
เรียนเขียนโปรแกรมคอร์สเรียน Full Stack Developer 2024 ด้วยเฟรมเวิร์คยอดนิยมในการพัฒนา A…