HTTP Cloud Functions ใน Python

1. บทนำ

b158ce75c3cccd6d.png

Python เป็นภาษาโปรแกรมโอเพนซอร์สยอดนิยมที่นักวิทยาศาสตร์ข้อมูล นักพัฒนาเว็บแอปพลิเคชัน ผู้ดูแลระบบ และอื่นๆ ใช้กัน

Cloud Functions เป็นแพลตฟอร์มการประมวลผลแบบ Serverless ที่ขับเคลื่อนด้วยเหตุการณ์ Cloud Functions ช่วยให้คุณเขียนโค้ดได้โดยไม่ต้องกังวลเรื่องการจัดสรรทรัพยากรหรือการปรับขนาดเพื่อรองรับข้อกำหนดที่เปลี่ยนแปลงไป

Cloud Functions มี 2 ประเภท ได้แก่

  • ฟังก์ชัน HTTP จะตอบกลับคำขอ HTTP คุณจะได้สร้างคู่ในโค้ดแล็บนี้
  • ฟังก์ชันเบื้องหลังจะทริกเกอร์โดยเหตุการณ์ เช่น การเผยแพร่ข้อความไปยัง Cloud Pub/Sub หรือการอัปโหลดไฟล์ไปยัง Cloud Storage เราจะไม่กล่าวถึงเรื่องนี้ในแล็บนี้ แต่คุณสามารถอ่านเพิ่มเติมได้ในเอกสารประกอบ

efb3268e3b74ed4f.png

Codelab นี้จะแนะนำขั้นตอนการสร้าง Cloud Functions ของคุณเองใน Python

สิ่งที่คุณจะสร้าง

ในโค้ดแล็บนี้ คุณจะเผยแพร่ Cloud Function ที่เมื่อเรียกใช้ผ่าน HTTP จะแสดงโลโก้"ขับเคลื่อนด้วย Python" ดังนี้

a7aaf656b78050fd.png

สิ่งที่คุณจะได้เรียนรู้

  • วิธีเขียน HTTP Cloud Function
  • วิธีเขียน HTTP Cloud Function ที่รับอาร์กิวเมนต์
  • วิธีทดสอบ HTTP Cloud Function
  • วิธีเรียกใช้เซิร์ฟเวอร์ HTTP ของ Python ในเครื่องเพื่อลองใช้ฟังก์ชัน
  • วิธีเขียน HTTP Cloud Function ที่แสดงผลรูปภาพ

2. การตั้งค่าและข้อกำหนด

การตั้งค่าสภาพแวดล้อมแบบเรียนรู้ด้วยตนเอง

  1. ลงชื่อเข้าใช้ Google Cloud Console แล้วสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • ชื่อโปรเจ็กต์คือชื่อที่แสดงสำหรับผู้เข้าร่วมโปรเจ็กต์นี้ ซึ่งเป็นสตริงอักขระที่ Google APIs ไม่ได้ใช้ คุณอัปเดตได้ทุกเมื่อ
  • รหัสโปรเจ็กต์จะไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมดและเปลี่ยนแปลงไม่ได้ (เปลี่ยนไม่ได้หลังจากตั้งค่าแล้ว) Cloud Console จะสร้างสตริงที่ไม่ซ้ำกันโดยอัตโนมัติ ซึ่งโดยปกติแล้วคุณไม่จำเป็นต้องสนใจว่าสตริงนั้นคืออะไร ใน Codelab ส่วนใหญ่ คุณจะต้องอ้างอิงรหัสโปรเจ็กต์ (โดยทั่วไปจะระบุเป็น PROJECT_ID) หากไม่ชอบรหัสที่สร้างขึ้น คุณอาจสร้างรหัสแบบสุ่มอีกรหัสหนึ่งได้ หรือคุณอาจลองใช้ชื่อของคุณเองและดูว่ามีชื่อนั้นหรือไม่ คุณจะเปลี่ยนแปลงรหัสนี้หลังจากขั้นตอนนี้ไม่ได้ และรหัสจะคงอยู่ตลอดระยะเวลาของโปรเจ็กต์
  • โปรดทราบว่ายังมีค่าที่ 3 ซึ่งคือหมายเลขโปรเจ็กต์ที่ API บางตัวใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับค่าทั้ง 3 นี้ได้ในเอกสารประกอบ
  1. จากนั้นคุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร/API ของ Cloud การทำตาม Codelab นี้จะไม่มีค่าใช้จ่ายมากนัก หรืออาจไม่มีค่าใช้จ่ายเลย หากต้องการปิดทรัพยากรเพื่อหลีกเลี่ยงการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่สร้างขึ้นหรือลบโปรเจ็กต์ได้ ผู้ใช้ Google Cloud รายใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรีมูลค่า$300 USD

