How to deploy fastapi to google cloud run - python

I have a fastapi app that I want to deploy to google cloud run.
With a gRPC python project, We deploy it as
gcloud beta run deploy countries --source .
But this doesn't deploy as expected. I watched a video that deploying from source uses Google cloud buildpacks.
Is there anyone with a way of doing that?
My code is like
from typing import List
import geopandas as gpd
from fastapi import FastAPI
from geojson_pydantic.geometries import Geometry
from geojson_pydantic.features import Feature
from pydantic import BaseModel
from shapely.geometry.geo import shape
import json
import shapely
class Country(BaseModel):
name: str
code: str
type: str
geometry: Feature
app = FastAPI(
title="Natural Earth Countries",
description="This is a list of endpoints which you can use to access natural earth data.",
version="0.0.1",
docs_url='/',
)
gdf = gpd.read_file('data/ne_110m_admin_0_countries.zip')
gdf['name'] = gdf['ADMIN'].apply(lambda x: x.lower())
#app.get('/countries/all')
def get_all_countries() -> List[Country]:
return rows_to_countries(gdf)
#app.get('/countries/search/{name}')
def search_countries(name: str):
name = name.lower()
subset = gdf.loc[gdf["name"].str.contains(name)]
return rows_to_countries(subset)
#app.get('/countries/within-boundary')
def countries_intersecting(boundary: Geometry):
bounds = shape(boundary)
subset = gdf.loc[gdf.intersects(bounds)]
return rows_to_countries(subset)
def row_to_country(row):
return Country(
name=row["ADMIN"],
type=row["TYPE"],
geometry=Feature(geometry=row['geometry']),
code=row["ADM0_A3"],
)
def rows_to_countries(gdf):
return [row_to_country(row) for _, row in gdf.iterrows()]
Appreciate your help

The solution to I've found is creating a Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./
RUN pip install -r requirements.txt
CMD exec gunicorn --bind :$PORT --workers 1 --worker-class uvicorn.workers.UvicornWorker --threads 8 main:app
Then running
gcloud builds submit --tag gcr.io/PROJECT-ID/countries_fastapi
Then after image is submitted to gcloud, running
gcloud run deploy --image gcr.io/bitnami-oyzgng8y5a/countries_fastapi --platform managed

What error message do you see when you deploy with the buildpacks? When deploying Python apps, you need to include a Procfile in the root of your application that declares the command you should use to start the app:
web: gunicorn --bind :$PORT --workers 1 --worker-class uvicorn.workers.UvicornWorker --threads 8 main:app

Related

Google Cloud Run Flask App error import module

I have developed a flask rest API and I'm using flask_smorest.
I'm developing into cloud shell editor into google cloud.
When I run google cloud run emulator, I receive the error:
##########Linting Output - pylint##########
************* Module app
3,0,error,import-error:Unable to import 'flask_smorest'
and this my dockerfile:
FROM python:3.10
ENV PYTHONUNBUFFERED True
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./
RUN pip install Flask gunicorn flask-smorest marshmallow
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:app
This is app.py file:
import os
from flask import Flask, request
from flask_smorest import Api
app = Flask(__name__)
app.config["PROPAGATE_EXCEPTIONS"] = True
app.config["API_TITLE"] = "Stores REST API"
app.config["API_VERSION"] = "v1"
app.config["OPENAPI_VERSION"] = "3.0.3"
app.config["OPENAPI_URL_PREFIX"] = "/"
app.config["OPENAPI_SWAGGER_UI_PATH"] = "/swagger-ui"
app.config["OPENAPI_SWAGGER_UI_URL"] ="https://cdn.jsdelivr.net/npm/swagger-ui-dist/"
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
how i can fix this issue? And why i have this error?
Posting this as a community wiki from #dev_:
The issue was resolved by installing the module from the Cloud Shell Terminal.

Flask Docker image not finding config file

I have a Flask application which works fine in a venv, but fails as a Docker image because the class-based config file cannot be found.
|app
|__init__.py
|main.py
|Dockerfile
|config.py
The init.py file:
app = Flask(__name__)
if app.config["ENV"] == "development":
app.config.from_object("config.DevelopmentConfig")
elif app.config["ENV"] == "production":
app.config.from_object('config.ProductionConfig')
else:
app.config.from_object("config.DevelopmentConfig")
Stripped down config file:
import os
from pymongo import MongoClient
import pymongo
class Config(object):
DEBUG = False
TESTING = False
class ProductionConfig():
...
class DevelopmentConfig(Config):
...
Error message:
werkzeug.utils.ImportStringError: import_string() failed for 'config.ProductionConfig'. Possible reasons are:
Docker file adapted from Google
# Use the official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.10-slim
# Allow statements and log messages to immediately appear in the Knative logs
ENV PYTHONUNBUFFERED True
ENV PORT 8080
# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./
# Install production dependencies.
# RUN pip install --no-cache-dir -r requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
# Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
Things I've tried:
Placing the config file in the app directory
Including app.config.from_pyfile('config.py') below the from_object
Following the instance structure at https://flask.palletsprojects.com/en/2.2.x/config/
Any help to unpick this is appreciated.

