HomeAboutPostsTagsProjectsRSS

cloud run

Updated
Words1532
TagsRead3 minutes

I recently ventured into deploying a service on Google Cloud Run. My goal was straightforward: create a service that fetches webpage titles and captures screenshots of URLs. However, the journey led me into a peculiar bug when I actually used it on Goole Cloud Run.

The Bug

During the development phase, I worked with a python:3.11-slim base image on macOS, and my Dockerfile functioned without a hitch. Here’s a snapshot of the Dockerfile I used:

from python:3.11-slim

RUN apt-get update && \
    apt-get install -y git && \
    python -m pip install --upgrade pip && \
    pip install -r requirements.txt && \
    pip install pytest-playwright && \
    playwright install-deps && \
    playwright install && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

Yet, upon deploying to Google Cloud Run and initiating the screenshot capture process, I hit a snag:

playwright._impl._api_types.Error: Executable doesn't exist at /home/.cache/ms-playwright/chromium-1084/chrome-linux/chrome
╔═════════════════════════════
║ Looks like Playwright was just installed or updated.                   
║ Please run the following command to download new browsers: 
║                                                                                                            
║     playwright install                                                                          
║                                                                                                            
║ <3 Playwright Team                                                                         
╚═════════════════════════════

Official Playwright Docker Image Saves the Day

Rather than wrestle with the error, I pivoted to an official Docker image of Playwright, and skipped installation of dependency:

mcr.microsoft.com/playwright/python:v1.39.0-jammy

docker image

Let’s dig down the issue:

The Compatibility Issue

Playwright demands compatibility. It officially supports Python versions 3.8 and higher, and it requires specific Linux distributions:

  • Debian 11 (bullseye)
  • Debian 12 (bookworm)
  • Ubuntu 20.04 (focal)
  • Ubuntu 22.04 (jammy)

However, on docker environment, the official image is only based on Unbuntu .

Use ENV PLAYWRIGHT_BROWSERS_PATH

After some search and experiments, I found the only solution in to specify the chromium binary files using ENV PLAYWRIGHT_BROWSERS_PATH during install . source code Dockerfile also use this environment variable to specify the broswer executable path.

using python:3.11-slim-bookworm

FROM python:3.11-slim-bookworm

ENV PLAYWRIGHT_BROWSERS_PATH=/app/ms-playwright
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

# Install git
RUN apt-get update

# install playwright or whatever
RUN python -m pip install --upgrade pip && \
    pip install -r requirements.txt

# install chrominum
RUN PLAYWRIGHT_BROWSERS_PATH=/app/ms-playwright && \
    playwright install --with-deps chromium

using ubuntu:22.04

FROM ubuntu:22.04

ENV PLAYWRIGHT_BROWSERS_PATH=/app/ms-playwright
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

# Install git
RUN apt-get update && \
    apt-get install -y python3-all python-is-python3 python3-pip

# install playwright or whatever
RUN python -m pip install --upgrade pip && \
    pip install -r requirements.txt

# install chrominum
RUN PLAYWRIGHT_BROWSERS_PATH=/app/ms-playwright && \
    playwright install --with-deps chromium

Memory requirement of Google Cloud Run

The playwright 1.39.0 requires slightly more than 512MB of memory to run on Google Cloud Run. Adjust the memory limit on GCR, as it’s 512 MB by default.

Conclusion

Use the official Docker image to save time, or specify the PLAYWRIGHT_BROWSERS_PATH environment variable on a supported linux docker image.

Further reading:

Updated
Words356
TagsRead2 minutes

Introduction

This code example demonstrates how to use Python’s Flask framework and the python-telegram-bot library (version 13.0) to build a webhook for a Telegram bot. We will utilize the Google Cloud Run container to handle incoming messages and invoke the bot without long polling.

from flask import Flask, request, jsonify
from telegram import Bot, Update
from telegram.ext import Dispatcher
import requests

app = Flask(__name__)
bot = Bot(token="YOUR_BOT_TOKEN_HERE")
dispatcher = Dispatcher(bot, None, workers=0, use_context=True)

Defining the Message Handler

To handle incoming messages, we define a function called photo_handler that will be used as a handler for photo messages.

def photo_handler(update: Update, context):
    user_id = update.message.from_user.id
    owner_id = 123456789  # Replace with your user_id

    if user_id == owner_id:
        file_id = update.message.photo[-1].file_id  # Get the highest-resolution photo
        file_info = bot.get_file(file_id)
        file_url = file_info.file_path
        
        # Download the file (assuming you have 'requests' installed)
        response = requests.get(file_url)
        with open("received_photo.jpg", "wb") as f:
            f.write(response.content)

Setting up the Webhook Endpoint

We define a route for the /webhook endpoint using the @app.route decorator. This endpoint will receive incoming updates from Telegram.

@app.route('/webhook', methods=['POST'])
def webhook():
    update = Update.de_json(request.get_json(), bot)
    dispatcher.process_update(update)
    return jsonify(success=True)

Adding the Message Handler to the Dispatcher

We add the photo_handler function as a message handler using the MessageHandler class from the telegram.ext module. We specify the Filters.photo filter to only handle photo messages.

if __name__ == "__main__":
    from telegram.ext import MessageHandler, Filters
    
    photo_handler = MessageHandler(Filters.photo, photo_handler)
    dispatcher.add_handler(photo_handler)
    app.run(port=5000)

Conclusion

By using Flask and the python-telegram-bot library, we can easily build a webhook for a Telegram bot. This allows us to handle incoming messages efficiently using a Google Cloud Run container instead of using long polling.