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 image container untuk mengurangi waktu pemuatan dan startup, menggunakan 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 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 diperlukan dari dependensi berat, atau impor modul saat diperlukan dalam kode, bukan 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 setelan ini, sesuaikan jumlah worker dan thread berdasarkan per aplikasi. Misalnya, coba gunakan jumlah pekerja yang sama dengan inti yang tersedia dan pastikan ada peningkatan performa, lalu sesuaikan jumlah thread. Menetapkan terlalu banyak pekerja atau thread dapat berdampak negatif, seperti latensi mulai dingin yang lebih lama, lebih banyak memori yang digunakan, permintaan per detik yang lebih kecil, dll.

Secara default, gunicorn membuat pekerja dan memproses port yang ditentukan saat memulai, bahkan sebelum mengevaluasi kode aplikasi Anda. Dalam hal ini, Anda harus menyiapkan pemeriksaan startup kustom untuk layanan Anda, karena pemeriksaan startup default Cloud Run segera menandai instance container sebagai responsif segera setelah mulai memproses di $PORT.

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

  • Mengidentifikasi bug runtime serius pada waktu deployment
  • Menghemat 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 juga dapat mengoptimalkan waktu startup dan penggunaan memori yang lebih cepat.

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 dieksekusi secara berurutan, tugas-tugas ini dapat berjalan lambat. Namun, jika Anda ingin menjalankannya secara paralel, tingkatkan jumlah core CPU.

Cloud Run mengirimkan permintaan pengguna yang sebenarnya untuk memicu instance cold start. Pengguna yang memiliki permintaan ditetapkan ke instance yang baru dimulai mungkin 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. Image python dilengkapi dengan compiler, praprosesor, dan utilitas inti GNU C.

Untuk mengidentifikasi sepuluh paket terbesar dalam 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, gambar berbasis slim juga menawarkan permukaan serangan yang lebih kecil 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 container 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.

Menggunakan variabel lingkungan PYTHONUNBUFFERED untuk logging

Untuk melihat log tanpa buffer dari aplikasi Python, tetapkan variabel lingkungan PYTHONUNBUFFERED. Saat Anda menyetel variabel ini, data stdout dan stderr akan langsung terlihat di log penampung, bukan ditahan dalam buffer hingga sejumlah data tertentu terkumpul atau streaming ditutup.

Langkah berikutnya

Untuk tips lainnya, lihat