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.pyfile to__init__.pyand move it to thelearn_flask/appdirectory. -
Then we can create a
config.pyfile in thelearn_flask/appdirectory to store our database configuration information:1 2 3 4 5 6 7import os basedir = os.path.abspath(os.path.dirname(__file__)) class Config(object): SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite://") SQLALCHEMY_TRACK_MODIFICATIONS = FalseThis way we can delete the database configuration information in
__init__.py:1 2app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://test:test_password@localhost:5432/weather_db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False -
We create a
manage.pyfile outside theappdirectory 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 30from 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
FlaskGroupto manage our application, and thecreate_dbanddrop_dbcommands are used to create and delete the database. Here we useProgrammingErrorto 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 8if __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:
|
|