Deploying a Django site on AWS Elastic Beanstalk
Tags: aws, elasticbeanstalk, django, webdev
Published: 2024-04-01 03:37:46

While a Django site can be set up locally with ease, making it accessible everywhere takes a substantial amount of effort. Ranging from infrastructure management to network configurations, these steps are complex, error-prone and time-consuming for developers and businesses, and hence the showstoppers to web application deployment.

AWS Elastic Beanstalk is a managed service for deploying and scaling web applications and services. Apart from capacity provisioning and load balancing, Elastic Beanstalk also provides built-in auto-scaling, monitoring and logging capabilities, enhancing the application reliability and availability.

![AWS Elastic Beanstalk](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwsiij2e95xo04f16dhw.png)

## Prerequisites

- Git
- Python 3
- `pip`
- `virtualenv`

## Configuring the Django project for Elastic Beanstalk

1. Change into the outer `djangoproj` directory, if needed.

    `cd djangoproj`

2. Get the required packages for the project.

    `pip freeze > requirements.txt`

3. Create a directory named `.ebextensions`.

    `mkdir .ebextensions`

4. Create a configuration file named `django.config` in the `.ebextensions` directory with the following contents.

    ```yaml
    option_settings:
      aws:elasticbeanstalk:container:python:
        WSGIPath: djangoproj.wsgi:application
    ```

    > **Note**: The above `WSGIPath` value refers to the `application` callable in the `wsgi.py` of the `djangoproj` project.

The following shows an expected directory structure after the configuration. You may have more directories if you have created or installed other applications.

```
djangoproj/
    .ebextensions/
        django.config
    djangoproj/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py
    db.sqlite3
    manage.py
    requirements.txt
```

## Installing the EB CLI

> **Note**: It is recommended to complete the installation on a separate terminal so that the changes would not affect the code for the Django project.

1. Clone the setup scripts.

    `git clone https://github.com/aws/aws-elastic-beanstalk-cli-setup.git`

2. Install/Upgrade the EB CLI.

    _Unix_: `python ./aws-elastic-beanstalk-cli-setup/scripts/ebcli_installer.py`
    _Windows_: `python .\aws-elastic-beanstalk-cli-setup\scripts\ebcli_installer.py`

    If the output contains an instruction to add the EB CLI (and Python) executable files to the shell's `$PATH` variable, follow it and run the command. You also need to restart the opened terminal(s) before running any `eb` commands there.

    Optionally, you can clean up the cloned setup scripts.

## Configuring the EB CLI

Run `eb init` and follow the prompt instructions.

1. Enter the number that corresponds to the desired region.

    ```
    Select a default region
    1) us-east-1 : US East (N. Virginia)
    2) us-west-1 : US West (N. California)
    3) us-west-2 : US West (Oregon)
    4) eu-west-1 : EU (Ireland)
    5) eu-central-1 : EU (Frankfurt)
    ...
    (default is 3): 
    ```

2. Enter the access key and secret key.

    > **Note**: The credentials are used by the EB CLI for managing Elastic Beanstalk applications. If you do not have the keys, follow [Manage access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) to create one.

    ```
    You have not yet set up your credentials or your credentials are incorrect.
    You must provide your credentials.
    (aws-access-id): 
    (aws-secret-key): 
    ```

3. Enter an Elastic Beanstalk application name.

    ```
    Enter Application Name
    (default is "djangoproj"):
    ```

4. Enter **Y** to confirm Python as the platform.

    ```
    It appears you are using Python. Is this correct?
    (Y/n):
    ```

5. Enter **1** to select Python 3.11 as the platform branch.

    ```
    Select a platform branch.
    1) Python 3.11 running on 64bit Amazon Linux 2023
    2) Python 3.9 running on 64bit Amazon Linux 2023
    3) Python 3.8 running on 64bit Amazon Linux 2
    4) Python 3.7 running on 64bit Amazon Linux 2 (Deprecated)
    (default is 1): 

    ```

