HTTP Cloud Functions ใน Python

1. บทนำ

b158ce75c3cccd6d.png

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

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

Cloud Function มี 2 ประเภทดังนี้

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

efb3268e3b74ed4f.png

Codelab นี้จะอธิบายการสร้าง Cloud Functions ของคุณเองใน Python

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

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

a7aaf656b78050fd.png

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

  • วิธีเขียน HTTP Cloud Function
  • วิธีเขียน HTTP Cloud Function ซึ่งใช้อาร์กิวเมนต์
  • วิธีทดสอบ HTTP Cloud Function
  • วิธีเรียกใช้เซิร์ฟเวอร์ Python HTTP ในเครื่องเพื่อลองใช้ฟังก์ชันนี้
  • วิธีเขียน 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 ของระบบคลาวด์ การใช้งาน Codelab นี้จะไม่มีค่าใช้จ่ายใดๆ หากมี หากต้องการปิดทรัพยากรเพื่อหลีกเลี่ยงการเรียกเก็บเงินที่นอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่คุณสร้างหรือลบโปรเจ็กต์ได้ ผู้ใช้ Google Cloud ใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรี$300 USD

เริ่มต้น Cloud Shell

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

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

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

3c1dabeca90e44e5.png

หากเริ่มต้นใช้งาน 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! 🚀

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

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

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

pip install flask

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

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

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

python -m unittest

หน่วยการทดสอบ 3 หน่วยควรผ่าน:

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

OK

ถัดไป คุณจะต้องสร้างฟังก์ชันใหม่ซึ่งส่งคืนฟังก์ชัน "Python ขับเคลื่อนโดย" โลโก้

6. การเขียน "Python ขับเคลื่อนโดย" ฟังก์ชัน HTTP Cloud

มาสร้างฟังก์ชันใหม่ให้สนุกยิ่งขึ้นโดยการส่งคืน "Python ขับเคลื่อนโดย" รูปภาพสำหรับทุกคำขอ:

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 ขับเคลื่อนโดย" โลโก้ในแท็บใหม่ของเบราว์เซอร์

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

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 Web Preview เพื่อทดสอบเว็บแอปในเบราว์เซอร์ ใน Cloud Shell ให้คลิก "ตัวอย่างเว็บ" แล้วเลือก "ดูตัวอย่างบนพอร์ต 8080"

6c9ff9e5c692c58e.gif

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

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 ได้ด้วย

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