Python'da HTTP Cloud Functions

1. Giriş

b158ce75c3cccd6d.png

Python, veri bilimciler, web uygulaması geliştiriciler ve sistem yöneticileri gibi birçok kişi tarafından kullanılan popüler bir açık kaynak programlama dilidir.

Cloud Functions, etkinlik odaklı bir sunucusuz işlem platformudur. Cloud Functions, kaynak sağlama veya değişen gereksinimleri karşılamak için ölçeklendirme konusunda endişelenmeden kodunuzu yazmanıza olanak tanır.

İki tür Cloud Functions vardır:

  • HTTP işlevleri, HTTP isteklerine yanıt verir. Bu codelab'de birkaç tane oluşturacaksınız.
  • Arka plan işlevleri, Cloud Pub/Sub'a mesaj yayınlanması veya Cloud Storage'a dosya yüklenmesi gibi etkinliklerle tetiklenir. Bu laboratuvarda bu konuya değinilmemektedir ancak dokümanlarda daha fazla bilgi edinebilirsiniz.

efb3268e3b74ed4f.png

Bu codelab'de, Python'da kendi Cloud Functions işlevlerinizi oluşturma adımları açıklanmaktadır.

Ne oluşturacaksınız?

Bu codelab'de, HTTP üzerinden çağrıldığında "Python Powered" logosunu gösteren bir Cloud Functions işlevi yayınlayacaksınız:

a7aaf656b78050fd.png

Neler öğreneceksiniz?

  • HTTP Cloud Functions işlevi yazma
  • Bağımsız değişken alan bir HTTP Cloud Functions işlevi yazma
  • HTTP Cloud Functions işlevini test etme
  • İşlevi denemek için yerel bir Python HTTP sunucusu çalıştırma
  • Görüntü döndüren bir HTTP Cloud Functions işlevi nasıl yazılır?

2. Kurulum ve şartlar

Yönlendirmesiz ortam kurulumu

  1. Google Cloud Console'da oturum açın ve yeni bir proje oluşturun veya mevcut bir projeyi yeniden kullanın. Gmail veya Google Workspace hesabınız yoksa hesap oluşturmanız gerekir.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Proje adı, bu projenin katılımcıları için görünen addır. Google API'leri tarafından kullanılmayan bir karakter dizesidir. Bu bilgiyi istediğiniz zaman güncelleyebilirsiniz.
  • Proje kimliği, tüm Google Cloud projelerinde benzersizdir ve sabittir (ayarlandıktan sonra değiştirilemez). Cloud Console, benzersiz bir dizeyi otomatik olarak oluşturur. Genellikle bu dizenin ne olduğuyla ilgilenmezsiniz. Çoğu codelab'de proje kimliğinize (genellikle PROJECT_ID olarak tanımlanır) başvurmanız gerekir. Oluşturulan kimliği beğenmezseniz başka bir rastgele kimlik oluşturabilirsiniz. Dilerseniz kendi adınızı deneyerek kullanılabilir olup olmadığını kontrol edebilirsiniz. Bu adım tamamlandıktan sonra değiştirilemez ve proje süresince geçerli kalır.
  • Bazı API'lerin kullandığı üçüncü bir değer olan Proje Numarası da vardır. Bu üç değer hakkında daha fazla bilgiyi belgelerde bulabilirsiniz.
  1. Ardından, Cloud kaynaklarını/API'lerini kullanmak için Cloud Console'da faturalandırmayı etkinleştirmeniz gerekir. Bu codelab'i tamamlamak neredeyse hiç maliyetli değildir. Bu eğitimin ötesinde faturalandırılmayı önlemek için kaynakları kapatmak üzere oluşturduğunuz kaynakları veya projeyi silebilirsiniz. Yeni Google Cloud kullanıcıları 300 ABD doları değerinde ücretsiz deneme programından yararlanabilir.

Cloud Shell'i Başlatma

Google Cloud, dizüstü bilgisayarınızdan uzaktan çalıştırılabilir ancak bu codelab'de bulutta çalışan bir komut satırı ortamı olan Cloud Shell'i kullanacaksınız.

Cloud Shell'i etkinleştirme

  1. Cloud Console'da Cloud Shell'i etkinleştir 'i 853e55310c205094.png tıklayın.

3c1dabeca90e44e5.png

Cloud Shell'i ilk kez başlatıyorsanız ne olduğunu açıklayan bir ara ekran gösterilir. Ara ekran gösterildiyse Devam'ı tıklayın.

9c92662c6a846a5c.png

Cloud Shell'in temel hazırlığı ve bağlanması yalnızca birkaç dakikanızı alır.

9f0e51b578fecce5.png