เริ่มต้น Cloud Shell

แม้ว่าคุณจะใช้งาน Google Cloud จากแล็ปท็อประยะไกลได้ แต่ใน Codelab นี้คุณจะใช้ Cloud Shell ซึ่งเป็นสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานในระบบคลาวด์

เปิดใช้งาน Cloud Shell

  1. จาก Cloud Console ให้คลิกเปิดใช้งาน Cloud Shell 853e55310c205094.png

3c1dabeca90e44e5.png

หากคุณเริ่มใช้ Cloud Shell เป็นครั้งแรก คุณจะเห็นหน้าจอระดับกลางที่อธิบายว่า Cloud Shell คืออะไร หากเห็นหน้าจอระดับกลาง ให้คลิกต่อไป

9c92662c6a846a5c.png

การจัดสรรและเชื่อมต่อกับ Cloud Shell จะใช้เวลาไม่นาน

9f0e51b578fecce5.png

เครื่องเสมือนนี้โหลดเครื่องมือพัฒนาซอฟต์แวร์ทั้งหมดที่จำเป็นไว้แล้ว โดยมีไดเรกทอรีหลักแบบถาวรขนาด 5 GB และทำงานใน Google Cloud ซึ่งช่วยเพิ่มประสิทธิภาพเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก คุณสามารถทำงานส่วนใหญ่หรือทั้งหมดในโค้ดแล็บนี้ได้ด้วยเบราว์เซอร์

เมื่อเชื่อมต่อกับ Cloud Shell แล้ว คุณควรเห็นว่าคุณได้รับการตรวจสอบสิทธิ์และระบบได้ตั้งค่าโปรเจ็กต์เป็นรหัสโปรเจ็กต์ของคุณ

  1. เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคุณได้รับการตรวจสอบสิทธิ์แล้ว
gcloud auth list

เอาต์พุตของคำสั่ง

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคำสั่ง gcloud รู้จักโปรเจ็กต์ของคุณ
gcloud config list project

เอาต์พุตของคำสั่ง

[core]
project = <PROJECT_ID>

หากไม่ได้ตั้งค่าไว้ คุณตั้งค่าได้ด้วยคำสั่งนี้

gcloud config set project <PROJECT_ID>

เอาต์พุตของคำสั่ง

Updated property [core/project].

ตรวจสอบว่าได้เปิดใช้ Cloud Functions API และ Cloud Build API แล้ว

เรียกใช้คำสั่งต่อไปนี้จาก Cloud Shell เพื่อให้แน่ใจว่าได้เปิดใช้ Cloud Functions และ Cloud Build API แล้ว

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

หมายเหตุ: ระบบจะเรียกใช้ Cloud Build ด้วยคำสั่ง gcloud functions deploy และจะสร้างโค้ดเป็นอิมเมจคอนเทนเนอร์โดยอัตโนมัติ

ดาวน์โหลดซอร์สโค้ด

จากเทอร์มินัล Cloud Shell ให้เรียกใช้คำสั่งต่อไปนี้

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

ตรวจสอบเนื้อหาของไดเรกทอรีต้นทาง

ls

คุณควรมีไฟล์ต่อไปนี้

main.py  python-powered.png  test_main.py  web_app.py

3. ขอแนะนำ HTTP Cloud Functions

HTTP Cloud Functions ใน Python เขียนเป็นฟังก์ชัน Python ปกติ ฟังก์ชันต้องยอมรับอาร์กิวเมนต์ flask.Request รายการเดียว ซึ่งมักจะมีชื่อว่า request

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

คุณเปิดไฟล์ด้วยโปรแกรมแก้ไขบรรทัดคำสั่งที่ต้องการได้ (nano, vim หรือ emacs) นอกจากนี้ คุณยังเปิดใน Cloud Shell Editor ได้หลังจากตั้งค่าไดเรกทอรีแหล่งที่มาเป็นพื้นที่ทำงานแล้ว โดยทำดังนี้

cloudshell open-workspace .

มาทำให้ฟังก์ชันนี้ใช้งานได้เป็น HTTP Cloud Function โดยใช้คำสั่ง gcloud functions deploy กัน

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

เอาต์พุตของคำสั่ง

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

