This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can trysigning in orchanging directories.
Access to this page requires authorization. You can trychanging directories.
In this tutorial, you deploy a data-driven Python web app (FastAPI ) toAzure App Service with theAzure Database for PostgreSQL relational database service. Azure App Service supportsPython in a Linux server environment. If you want, see theFlask tutorial or theDjango tutorial instead.
To complete this tutorial, you'll need:
WithAzure Developer CLI installed, you can skip to the end of the tutorial by running the following commands in an empty working directory:
azd auth loginazd init --template msdocs-fastapi-postgresql-sample-appazd up
A sample Python application using FastAPI framework is provided to help you follow along with this tutorial. To deploy it without running it locally, skip this part.
To run the application locally, make sure you havePython 3.8 or higher andPostgreSQL installed locally. Then, clone the sample repository'sstarter-no-infra
branch and change to the repository root.
git clone -b starter-no-infra https://github.com/Azure-Samples/msdocs-fastapi-postgresql-sample-appcd msdocs-fastapi-postgresql-sample-app
Create an.env file as shown below using the.env.sample file as a guide. Set the value ofDBNAME
to the name of an existing database in your local PostgreSQL instance. Set the values ofDBHOST
,DBUSER
, andDBPASS
as appropriate for your local PostgreSQL instance.
DBNAME=<database name>DBHOST=<database-hostname>DBUSER=<db-user-name>DBPASS=<db-password>
Create a virtual environment for the app:
py -m venv .venv.venv\scripts\activate
Install the dependencies:
python3 -m pip install -r src/requirements.txt
Install the app as an editable package:
python3 -m pip install -e src
Run the sample application with the following commands:
# Run database migrationpython3 src/fastapi_app/seed_data.py# Run the app at http://127.0.0.1:8000python3 -m uvicorn fastapi_app:app --reload --port=8000
In this step, you create the Azure resources. The steps used in this tutorial create a set of secure-by-default resources that include App Service and Azure Database for PostgreSQL. For the creation process, you specify:
Sign in to theAzure portal and follow these steps to create your Azure App Service resources.
Step 1: In the Azure portal:
Step 2: In theCreate Web App + Database page, fill out the form as follows.
Step 3: The deployment takes a few minutes to complete. Once deployment completes, select theGo to resource button. You're taken directly to the App Service app, but the following resources are created:
Step 4: For FastAPI apps, you must enter a startup command so App service can start your app. On the App Service page:
src/entrypoint.sh
in theStartup Command field underStack settings.The creation wizard generated the connectivity variables for you already asapp settings. App settings are one way to keep connection secrets out of your code repository. When you're ready to move your secrets to a more secure location, here's anarticle on storing in Azure Key Vault.
Step 1: In the App Service page, in the left menu, selectEnvironment variables.
Step 2: In theApp settings tab of theEnvironment variables page, verify thatAZURE_POSTGRESQL_CONNECTIONSTRING
is present. The connection string will be injected into the runtime environment as an environment variable.
In this step, you configure GitHub deployment using GitHub Actions. It's just one of many ways to deploy to App Service, but also a great way to have continuous integration in your deployment process. By default, everygit push
to your GitHub repository will kick off the build and deploy action.
Step 1: In a new browser window:
Step 2: In the GitHub page, open Visual Studio Code in the browser by pressing the.
key.
Step 3: In Visual Studio Code in the browser, opensrc/fastapi/models.py in the explorer.See the environment variables being used in the production environment, including the app settings that you saw in the configuration page.
Step 4: Back in the App Service page, in the left menu, underDeployment, selectDeployment Center.
Step 5: In the Deployment Center page:
.github/workflows
directory.Step 6: In the Deployment Center page:
Step 7: You're taken to your GitHub repository and see that the GitHub action is running. The workflow file defines two separate stages, build and deploy. Wait for the GitHub run to show a status ofComplete. It takes about 5 minutes.
Having issues? Check theTroubleshooting guide.
In previous section, you addedsrc/entrypoint.sh as the startup command for your app.entrypoint.sh contains the following line:python3 src/fastapi_app/seed_data.py
. This command migrates your database. In the sample app, it only ensures that the correct tables are created in your database. It doesn't populate these tables with any data.
In this section, you'll run this command manually for demonstration purposes. With the PostgreSQL database protected by the virtual network, the easiest way to run the command is in an SSH session with the App Service container.
Step 1: Back in the App Service page, in the left menu,
Step 2: In the SSH terminal, runpython3 src/fastapi_app/seed_data.py
. If it succeeds, App Service isconnecting successfully to the database.Only changes to files in/home
can persist beyond app restarts. Changes outside of/home
aren't persisted.
Step 1: In the App Service page:
Step 2: Add a few restaurants to the list.Congratulations, you're running a web app in Azure App Service, with secure connectivity to Azure Database for PostgreSQL.
The sample app uses the Python Standard Library logging module to help you diagnose issues with your application. The sample app includes calls to the logger as shown in the following code.
@app.get("/", response_class=HTMLResponse)async def index(request: Request, session: Session = Depends(get_db_session)): logger.info("root called") statement = ( select(Restaurant, func.avg(Review.rating).label("avg_rating"), func.count(Review.id).label("review_count")) .outerjoin(Review, Review.restaurant == Restaurant.id) .group_by(Restaurant.id) )
Step 1: In the App Service page:
Step 2: From the left menu, selectLog stream. You see the logs for your app, including platform logs and logs from inside the container.
Events can take several minutes to show up in the diagnostic logs. Learn more about logging in Python apps in the series onsetting up Azure Monitor for your Python application.
When you're finished, you can delete all of the resources from your Azure subscription by deleting the resource group.
Step 1: In the search bar at the top of the Azure portal:
Step 2: In the resource group page, selectDelete resource group.
Step 3:
In this step, you create the Azure resources and deploy a sample app to App Service on Linux. The steps used in this tutorial create a set of secure-by-default resources that include App Service and Azure Database for PostgreSQL.
If you haven't already, clone the sample repository'sstarter-no-infra
branch in a local terminal.
git clone -b starter-no-infra https://github.com/Azure-Samples/msdocs-fastapi-postgresql-sample-appcd msdocs-fastapi-postgresql-sample-app
This cloned branch is your starting point. It contains a simple data-drive FastAPI application.
From the repository root, runazd init
.
azd init --template msdocs-fastapi-postgresql-sample-app
When prompted, give the following answers:
Question | Answer |
---|---|
The current directory is not empty. Would you like to initialize a project here in '<your-directory>'? | Y |
What would you like to do with these files? | Keep my existing files unchanged |
Enter a new environment name | Type a unique name. The azd template uses this name as part of the DNS name of your web app in Azure (<app-name>.azurewebsites.net ). Alphanumeric characters and hyphens are allowed. |
Run theazd up
command to provision the necessary Azure resources and deploy the app code. If you aren't already signed-in to Azure, the browser will launch and ask you to sign-in. Theazd up
command will also prompt you to select the desired subscription and location to deploy to.
azd up
Theazd up
command can take several minutes to complete. It also compiles and deploys your application code. While it's running, the command provides messages about the provisioning and deployment process, including a link to the deployment in Azure. When it finishes, the command also displays a link to the deploy application.
This azd template contains files (azure.yaml and theinfra directory) that generate a secure-by-default architecture with the following Azure resources:
When theazd up
command completes, note down the values for theSubscription ID (Guid), theApp Service, and theResource Group in the output. You use them in the following sections. Your output will look similar to the following (partial) output:
Subscription: Your subscription name (1111111-1111-1111-1111-111111111111)Location: East US You can view detailed progress in the Azure Portal: https://portal.azure.com/#view/HubsExtension/DeploymentDetailsBlade/~/overview/id/%2Fsubscriptions%2F1111111-1111-1111-1111-111111111111%2Fproviders%2FMicrosoft.Resources%2Fdeployments%2Fyourenv-1721867673 (✓) Done: Resource group: yourenv-rg (✓) Done: Virtual Network: yourenv-e2najjk4vewf2-vnet (✓) Done: App Service plan: yourenv-e2najjk4vewf2-service-plan (✓) Done: Log Analytics workspace: yourenv-e2najjk4vewf2-workspace (✓) Done: Application Insights: yourenv-e2najjk4vewf2-appinsights (✓) Done: Portal dashboard: yourenv-e2najjk4vewf2-dashboard (✓) Done: App Service: yourenv-e2najjk4vewf2-app-service (✓) Done: Azure Database for PostgreSQL flexible server: yourenv-e2najjk4vewf2-postgres-server (✓) Done: Cache for Redis: yourenv-e2najjk4vewf2-redisCache (✓) Done: Private Endpoint: cache-privateEndpointSUCCESS: Your application was provisioned in Azure in 32 minutes.You can view the resources created under the resource group yourenv-rg in Azure Portal:https://portal.azure.com/#@/resource/subscriptions/1111111-1111-1111-1111-111111111111/resourceGroups/yourenv-rg/overviewDeploying services (azd deploy) (✓) Done: Deploying service web - Endpoint: https://yourenv-e2najjk4vewf2-app-service.azurewebsites.net/
The azd template generates the connectivity variables for you asapp settings. App settings are one way to keep connection secrets out of your code repository.
In theinfra/resources.bicep
file, find the app settings and find the setting forAZURE_POSTGRESQL_CONNECTIONSTRING
.
resource appSettings 'config' = { name: 'appsettings' properties: { SCM_DO_BUILD_DURING_DEPLOYMENT: 'true' AZURE_POSTGRESQL_CONNECTIONSTRING: 'dbname=${pythonAppDatabase.name} host=${postgresServer.name}.postgres.database.azure.com port=5432 sslmode=require user=${postgresServer.properties.administratorLogin} password=${databasePassword}' SECRET_KEY: secretKey AZURE_REDIS_CONNECTIONSTRING: 'rediss://:${redisCache.listKeys().primaryKey}@${redisCache.name}.redis.cache.windows.net:6380/0' }}
AZURE_POSTGRESQL_CONNECTIONSTRING
contains the connection string to the Postgres database in Azure. You need to use it in your code to connect to it. You can find the code that uses this environment variable insrc/fastapi/models.py:
sql_url = ""if os.getenv("WEBSITE_HOSTNAME"): logger.info("Connecting to Azure PostgreSQL Flexible server based on AZURE_POSTGRESQL_CONNECTIONSTRING...") env_connection_string = os.getenv("AZURE_POSTGRESQL_CONNECTIONSTRING") if env_connection_string is None: logger.info("Missing environment variable AZURE_POSTGRESQL_CONNECTIONSTRING") else: # Parse the connection string details = dict(item.split('=') for item in env_connection_string.split()) # Properly format the URL for SQLAlchemy sql_url = ( f"postgresql://{quote_plus(details['user'])}:{quote_plus(details['password'])}" f"@{details['host']}:{details['port']}/{details['dbname']}?sslmode={details['sslmode']}" )else: logger.info("Connecting to local PostgreSQL server based on .env file...") load_dotenv() POSTGRES_USERNAME = os.environ.get("DBUSER") POSTGRES_PASSWORD = os.environ.get("DBPASS") POSTGRES_HOST = os.environ.get("DBHOST") POSTGRES_DATABASE = os.environ.get("DBNAME") POSTGRES_PORT = os.environ.get("DBPORT", 5432) sql_url = f"postgresql://{POSTGRES_USERNAME}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DATABASE}"engine = create_engine(sql_url)
Azure App Service requires a startup command to run your FastAPI app. The azd template sets this command for you in your App Service instance.
In theinfra/resources.bicep
file, find the declaration for your web site and then find the setting forappCommandLine
. This is the setting for your startup command.
resource web 'Microsoft.Web/sites@2022-03-01' = { name: '${prefix}-app-service' location: location tags: union(tags, { 'azd-service-name': 'web' }) kind: 'app,linux' properties: { serverFarmId: appServicePlan.id siteConfig: { alwaysOn: true linuxFxVersion: 'PYTHON|3.11' ftpsState: 'Disabled' appCommandLine: 'src/entrypoint.sh' minTlsVersion: '1.2' } httpsOnly: true } identity: { type: 'SystemAssigned' }
The startup command runs the filesrc/entrypoint.sh. Examine the code in that file to understand the commands that App Service runs to start your app:
#!/bin/bashset -epython3 -m pip install --upgrade pippython3 -m pip install -e srcpython3 src/fastapi_app/seed_data.pypython3 -m gunicorn fastapi_app:app -c src/gunicorn.conf.py
To learn more about app configuration and startup in App Service, seeConfigure a Linux Python app for Azure App Service.
You might have noticed in the previous section thatentrypoint.sh contains the following line:python3 src/fastapi_app/seed_data.py
. This command migrates your database. In the sample app, it only ensures that the correct tables are created in your database. It doesn't populate these tables with any data.
In this section, you'll run this command manually for demonstration purposes. With the PostgreSQL database protected by the virtual network, the easiest way to run the command is in an SSH session with the App Service container.
Use the value of theApp Service that you noted previously in the azd output to construct the URL for the SSH session and navigate to it in the browser:
In the SSH terminal, runpython3 src/fastapi_app/seed_data.py
. If it succeeds, App Service isconnecting successfully to the database.
Note
Only changes to files in/home
can persist beyond app restarts. Changes outside of/home
aren't persisted.
In the azd output, find the URL of your app and navigate to it in the browser. The URL looks like this in the AZD output:
Deploying services (azd deploy) (✓) Done: Deploying service web - Endpoint: <URL>
Add a few restaurants to the list.
Congratulations, you're running a web app in Azure App Service, with secure connectivity to Azure Database for PostgreSQL.
Azure App Service can capture logs to help you diagnose issues with your application. For convenience, the azd template has alreadyenabled logging to the local file system.
The sample app uses the Python Standard Library logging module to output logs. The sample app includes calls to the logger as shown below.
@app.get("/", response_class=HTMLResponse)async def index(request: Request, session: Session = Depends(get_db_session)): logger.info("root called") statement = ( select(Restaurant, func.avg(Review.rating).label("avg_rating"), func.count(Review.id).label("review_count")) .outerjoin(Review, Review.restaurant == Restaurant.id) .group_by(Restaurant.id) )
To access the log stream, open your app in the Azure portal. SelectMonitoring >Log stream.
Events can take several minutes to show up in the diagnostic logs. Learn more about logging in Python apps in the series onsetting up Azure Monitor for your Python application.
To delete all Azure resources in the current deployment environment, runazd down
.
azd down
Listed below are issues you might encounter while trying to work through this tutorial and steps to resolve them.
If you can't connect to the SSH session, then the app itself has failed to start. Check thediagnostic logs for details. For example, if you see an error likeKeyError: 'AZURE_POSTGRESQL_CONNECTIONSTRING'
, it might mean that the environment variable is missing (you might have removed the app setting).
If you encounter any errors related to connecting to the database, check if the app settings (AZURE_POSTGRESQL_CONNECTIONSTRING
) have been changed. Without that connection string, the migrate command can't communicate with the database.
Pricing for the created resources is as follows:
psql
from the app's SSH terminal.Using the autogenerated workflow file from App Service as an example, eachgit push
kicks off a new build and deployment run. From a local clone of the GitHub repository, you make the desired updates and push to GitHub. For example:
git add .git commit -m "<some-message>"git push origin main
Advance to the next tutorial to learn how to secure your app with a custom domain and certificate.
Learn how App Service runs a Python app:
Was this page helpful?
Was this page helpful?