Bu sanal makineye, ihtiyaç duyacağınız tüm geliştirme araçları yüklenmiştir. 5 GB boyutunda kalıcı bir ana dizin bulunur ve Google Cloud'da çalışır. Bu sayede ağ performansı ve kimlik doğrulama önemli ölçüde güçlenir. Bu codelab'deki çalışmalarınızın neredeyse tamamını tarayıcıyla yapabilirsiniz.

Cloud Shell'e bağlandıktan sonra kimliğinizin doğrulandığını ve projenin, proje kimliğinize ayarlandığını görürsünüz.

  1. Kimliğinizin doğrulandığını onaylamak için Cloud Shell'de şu komutu çalıştırın:
gcloud auth list

Komut çıkışı

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. gcloud komutunun projeniz hakkında bilgi sahibi olduğunu onaylamak için Cloud Shell'de aşağıdaki komutu çalıştırın:
gcloud config list project

Komut çıkışı

[core]
project = <PROJECT_ID>

Değilse şu komutla ayarlayabilirsiniz:

gcloud config set project <PROJECT_ID>

Komut çıkışı

Updated property [core/project].

Cloud Functions ve Cloud Build API'lerinin etkinleştirildiğinden emin olun.

Cloud Functions ve Cloud Build API'lerinin etkinleştirildiğinden emin olmak için Cloud Shell'de aşağıdaki komutu çalıştırın:

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

Not: Cloud Build, gcloud functions deploy komutuyla çağrılır ve kodunuzu otomatik olarak bir container görüntüsüne derler.

Kaynak kodunu indirme

Cloud Shell terminalinden aşağıdaki komutları çalıştırın:

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

Kaynak dizinin içeriğine göz atın:

ls

Aşağıdaki dosyalara sahip olmanız gerekir:

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

3. HTTP Cloud Functions'ın Tanıtımı

Python'daki HTTP Cloud Functions işlevleri, normal Python işlevleri olarak yazılır. İşlev, genellikle request olarak adlandırılan tek bir flask.Request bağımsız değişkenini kabul etmelidir.

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")

# ...

Dosyayı tercih ettiğiniz komut satırı düzenleyicisiyle (nano, vim veya emacs) açabilirsiniz. Kaynak dizini çalışma alanı olarak ayarladıktan sonra Cloud Shell Düzenleyici'de de açabilirsiniz:

cloudshell open-workspace .

Bu işlevi gcloud functions deploy komutunu kullanarak HTTP Cloud Functions işlevi olarak dağıtalım:

FUNCTION_NAME="hello_world"

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

Komut çıkışı:

...
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 seçenekleriyle ilgili notlar:

  • --runtime: Bu, dil çalışma zamanını belirtir. Python için bu değer şu anda python37, python38, python39, python310 veya python312 olabilir. Çalışma zamanları başlıklı makaleyi inceleyin.
  • --trigger-http: İşleve bir uç nokta atanır. Uç noktaya yapılan HTTP istekleri (POST, PUT, GET, DELETE ve OPTIONS) işlev yürütmeyi tetikler.
  • --allow-unauthenticated: İşlev herkese açık olacak ve kimlik doğrulama kontrolü yapılmadan tüm arayanlara izin verilecek.
  • Daha fazla bilgi edinmek için gcloud functions deploy konusuna bakın.

İşlevi test etmek için yukarıdaki komut çıkışında gösterilen httpsTrigger.url URL'sini tıklayabilirsiniz. Ayrıca, URL'yi programatik olarak alabilir ve aşağıdaki komutlarla işlevi çağırabilirsiniz:

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

Aşağıdaki sonucu almanız gerekir:

Hello World! 👋

4. Bağımsız değişken alan bir HTTP Cloud Functions işlevi yazma

İşlevler, bağımsız değişkenleri kabul ettiklerinde daha çok yönlü olur. name parametresini destekleyen yeni bir işlev hello_name tanımlayalım:

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")

# ...

Bu yeni işlevi dağıtalım:

FUNCTION_NAME="hello_name"

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

Komut çıkışı:

...
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
...

İşlevi test etmek için yukarıdaki komut çıkışında gösterilen httpsTrigger.url URL'sini tıklayabilirsiniz. Ayrıca, URL'yi programatik olarak alabilir ve aşağıdaki komutlarla işlevi çağırabilirsiniz:

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

Varsayılan sonucu alırsınız:

Hello World! 🚀

name bağımsız değişkeni ayarlanmadığı için varsayılan sonucu alıyorsunuz. URL'ye bir parametre ekleyin:

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

Bu kez özel yanıtınızı alırsınız:

Hello YOUR NAME! 🚀

Bir sonraki adım, kaynak kodu güncellendiğinde işlevlerinizin amaçlandığı gibi çalışmaya devam etmesini sağlamak için birim testleri eklemektir.

5. Yazma testleri

