Mengoptimalkan aplikasi Python untuk Cloud Run

Panduan ini menjelaskan pengoptimalan pada layanan Cloud Run yang ditulis dalam bahasa pemrograman Python, beserta informasi latar belakang untuk membantu Anda memahami kompromi yang terlibat pada beberapa pengoptimalan. Informasi di halaman ini melengkapi tips pengoptimalan umum, yang juga berlaku pada Python.

Sebagian besar praktik terbaik dan pengoptimalan dalam aplikasi berbasis web Python umum berkisar tentang:

  • Menangani permintaan serentak (baik I/O yang berbasis thread maupun yang tidak memblokir)
  • Mengurangi latensi respons menggunakan penggabungan koneksi dan mengelompokkan fungsi yang tidak penting. Misalnya, mengirim trace dan metrik ke tugas latar belakang.

Mengoptimalkan image container

Optimalkan citra kontainer untuk mengurangi beban dan waktu startup, menggunakan metode-metode berikut:

  • Meminimalkan file yang Anda muat saat startup
  • Mengoptimalkan server WSGI

Meminimalkan file yang Anda muat saat startup

Untuk mengoptimalkan waktu startup, muat hanya file yang diperlukan saat startup, dan kurangi ukurannya. Untuk file besar, pertimbangkan opsi berikut:

  • Simpan file besar, seperti model AI, di container Anda untuk akses yang lebih cepat. Pertimbangkan untuk memuat file-file ini setelah startup atau saat runtime.

  • Pertimbangkan untuk mengonfigurasi pemasangan volume Cloud Storage untuk file besar yang tidak penting saat startup, seperti aset media.

  • Impor hanya submodul yang dibutuhkan dari dependensi yang berat, atau impor modul saat dibutuhkan dalam kode Anda, alih-alih memuatnya saat aplikasi dimulai.

Mengoptimalkan server WSGI

Python telah menstandardisasi cara aplikasi dapat berinteraksi dengan server web dengan penerapan standar WSGI, PEP-3333. Salah satu server WSGI yang lebih umum adalah gunicorn, yang digunakan dalam sebagian besar dokumentasi contoh.

Mengoptimalkan gunicorn

Tambahkan CMD berikut ke Dockerfile untuk mengoptimalkan pemanggilan gunicorn:

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

Jika Anda mempertimbangkan untuk mengubah pengaturan ini, sesuaikan jumlah pekerja dan thread untuk setiap aplikasi secara individual. Misalnya, coba gunakan jumlah worker yang sama dengan jumlah core yang tersedia dan pastikan ada peningkatan performa, lalu sesuaikan jumlah thread. Menetapkan terlalu banyak pekerja atau thread dapat berdampak negatif, seperti latensi cold start yang lebih lama, memori yang dikonsumsi lebih banyak, permintaan per detik yang lebih kecil, dan lain sebagainya.

Secara default, gunicorn membuat worker dan memproses port yang ditentukan saat memulai, bahkan sebelum mengevaluasi kode aplikasi Anda. Dalam hal ini, Anda harus menyiapkan custom startup probes untuk layanan Anda, karena Cloud Run default startup probe langsung menandai instance kontainer sebagai sehat segera setelah mulai mendengarkan pada $PORT.

Jika Anda ingin mengubah perilaku ini, Anda dapat memanggil gunicorn dengan pengaturan --preload untuk mengevaluasi kode aplikasi Anda sebelum mendengarkan. Hal ini dapat membantu untuk:

  • Mengidentifikasi bug runtime serius pada waktu deployment
  • Menyimpan resource memori

Anda harus mempertimbangkan apa yang dimuat sebelumnya oleh aplikasi Anda sebelum menambahkan ini.

Server WSGI lainnya

Anda tidak dibatasi untuk menggunakan gunicorn dalam menjalankan Python di container. Anda dapat menggunakan server web WSGI atau ASGI apa pun, selama container memproses port HTTP $PORT, sesuai dengan Kontrak runtime container.

Alternatif umum mencakup uwsgi, uvicorn, dan waitress.

Misalnya, dengan file bernama main.py yang berisi objek app, pemanggilan berikut akan memulai server WSGI:

# uwsgi: pip install pyuwsgi
uwsgi --http :$PORT -s /tmp/app.sock --manage-script-name --mount /app=main:app