หมายเหตุเกี่ยวกับตัวเลือก gcloud functions deploy

  • --runtime: ระบุรันไทม์ของภาษา สำหรับ Python ขณะนี้อาจเป็น python37, python38, python39, python310 หรือ python312 ดูรันไทม์
  • --trigger-http: ระบบจะกำหนดปลายทางให้กับฟังก์ชัน คำขอ HTTP (POST, PUT, GET, DELETE และ OPTIONS) ไปยังปลายทางจะทริกเกอร์การเรียกใช้ฟังก์ชัน
  • --allow-unauthenticated: ฟังก์ชันนี้จะเป็นแบบสาธารณะ ซึ่งจะอนุญาตให้ผู้โทรทุกคนโทรได้โดยไม่ต้องตรวจสอบการตรวจสอบสิทธิ์
  • ดูข้อมูลเพิ่มเติมได้ที่ gcloud functions deploy

หากต้องการทดสอบฟังก์ชัน ให้คลิก httpsTrigger.url URL ที่แสดงในเอาต์พุตคำสั่งด้านบน นอกจากนี้ คุณยังดึงข้อมูล URL และเรียกใช้ฟังก์ชันด้วยคำสั่งต่อไปนี้ได้ด้วย

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

คุณควรได้รับผลลัพธ์ต่อไปนี้

Hello World! 👋

4. การเขียน Cloud Function แบบ HTTP ที่รับอาร์กิวเมนต์

ฟังก์ชันจะมีความหลากหลายมากขึ้นเมื่อรับอาร์กิวเมนต์ มากำหนดฟังก์ชันใหม่ hello_name ซึ่งรองรับพารามิเตอร์ name กัน

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

มาทำให้ฟังก์ชันใหม่นี้ใช้งานได้กัน

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

เอาต์พุตของคำสั่ง

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

หากต้องการทดสอบฟังก์ชัน ให้คลิก httpsTrigger.url URL ที่แสดงในเอาต์พุตคำสั่งด้านบน นอกจากนี้ คุณยังดึงข้อมูล URL และเรียกใช้ฟังก์ชันด้วยคำสั่งต่อไปนี้ได้ด้วย

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

คุณควรได้รับผลลัพธ์เริ่มต้นดังนี้

Hello World! 🚀

คุณได้รับผลลัพธ์เริ่มต้นเนื่องจากไม่ได้ตั้งค่าอาร์กิวเมนต์ name เพิ่มพารามิเตอร์ลงใน URL โดยทำดังนี้

curl -w "\n" $URL?name=YOUR%20NAME

คราวนี้คุณจะได้รับคำตอบที่กำหนดเอง ดังนี้

Hello YOUR NAME! 🚀

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

5. การทดสอบการเขียน

ฟังก์ชันระบบคลาวด์ HTTP ใน Python จะได้รับการทดสอบโดยใช้โมดูล unittest จากไลบรารีมาตรฐาน คุณไม่จำเป็นต้องเรียกใช้โปรแกรมจำลองหรือการจำลองอื่นๆ เพื่อทดสอบฟังก์ชัน เพียงแค่ใช้โค้ด Python ปกติก็พอ

ตัวอย่างการทดสอบฟังก์ชัน hello_world และ hello_name มีดังนี้

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. การเขียนการทดสอบ Python จะใช้วิธีเดียวกับไฟล์ Python อื่นๆ โดยจะเริ่มต้นด้วยชุดการนำเข้า จากนั้นจึงกำหนดคลาสและฟังก์ชัน
  2. การประกาศการทดสอบมีรูปแบบ class TestHello(TestCase) ต้องเป็นคลาสที่สืบทอดมาจาก unittest.TestCase
  3. คลาสการทดสอบมีเมธอด ซึ่งแต่ละเมธอดต้องขึ้นต้นด้วย test_ ซึ่งแสดงถึงกรณีทดสอบแต่ละรายการ
  4. กรณีทดสอบแต่ละรายการจะทดสอบฟังก์ชันของเรา 1 รายการโดยการจำลองพารามิเตอร์ request (เช่น แทนที่ด้วยออบเจ็กต์ปลอมที่มีข้อมูลเฉพาะที่จำเป็นสำหรับการทดสอบ)
  5. หลังจากเรียกใช้แต่ละฟังก์ชันแล้ว การทดสอบจะตรวจสอบการตอบกลับ HTTP เพื่อให้แน่ใจว่าเป็นการตอบกลับที่เราคาดหวัง

เนื่องจาก main.py ขึ้นอยู่กับ flask ให้ตรวจสอบว่าได้ติดตั้งเฟรมเวิร์ก Flask ในสภาพแวดล้อมการทดสอบแล้ว

pip install flask