Python'daki HTTP Cloud Functions işlevleri, standart kitaplıktaki unittest modülü kullanılarak test edilir. İşlevinizi test etmek için emülatör veya başka bir simülasyon çalıştırmanız gerekmez. Normal Python kodu yeterlidir.

hello_world ve hello_name işlevleri için testin nasıl göründüğünü aşağıda bulabilirsiniz:

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 testleri, diğer Python dosyalarıyla aynı şekilde yazılır. Bir dizi içe aktarma işlemiyle başlar, ardından sınıfları ve işlevleri tanımlar.
  2. Test beyanı class TestHello(TestCase) biçimindedir. unittest.TestCase sınıfından devralınan bir sınıf olmalıdır.
  3. Test sınıfında, her biri test_ ile başlaması gereken ve ayrı test senaryolarını temsil eden yöntemler bulunur.
  4. Her test durumu, request parametresini taklit ederek (ör. testi için gereken belirli verileri içeren sahte bir nesneyle değiştirerek) işlevlerimizden birini test eder.
  5. Test, her işlev çağrıldıktan sonra HTTP yanıtını kontrol ederek beklediğimiz yanıtın verildiğinden emin olur.

main.py, flask'ye bağlı olduğundan Flask çerçevesinin test ortamınıza yüklendiğinden emin olun:

pip install flask

Flask'i yüklediğinizde aşağıdaki gibi bir sonuç elde edersiniz:

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

Bu testleri yerel olarak çalıştırın:

python -m unittest

Üç birim testi de başarılı olmalıdır:

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

OK

Ardından, "Python Powered" logosunu döndüren yeni bir işlev oluşturacaksınız.

6. "Python Powered" HTTP Cloud Functions işlevini yazma

Her istek için "Python Powered" resmini döndürerek yeni bir işlevi biraz daha eğlenceli hale getirelim:

a7aaf656b78050fd.png

Aşağıdaki listede, bu işlemi gerçekleştirecek kod gösterilmektedir:

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")

Yeni bir python_powered işlevi dağıtın:

FUNCTION_NAME="python_powered"

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

Komut çıkışı:

...
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
...

İşlevi test etmek için yukarıdaki komut çıkışında gösterilen httpsTrigger.url URL'sini tıklayın. Her şey doğru şekilde çalışıyorsa yeni bir tarayıcı sekmesinde "Python Powered" logosunu görürsünüz.

Ardından, dağıtımdan önce işlevinizi yerel olarak çalıştırıp deneyebilmeniz için bir uygulama oluşturacaksınız.

7. İşlevi yerel olarak çalıştırma

Web uygulaması oluşturup işlevinizi bir rotada çağırarak HTTP işlevini yerel olarak çalıştırabilirsiniz. İşlevinizle aynı dizine ekleyebilirsiniz. web_app.py adlı dosyada şu içerik var:

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. Bu dosya, bir Flask uygulaması oluşturur.
  2. Temel URL'de index() adlı bir işlevle işlenen bir rota kaydeder.
  3. index() işlevi, geçerli isteği ileterek python_powered işlevimizi çağırır.

Geliştirme ortamınızda Flask çerçevesinin yüklü olduğundan emin olun:

pip install flask

Flask'i yüklediğinizde aşağıdaki gibi bir sonuç elde edersiniz:

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

Bu uygulamayı yerel olarak çalıştırmak için aşağıdaki komutu çalıştırın:

python web_app.py

Şimdi web uygulamasını tarayıcınızda test etmek için Cloud Shell Web Önizleme'yi kullanın. Cloud Shell'de "Web Önizlemesi" düğmesini tıklayın ve "8080 numaralı bağlantı noktasında önizle"yi seçin:

6c9ff9e5c692c58e.gif

Cloud Shell, proxy hizmetindeki önizleme URL'sini yeni bir tarayıcı penceresinde açar. Web önizlemesi, HTTPS üzerinden erişimi yalnızca kullanıcı hesabınızla kısıtlar. Her şey düzgün çalışıyorsa "Python Powered" logosunu görmeniz gerekir.

8e5c3ead11cfd103.png

8. Tebrikler!

b158ce75c3cccd6d.png

Flask çerçevesiyle web isteklerini işleyen deyimsel işlevleri kullanarak HTTP Cloud Functions işlevleri dağıttınız.

Cloud Functions fiyatlandırması, işlevinizin çağrılma sıklığına göre belirlenir. Sık çalışmayan işlevler için ücretsiz katman da sunulur. Cloud Functions işlevlerinizi test etmeyi tamamladığınızda gcloud kullanarak bunları silebilirsiniz:

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

İşlevleri Google Cloud Console'dan da silebilirsiniz.

Python'da Cloud Functions'ı keyifle kullanacağınızı umuyoruz.