HTTP Cloud Functions ใน Python

1. บทนำ

b158ce75c3cccd6d.png

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

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

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

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

efb3268e3b74ed4f.png

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

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

ใน Codelab นี้ คุณจะเผยแพร่ Cloud Function ซึ่งจะแสดงโลโก้ "Python Powered" เมื่อเรียกใช้ผ่าน HTTP

a7aaf656b78050fd.png

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

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

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

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

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

fbef9caa1602edd0.pnga99b7ace416376c4.png5e3ff691252acf41.png

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

เริ่มต้น Cloud Shell

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

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

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

3c1dabeca90e44e5.png

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

9c92662c6a846a5c.png

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

9f0e51b578fecce5.png

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

เมื่อเชื่อมต่อกับ 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 และ Cloud Build API แล้ว

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

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

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

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

เรียกใช้คำสั่งต่อไปนี้จากเทอร์มินัล 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

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

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

คุณควรเห็นผลลัพธ์ต่อไปนี้

Hello World! 👋

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

ฟังก์ชันจะใช้งานได้หลากหลายมากขึ้นเมื่อยอมรับอาร์กิวเมนต์ เรามากำหนดฟังก์ชันใหม่ 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
...

หากต้องการทดสอบฟังก์ชัน ให้คลิก URL httpsTrigger.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! 🚀

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

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

HTTP Cloud Functions ใน 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. กรอบการทดสอบแต่ละรายการจะทดสอบฟังก์ชันของเราโดยจำลองพารามิเตอร์ request (เช่น แทนที่ด้วยออบเจ็กต์จำลองที่มีข้อมูลเฉพาะที่จำเป็นสำหรับการทดสอบ)
  5. หลังจากเรียกใช้แต่ละฟังก์ชันแล้ว การทดสอบจะตรวจสอบการตอบกลับ HTTP เพื่อให้แน่ใจว่าเป็นไปตามที่เราคาดไว้

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

pip install flask

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

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

เรียกใช้การทดสอบเหล่านี้ในเครื่อง

python -m unittest

Unit Test ทั้ง 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

คุณได้ติดตั้งใช้งาน HTTP Cloud Functions โดยใช้ฟังก์ชันที่จัดการคำขอเว็บด้วยเฟรมเวิร์ก 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 ได้ด้วย

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