# uvicorn: pip install uvicorn
uvicorn --port $PORT --host 0.0.0.0 main:app

# waitress: pip install waitress
waitress-serve --port $PORT main:app

Ini dapat ditambahkan sebagai baris CMD exec dalam Dockerfile, atau sebagai entri web: dalam Procfile saat menggunakan buildpack Google Cloud.

Mengoptimalkan aplikasi

Dalam kode layanan Cloud Run Anda, Anda juga dapat melakukan optimasi untuk waktu startup yang lebih cepat dan penggunaan memori yang lebih efisien.

Mengurangi rangkaian pesan

Anda dapat mengoptimalkan memori dengan mengurangi jumlah thread, yaitu dengan cara menggunakan strategi reaktif yang tidak memblokir dan menghindari aktivitas latar belakang. Hindari juga menulis ke sistem file, seperti yang disebutkan di halaman tips umum.

Jika Anda ingin mendukung aktivitas latar belakang di layanan Cloud Run, setel layanan Cloud Run ke penagihan berbasis instance agar Anda dapat menjalankan aktivitas latar belakang di luar permintaan dan tetap memiliki akses CPU.

Mengurangi tugas startup

Aplikasi berbasis web Python dapat memiliki banyak tugas yang harus diselesaikan selama startup, seperti memuat data terlebih dahulu, memanaskan cache, dan membuat pool koneksi. Jika dijalankan secara berurutan, tugas ini dapat berjalan lambat. Namun, jika Anda ingin menjalankannya secara paralel, tingkatkan jumlah core CPU.

Cloud Run mengirimkan permintaan pengguna sungguhan untuk memicu instance cold start. Pengguna yang memiliki permintaan ditetapkan ke instance yang baru dimulai, mungkin akan mengalami penundaan yang lama.

Meningkatkan keamanan dengan image dasar yang ramping

Untuk meningkatkan keamanan aplikasi Anda, gunakan image dasar slimline dengan lebih sedikit paket dan library.

Jika Anda memilih untuk tidak menginstal Python dari sumber dalam container, gunakan image dasar Python resmi dari Docker Hub. Image ini didasarkan pada sistem operasi Debian.

Jika Anda menggunakan image python dari Docker Hub, pertimbangkan untuk menggunakan versi slim. Gambar ini lebih kecil karena tidak menyertakan sejumlah paket yang akan digunakan untuk membuat roda, yang mungkin tidak perlu Anda lakukan untuk aplikasi Anda. Citra python dilengkapi dengan kompiler GNU C, praprosesor, dan utilitas inti.

Untuk mengidentifikasi sepuluh paket terbesar dalam sebuah image dasar, jalankan perintah berikut:

DOCKER_IMAGE=python # or python:slim
docker run --rm ${DOCKER_IMAGE} dpkg-query -Wf '${Installed-Size}\t${Package}\t${Description}\n' | sort -n | tail -n10 | column -t -s $'\t'

Karena paket tingkat rendah ini lebih sedikit, image berbasis slim juga menawarkan lebih sedikit permukaan serangan untuk potensi kerentanan. Beberapa gambar ini mungkin tidak menyertakan elemen yang diperlukan untuk membuat roda dari sumber.

Anda dapat menambahkan kembali paket tertentu dengan menambahkan baris RUN apt install ke Dockerfile. Untuk mengetahui informasi selengkapnya, lihat Menggunakan paket sistem di Cloud Run.

Ada juga opsi untuk container berbasis non-Debian. Opsi python:alpine dapat menghasilkan penampung yang jauh lebih kecil, tetapi banyak paket Python mungkin tidak memiliki wheel yang telah dikompilasi sebelumnya yang mendukung sistem berbasis alpine. Dukungan terus meningkat (lihat PEP-656), tetapi terus bervariasi. Sebaiknya gunakan distroless base image, yang tidak berisi pengelola paket, shell, atau program lainnya.

Gunakan variabel lingkungan PYTHONUNBUFFERED untuk pencatatan log.

Untuk melihat log yang tidak di-buffer dari aplikasi Python Anda, atur variabel lingkungan PYTHONUNBUFFERED. Saat Anda mengatur variabel ini, data stdout dan stderr akan langsung terlihat di log kontainer, alih-alih disimpan dalam buffer hingga sejumlah data tertentu terkumpul atau aliran ditutup.

Langkah berikutnya

Untuk tips lainnya, lihat