Introduction
In the previous three articles of this series, we used the Flask framework of Python to develop a simple weather forecast website. So far, our website is still limited to access on our own computer. In this article, we will use Docker, Gunicorn and Nginx to deploy this website, so that our website can be accessed on the public network.
Prerequisites
- Already know how to develop a simple web application using the Flask framework (see “Learn Web Development with Python (1): Use Flask Framework”)
- Already installed PostgreSQL database and know how to use SQLAlchemy to operate the database (see “Learn Web Development with Python (2): Use PostgreSQL and SQLAlchemy”)
- Already installed Docker and docker-compose
- Already have a domain name
Project Structure (Development Version)
Up to the previous article “Learn Web Development with Python (3): Use Input and API”, our project structure is as follows:
|
|
In this article, we will use Docker and Gunicorn to deploy this website, so we need to add some files. For convenience, we will adjust the project structure to the following structure:
|
|
Next we will create these files step by step and introduce the functions of these files.
Add Dependencies
Before developing this web application, we have defined a Python virtual environment, which contains the dependencies we need. We can use the following command to export the dependencies in the current environment to the requirements.txt
file:
|
|
For some reason, there are some unnecessary dependencies in it, such as click
, itsdangerous
, Jinja2
, MarkupSafe
, Werkzeug
, we can manually delete these dependencies. Finally, our requirements.txt
file is as follows:
|
|
Package the Application as a Python Package
-
We rename the original
app.py
file to__init__.py
and move it to thelearn_flask/app
directory. -
Then we can create a
config.py
file in thelearn_flask/app
directory to store our database configuration information:1 2 3 4 5 6 7
import os basedir = os.path.abspath(os.path.dirname(__file__)) class Config(object): SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite://") SQLALCHEMY_TRACK_MODIFICATIONS = False
This way we can delete the database configuration information in
__init__.py
:1 2
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://test:test_password@localhost:5432/weather_db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
-
We create a
manage.py
file outside theapp
directory to manage our application. We can add the following code tomanage.py
:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
from flask.cli import FlaskGroup from sqlalchemy.exc import ProgrammingError from app import app cli = FlaskGroup(app) @cli.command("create_db") def create_db(): from app import db from app import Weather with app.app_context(): try: db.create_all() db.session.commit() except ProgrammingError: pass @cli.command("drop_db") def drop_db(): from app import db from app import Weather with app.app_context(): try: db.drop_all() db.session.commit() except ProgrammingError: pass if __name__ == "__main__": cli()
Here we use
FlaskGroup
to manage our application, and thecreate_db
anddrop_db
commands are used to create and delete the database. Here we useProgrammingError
to determine whether the database exists. If the database does not exist, the database will not be deleted.Then we can delete the code for creating and deleting the database in
__init__.py
:1 2 3 4 5 6 7 8
if __name__ == '__main__': with app.app_context(): try: db.create_all() db.session.commit() except ProgrammingError: pass app.run()
Deploy the Application with Docker
Create Dockerfile
We create a Dockerfile
file under the learn_flask
directory, which is used to build our application. We can add the following code to Dockerfile
:
|
|
The function of each command is marked in the comments.
Create docker-compose.yml
We create a docker-compose.yml
file under the learn_flask
directory, which is used to manage our application. We can add the following code to docker-compose.yml
:
|
|
In this docker-compose.yml
file, we define two services, one is the application learn_flask
, and the other is the database db
.
Create .env File
In the above docker-compose.yml
file, we used the .env
file and .env.db
file, we can create these two files under the learn_flask
directory to store our environment variables. We can add the following environment variables to the .env
file:
|
|
Add database environment variables to the .env.db
file:
|
|
Deploy the Application (Development Version)
Build the Image
When building the image for the first time, our application has not yet created the database, so we need to create the database first. We need to change the command
in docker-compose.yml
to the following code:
|
|
Then we can use the following command to build the image:
|
|
If everything goes well, you can access our website in the browser by entering http://localhost:5001
.
Deploy the Application
After the first successful build, we can temporarily stop the application, and then change the command
in docker-compose.yml
back to the original command:
|
|
Then we can use the following command to deploy the application:
|
|
Deploy the Application (Production Version)
After completing the deployment according to the above steps, our program is still running in development mode. We can see the following prompt information in the docker log:
|
|
This prompts us that the above deployment is not enough for the production environment, so we need to make some adjustments to make our application run in the production environment.
docker-compose
First, we create a new docker-compose.yml
file for the production environment deployment. We can create a docker-compose.prod.yml
file under the learn_flask
directory:
|
|
Note that we no longer specify volumes
for the application, because we no longer need to mount the code of the application to the container, but package the application into the image.
Dockerfile
In the docker-compose.prod.yml
file above, we used a new Dockerfile.prod
file, we can create a Dockerfile.prod
file under the learn_flask/learn_flask
directory to build our application image:
|
|
entrypoint
In the Dockerfile.prod
file above, we used an entrypoint.prod.sh
file, we can create an entrypoint.prod.sh
file under the learn_flask/learn_flask
directory to run our application:
|
|
Note: After creating the entrypoint.prod.sh
file, we need to change its permissions to executable, otherwise an error will be reported when running docker later:
|
|
Environment Variables
Similar to deploying in the development environment, we need to create .env.prod
and .env.prod.db
files to store the environment variables for the production environment. We can add the following environment variables to the .env.prod
file:
|
|
Add database environment variables to the .env.prod.db
file:
|
|
Build the Image
We can use the following command to build the image:
|
|
At this time, we only built the image, and the database is still empty, so we need to create the database in the container. We can use the following command to create the database in the container:
|
|
Then we can enter http://localhost:5001
in the browser to see our website.
Project Structure (Production Version)
Finally, our directory structure for the production environment is as follows:
|
|
Use Nginx Reverse Proxy
After completing the above deployment, we can still only access our website locally. If we want to access it on the public network, we need to use Nginx reverse proxy. Since Nginx has been installed on my server, I only need to add a configuration file on the server. We can create a learn_flask.conf
file under the /etc/nginx/sites-available
directory on the server to store our configuration information. We can add the following code to the learn_flask.conf
file:
|
|
Then we can use the following command to activate this configuration file:
|
|