One place for hosting & domains

      Gunicorn

      How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 20.04


      Not using Ubuntu 20.04?


      Choose a different version or distribution.

      Introduction

      Django is a powerful web framework that can help you get your Python application or website off the ground. Django includes a simplified development server for testing your code locally, but for anything even slightly production related, a more secure and powerful web server is required.

      In this guide, we will demonstrate how to install and configure some components on Ubuntu 20.04 to support and serve Django applications. We will be setting up a PostgreSQL database instead of using the default SQLite database. We will configure the Gunicorn application server to interface with our applications. We will then set up Nginx to reverse proxy to Gunicorn, giving us access to its security and performance features to serve our apps.

      Prerequisites and Goals

      In order to complete this guide, you should have a fresh Ubuntu 20.04 server instance with a basic firewall and a non-root user with sudo privileges configured. You can learn how to set this up by running through our initial server setup guide.

      We will be installing Django within a virtual environment. Installing Django into an environment specific to your project will allow your projects and their requirements to be handled separately.

      Once we have our database and application up and running, we will install and configure the Gunicorn application server. This will serve as an interface to our application, translating client requests from HTTP to Python calls that our application can process. We will then set up Nginx in front of Gunicorn to take advantage of its high performance connection handling mechanisms and its easy-to-implement security features.

      Let’s get started.

      Installing the Packages from the Ubuntu Repositories

      To begin the process, we’ll download and install all of the items we need from the Ubuntu repositories. We will use the Python package manager pip to install additional components a bit later.

      We need to update the local apt package index and then download and install the packages. The packages we install depend on which version of Python your project will use.

      If you are using Django with Python 3, type:

      • sudo apt update
      • sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl

      Django 1.11 is the last release of Django that will support Python 2. If you are starting new projects, it is strongly recommended that you choose Python 3. If you still need to use Python 2, type:

      • sudo apt update
      • sudo apt install python-pip python-dev libpq-dev postgresql postgresql-contrib nginx curl

      This will install pip, the Python development files needed to build Gunicorn later, the Postgres database system and the libraries needed to interact with it, and the Nginx web server.

      Creating the PostgreSQL Database and User

      We’re going to jump right in and create a database and database user for our Django application.

      By default, Postgres uses an authentication scheme called “peer authentication” for local connections. Basically, this means that if the user’s operating system username matches a valid Postgres username, that user can login with no further authentication.

      During the Postgres installation, an operating system user named postgres was created to correspond to the postgres PostgreSQL administrative user. We need to use this user to perform administrative tasks. We can use sudo and pass in the username with the -u option.

      Log into an interactive Postgres session by typing:

      You will be given a PostgreSQL prompt where we can set up our requirements.

      First, create a database for your project:

      • CREATE DATABASE myproject;

      Note: Every Postgres statement must end with a semi-colon, so make sure that your command ends with one if you are experiencing issues.

      Next, create a database user for our project. Make sure to select a secure password:

      • CREATE USER myprojectuser WITH PASSWORD 'password';

      Afterwards, we’ll modify a few of the connection parameters for the user we just created. This will speed up database operations so that the correct values do not have to be queried and set each time a connection is established.

      We are setting the default encoding to UTF-8, which Django expects. We are also setting the default transaction isolation scheme to “read committed”, which blocks reads from uncommitted transactions. Lastly, we are setting the timezone. By default, our Django projects will be set to use UTC. These are all recommendations from the Django project itself:

      • ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
      • ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
      • ALTER ROLE myprojectuser SET timezone TO 'UTC';

      Now, we can give our new user access to administer our new database:

      • GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;

      When you are finished, exit out of the PostgreSQL prompt by typing:

      Postgres is now set up so that Django can connect to and manage its database information.

      Creating a Python Virtual Environment for your Project

      Now that we have our database, we can begin getting the rest of our project requirements ready. We will be installing our Python requirements within a virtual environment for easier management.

      To do this, we first need access to the virtualenv command. We can install this with pip.

      If you are using Python 3, upgrade pip and install the package by typing:

      • sudo -H pip3 install --upgrade pip
      • sudo -H pip3 install virtualenv

      If you are using Python 2, upgrade pip and install the package by typing:

      • sudo -H pip install --upgrade pip
      • sudo -H pip install virtualenv

      With virtualenv installed, we can start forming our project. Create and move into a directory where we can keep our project files:

      • mkdir ~/myprojectdir
      • cd ~/myprojectdir

      Within the project directory, create a Python virtual environment by typing:

      This will create a directory called myprojectenv within your myprojectdir directory. Inside, it will install a local version of Python and a local version of pip. We can use this to install and configure an isolated Python environment for our project.

      Before we install our project’s Python requirements, we need to activate the virtual environment. You can do that by typing:

      • source myprojectenv/bin/activate

      Your prompt should change to indicate that you are now operating within a Python virtual environment. It will look something like this: (myprojectenv)user@host:~/myprojectdir$.

      With your virtual environment active, install Django, Gunicorn, and the psycopg2 PostgreSQL adaptor with the local instance of pip:

      Note: When the virtual environment is activated (when your prompt has (myprojectenv) preceding it), use pip instead of pip3, even if you are using Python 3. The virtual environment’s copy of the tool is always named pip, regardless of the Python version.

      • pip install django gunicorn psycopg2-binary

      You should now have all of the software needed to start a Django project.

      Creating and Configuring a New Django Project

      With our Python components installed, we can create the actual Django project files.

      Creating the Django Project

      Since we already have a project directory, we will tell Django to install the files here. It will create a second level directory with the actual code, which is normal, and place a management script in this directory. The key to this is that we are defining the directory explicitly instead of allowing Django to make decisions relative to our current directory:

      • django-admin.py startproject myproject ~/myprojectdir

      At this point, your project directory (~/myprojectdir in our case) should have the following content:

      • ~/myprojectdir/manage.py: A Django project management script.
      • ~/myprojectdir/myproject/: The Django project package. This should contain the __init__.py, settings.py, urls.py, asgi.py, and wsgi.py files.
      • ~/myprojectdir/myprojectenv/: The virtual environment directory we created earlier.

      Adjusting the Project Settings

      The first thing we should do with our newly created project files is adjust the settings. Open the settings file in your text editor:

      • nano ~/myprojectdir/myproject/settings.py

      Start by locating the ALLOWED_HOSTS directive. This defines a list of the server’s addresses or domain names may be used to connect to the Django instance. Any incoming requests with a Host header that is not in this list will raise an exception. Django requires that you set this to prevent a certain class of security vulnerability.

      In the square brackets, list the IP addresses or domain names that are associated with your Django server. Each item should be listed in quotations with entries separated by a comma. If you wish requests for an entire domain and any subdomains, prepend a period to the beginning of the entry. In the snippet below, there are a few commented out examples used to demonstrate:

      Note: Be sure to include localhost as one of the options since we will be proxying connections through a local Nginx instance.

      ~/myprojectdir/myproject/settings.py

      . . .
      # The simplest case: just add the domain name(s) and IP addresses of your Django server
      # ALLOWED_HOSTS = [ 'example.com', '203.0.113.5']
      # To respond to 'example.com' and any subdomains, start the domain with a dot
      # ALLOWED_HOSTS = ['.example.com', '203.0.113.5']
      ALLOWED_HOSTS = ['your_server_domain_or_IP', 'second_domain_or_IP', . . ., 'localhost']
      

      Next, find the section that configures database access. It will start with DATABASES. The configuration in the file is for a SQLite database. We already created a PostgreSQL database for our project, so we need to adjust the settings.

      Change the settings with your PostgreSQL database information. We tell Django to use the psycopg2 adaptor we installed with pip. We need to give the database name, the database username, the database user’s password, and then specify that the database is located on the local computer. You can leave the PORT setting as an empty string:

      ~/myprojectdir/myproject/settings.py

      . . .
      
      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.postgresql_psycopg2',
              'NAME': 'myproject',
              'USER': 'myprojectuser',
              'PASSWORD': 'password',
              'HOST': 'localhost',
              'PORT': '',
          }
      }
      
      . . .
      

      Next, move down to the bottom of the file and add a setting indicating where the static files should be placed. This is necessary so that Nginx can handle requests for these items. The following line tells Django to place them in a directory called static in the base project directory:

      ~/myprojectdir/myproject/settings.py

      . . .
      
      STATIC_URL = '/static/'
      STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
      

      Save and close the file when you are finished.

      Completing Initial Project Setup

      Now, we can migrate the initial database schema to our PostgreSQL database using the management script:

      • ~/myprojectdir/manage.py makemigrations
      • ~/myprojectdir/manage.py migrate

      Create an administrative user for the project by typing:

      • ~/myprojectdir/manage.py createsuperuser

      You will have to select a username, provide an email address, and choose and confirm a password.

      We can collect all of the static content into the directory location we configured by typing:

      • ~/myprojectdir/manage.py collectstatic

      You will have to confirm the operation. The static files will then be placed in a directory called static within your project directory.

      If you followed the initial server setup guide, you should have a UFW firewall protecting your server. In order to test the development server, we’ll have to allow access to the port we’ll be using.

      Create an exception for port 8000 by typing:

      Finally, you can test our your project by starting up the Django development server with this command:

      • ~/myprojectdir/manage.py runserver 0.0.0.0:8000

      In your web browser, visit your server’s domain name or IP address followed by :8000:

      http://server_domain_or_IP:8000
      

      You should receive the default Django index page:

      Django index page

      If you append /admin to the end of the URL in the address bar, you will be prompted for the administrative username and password you created with the createsuperuser command:

      Django admin login

      After authenticating, you can access the default Django admin interface:

      Django admin interface

      When you are finished exploring, hit CTRL-C in the terminal window to shut down the development server.

      Testing Gunicorn’s Ability to Serve the Project

      The last thing we want to do before leaving our virtual environment is test Gunicorn to make sure that it can serve the application. We can do this by entering our project directory and using gunicorn to load the project’s WSGI module:

      • cd ~/myprojectdir
      • gunicorn --bind 0.0.0.0:8000 myproject.wsgi

      This will start Gunicorn on the same interface that the Django development server was running on. You can go back and test the app again.

      Note: The admin interface will not have any of the styling applied since Gunicorn does not know how to find the static CSS content responsible for this.

      We passed Gunicorn a module by specifying the relative directory path to Django’s wsgi.py file, which is the entry point to our application, using Python’s module syntax. Inside of this file, a function called application is defined, which is used to communicate with the application. To learn more about the WSGI specification, click here.

      When you are finished testing, hit CTRL-C in the terminal window to stop Gunicorn.

      We’re now finished configuring our Django application. We can back out of our virtual environment by typing:

      The virtual environment indicator in your prompt will be removed.

      Creating systemd Socket and Service Files for Gunicorn

      We have tested that Gunicorn can interact with our Django application, but we should implement a more robust way of starting and stopping the application server. To accomplish this, we’ll make systemd service and socket files.

      The Gunicorn socket will be created at boot and will listen for connections. When a connection occurs, systemd will automatically start the Gunicorn process to handle the connection.

      Start by creating and opening a systemd socket file for Gunicorn with sudo privileges:

      • sudo nano /etc/systemd/system/gunicorn.socket

      Inside, we will create a [Unit] section to describe the socket, a [Socket] section to define the socket location, and an [Install] section to make sure the socket is created at the right time:

      /etc/systemd/system/gunicorn.socket

      [Unit]
      Description=gunicorn socket
      
      [Socket]
      ListenStream=/run/gunicorn.sock
      
      [Install]
      WantedBy=sockets.target
      

      Save and close the file when you are finished.

      Next, create and open a systemd service file for Gunicorn with sudo privileges in your text editor. The service filename should match the socket filename with the exception of the extension:

      • sudo nano /etc/systemd/system/gunicorn.service

      Start with the [Unit] section, which is used to specify metadata and dependencies. We’ll put a description of our service here and tell the init system to only start this after the networking target has been reached. Because our service relies on the socket from the socket file, we need to include a Requires directive to indicate that relationship:

      /etc/systemd/system/gunicorn.service

      [Unit]
      Description=gunicorn daemon
      Requires=gunicorn.socket
      After=network.target
      

      Next, we’ll open up the [Service] section. We’ll specify the user and group that we want to process to run under. We will give our regular user account ownership of the process since it owns all of the relevant files. We’ll give group ownership to the www-data group so that Nginx can communicate easily with Gunicorn.

      We’ll then map out the working directory and specify the command to use to start the service. In this case, we’ll have to specify the full path to the Gunicorn executable, which is installed within our virtual environment. We will bind the process to the Unix socket we created within the /run directory so that the process can communicate with Nginx. We log all data to standard output so that the journald process can collect the Gunicorn logs. We can also specify any optional Gunicorn tweaks here. For example, we specified 3 worker processes in this case:

      /etc/systemd/system/gunicorn.service

      [Unit]
      Description=gunicorn daemon
      Requires=gunicorn.socket
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      WorkingDirectory=/home/sammy/myprojectdir
      ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn 
                --access-logfile - 
                --workers 3 
                --bind unix:/run/gunicorn.sock 
                myproject.wsgi:application
      

      Finally, we’ll add an [Install] section. This will tell systemd what to link this service to if we enable it to start at boot. We want this service to start when the regular multi-user system is up and running:

      /etc/systemd/system/gunicorn.service

      [Unit]
      Description=gunicorn daemon
      Requires=gunicorn.socket
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      WorkingDirectory=/home/sammy/myprojectdir
      ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn 
                --access-logfile - 
                --workers 3 
                --bind unix:/run/gunicorn.sock 
                myproject.wsgi:application
      
      [Install]
      WantedBy=multi-user.target
      

      With that, our systemd service file is complete. Save and close it now.

      We can now start and enable the Gunicorn socket. This will create the socket file at /run/gunicorn.sock now and at boot. When a connection is made to that socket, systemd will automatically start the gunicorn.service to handle it:

      • sudo systemctl start gunicorn.socket
      • sudo systemctl enable gunicorn.socket

      We can confirm that the operation was successful by checking for the socket file.

      Checking for the Gunicorn Socket File

      Check the status of the process to find out whether it was able to start:

      • sudo systemctl status gunicorn.socket

      You should receive an output like this:

      Output

      ● gunicorn.socket - gunicorn socket Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor prese> Active: active (listening) since Fri 2020-06-26 17:53:10 UTC; 14s ago Triggers: ● gunicorn.service Listen: /run/gunicorn.sock (Stream) Tasks: 0 (limit: 1137) Memory: 0B CGroup: /system.slice/gunicorn.socket

      Next, check for the existence of the gunicorn.sock file within the /run directory:

      Output

      /run/gunicorn.sock: socket

      If the systemctl status command indicated that an error occurred or if you do not find the gunicorn.sock file in the directory, it’s an indication that the Gunicorn socket was not able to be created correctly. Check the Gunicorn socket’s logs by typing:

      • sudo journalctl -u gunicorn.socket

      Take another look at your /etc/systemd/system/gunicorn.socket file to fix any problems before continuing.

      Testing Socket Activation

      Currently, if you’ve only started the gunicorn.socket unit, the gunicorn.service will not be active yet since the socket has not yet received any connections. You can check this by typing:

      • sudo systemctl status gunicorn

      Output

      ● gunicorn.service - gunicorn daemon Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled) Active: inactive (dead)

      To test the socket activation mechanism, we can send a connection to the socket through curl by typing:

      • curl --unix-socket /run/gunicorn.sock localhost

      You should receive the HTML output from your application in the terminal. This indicates that Gunicorn was started and was able to serve your Django application. You can verify that the Gunicorn service is running by typing:

      • sudo systemctl status gunicorn

      Output

      ● gunicorn.service - gunicorn daemon Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled) Active: active (running) since Fri 2020-06-26 18:52:21 UTC; 2s ago TriggeredBy: ● gunicorn.socket Main PID: 22914 (gunicorn) Tasks: 4 (limit: 1137) Memory: 89.1M CGroup: /system.slice/gunicorn.service ├─22914 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico> ├─22927 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico> ├─22928 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico> └─22929 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico> Jun 26 18:52:21 django-tutorial systemd[1]: Started gunicorn daemon. Jun 26 18:52:21 django-tutorial gunicorn[22914]: [2020-06-26 18:52:21 +0000] [22914] [INFO] Starting gunicorn 20.0.4 Jun 26 18:52:21 django-tutorial gunicorn[22914]: [2020-06-26 18:52:21 +0000] [22914] [INFO] Listening at: unix:/run/gunicorn.sock (22914) Jun 26 18:52:21 django-tutorial gunicorn[22914]: [2020-06-26 18:52:21 +0000] [22914] [INFO] Using worker: sync Jun 26 18:52:21 django-tutorial gunicorn[22927]: [2020-06-26 18:52:21 +0000] [22927] [INFO] Booting worker with pid: 22927 Jun 26 18:52:21 django-tutorial gunicorn[22928]: [2020-06-26 18:52:21 +0000] [22928] [INFO] Booting worker with pid: 22928 Jun 26 18:52:21 django-tutorial gunicorn[22929]: [2020-06-26 18:52:21 +0000] [22929] [INFO] Booting worker with pid: 22929

      If the output from curl or the output of systemctl status indicates that a problem occurred, check the logs for additional details:

      • sudo journalctl -u gunicorn

      Check your /etc/systemd/system/gunicorn.service file for problems. If you make changes to the /etc/systemd/system/gunicorn.service file, reload the daemon to reread the service definition and restart the Gunicorn process by typing:

      • sudo systemctl daemon-reload
      • sudo systemctl restart gunicorn

      Make sure you troubleshoot the above issues before continuing.

      Configure Nginx to Proxy Pass to Gunicorn

      Now that Gunicorn is set up, we need to configure Nginx to pass traffic to the process.

      Start by creating and opening a new server block in Nginx’s sites-available directory:

      • sudo nano /etc/nginx/sites-available/myproject

      Inside, open up a new server block. We will start by specifying that this block should listen on the normal port 80 and that it should respond to our server’s domain name or IP address:

      /etc/nginx/sites-available/myproject

      server {
          listen 80;
          server_name server_domain_or_IP;
      }
      

      Next, we will tell Nginx to ignore any problems with finding a favicon. We will also tell it where to find the static assets that we collected in our ~/myprojectdir/static directory. All of these files have a standard URI prefix of “/static”, so we can create a location block to match those requests:

      /etc/nginx/sites-available/myproject

      server {
          listen 80;
          server_name server_domain_or_IP;
      
          location = /favicon.ico { access_log off; log_not_found off; }
          location /static/ {
              root /home/sammy/myprojectdir;
          }
      }
      

      Finally, we’ll create a location / {} block to match all other requests. Inside of this location, we’ll include the standard proxy_params file included with the Nginx installation and then we will pass the traffic directly to the Gunicorn socket:

      /etc/nginx/sites-available/myproject

      server {
          listen 80;
          server_name server_domain_or_IP;
      
          location = /favicon.ico { access_log off; log_not_found off; }
          location /static/ {
              root /home/sammy/myprojectdir;
          }
      
          location / {
              include proxy_params;
              proxy_pass http://unix:/run/gunicorn.sock;
          }
      }
      

      Save and close the file when you are finished. Now, we can enable the file by linking it to the sites-enabled directory:

      • sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

      Test your Nginx configuration for syntax errors by typing:

      If no errors are reported, go ahead and restart Nginx by typing:

      • sudo systemctl restart nginx

      Finally, we need to open up our firewall to normal traffic on port 80. Since we no longer need access to the development server, we can remove the rule to open port 8000 as well:

      • sudo ufw delete allow 8000
      • sudo ufw allow 'Nginx Full'

      You should now be able to go to your server’s domain or IP address to view your application.

      Note: After configuring Nginx, the next step should be securing traffic to the server using SSL/TLS. This is important because without it, all information, including passwords are sent over the network in plain text.

      If you have a domain name, the easiest way to get an SSL certificate to secure your traffic is using Let’s Encrypt. Follow this guide to set up Let’s Encrypt with Nginx on Ubuntu 20.04. Follow the procedure using the Nginx server block we created in this guide.

      Troubleshooting Nginx and Gunicorn

      If this last step does not show your application, you will need to troubleshoot your installation.

      Nginx Is Showing the Default Page Instead of the Django Application

      If Nginx displays the default page instead of proxying to your application, it usually means that you need to adjust the server_name within the /etc/nginx/sites-available/myproject file to point to your server’s IP address or domain name.

      Nginx uses the server_name to determine which server block to use to respond to requests. If you receive the default Nginx page, it is a sign that Nginx wasn’t able to match the request to a sever block explicitly, so it’s falling back on the default block defined in /etc/nginx/sites-available/default.

      The server_name in your project’s server block must be more specific than the one in the default server block to be selected.

      Nginx Is Displaying a 502 Bad Gateway Error Instead of the Django Application

      A 502 error indicates that Nginx is unable to successfully proxy the request. A wide range of configuration problems express themselves with a 502 error, so more information is required to troubleshoot properly.

      The primary place to look for more information is in Nginx’s error logs. Generally, this will tell you what conditions caused problems during the proxying event. Follow the Nginx error logs by typing:

      • sudo tail -F /var/log/nginx/error.log

      Now, make another request in your browser to generate a fresh error (try refreshing the page). You should receive a fresh error message written to the log. If you look at the message, it should help you narrow down the problem.

      You might receive the following message:

      connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)

      This indicates that Nginx was unable to find the gunicorn.sock file at the given location. You should compare the proxy_pass location defined within /etc/nginx/sites-available/myproject file to the actual location of the gunicorn.sock file generated by the gunicorn.socket systemd unit.

      If you cannot find a gunicorn.sock file within the /run directory, it generally means that the systemd socket file was unable to create it. Go back to the section on checking for the Gunicorn socket file to step through the troubleshooting steps for Gunicorn.

      connect() to unix:/run/gunicorn.sock failed (13: Permission denied)

      This indicates that Nginx was unable to connect to the Gunicorn socket because of permissions problems. This can happen when the procedure is followed using the root user instead of a sudo user. While systemd is able to create the Gunicorn socket file, Nginx is unable to access it.

      This can happen if there are limited permissions at any point between the root directory (/) the gunicorn.sock file. We can review the permissions and ownership values of the socket file and each of its parent directories by passing the absolute path to our socket file to the namei command:

      • namei -l /run/gunicorn.sock

      Output

      f: /run/gunicorn.sock drwxr-xr-x root root / drwxr-xr-x root root run srw-rw-rw- root root gunicorn.sock

      The output displays the permissions of each of the directory components. By looking at the permissions (first column), owner (second column) and group owner (third column), we can figure out what type of access is allowed to the socket file.

      In the above example, the socket file and each of the directories leading up to the socket file have world read and execute permissions (the permissions column for the directories end with r-x instead of ---). The Nginx process should be able to access the socket successfully.

      If any of the directories leading up to the socket do not have world read and execute permission, Nginx will not be able to access the socket without allowing world read and execute permissions or making sure group ownership is given to a group that Nginx is a part of.

      Django Is Displaying: “could not connect to server: Connection refused”

      One message that you may receive from Django when attempting to access parts of the application in the web browser is:

      OperationalError at /admin/login/
      could not connect to server: Connection refused
          Is the server running on host "localhost" (127.0.0.1) and accepting
          TCP/IP connections on port 5432?
      

      This indicates that Django is unable to connect to the Postgres database. Make sure that the Postgres instance is running by typing:

      • sudo systemctl status postgresql

      If it is not, you can start it and enable it to start automatically at boot (if it is not already configured to do so) by typing:

      • sudo systemctl start postgresql
      • sudo systemctl enable postgresql

      If you are still having issues, make sure the database settings defined in the ~/myprojectdir/myproject/settings.py file are correct.

      Further Troubleshooting

      For additional troubleshooting, the logs can help narrow down root causes. Check each of them in turn and look for messages indicating problem areas.

      The following logs may be helpful:

      • Check the Nginx process logs by typing: sudo journalctl -u nginx
      • Check the Nginx access logs by typing: sudo less /var/log/nginx/access.log
      • Check the Nginx error logs by typing: sudo less /var/log/nginx/error.log
      • Check the Gunicorn application logs by typing: sudo journalctl -u gunicorn
      • Check the Gunicorn socket logs by typing: sudo journalctl -u gunicorn.socket

      As you update your configuration or application, you will likely need to restart the processes to adjust to your changes.

      If you update your Django application, you can restart the Gunicorn process to pick up the changes by typing:

      • sudo systemctl restart gunicorn

      If you change Gunicorn socket or service files, reload the daemon and restart the process by typing:

      • sudo systemctl daemon-reload
      • sudo systemctl restart gunicorn.socket gunicorn.service

      If you change the Nginx server block configuration, test the configuration and then Nginx by typing:

      • sudo nginx -t && sudo systemctl restart nginx

      These commands are helpful for picking up changes as you adjust your configuration.

      Conclusion

      In this guide, we’ve set up a Django project in its own virtual environment. We’ve configured Gunicorn to translate client requests so that Django can handle them. Afterwards, we set up Nginx to act as a reverse proxy to handle client connections and serve the correct project depending on the client request.

      Django makes creating projects and applications simple by providing many of the common pieces, allowing you to focus on the unique elements. By leveraging the general tool chain described in this article, you can easily serve the applications you create from a single server.



      Source link

      Bedienen von Flask-Anwendungen mit Gunicorn und Nginx unter Ubuntu 20.04


      Einführung

      In diesem Leitfaden erstellen Sie eine Python-Anwendung mithilfe des Flask-Microframeworks unter Ubuntu 20.04. Im Großteil dieses Artikels geht es um die Einrichtung des Gunicorn Anwendungsservers und das Starten der Anwendung sowie um die Konfiguration von Nginx als Front-end-Reverse-Proxy.

      Voraussetzungen

      Bevor Sie mit diesem Leitfaden beginnen, sollten Sie folgende Voraussetzungen erfüllt haben:

      Schritt 1 — Installieren der Komponenten aus den Ubuntu-Repositorys

      Unser erster Schritt besteht darin, alle Elemente zu installieren, die aus den Ubuntu-Repositorys benötigt werden. Dazu gehört pip, der Python-Paketmanager, der unsere Python-Komponenten verwalten wird. Außerdem erhalten wir die zum Erstellen einiger Gunicorn-Komponenten benötigten Python-Entwicklungsdateien.

      Wir aktualisieren zuerst den lokalen Paketindex und installieren die Pakete, mit denen wir unsere Python-Umgebung aufbauen können. Dazu gehören python3-pip sowie einige weitere Pakete und Entwicklungs-Tools, die für eine robuste Programmierumgebung erforderlich sind:

      • sudo apt update
      • sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

      Wenn diese Pakete eingerichtet sind, fahren wir mit der Erstellung einer virtuellen Umgebung für unser Projekt fort.

      Schritt 2 — Erstellen einer virtuellen Python-Umgebung

      Als Nächstes richten wir eine virtuelle Umgebung ein, um unsere Flask-Anwendung von anderen Python-Dateien auf dem System zu isolieren.

      Beginnen Sie mit der Installation des python3-venv Pakets, womit das venv Modul installiert wird:

      • sudo apt install python3-venv

      Als Nächstes erstellen wir ein übergeordnetes Verzeichnis für unser Flask-Projekt. Nachdem Sie es erstellt haben, wechseln Sie in das Verzeichnis:

      • mkdir ~/myproject
      • cd ~/myproject

      Erstellen Sie eine virtuelle Umgebung, um die Python-Anforderungen Ihres Flask-Projekts zu speichern, indem Sie Folgendes eingeben:

      • python3 -m venv myprojectenv

      Auf diese Weise wird eine lokale Kopie von Python und pip in ein Verzeichnis mit dem Namen myprojectenv innerhalb Ihres Projektverzeichnisses installiert.

      Bevor Sie Anwendungen innerhalb der virtuellen Umgebung installieren, müssen Sie sie aktivieren. Geben Sie hierfür Folgendes ein:

      • source myprojectenv/bin/activate

      Ihre Eingabeaufforderung ändert sich und zeigt an, dass Sie jetzt innerhalb der virtuellen Umgebung arbeiten. Sie sieht etwa wie folgt aus: (myprojectenv)user@host:~/myproject$.

      Schritt 3 — Einrichten einer Flask-Anwendung

      Sie befinden sich nun in Ihrer virtuellen Umgebung und können somit Flask und Gunicorn installieren und mit der Gestaltung Ihrer Anwendung beginnen.

      Wir installieren zuerst wheel mit der lokalen Instanz von pip, um sicherzustellen, dass unsere Pakete installiert werden, selbst wenn wheel-Archive fehlen:

      Hinweis


      Unabhängig davon, welche Version von Python Sie verwenden, wenn die virtuelle Umgebung aktiviert ist, sollten Sie den Befehl pip (nicht pip3) verwenden.

      Als Nächstes installieren wir Flask und Gunicorn:

      • pip install gunicorn flask

      Erstellen einer Beispiel-App

      Nachdem Sie jetzt über Flask verfügen, können Sie eine einfache Anwendung erstellen. Flask ist ein Microframework. Es enthält nicht viele der Tools, die vollwertige Frameworks möglicherweise ausmachen, und besteht hauptsächlich als Modul, das Sie in Ihre Projekte importieren können, um Sie bei der Initialisierung einer Webanwendung zu unterstützen.

      Während Ihre Anwendung möglicherweise komplexer ist, erstellen wir unsere Flask-App in einer einzigen Datei mit dem Namen myproject.py:

      • nano ~/myproject/myproject.py

      Der Anwendungscode ist in dieser Datei enthalten. Er importiert Flask und instanziiert ein Flask-Objekt. Sie können damit die Funktionen definieren, die nach der Anfrage einer bestimmten Route ausgeführt werden sollen:

      ~/myproject/myproject.py

      from flask import Flask
      app = Flask(__name__)
      
      @app.route("/")
      def hello():
          return "<h1 style="color:blue">Hello There!</h1>"
      
      if __name__ == "__main__":
          app.run(host="0.0.0.0")
      

      Dies definiert im Wesentlichen, welche Inhalte beim Zugriff auf die Stammdomäne gezeigt werden. Speichern und schließen Sie abschließend die Datei.

      Wenn Sie den Leitfaden zur Servereinrichtung befolgt haben, sollte Ihre UFW-Firewall aktiviert sein. Um die Anwendung zu testen, müssen Sie den Zugriff auf Port 5000 zulassen:

      Geben Sie Folgendes ein, um Ihre Flask-App zu testen:

      Sie sehen eine Ausgabe ähnlich wie die Folgende mit einer praktischen Warnung, die Sie darauf hinweist, diese Servereinrichtung nicht produktiv zu nutzen:

      Output

      * Serving Flask app "myproject" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

      Gehen Sie auf die IP-Adresse Ihres Servers, gefolgt von :5000 in Ihrem Webbrowser:

      http://your_server_ip:5000
      

      Sie sollten in etwa Folgendes sehen:

      Flask Beispiel-App

      Wenn Sie fertig sind, betätigen Sie STRG-C in Ihrem Terminalfenster, um den Flask-Entwicklungsserver anzuhalten.

      Erstellen des WSGI-Einstiegspunkts

      Als Nächstes erstellen wir eine Datei, die als Einstiegspunkt für unsere Anwendung dient. Diese sagt dem Gunicorn-Server, wie die Interaktion mit der Anwendung erfolgt.

      Wir wollen die Datei wsgi.py nennen:

      In diese Datei importieren wir die Flask-Instanz aus unserer Anwendung und führen sie dann aus:

      ~/myproject/wsgi.py

      from myproject import app
      
      if __name__ == "__main__":
          app.run()
      

      Wenn Sie fertig sind, speichern und schließen Sie die Datei.

      Schritt 4 — Konfigurieren von Gunicorn

      Ihre Anwendung ist jetzt mit einem bestehenden Einstiegspunkt geschrieben. Wir können jetzt mit der Konfiguration von Gunicorn fortfahren.

      Bevor wir fortfahren, sollten wir überprüfen, ob Gunicorn die Anwendung korrekt bedienen kann.

      Dazu geben wir einfach den Namen des Einstiegspunktes weiter. Dieser ist als der Name des Moduls (minus die Erweiterung .py) plus der Name des Callable innerhalb der Anwendung aufgebaut. In unserem Fall ist das wsgi:app.

      Außerdem geben wir die Schnittstelle und den Port an, damit die Anwendung auf einer öffentlich verfügbaren Schnittstelle gestartet wird:

      • cd ~/myproject
      • gunicorn --bind 0.0.0.0:5000 wsgi:app

      Sie sollten eine Ausgabe wie die folgende sehen:

      Output

      [2020-05-20 14:13:00 +0000] [46419] [INFO] Starting gunicorn 20.0.4 [2020-05-20 14:13:00 +0000] [46419] [INFO] Listening at: http://0.0.0.0:5000 (46419) [2020-05-20 14:13:00 +0000] [46419] [INFO] Using worker: sync [2020-05-20 14:13:00 +0000] [46421] [INFO] Booting worker with pid: 46421

      Gehen Sie auf die IP-Adresse Ihres Servers mit :5000, das am Ende Ihres Webbrowsers erneut angehängt ist:

      http://your_server_ip:5000
      

      Sie sollten die Ausgabe Ihrer Anwendung sehen:

      Flask Beispiel-App

      Wenn Sie die ordnungsgemäße Funktionsweise bestätigt haben, betätigen Sie STRG-C in Ihrem Terminalfenster.

      Wir sind jetzt mit unserer virtuellen Umgebung fertig und können sie deaktivieren:

      Alle Python-Befehle nutzen jetzt wieder die Python-Umgebung des Systems.

      Als Nächstes erstellen wir die systemd-Diensteinheit-Datei. Das Erstellen einer systemd Unit-Datei ermöglicht, dass das Init-System von Ubuntu Gunicorn automatisch startet und die Flask-Anwendung bei jedem Hochfahren des Servers bedient wird.

      Erstellen Sie zunächst eine Unit-Datei, die auf .service endet, innerhalb des Verzeichnisses /etc/systemd/system:

      • sudo nano /etc/systemd/system/myproject.service

      Darin beginnen wir mit dem Abschnitt [Unit], mit dem Metadaten und Abhängigkeiten angegeben werden. Wir wollen hier eine Beschreibung unseres Dienstes eingeben und dem Init-System mitteilen, ihn nur zu starten, nachdem das Netzwerkziel erreicht wurde:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      

      Als Nächstes wollen wir den Abschnitt [Service] öffnen. Damit werden der Benutzer und die Gruppe angegeben, unter denen wir den Prozess ausführen möchten. Wir wollen unserem regulären Benutzerkonto die Prozessverantwortung übergeben, da es alle relevanten Dateien besitzt. Wir wollen außerdem der Gruppe www-data die Gruppenverantwortung übertragen, damit Nginx einfach mit den Gunicorn-Prozessen kommunizieren kann. Denken Sie daran, den Benutzername hier durch Ihren Benutzernamen zu ersetzen:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      

      Als Nächstes wollen wir das Arbeitsverzeichnis darstellen und die Umgebungsvariable PATH setzen, damit das Init-System weiß, dass sich die ausführbaren Daten für den Prozess innerhalb unserer virtuellen Umgebung befinden. Wir wollen auch den Befehl zum Starten des Dienstes angeben. Dieser Befehl führt Folgendes aus:

      • Starten von 3 Worker-Prozessen (bei Bedarf anpassen)
      • Erstellen und binden an eine myproject.sock Unix Socket-Datei, innerhalb unseres Projektverzeichnisses. Wir setzen einen umask-Wert von 007 ein, um die Socket-Datei zu erstellen, die den Zugriff auf den Eigentümer und die Gruppe erteilt, aber den Zugriff anderer einschränkt.
      • Geben Sie den WSGI Einstiegspunkt-Dateinamen zusammen mit dem Python Callable innerhalb dieser Datei (wsgi:app) an.

      Systemd erfordert, dass wir der ausführbaren Gunicorn den vollen Pfad geben, der innerhalb unserer virtuellen Umgebung installiert ist.

      Denken Sie daran, den Benutzernamen und die Projektpfade durch Ihre eigenen Informationen zu ersetzen:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      WorkingDirectory=/home/sammy/myproject
      Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
      ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
      

      Schließlich fügen wir einen [Install]-Abschnitt hinzu. Dies teilt systemd mit, womit dieser Dienst verknüpft werden soll, wenn wir festlegen, dass er während des Startvorgangs starten soll. Wir wollen, dass dieser Dienst startet, wenn das normale Mehrbenutzersystem arbeitet.

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      WorkingDirectory=/home/sammy/myproject
      Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
      ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
      
      [Install]
      WantedBy=multi-user.target
      

      Damit ist unsere systemd-Dienstdatei fertiggestellt. Speichern und schließen Sie diese jetzt.

      Wir können nun den Gunicorn-Dienst starten, den wir erstellt haben, und festlegen, dass er während des Startvorgangs startet:

      • sudo systemctl start myproject
      • sudo systemctl enable myproject

      Wir wollen den Status überprüfen:

      • sudo systemctl status myproject

      Sie sollten eine Ausgabe wie diese sehen:

      Output

      ● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-05-20 14:15:18 UTC; 1s ago Main PID: 46430 (gunicorn) Tasks: 4 (limit: 2344) Memory: 51.3M CGroup: /system.slice/myproject.service ├─46430 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46449 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46450 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─46451 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

      Wenn Sie Fehler sehen, müssen Sie sie beheben, bevor Sie mit dem Tutorial fortfahren.

      Schritt 5 — Konfigurieren von Nginx für Proxy-Anfragen

      Unser Gunicorn-Anwendungsserver sollte nun ausgeführt werden und darauf warten, dass Anfragen an die Socket-Datei im Projektverzeichnis gestellt werden. Wir wollen nun konfigurieren, dass Nginx Webanfragen an diesen Socket übergibt, indem wir einige kleine Ergänzungen zu dieser Konfigurationsdatei hinzufügen.

      Beginnen Sie mit der Erstellung einer neuen Serverblockkonfigurationsdatei im Nginx-Verzeichnis sites-available. Wir wollen es myproject nennen, um die Einheitlichkeit mit diesen Leitfaden zu wahren:

      • sudo nano /etc/nginx/sites-available/myproject

      Öffnen Sie einen Serverblock und weisen Sie Nginx an, den Standardport 80 abzuhören. Wir wollen ihm außerdem mitteilen, diesen Block für Anfragen für den Domänennamen unseres Servers zu verwenden:

      /etc/nginx/sites-available/myproject

      server {
          listen 80;
          server_name your_domain www.your_domain;
      }
      

      Als Nächstes fügen wir eineSpeicherort hinzu, der mit jeder Anfrage übereinstimmt. Innerhalb dieses Blocks schließen wir die Datei proxy_params ein, die einige allgemeine Proxying-Parameter angibt, die eingestellt werden müssen. Wir übergeben dann die Anfragen an das Socket, das wir mit der Anweisung proxy_pass definiert haben.

      /etc/nginx/sites-available/myproject

      server {
          listen 80;
          server_name your_domain www.your_domain;
      
          location / {
              include proxy_params;
              proxy_pass http://unix:/home/sammy/myproject/myproject.sock;
          }
      }
      

      Speichern und schließen Sie abschließend die Datei.

      Um die Nginx-Serverblockkonfiguration zu aktivieren, die Sie gerade erstellt haben, verknüpfen Sie die Datei mit dem Verzeichnis sites-enabled:

      • sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

      Mit der Datei in diesem Verzeichnis können Sie auf Syntaxfehler testen:

      Wenn eine Rückmeldung ohne Fehleranzeige erfolgt, starten Sie den Nginx-Prozess neu, um die neue Konfiguration auszulesen:

      • sudo systemctl restart nginx

      Abschließend wollen wir noch einmal die Firewall anpassen. Wir brauchen keinen Zugriff über Port 5000, sodass wir diese Regel entfernen können. Wir können dann vollen Zugriff auf den Nginx-Server zulassen:

      • sudo ufw delete allow 5000
      • sudo ufw allow 'Nginx Full'

      Sie sollten nun in Ihrem Webbrowser zu dem Domänennamen Ihres Servers navigieren können:

      http://your_domain
      

      Sie sollten die Ausgabe Ihrer Anwendung sehen:

      Flask Beispiel-App

      Wenn Sie Fehler feststellen, versuchen Sie, Folgendes zu überprüfen:

      • sudo less /var/log/nginx/error.log: überprüft die Nginx-Fehlerprotokolle.
      • sudo less /var/log/nginx/access.log: überprüft die Nginx-Zugriffsprotokolle.
      • sudo journalctl -u nginx: überprüft die Nginx-Prozessprotokolle.
      • sudo journalctl -u myproject: überprüft die Gunicorn-Protokolle Ihrer Flask App.

      Schritt 6 – Sichern der Anwendung

      Um sicherzustellen, dass der Datenverkehr zu Ihrem Server sicher bleibt, benötigen wir ein SSL-Zertifikat für Ihre Domäne. Um ein Zertifikat zu erhalten, gibt es mehrere Möglichkeiten. U. a. können Sie ein kostenloses Zertifikat von Let’s Encrypt anfordern, ein selbst signiertes Zertifikat generieren oder ein Zertifikat von einem anderen Anbieter erwerben. Konfigurieren Sie Nginx entsprechend, um das Zertifikat zu nutzen, indem Sie den Schritten 2 bis 6 von Erstellen eines selbst signierten SSL-Zertifikats für Nginx in Ubuntu 20.04 folgen. Wir nutzen aus praktischen Gründen die erste Option.

      Installieren Sie das Nginx-Paket von Certbot mit apt:

      • sudo apt install python3-certbot-nginx

      Certbot bietet eine Vielzahl von Möglichkeiten, um SSL-Zertifikate über Plugins zu erhalten. Das Nginx-Plugin übernimmt die Neukonfiguration von Nginx und das Neuladen der Konfiguration, wenn nötig. Geben Sie Folgendes ein, um dieses Plugin zu verwenden:

      • sudo certbot --nginx -d your_domain -d www.your_domain

      Damit wird certbot mit dem --nginx Plugin ausgeführt, wobei über -d die Namen angegeben werden, für die das Zertifikat gültig sein soll.

      Wenn Sie Certbot zum ersten Mal ausführen, werden Sie aufgefordert, eine E-Mail-Adresse einzugeben und die Bedingungen des Dienstes zu akzeptieren. Danach kommuniziert Certbot mit dem Let’s Encrypt-Server und stellt dann anhand einer Prüfung sicher, dass Sie die Domäne, für die Sie das Zertifikat anfordern, auch kontrollieren.

      Nach erfolgreicher Überprüfung fragt certbot, wie Sie Ihre HTTPS-Einstellungen konfigurieren möchten.

      Output

      Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

      Treffen Sie Ihre Wahl und drücken Sie dann ENTER. Die Konfiguration wird aktualisiert und Nginx zum Abholen der neuen Einstellungen neu geladen. Certbot schließt mit einer Mitteilung, dass der Prozess erfolgreich war und wo Ihre Zertifikate gespeichert sind:

      Output

      IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain/privkey.pem Your cert will expire on 2020-08-18. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

      Wenn Sie die Nginx-Installationsanweisungen in den Voraussetzungen befolgt haben, benötigen Sie die redundante HTTP-Profil-Zulassung nicht mehr:

      • sudo ufw delete allow 'Nginx HTTP'

      Um die Konfiguration zu überprüfen, navigieren Sie mit https://: erneut in Ihre Domäne:

      https://your_domain
      

      Sie sollten wieder die Ausgabe Ihrer Anwendung sowie den Sicherheitsindikator Ihres Browsers sehen, der jetzt darauf hinweist, dass die Site sicher ist.

      Zusammenfassung

      In diesem Leitfaden haben Sie eine einfache Flask-Anwendung in einer virtuellen Python-Umgebung erstellt und gesichert. Sie haben einen WSGI Einstiegspunkt erstellt, sodass jeder beliebige WSGI-fähige Anwendungsserver sich damit verbinden kann, und dann den Gunicorn App-Server so konfiguriert, dass er die Funktion bereitstellt. Danach haben Sie eine systemd-Dienstdatei erstellt, um den Anwendungsserver automatisch beim Boot zu starten. Außerdem haben Sie einen Nginx-Serverblock erstellt, der den Web-Client-Verkehr an den Anwendungsserver übergibt und externe Anfragen und den gesicherten Datenverkehr mit Let’s Encrypt an Ihren Server weiterleitet.

      Flask ist ein sehr einfaches, aber extrem flexibles Framework, das Ihren Anwendungen Funktionalitäten bereitstellt, ohne hinsichtlich Struktur und Gestaltung zu restriktiv zu sein. Sie können den in diesem Leitfaden beschriebenen allgemeinen Stack verwenden, um die von Ihnen gestalteten Flask-Anwendungen zu bedienen.



      Source link

      Cómo presentar aplicaciones de Flask con Gunicorn y Nginx en Ubuntu 20.04


      Introducción

      A través de esta guía, creará una aplicación de Python utilizando el microframework de Flask en Ubuntu 20.04. En la mayor parte de este artículo se abordarán la configuración del servidor de la aplicación Gunicorn y la forma de iniciar la aplicación y configurar Nginx para que funcione como un proxy inverso de cliente.

      Requisitos previos

      Antes de comenzar con esta guía, deberá contar con lo siguiente:

      • Un servidor con Ubuntu 20.04 instalado y un usuario no root con privilegios sudo. Siga nuestra guía de configuración inicial para servidores a modo de orientación.
      • Nginx instalado conforme a los pasos 1 y 2 de Cómo instalar Nginx en Ubuntu 20.04.
      • Un nombre de dominio configurado para que apunte a su servidor. Puede adquirir uno en Namecheap u obtener uno de forma gratuita en Freenom. Puede aprender a apuntar dominios a DigitalOcean siguiendo la documentación sobre dominios y DNS pertinente. Asegúrese de crear los siguientes registros DNS:

        • Un registro A con your_domain orientado a la dirección IP pública de su servidor.
        • Un registro A con www.your_domain orientado a la dirección IP pública de su servidor.
      • Conocimientos sobre la especificación WSGI, que el servidor de Gunicorn usará para comunicarse con su aplicación Flask. En esta discusión se abarca WSGI de forma más deallada.

      Paso 1: Instalar los componentes desde los repositorios de Ubuntu

      Nuestro primer paso será instalar todo lo que necesitamos desde los repositorios de Ubuntu. Esto incluye pip, el administrador de paquetes de Python, que gestionará nuestros componentes de Python. También obtendremos los archivos de desarrollo de Python necesarios para crear algunos de los componentes de Gunicorn.

      Primero, actualizaremos el índice de paquetes locales e instalaremos los paquetes que nos permitirán crear nuestro entorno de Python. Entre ellos está phyton3-pip, junto con paquetes y herramientas de desarrollo adicionales que se necesitan para un entorno de programación sólido:

      • sudo apt update
      • sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

      Una vez implementados estos paquetes, crearemos un entorno virtual para nuestro proyecto.

      Paso 2: Crear un entorno virtual de Python

      A continuación, configuraremos un entorno virtual para aislar nuestra aplicación de Flask de los otros archivos de Python del sistema.

      Comience instalando el paquete python3-venv, que instalará el módulo venv:

      • sudo apt install python3-venv

      Luego, crearemos un directorio principal para nuestro proyecto de Flask. Después de crearlo, posiciónese en él:

      • mkdir ~/myproject
      • cd ~/myproject

      Cree un entorno virtual para almacenar los requisitos de Python de su proyecto de Flask escribiendo lo siguiente:

      • python3 -m venv myprojectenv

      Con esto se instalará una copia local de Python y pip en un directorio llamado myprojectenv dentro del directorio de su proyecto.

      Antes de instalar aplicaciones dentro del entorno virtual, deberá activarlo. Hágalo escribiendo lo siguiente:

      • source myprojectenv/bin/activate

      Su mensaje cambiará para indicar que ahora realiza operaciones dentro del entorno virtual. Se parecerá a esto: (myprojectenv)user@host:~/myproject$.

      Paso 3: Configurar una aplicación de Flask

      Ahora que se encuentra en su entorno virtual, podrá instalar Flask y Gunicorn y comenzar a diseñar su aplicación.

      Primero, instalaremos wheel con la instancia local de pip para asegurarnos de que nuestros paquetes se instalen aunque falten archivos de wheel:

      Nota


      Independientemente de la versión de Phyton que use, cuando se active el entorno virtual deberá utilizar el comando pip (no pip3).

      A continuación, instalaremos Flask y Gunicorn:

      • pip install gunicorn flask

      Creación de una aplicación de ejemplo

      Ahora que dispone de Flask, puede crear una aplicación sencilla. Flask es un microframework. No cuenta con muchas de las herramientas que podrían incluirse en frameworks con más características y existe sobre todo como un módulo que puede importar a sus proyectos para que pueda inicializar una aplicación web.

      Aunque la complejidad podría ser mayor, crearemos nuestra app de Flask en un único archivo, llamado myproject.py:

      • nano ~/myproject/myproject.py

      El código de aplicación residirá en este archivo. Importará Flask y creará una instancia de un objeto de Flask. Puede utilizarlo para definir las funciones que deberían ejecutarse cuando se solicita una ruta específica:

      ~/myproject/myproject.py

      from flask import Flask
      app = Flask(__name__)
      
      @app.route("/")
      def hello():
          return "<h1 style="color:blue">Hello There!</h1>"
      
      if __name__ == "__main__":
          app.run(host="0.0.0.0")
      

      Esto define básicamente el contenido que se presentará al acceder al dominio root. Guarde y cierre el archivo cuando termine.

      Si siguió la guía de configuración inicial para servidores, debería tener activado un firewall UFW. Para probar la aplicación, debe permitir el acceso al puerto 5000:

      Ahora podrá probar su aplicación de Flask escribiendo lo siguiente:

      Verá un resultado como el siguiente, en el cual se incluirá una advertencia útil que le recordará no utilizar esta configuración de servidor en la producción:

      Output

      * Serving Flask app "myproject" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

      Agregue :5000 al final de la dirección IP de su servidor en su navegador web y visítela:

      http://your_server_ip:5000
      

      Debería ver algo como esto:

      Aplicación de ejemplo de Flask

      Cuanto termine, pulse CTRL-C en la ventana de su terminal para detener el servidor de desarrollo Flask.

      Creación de un punto de entrada de WSGI

      A continuación, crearemos un archivo que servirá como punto de entrada para nuestra aplicación. Esto indicará a nuestro servidor de Gunicorn cómo interactuar con la aplicación.

      Llamemos al archivo wsgi.py:

      En él, importaremos la instancia de Flask desde nuestra aplicación y luego la ejecutaremos:

      ~/myproject/wsgi.py

      from myproject import app
      
      if __name__ == "__main__":
          app.run()
      

      Guarde y cierre el archivo cuando termine.

      Paso 4: Configurar Gunicorn

      Su aplicación quedará, así, escrita con un punto de entrada establecido. Ahora, podemos continuar con la configuración de Gunicorn.

      Antes de continuar, debe comprobar que Gunicorn pueda proveer correctamente la aplicación.

      Podemos hacerlo con solo pasarle el nombre de nuestro punto de entrada. Se construye como el nombre del módulo (menos la extensión .py) más el nombre del elemento invocable dentro de la aplicación. En nuestro caso, es wsgi:app.

      También especificaremos la interfaz y el puerto que se vinculará para que la aplicación se inicie en una interfaz disponible de forma pública:

      • cd ~/myproject
      • gunicorn --bind 0.0.0.0:5000 wsgi:app

      Debería ver un resultado como el siguiente:

      Output

      [2020-05-20 14:13:00 +0000] [46419] [INFO] Starting gunicorn 20.0.4 [2020-05-20 14:13:00 +0000] [46419] [INFO] Listening at: http://0.0.0.0:5000 (46419) [2020-05-20 14:13:00 +0000] [46419] [INFO] Using worker: sync [2020-05-20 14:13:00 +0000] [46421] [INFO] Booting worker with pid: 46421

      Visite de nuevo la dirección IP de su servidor con :5000 agregado al final en su navegador web:

      http://your_server_ip:5000
      

      Debería ver el resultado de su aplicación:

      Aplicación de ejemplo de Flask

      Cuando confirme que funciona correctamente, pulse CTRL-C en la ventana de su terminal.

      Ya completamos las tareas de nuestro entorno virtual, por lo que podemos desactivarlo:

      Ahora todos los comandos de Python usarán de nuevo el entorno de Phyton del sistema.

      A continuación, crearemos el archivo de unidad de servicio systemd. Crear un archivo de unidad systemd permitirá que el sistema init de Ubuntu inicie automáticamente Gunicorn y haga funcionar la aplicación de Flask cuando el servidor se cargue.

      Cree un archivo de unidad terminado en .service dentro del directorio /etc/systemd/system para empezar:

      • sudo nano /etc/systemd/system/myproject.service

      En su interior, empezaremos con la sección [Unit] que se usa para especificar metadatos y dependencias. Aquí agregaremos una descripción de nuestro servicio e indicaremos al sistema init que lo inicie solo tras haber alcanzado el objetivo de red:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      

      A continuación, abriremos la sección [Service]. Esto especificará el usuario y el grupo con los cuales deseamos que se ejecute el proceso. Otorgaremos la propiedad del proceso a nuestra cuenta de usuario normal, ya que tiene la propiedad de todos los archivos pertinentes. También otorgaremos la propiedad del grupo al grupo www-data para que Nginx pueda comunicarse fácilmente con los procesos de Gunicorn. No se olvide de sustituir el nombre de usuario por el suyo:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      

      A continuación, planearemos los detalles del directorio de trabajo y estableceremos el entorno variable PATH para que el sistema init sepa que los ejecutables para el proceso están ubicados dentro de nuestro entorno virtual. También especificaremos el comando para iniciar el servicio. Este comando hará lo siguiente:

      • Iniciar 3 procesos de trabajadores (debería, no obstante, ajustar esto si es necesario)
      • Crear un archivo de socket de Unix, myproject.sock, dentro del directorio de nuestro proyecto y establecer un vínculo con él. Estableceremos un valor sin máscara de 007 para que se cree el archivo de socket, se proporcione acceso al propietario y, al mismo tiempo, se restrinjan otros accesos.
      • Especifique el nombre del archivo del punto de entrada de WSGI junto con el elemento invocable de Python dentro de ese archivo (wsgi:app).

      Systemd necesita que le proporcionemos la ruta completa al ejecutable de Gunicorn, que se instala dentro de nuestro entorno virtual.

      No se olvide de sustituir el nombre del usuario y las rutas del proyecto por su propia información:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      WorkingDirectory=/home/sammy/myproject
      Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
      ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
      

      Por último, vamos a añadiremos una sección [Install]. Esto indicará a systemd a qué deberá vincular este servicio si lo habilitamos para que se cargue en el inicio. Queremos que este servicio se inicie cuando el sistema multiusuario normal esté en funcionamiento:

      /etc/systemd/system/myproject.service

      [Unit]
      Description=Gunicorn instance to serve myproject
      After=network.target
      
      [Service]
      User=sammy
      Group=www-data
      WorkingDirectory=/home/sammy/myproject
      Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
      ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
      
      [Install]
      WantedBy=multi-user.target
      

      Con eso, nuestro archivo de servicio de systemd quedará completo. Guárdelo y ciérrelo ahora.

      Ya podemos iniciar el servicio Gunicorn que creamos y activarlo para que se cargue en el inicio:

      • sudo systemctl start myproject
      • sudo systemctl enable myproject

      Comprobemos el estado:

      • sudo systemctl status myproject

      Debería ver el siguiente resultado:

      Output

      ● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-05-20 14:15:18 UTC; 1s ago Main PID: 46430 (gunicorn) Tasks: 4 (limit: 2344) Memory: 51.3M CGroup: /system.slice/myproject.service ├─46430 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46449 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46450 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─46451 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

      Si detecta errores, asegúrese de resolverlos antes de continuar con el tutorial.

      Paso 5: Configurar Nginx para solicitudes de proxy

      Ahora, nuestro servidor de aplicación Gunicorn debería estar funcionando, esperando solicitudes en el archivo de socket del directorio del proyecto. Configuraremos Nginx para que transmita las solicitudes web al socket haciendo algunas pequeñas adiciones a su archivo de configuración.

      Comencemos creando un nuevo archivo de configuración de bloque de servidor en el directorio sites-available de Nginx. Lo llamaremos myproject para que se adecue al resto de esta guía:

      • sudo nano /etc/nginx/sites-available/myproject

      Abra un bloque de servidor e indique a Nginx que escuche en el puerto predeterminado 80. También le indicaremos que utilice este bloque para solicitudes para el nombre de dominio de nuestro servidor:

      /etc/nginx/sites-available/myproject

      server {
          listen 80;
          server_name your_domain www.your_domain;
      }
      

      A continuación, agregaremos un bloque de ubicación que coincida con cada solicitud. Dentro de este bloque, incluiremos el archivo proxy_params que especifica algunos parámetros de proxy generales que deben configurarse. Luego, pasaremos las solicitudes al socket que definimos usando la directiva proxy_pass:

      /etc/nginx/sites-available/myproject

      server {
          listen 80;
          server_name your_domain www.your_domain;
      
          location / {
              include proxy_params;
              proxy_pass http://unix:/home/sammy/myproject/myproject.sock;
          }
      }
      

      Guarde y cierre el archivo al finalizar.

      Para habilitar la configuración del bloque de servidor de Nginx que acaba de crear, vincule el archivo al directorio sites-enabled​​​:

      • sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

      Con el archivo en ese directorio, puede realizar una verificación en busca de errores de sintaxis:

      Si no se indican problemas, reinicie el proceso de Nginx para que lea la nueva configuración:

      • sudo systemctl restart nginx

      Por último, ajustaremos el firewall de nuevo. Ya no necesitamos acceso a través del puerto 5000, por lo que podemos eliminar esta regla. Luego, podemos permitir el acceso completo al servidor de Nginx:

      • sudo ufw delete allow 5000
      • sudo ufw allow 'Nginx Full'

      Ahora debería poder visitar el nombre de dominio de su servidor en su navegador web:

      http://your_domain
      

      Debería ver el resultado de su aplicación:

      Aplicación de ejemplo de Flask

      Si encuentra algún error, intente verificar lo siguiente:

      • sudo less /var/log/nginx/error.log: verifica los registros de error de Nginx.
      • sudo less /var/log/nginx/access.log: verifica los registros de acceso de Nginx.
      • sudo journalctl -u nginx: verifica los registros de proceso de Nginx.
      • sudo journalctl -u myproject: verifica los registros de Gunicorn de su app de Flask.

      Paso 6: Proteger la aplicación

      Para asegurarse de que el tráfico hacia su servidor siga siendo seguro, obtendremos un certificado SSL para su dominio. Existen varias formas de hacer esto, incluyendo obtener un certificado gratuito de Let´s Encrypt, generar un certificado auto firmado, o comprar uno de otro proveedor, y configurar Nginx para usarlo siguiendo los Pasos 2 al 6 de Cómo crear un certificado SSL auto firmado para Nginx en Ubuntu 20.04. Por motivos de conveniencia, elegiremos la primera opción.

      Instale el paquete de Nginx de Certbot con apt:

      • sudo apt install python3-certbot-nginx

      Certbot ofrece varias alternativas para obtener certificados SSL a través de complementos. El complemento de Nginx se encargará de reconfigurar Nginx y volver a cargar la configuración cuando sea necesario. Para utilizar este complemento, escriba lo siguiente:

      • sudo certbot --nginx -d your_domain -d www.your_domain

      Con esto, se ejecuta certbot con el complemento --nginx usando -d para especificar los nombres para los cuales deseamos que el certificado tenga validez.

      Si es la primera vez que ejecuta certbot, se le solicitará introducir una dirección de correo electrónico y aceptar las condiciones de servicio. A continuación, certbot se comunicará con el servidor de Let’s Encrypt y, luego, realizará una comprobación para verificar que usted controle el dominio para el que solicita un certificado.

      Si la comprobación se realiza correctamente, certbot le preguntará cómo desea configurar sus ajustes de HTTPS:

      Output

      Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

      Seleccione su elección y luego ENTER. La configuración se actualizará y Nginx se volverá a cargar para aplicar los ajustes nuevos. certbot concluirá con un mensaje que le indicará que el proceso tuvo éxito e indicará la ubicación de almacenamiento de sus certificados:

      Output

      IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain/privkey.pem Your cert will expire on 2020-08-18. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

      Si siguió las instrucciones de instalación de Nginx en los requisitos previos, ya no necesitará la asignación de perfil HTTP redundante:

      • sudo ufw delete allow 'Nginx HTTP'

      Para verificar la configuración, acceda una vez más a su dominio utilizando https://:

      https://your_domain
      

      Una vez más, debería ver el resultado de su aplicación junto con el indicador de seguridad de su navegador, el cual debería indicar que el sitio está protegido.

      Conclusión

      A través de esta guía, creó y aseguró una aplicación de Flask simple dentro de un entorno virtual de Python. Creó un punto de entrada de WSGI para que cualquier servidor de aplicación con capacidad para WSGI pueda interactuar con él y configuró el servidor de aplicación de Gunicorn para proporcionar esta función. Luego, creó un archivo de servicio systemd para iniciar automáticamente el servidor de aplicación en el inicio. También creó un bloque de servidor de Nginx que transmite el tráfico de clientes web al servidor de la aplicación, y reenvía solicitudes externas, y protegió el tráfico hacia su servidor con Let’s Encrypt.

      Flask es un framework muy sencillo, pero extremadamente flexible, diseñado para proporcionar funcionalidad a sus aplicaciones sin ser demasiado restrictivo respecto de la estructura y del diseño. Puede utilizar la pila general descrita en esta guía para hacer funcionar las aplicaciones de Flask que diseñe.



      Source link