การติดตั้ง Flask จะแสดงผลลัพธ์ที่คล้ายกับต่อไปนี้

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

เรียกใช้การทดสอบต่อไปนี้ในเครื่อง

python -m unittest

การทดสอบ 1 หน่วยทั้ง 3 รายการควรผ่าน

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

จากนั้น คุณจะสร้างฟังก์ชันใหม่ซึ่งแสดงผลโลโก้ "Python Powered"

6. การเขียน HTTP Cloud Function ที่ "ขับเคลื่อนด้วย Python"

มาทำให้ฟังก์ชันใหม่นี้สนุกขึ้นอีกหน่อยด้วยการแสดงรูปภาพ "Python Powered" สำหรับทุกคำขอ

a7aaf656b78050fd.png

รายการต่อไปนี้แสดงโค้ดที่ใช้ในการดำเนินการดังกล่าว

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

ทำให้ฟังก์ชัน python_powered ใหม่ใช้งานได้

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

เอาต์พุตของคำสั่ง

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

หากต้องการทดสอบฟังก์ชัน ให้คลิก URL httpsTrigger.url ที่แสดงในเอาต์พุตคำสั่งด้านบน หากทุกอย่างทำงานอย่างถูกต้อง คุณจะเห็นโลโก้ "Python Powered" ในแท็บเบราว์เซอร์ใหม่

จากนั้นคุณจะสร้างแอปเพื่อให้เรียกใช้และทดลองใช้ฟังก์ชันในเครื่องได้ก่อนที่จะนำไปใช้งาน

7. เรียกใช้ฟังก์ชันในเครื่อง

คุณเรียกใช้ฟังก์ชัน HTTP ได้ในเครื่องโดยสร้างเว็บแอปและเรียกใช้ฟังก์ชันในเส้นทาง คุณเพิ่มได้ในไดเรกทอรีเดียวกับฟังก์ชัน ไฟล์ชื่อ web_app.py มีเนื้อหาดังนี้

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. ไฟล์นี้จะสร้างแอปพลิเคชัน Flask
  2. โดยจะลงทะเบียนเส้นทางที่ URL ฐานซึ่งจัดการด้วยฟังก์ชันชื่อ index()
  3. จากนั้นฟังก์ชัน index() จะเรียกใช้ฟังก์ชัน python_powered ของเราและส่งคำขอปัจจุบันไปให้

ตรวจสอบว่าได้ติดตั้งเฟรมเวิร์ก Flask ในสภาพแวดล้อมในการพัฒนาแล้ว

pip install flask

การติดตั้ง Flask จะแสดงผลลัพธ์ที่คล้ายกับต่อไปนี้

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

หากต้องการเรียกใช้แอปพลิเคชันนี้ในเครื่อง ให้เรียกใช้คำสั่งต่อไปนี้

python web_app.py

ตอนนี้ให้ใช้การแสดงตัวอย่างเว็บของ Cloud Shell เพื่อทดสอบเว็บแอปในเบราว์เซอร์ ใน Cloud Shell ให้คลิกปุ่ม "ตัวอย่างเว็บ" แล้วเลือก "แสดงตัวอย่างบนพอร์ต 8080"

6c9ff9e5c692c58e.gif

Cloud Shell จะเปิด URL ตัวอย่างบนบริการพร็อกซีในหน้าต่างเบราว์เซอร์ใหม่ การแสดงตัวอย่างบนเว็บจะจำกัดการเข้าถึงผ่าน HTTPS ไว้เฉพาะบัญชีผู้ใช้ของคุณเท่านั้น หากทุกอย่างทำงานอย่างถูกต้อง คุณควรเห็นโลโก้ "Python Powered"

8e5c3ead11cfd103.png

8. ยินดีด้วย

b158ce75c3cccd6d.png

คุณได้ติดตั้งใช้งาน Cloud Functions ของ HTTP โดยใช้ฟังก์ชันที่เหมาะสมซึ่งจัดการคำขอเว็บด้วยเฟรมเวิร์ก Flask

ราคาของ Cloud Functions ขึ้นอยู่กับความถี่ในการเรียกใช้ฟังก์ชัน ซึ่งรวมถึงระดับฟรีสำหรับฟังก์ชันที่ไม่ได้ทำงานบ่อยนัก เมื่อทดสอบ Cloud Functions เสร็จแล้ว คุณจะลบฟังก์ชันดังกล่าวได้โดยใช้ gcloud

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

นอกจากนี้ คุณยังลบฟังก์ชันจาก Google Cloud Console ได้ด้วย

เราหวังว่าคุณจะสนุกกับการใช้ Cloud Functions ใน Python