Docker exit code 0 issue

Here is my logs after trying to dockerizing a fastapi script:
$ docker-compose logs -f
Attaching to docker-name
docker-name exited with code 0
Here is my docker compose file:
version: '3'
services:
observatory-service:
build: ./observatory
volumes:
- ./observatory:/usr/src/app
ports:
- 8000:8000
docker file:
FROM python:3.9
COPY requirements.txt /usr/src/app/
RUN pip install -r /usr/src/app/requirements.txt
COPY . /usr/src/app
CMD ["python", "/usr/src/app/api.py"]
api.py:
from fastapi import FastAPI
app = FastAPI()
#app.get("/")
def read_root():
return { "Lux app": "Welcome to Lux app" }
#app.post("/extract_text")
def ali():
return {"Hello": "World"}
I am sure it goes through api.py but it exits out of it without error. I am running this on Windows docker.
The problem is that you're directly calling the fastAPI server script with python and not wrapped with a ASGI web server such as Uvicorn. This is explained in the debugging page of fastAPI.
I tested with latest versions of fastapi/uvicorn and it works just fine. I suggest you make these two changes:
1 - Add this line to your requirements.txt file:
uvicorn
2- Then add the first line and the two last lines to the end of your api.py file:
import uvicorn
from fastapi import FastAPI
app = FastAPI()
#app.get("/")
def read_root():
return { "Lux app": "Welcome to Lux app" }
#app.post("/extract_text")
def ali():
return {"Hello": "World"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Result of GET /
{"Lux app":"Welcome to Lux app"}
Finally, another alternative would be following the docker guide provided by fastAPI which solves this easily by calling your fastapi module directly with uvicorn. This will achieve the same as above.
Example of Dockerfile:
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]

GCP Cloud Run deployment failure

I am trying to execute a basic GCP Cloud Run example.
Code it self is very simple:
import os
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello_world():
name = os.environ.get("NAME", "World")
return "Hello {} This is our first application !".format(name)
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
Then I tried a deployment using Cloud Code/Deploy on Cloud Run
Here is the result I get when I hit Deploy:
"Failed to build the app. Error: Build Failed. No push access to specified image repository. Try running with --default-repo flag."
Note that I am using Artifact-Registry, repo creation works fine
Docker file content is this:
FROM python:3.9-slim
ENV PYTHONUNBUFFERED True
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./
RUN pip install Flask gunicorn
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
Anybody face this issue ?
Any help is appreciated.

Getting ERROR 502 when trying to run a Docker Flask app on App Engine

I'd like to deploy a Docker image on App Engine which contains a basic Flask app. Also, this app will be a web-socket endpoint for my front-end.
Deployment is a success. But i'm getting an error 502.
I've read the doc, I've tried many configuration but I don't get it works, if someone has any idea, i'd highly appreciate.
My project structure :
- socket/
- socket/
- main.py
- Dockerfile
- app.yaml
- requirements.txt
- cloudbuild.yaml
main.py
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def hello():
"""Return a friendly HTTP greeting."""
return 'Hello World!'
if __name__ == '__main__':
# This will be only used locally. App Engine will use gunicorn.
app.run(host='127.0.0.1', port=8080, debug=True)
requirements.txt
Flask==1.0.3
Flask-Sockets==0.2.1
gunicorn==19.9.0
Dockerfile
FROM python:2.7
WORKDIR /app
COPY . /app
EXPOSE 8080
RUN pip install --trusted-host pypi.python.org -r requirements.txt
ENTRYPOINT ["gunicorn","--bind=0.0.0.0:8080", "-k flask_sockets.worker", "main:app"]
app.yaml
runtime: custom
env: flex
service: socket
entrypoint: gunicorn -b :$PORT -k flask_sockets.worker main:app
manual_scaling:
instances: 1
network:
session_affinity: true
cloudbuild.yaml
steps:
# Decrypt env secrets
- name: gcr.io/cloud-builders/gcloud
args:
- kms
- decrypt
- --ciphertext-file=./socket/conf/gcp_credentials.json.enc
- --plaintext-file=./socket/conf/gcp_credentials.json
- --location=global
- --keyring=cloudbuild-env
- --key=cloudbuild-env
id: decrypt
# Build the docker image
- name: gcr.io/cloud-builders/docker
args: [ 'build', '-t', 'gcr.io/$PROJECT_ID/tweets-socket-dataflow', './socket/' ]
id: build
# Deploy the image
- name: gcr.io/cloud-builders/gcloud
args: ['app', 'deploy', './socket/app.yaml', '-v', '1-socket-latest']
id: deploy
timeout: 600s
This actually works locally.
Docker command used
docker run -it -d -p 8080:8080 144005a4e426
docker ps
c2763d0b8b75 144005a4e426 "gunicorn --bind=0.0…" 3 seconds ago Up 1 second 0.0.0.0:8080->8080/tcp sleepy_ganguly
http:127.0.0.1:8080

Categories

Resources