ผลต่างระหว่างรุ่นของ "01204223/deployment"
Jittat (คุย | มีส่วนร่วม) |
Jittat (คุย | มีส่วนร่วม) |
||
| (ไม่แสดง 10 รุ่นระหว่างกลางโดยผู้ใช้คนเดียวกัน) | |||
| แถว 7: | แถว 7: | ||
เนื่องจากแอพของเรามีสองส่วน backend (ที่จะต้องทำงานบนเซิร์ฟเวอร์) และ frontend (ที่เมื่อ react build เสร็จแล้ว จะเป็นไฟล์ static ที่ส่งให้บราวเซอร์ได้เลย) จึงมีสองแนวทางในการ deploy คือการ deploy สองส่วนแยกกัน หรือจะรวมสองส่วนเข้าด้วยกันเพื่อ deploy ไปด้วยกัน เพื่อความง่ายเราจะใช้แบบที่สอง | เนื่องจากแอพของเรามีสองส่วน backend (ที่จะต้องทำงานบนเซิร์ฟเวอร์) และ frontend (ที่เมื่อ react build เสร็จแล้ว จะเป็นไฟล์ static ที่ส่งให้บราวเซอร์ได้เลย) จึงมีสองแนวทางในการ deploy คือการ deploy สองส่วนแยกกัน หรือจะรวมสองส่วนเข้าด้วยกันเพื่อ deploy ไปด้วยกัน เพื่อความง่ายเราจะใช้แบบที่สอง | ||
| − | == | + | == ปรับโค้ดเพื่อ deploy == |
| − | |||
| − | |||
เราต้องปรับโค้ดของเราให้พร้อมที่จะนำไป deploy บน Google cloud run เสียก่อน โดยเครื่องมือที่เราใช้จะ copy โค้ดทั้งหมดของเราไปที่ระบบของ google และสร้าง container image โดยติดตั้ง package ตาม requirements.txt และเรียกให้ไฟล์ main.py ทำงาน ดังนี้เราต้องจัดการดังนี้ | เราต้องปรับโค้ดของเราให้พร้อมที่จะนำไป deploy บน Google cloud run เสียก่อน โดยเครื่องมือที่เราใช้จะ copy โค้ดทั้งหมดของเราไปที่ระบบของ google และสร้าง container image โดยติดตั้ง package ตาม requirements.txt และเรียกให้ไฟล์ main.py ทำงาน ดังนี้เราต้องจัดการดังนี้ | ||
| แถว 31: | แถว 29: | ||
เราจะทำไปทีละขั้น | เราจะทำไปทีละขั้น | ||
| − | + | === ปรับ main.py ให้ทำงานได้ และโหลด config จากไฟล์ === | |
<syntaxhighlight lang="python"> | <syntaxhighlight lang="python"> | ||
| แถว 54: | แถว 52: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | + | === แก้ react API URL และตั้งค่าการ build === | |
<syntaxhighlight lang="js"> | <syntaxhighlight lang="js"> | ||
| แถว 82: | แถว 80: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | + | === ปรับให้ flask แจกจ่ายไฟล์ของ react === | |
<syntaxhighlight lang="python"> | <syntaxhighlight lang="python"> | ||
| แถว 99: | แถว 97: | ||
return send_from_directory('frontend-static', 'index.html') | return send_from_directory('frontend-static', 'index.html') | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| + | |||
| + | === ทดสอบ === | ||
| + | |||
| + | : ''TODO'' | ||
| + | |||
| + | == Deploy ไปยัง Google cloud run == | ||
=== ตั้งค่าพื้นฐาน Google Cloud Run === | === ตั้งค่าพื้นฐาน Google Cloud Run === | ||
| แถว 132: | แถว 136: | ||
==== การ debug ==== | ==== การ debug ==== | ||
| + | |||
| + | == Deploy ไปยัง Microsoft Azure == | ||
| + | |||
| + | === สมัคร Azure === | ||
| + | ก่อนอื่นไปสมัครสมาชิก Microsoft azure ก่อน ที่ [https://azure.microsoft.com/en-us/products/app-service/web https://azure.microsoft.com/en-us/products/app-service/web] '''จากที่ทดลอง ไม่สามารถใช้บัญชี @live.ku.th ได้''' | ||
| + | |||
| + | ระบบจะให้สมัครสมาชิก ให้เลือก free trial | ||
| + | |||
| + | [[Image:223-azure-sub.png|300px]] | ||
| + | |||
| + | ต้องกรอกข้อมูลให้เรียบร้อย ให้เลือก Free trial (อย่าเลือก Pay-as-you-go) ต้องใส่ข้อมูลบัตรเครดิตไว้ก่อนด้วย ถ้าทำเสร็จเรียบร้อยจะได้หน้าจอประมาณนี้ | ||
| + | |||
| + | [[Image:223-azure-account-success.png|500px]] | ||
| + | |||
| + | === ติดตั้งเครื่องมือ CLI: az === | ||
| + | |||
| + | จากนั้นในการ deploy เราสามารถใช้ web interface ก็ได้ แต่เราจะใช้ cli แทน โดยให้ติดตั้ง Azure CLI โดยไปที่ [https://learn.microsoft.com/en-us/cli/azure/?view=azure-cli-latest https://learn.microsoft.com/en-us/cli/azure/?view=azure-cli-latest] แล้วดำเนินการตามส่วน Installation | ||
| + | |||
| + | [[Image:223-azure-cli-installation.png|500px]] | ||
| + | |||
| + | ถ้าติดตั้งเรียบร้อยจะสามารถเรียก cli ชื่อ <tt>az</tt> ได้ | ||
| + | |||
| + | az | ||
| + | |||
| + | === Deploy === | ||
| + | |||
| + | ถ้าเราปรับโค้ดตามข้างต้นเรียบร้อย เราแทบจะเสร็จสิ้นการเตรียมการ อย่างไรก็ตาม Azure ต้องการให้โปรแกรมหลักของเราชื่อ <tt>app.py</tt> แทนที่จะเป็น <tt>main.py</tt> | ||
| + | |||
| + | ให้เปลี่ยนชื่อ main.py เป็น app.py ก่อน โดยอาจจะสั่ง | ||
| + | |||
| + | mv main.py app.py | ||
| + | |||
| + | และให้ทดลองเรียก Flask ให้ทำงาน (อย่าลืมตั้งค่า FLASK_APP ด้วย) ถ้าจะ commit ลง git อย่าลืม add app.py | ||
| + | |||
| + | เราสามารถ deploy อัตโนมัติได้สะดวกมาก โดยเข้าไปใน backend แล้วสั่ง | ||
| + | |||
| + | az login | ||
| + | |||
| + | จากนั้นจะมี web browser ปรากฏขึ้น ใน login ให้เรียบร้อย | ||
| + | |||
| + | จากนั้นสั่ง | ||
| + | |||
| + | az webapp up --runtime PYTHON:3.14 --sku F1 --logs | ||
| + | |||
| + | ตรงส่วน sku ให้ดูดี ๆ ให้ใส่ F1 จะเป็น free tier | ||
| + | |||
| + | จากนั้นรอพักใหญ่ สคริปต์จะทำงานให้ผลลัพธ์ประมาณนี้ '''และจะรันค้างอยู่ เพราะว่าเราสั่งให้มีการแสดง logs ค้างไว้''' เมื่อแอพ deploy เรียบร้อยให้กด Ctrl-C ออกมาได้เลย | ||
| + | |||
| + | <pre> | ||
| + | The webapp 'jolly-rock-xxxyyyzzzxxxyyyzzz' doesn't exist | ||
| + | Creating Resource group 'jittat_rg_1414' ... | ||
| + | Resource group creation complete | ||
| + | Creating AppServicePlan 'jittat_asp_2900' or Updating if already exists | ||
| + | Resource provider 'Microsoft.Web' used by this operation is not registered. We are registering for you. | ||
| + | Registration succeeded. | ||
| + | Creating webapp 'jolly-rock-xxxyyyzzzxxxyyyzzz' ... | ||
| + | vnet_route_all_enabled is not a known attribute of class <class 'azure.mgmt.web.v2024_11_01.models._models_py3.Site'> and will be ignored | ||
| + | Configuring default logging for the app, if not already enabled | ||
| + | Creating zip with contents of dir /home/jittat/prog/test/flask-react-todo-start/backend ... | ||
| + | Getting scm site credentials for zip deployment | ||
| + | Starting zip deployment. This operation can take a while to complete ... | ||
| + | Warming up Kudu before deployment. | ||
| + | Deployment endpoint responded with status code 202 | ||
| + | Polling the status of async deployment. Start Time: 2026-03-03 03:40:15.411645+00:00 UTC | ||
| + | Status: Building the app... Time: 1(s) | ||
| + | Status: Building the app... Time: 18(s) | ||
| + | Status: Build successful. Time: 34(s) | ||
| + | Status: Starting the site... Time: 51(s) | ||
| + | Status: Starting the site... Time: 67(s) | ||
| + | Status: Starting the site... Time: 83(s) | ||
| + | Status: Starting the site... Time: 99(s) | ||
| + | Status: Site started successfully. Time: 115(s) | ||
| + | You can launch the app at http://jolly-rock-xxxyyyzzzxxxyyyzzz.azurewebsites.net | ||
| + | Configuring default logging for the app, if not already enabled | ||
| + | 2026-03-03T03:52:16 Welcome, you are now connected to log-streaming service. | ||
| + | Starting Log Tail -n 10 of existing logs ---- | ||
| + | /home/LogFiles/kudu/trace/7398fc64b7fd-4e462719-2827-4576-8e9f-a6874c374779.txt (https://xxxxxxxxxxxxxxxxxxxxxxxx) | ||
| + | 2026-03-03T03:50:12 Startup Request, url: /api/zipdeploy?isAsync=true, method: POST, type: request, pid: 852,1,14, ScmType: None, SCM_DO_BUILD_DURING_DEPLOYMENT: True | ||
| + | /home/LogFiles/2026_03_03_10-30-0-139_default_docker.log (https://xxxxxxxxxxxxxxxxxxxxxxxx/api/vfs/LogFiles/2026_03_03_10-30-0-139_default_docker.log) | ||
| + | </pre> | ||
| + | |||
| + | app ที่เรา deploy แล้วจะปรากฏในรายการ App Services ของ Azure portal ทุกครั้งที่เราสั่ง deploy ตามด้านบน เราไม่ได้กำหนดชื่อ จะทำให้ได้ app ใหม่เกิดขึ้นเรื่อย ๆ | ||
| + | |||
| + | [[Image:223-azure-deployed.png|700px]] | ||
| + | |||
| + | ถ้าต้องการให้ deploy ทับของเดิม... | ||
รุ่นแก้ไขปัจจุบันเมื่อ 04:26, 3 มีนาคม 2569
- หน้านี้เป็นส่วนหนึ่งของ 01204223
เราจะฝึกการนำแอพที่เราสร้างไปติดตั้งในระบบเพื่อใช้งานจริง ถ้าเราไม่ได้มีเซิร์ฟเวอร์เอง การใช้บริการ cloud deployment ก็เป็นอีกทางเลือกที่มีประโยชน์ หลายครั้งยังทำให้ระบบที่เราทำขึ้นรองรับโหลดได้หลากหลาย โดยแทบไม่ต้องปรับแก้
วิธีการที่มาตรฐานที่สุดคือการใช้ container แต่ในวิชานี้เราไม่มีเวลาศึกษาเรื่องดังกล่าว เราจึงจะใช้การติดตั้งแบบอัตโนมัติที่มีการสร้าง container ถ้าเราใช้โครงสร้าง project ตามรูปแบบมาตรฐาน
เนื่องจากแอพของเรามีสองส่วน backend (ที่จะต้องทำงานบนเซิร์ฟเวอร์) และ frontend (ที่เมื่อ react build เสร็จแล้ว จะเป็นไฟล์ static ที่ส่งให้บราวเซอร์ได้เลย) จึงมีสองแนวทางในการ deploy คือการ deploy สองส่วนแยกกัน หรือจะรวมสองส่วนเข้าด้วยกันเพื่อ deploy ไปด้วยกัน เพื่อความง่ายเราจะใช้แบบที่สอง
ปรับโค้ดเพื่อ deploy
เราต้องปรับโค้ดของเราให้พร้อมที่จะนำไป deploy บน Google cloud run เสียก่อน โดยเครื่องมือที่เราใช้จะ copy โค้ดทั้งหมดของเราไปที่ระบบของ google และสร้าง container image โดยติดตั้ง package ตาม requirements.txt และเรียกให้ไฟล์ main.py ทำงาน ดังนี้เราต้องจัดการดังนี้
- ทำให้การติดตั้ง package ทำงานได้สมบูรณ์ (ปรับ requirements.txt)
- ทำให้ main.py ทำงานได้ (พร้อมกับ config ต่าง ๆ)
- รวมโค้ด frontend จาก react ที่ปรับ api url แล้ว, และเพิ่มโค้ดให้ flask server เป็นคนแจกจ่าย frontend code
ขั้นตอนข้างต้นมีรายละเอียดคร่าว ๆ ดังนี้
- โปรแกรมหลักที่จะถูกเรียกทำงานคือ main.py เราต้องปรับให้โปรแกรมดังกล่าวทำงานได้เลย (แทนที่เดิมเราเคยเรียก flask run)
- เดิมเราใช้ package mysqlclient ในการเชื่อมต่อกับ MySQL แต่การติดตั้ง package ดังกล่าวมีการคอมไพล์โค้ดภาษาซีซึ่งใน container ไม่ได้มีมาด้วย เราจะต้องเปลี่ยนไปใช้ package PyMySQL ที่เป็นไลบรารี python ล้วนในการเชื่อมต่อ
- เราต้องติดตั้ง PyMySQL
- แก้ requirements.txt
- ปรับ URI ของ database
- ต้องเปลี่ยนการจัดการ config ให้โหลดจากไฟล์ local_config.py
- อย่าลืมปรับ .gitignore
- เราต้อง build โค้ดส่วน frontend และนำมาใส่ไดเร็กทอรี backend
- ต้องปรับ URL endpoint ด้วย
- เพิ่มโค้ดใน Flask ให้ serve ไฟล์ frontend ด้วย
เราจะทำไปทีละขั้น
ปรับ main.py ให้ทำงานได้ และโหลด config จากไฟล์
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
CONFIG_DB_URI = "mysql+pymysql://USERNAME:PASSWORD@p1.secondtrain.org/DBNAME"
CONFIG_JWT_SECRET = 'fdslkfjsdlkufewhjroiewurewrew'
try:
from local_config import CONFIG_DB_URI, CONFIG_JWT_SECRET
except:
CONFIG_DB_URI = 'sqlite:///todos.db'
CONFIG_JWT_SECRET = 'fdslkfjsdlkufewhjroiewurewrew'
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('SQLALCHEMY_DATABASE_URI', CONFIG_DB_URI)
app.config['JWT_SECRET_KEY'] = os.getenv('JWT_SECRET_KEY', CONFIG_JWT_SECRET)
แก้ react API URL และตั้งค่าการ build
export default defineConfig({
// ...
server: {
proxy: {
'/api': {
target: 'http://localhost:5000/',
changeOrigin: true,
},
},
},
build: {
outDir: '../backend/frontend-static',
emptyOutDir: true,
},
})
function App() {
const TODOLIST_API_URL = '/api/todos/';
const TODOLIST_LOGIN_URL = '/api/login/';
//...
}
ปรับให้ flask แจกจ่ายไฟล์ของ react
from flask import send_from_directory
# Catch-all route: if the requested file exists in frontend-static, serve it;
# otherwise fall back to serving the React app's index.html.
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def serve_frontend(path):
static_dir = os.path.join(app.root_path, 'frontend-static')
# If a specific file is requested and exists, serve it
if path and os.path.isfile(os.path.join(static_dir, path)):
return send_from_directory('frontend-static', path)
# Otherwise serve the app entrypoint
return send_from_directory('frontend-static', 'index.html')
ทดสอบ
- TODO
Deploy ไปยัง Google cloud run
ตั้งค่าพื้นฐาน Google Cloud Run
Deploy
gcloud run deploy --source .
Service name (backend): practicum68-backend Please specify a region: ... [8] asia-south2 [9] asia-southeast1 [10] asia-southeast2 [11] asia-southeast3 [12] australia-southeast1 [13] australia-southeast2 ... Please enter numeric choice or text value (must exactly match list item): 11
Building using Buildpacks and deploying container to Cloud Run service [practicum68-backend] in project [practicum-68] region [asia-southeast3] ✓ Building and deploying... Done. ✓ Validating configuration... ✓ Uploading sources... ✓ Building Container... Logs are available at [https://console.cloud.google.com/cloud-build/builds;region=asia-sou theast1/xxxxxxxx?project=yyyyyyyyyyyy]. ✓ Creating Revision... ✓ Routing traffic... Done. Service [practicum68-backend] revision [practicum68-backend-00007-cx2] has been deployed and is serving 100 percent of traffic. Service URL: https://practicum68-backend-XXXXXXXXX.asia-southeast3.run.app
การ debug
Deploy ไปยัง Microsoft Azure
สมัคร Azure
ก่อนอื่นไปสมัครสมาชิก Microsoft azure ก่อน ที่ https://azure.microsoft.com/en-us/products/app-service/web จากที่ทดลอง ไม่สามารถใช้บัญชี @live.ku.th ได้
ระบบจะให้สมัครสมาชิก ให้เลือก free trial
ต้องกรอกข้อมูลให้เรียบร้อย ให้เลือก Free trial (อย่าเลือก Pay-as-you-go) ต้องใส่ข้อมูลบัตรเครดิตไว้ก่อนด้วย ถ้าทำเสร็จเรียบร้อยจะได้หน้าจอประมาณนี้
ติดตั้งเครื่องมือ CLI: az
จากนั้นในการ deploy เราสามารถใช้ web interface ก็ได้ แต่เราจะใช้ cli แทน โดยให้ติดตั้ง Azure CLI โดยไปที่ https://learn.microsoft.com/en-us/cli/azure/?view=azure-cli-latest แล้วดำเนินการตามส่วน Installation
ถ้าติดตั้งเรียบร้อยจะสามารถเรียก cli ชื่อ az ได้
az
Deploy
ถ้าเราปรับโค้ดตามข้างต้นเรียบร้อย เราแทบจะเสร็จสิ้นการเตรียมการ อย่างไรก็ตาม Azure ต้องการให้โปรแกรมหลักของเราชื่อ app.py แทนที่จะเป็น main.py
ให้เปลี่ยนชื่อ main.py เป็น app.py ก่อน โดยอาจจะสั่ง
mv main.py app.py
และให้ทดลองเรียก Flask ให้ทำงาน (อย่าลืมตั้งค่า FLASK_APP ด้วย) ถ้าจะ commit ลง git อย่าลืม add app.py
เราสามารถ deploy อัตโนมัติได้สะดวกมาก โดยเข้าไปใน backend แล้วสั่ง
az login
จากนั้นจะมี web browser ปรากฏขึ้น ใน login ให้เรียบร้อย
จากนั้นสั่ง
az webapp up --runtime PYTHON:3.14 --sku F1 --logs
ตรงส่วน sku ให้ดูดี ๆ ให้ใส่ F1 จะเป็น free tier
จากนั้นรอพักใหญ่ สคริปต์จะทำงานให้ผลลัพธ์ประมาณนี้ และจะรันค้างอยู่ เพราะว่าเราสั่งให้มีการแสดง logs ค้างไว้ เมื่อแอพ deploy เรียบร้อยให้กด Ctrl-C ออกมาได้เลย
The webapp 'jolly-rock-xxxyyyzzzxxxyyyzzz' doesn't exist Creating Resource group 'jittat_rg_1414' ... Resource group creation complete Creating AppServicePlan 'jittat_asp_2900' or Updating if already exists Resource provider 'Microsoft.Web' used by this operation is not registered. We are registering for you. Registration succeeded. Creating webapp 'jolly-rock-xxxyyyzzzxxxyyyzzz' ... vnet_route_all_enabled is not a known attribute of class <class 'azure.mgmt.web.v2024_11_01.models._models_py3.Site'> and will be ignored Configuring default logging for the app, if not already enabled Creating zip with contents of dir /home/jittat/prog/test/flask-react-todo-start/backend ... Getting scm site credentials for zip deployment Starting zip deployment. This operation can take a while to complete ... Warming up Kudu before deployment. Deployment endpoint responded with status code 202 Polling the status of async deployment. Start Time: 2026-03-03 03:40:15.411645+00:00 UTC Status: Building the app... Time: 1(s) Status: Building the app... Time: 18(s) Status: Build successful. Time: 34(s) Status: Starting the site... Time: 51(s) Status: Starting the site... Time: 67(s) Status: Starting the site... Time: 83(s) Status: Starting the site... Time: 99(s) Status: Site started successfully. Time: 115(s) You can launch the app at http://jolly-rock-xxxyyyzzzxxxyyyzzz.azurewebsites.net Configuring default logging for the app, if not already enabled 2026-03-03T03:52:16 Welcome, you are now connected to log-streaming service. Starting Log Tail -n 10 of existing logs ---- /home/LogFiles/kudu/trace/7398fc64b7fd-4e462719-2827-4576-8e9f-a6874c374779.txt (https://xxxxxxxxxxxxxxxxxxxxxxxx) 2026-03-03T03:50:12 Startup Request, url: /api/zipdeploy?isAsync=true, method: POST, type: request, pid: 852,1,14, ScmType: None, SCM_DO_BUILD_DURING_DEPLOYMENT: True /home/LogFiles/2026_03_03_10-30-0-139_default_docker.log (https://xxxxxxxxxxxxxxxxxxxxxxxx/api/vfs/LogFiles/2026_03_03_10-30-0-139_default_docker.log)
app ที่เรา deploy แล้วจะปรากฏในรายการ App Services ของ Azure portal ทุกครั้งที่เราสั่ง deploy ตามด้านบน เราไม่ได้กำหนดชื่อ จะทำให้ได้ app ใหม่เกิดขึ้นเรื่อย ๆ
ถ้าต้องการให้ deploy ทับของเดิม...