6. Ignore the message related to CodeCommit setup.

    ```
    Cannot setup CodeCommit because there is no Source Control setup, continuing with initialization
    ```

    > **Note**: Alternatively, the EB CLI provides integration with Git. For details, please refer to [Using the EB CLI with Git](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb3-cli-git.html).

7. Enter **Y** to set up SSH connection to any instances in the Elastic Beanstalk environment.

    ```
    Do you want to set up SSH for your instances?
    (Y/n): 
    ```

    > **Note**: This connection allows you to inspect logs or to check other metrics on the instances (to be created in the next step) for easier troubleshooting.

8. Enter a SSH key pair name and enter a passphrase.

    ```
    Type a keypair name.
    (Default is aws-eb): 
    ```

    > **Note**: The EB CLI registers this key pair with the instances and stores the private key in the local folder named `.ssh` in the user directory.

In the Elastic Beanstalk console, you should see the newly created application is listed on the [Applications](https://us-east-1.console.aws.amazon.com/elasticbeanstalk/home?region=us-east-1#/applications) page.

![Elastic Beanstalk Applications](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3q0xfph095cl4dec8a89.png)

## Deploying the Django site

1. Create an Elastic Beanstalk environment named `djangoproj-dev`.

    `eb create djangoproj-dev`

    > **Note**: The load-balanced environment is created under the application created in the previous step. The Django site will also be deployed to the this environment. The entire creation takes several minutes.

    Follow the next steps only after the environment creation completes.

2. Get the domain name of the environment.

    `eb status`

    ```
    Environment details for: djangoproj-dev
      Application name: djangoproj
      ...
      CNAME: djangoproj-dev.eba-ttkddb9r.us-east-1.elasticbeanstalk.com
      ...
    ```

    > **Note**: The domain name refers to the above `CNAME` value.

    The domain name can be also obtained from the [Environments](https://us-east-1.console.aws.amazon.com/elasticbeanstalk/home?region=us-east-1#/environments) page. ![Elastic Beanstalk Environment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oaf5pdj9q0weeelc7i7o.png)

3. Append the domain name to the `ALLOWED_HOSTS` list in the file named `settings.py` in the `ebdjango` directory, and save the file.

    ```python
    ALLOWED_HOSTS = ["djangoproj-dev.eba-ttkddb9r.us-east-1.elasticbeanstalk.com"]
    ```

4. Re-deploy the Django site.

    `eb deploy`

After the environment update completes, the Django site is deployed successfully with Elastic Beanstalk. You will see the following screen after navigating to the domain name above. Alternatively, this can be achieved by running `eb open`.

![Django Site](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6n5wof4c1wket7288m0d.png)

## Configuring the health check (Optional)

You may notice the `Health` value is `Red` from the terminal or `Severe` from the Elastic Beanstalk console. While this issue should not cause any impacts to your application, this section provides a simple fix.

1. Create a file named `middleware.py` in the inner `djangoproj` directory with the following contents.

    ```python
    from django.http import HttpResponse

    class HealthCheckMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response

        def __call__(self, request):
            if request.path == "/_health":
                return HttpResponse("OK")
            return self.get_response(request)
    ```

    > **Note**: The health check path is set to the root (i.e. `"/_health"`).

2. Prepend the health check middleware to the `MIDDLEWARE` list in the file named `settings.py` in the `ebdjango` directory, and save the file.

    ```python
    MIDDLEWARE = [
        "djangoproj.middleware.HealthCheckMiddleware",
        ...
        "django.middleware.common.CommonMiddleware",
        ...
    ]
    ```

3. Create a configuration file named `healthcheck.config` in the `.ebextensions` directory with the following contents.

    ```yaml
    option_settings:
      AWSEBV2LoadBalancerTargetGroup.aws:elasticbeanstalk:environment:process:default:
        HealthCheckPath: /_health
    ```

4. Re-deploy the Django site.

    `eb deploy`


## References

- https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-django.html
- https://github.com/aws/aws-elastic-beanstalk-cli-setup
- https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-configuration.html
- https://docs.djangoproject.com/en/5.0/ref/request-response/#django.http.HttpRequest.get_host