ผลต่างระหว่างรุ่นของ "01204223/deployment"

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
(สร้างหน้าด้วย ": ''หน้านี้เป็นส่วนหนึ่งของ 01204223'' เราจะฝึกการนำแอพที่เร...")
 
(ไม่แสดง 5 รุ่นระหว่างกลางโดยผู้ใช้คนเดียวกัน)
แถว 9: แถว 9:
 
== Deploy ไปยัง Google cloud run ==
 
== Deploy ไปยัง Google cloud run ==
  
 +
=== ปรับโค้ด ===
 
เราต้องปรับโค้ดของเราให้พร้อมที่จะนำไป deploy บน Google cloud run เสียก่อน โดยมีรายละเอียดคร่าว ๆ  ดังนี้
 
เราต้องปรับโค้ดของเราให้พร้อมที่จะนำไป deploy บน Google cloud run เสียก่อน โดยมีรายละเอียดคร่าว ๆ  ดังนี้
  
 
* โปรแกรมหลักที่จะถูกเรียกทำงานคือ main.py เราต้องปรับให้โปรแกรมดังกล่าวทำงานได้เลย (แทนที่เดิมเราเคยเรียก flask run)
 
* โปรแกรมหลักที่จะถูกเรียกทำงานคือ main.py เราต้องปรับให้โปรแกรมดังกล่าวทำงานได้เลย (แทนที่เดิมเราเคยเรียก flask run)
* ต้องเพิ่มบรรทัด load_dotenv เอง เพื่อโหลด config จากไฟล์ .env
 
 
* เดิมเราใช้ package mysqlclient ในการเชื่อมต่อกับ MySQL แต่การติดตั้ง package ดังกล่าวมีการคอมไพล์โค้ดภาษาซีซึ่งใน container ไม่ได้มีมาด้วย เราจะต้องเปลี่ยนไปใช้ package PyMySQL ที่เป็นไลบรารี python ล้วนในการเชื่อมต่อ
 
* เดิมเราใช้ package mysqlclient ในการเชื่อมต่อกับ MySQL แต่การติดตั้ง package ดังกล่าวมีการคอมไพล์โค้ดภาษาซีซึ่งใน container ไม่ได้มีมาด้วย เราจะต้องเปลี่ยนไปใช้ package PyMySQL ที่เป็นไลบรารี python ล้วนในการเชื่อมต่อ
 
** เราต้องติดตั้ง PyMySQL
 
** เราต้องติดตั้ง PyMySQL
 
** แก้ requirements.txt
 
** แก้ requirements.txt
 
** ปรับ URI ของ database
 
** ปรับ URI ของ database
 +
* ต้องเปลี่ยนการจัดการ config ให้โหลดจากไฟล์ local_config.py
 +
** อย่าลืมปรับ .gitignore
 
* เราต้อง build โค้ดส่วน frontend และนำมาใส่ไดเร็กทอรี backend
 
* เราต้อง build โค้ดส่วน frontend และนำมาใส่ไดเร็กทอรี backend
 
** ต้องปรับ URL endpoint ด้วย
 
** ต้องปรับ URL endpoint ด้วย
แถว 22: แถว 24:
  
 
เราจะทำไปทีละขั้น
 
เราจะทำไปทีละขั้น
 +
 +
<syntaxhighlight lang="python">
 +
if __name__ == "__main__":
 +
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang="python">
 +
CONFIG_DB_URI = "mysql+pymysql://USERNAME:PASSWORD@p1.secondtrain.org/DBNAME"
 +
CONFIG_JWT_SECRET = 'fdslkfjsdlkufewhjroiewurewrew'
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang="python">
 +
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)
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang="js">
 +
export default defineConfig({
 +
  // ...
 +
  server: {
 +
    proxy: {
 +
      '/api': {
 +
        target: 'http://localhost:5000/',
 +
        changeOrigin: true,
 +
      },
 +
    },
 +
  },
 +
  build: {
 +
    outDir: '../backend/frontend-static',
 +
    emptyOutDir: true,
 +
  },
 +
})
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang="js">
 +
function App() {
 +
  const TODOLIST_API_URL = '/api/todos/';
 +
  const TODOLIST_LOGIN_URL = '/api/login/';
 +
  //...
 +
}
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang="python">
 +
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')
 +
</syntaxhighlight>
 +
 +
=== ตั้งค่าพื้นฐาน 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 ====

รุ่นแก้ไขเมื่อ 23:05, 2 มีนาคม 2569

หน้านี้เป็นส่วนหนึ่งของ 01204223

เราจะฝึกการนำแอพที่เราสร้างไปติดตั้งในระบบเพื่อใช้งานจริง ถ้าเราไม่ได้มีเซิร์ฟเวอร์เอง การใช้บริการ cloud deployment ก็เป็นอีกทางเลือกที่มีประโยชน์ หลายครั้งยังทำให้ระบบที่เราทำขึ้นรองรับโหลดได้หลากหลาย โดยแทบไม่ต้องปรับแก้

วิธีการที่มาตรฐานที่สุดคือการใช้ container แต่ในวิชานี้เราไม่มีเวลาศึกษาเรื่องดังกล่าว เราจึงจะใช้การติดตั้งแบบอัตโนมัติที่มีการสร้าง container ถ้าเราใช้โครงสร้าง project ตามรูปแบบมาตรฐาน

เนื่องจากแอพของเรามีสองส่วน backend (ที่จะต้องทำงานบนเซิร์ฟเวอร์) และ frontend (ที่เมื่อ react build เสร็จแล้ว จะเป็นไฟล์ static ที่ส่งให้บราวเซอร์ได้เลย) จึงมีสองแนวทางในการ deploy คือการ deploy สองส่วนแยกกัน หรือจะรวมสองส่วนเข้าด้วยกันเพื่อ deploy ไปด้วยกัน เพื่อความง่ายเราจะใช้แบบที่สอง

Deploy ไปยัง Google cloud run

ปรับโค้ด

เราต้องปรับโค้ดของเราให้พร้อมที่จะนำไป deploy บน Google cloud run เสียก่อน โดยมีรายละเอียดคร่าว ๆ ดังนี้

  • โปรแกรมหลักที่จะถูกเรียกทำงานคือ 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 ด้วย

เราจะทำไปทีละขั้น

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)
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/';
  //...
}
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')

ตั้งค่าพื้นฐาน 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