<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Valentyn Solonechnyi</title>
    <description>The latest articles on DEV Community by Valentyn Solonechnyi (@vlntsolo).</description>
    <link>https://dev.to/vlntsolo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F627989%2Fceac5ee2-be65-430c-b448-c703310f055c.jpg</url>
      <title>DEV Community: Valentyn Solonechnyi</title>
      <link>https://dev.to/vlntsolo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vlntsolo"/>
    <language>en</language>
    <item>
      <title>Django full CI-CD flow to AWS with GitHub Actions and S3</title>
      <dc:creator>Valentyn Solonechnyi</dc:creator>
      <pubDate>Tue, 06 Jul 2021 17:24:27 +0000</pubDate>
      <link>https://dev.to/vlntsolo/django-full-ci-cd-flow-to-aws-with-github-actions-and-s3-2enp</link>
      <guid>https://dev.to/vlntsolo/django-full-ci-cd-flow-to-aws-with-github-actions-and-s3-2enp</guid>
      <description>&lt;p&gt;This guide covers all steps needed for setting up your CI-CD workflow for Django project with Amazon Beanstalk and GitHub Actions under the hood.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Link to sample project: &lt;a href="https://github.com/vlntsolo/django-cicd" rel="noopener noreferrer"&gt;https://github.com/vlntsolo/django-cicd&lt;/a&gt;&lt;/em&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  TOC
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;AWS Beanstalk environment&lt;/li&gt;
&lt;li&gt;S3 configuration&lt;/li&gt;
&lt;li&gt;IAM user access&lt;/li&gt;
&lt;li&gt;GitHub workflow settings&lt;/li&gt;
&lt;li&gt;Preparing Django settings&lt;/li&gt;
&lt;li&gt;First run&lt;/li&gt;
&lt;li&gt;Composing postdeploy commands (hooks)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before we start, what is CI-CD flow in the first place?&lt;/p&gt;

&lt;p&gt;Despite the fact that Django code doesn't require any build step compared to compiled apps, you might find it quite frustrating to repeat a manual re-upload each time you need to deploy a new feature or a bug fix. Because of this, CI-CD (Continuous integration and Continuous delivery) workflows were invented.&lt;/p&gt;

&lt;p&gt;CI-CD workflows or pipelines allow us to safely build, test and deploy web applications in a relatively short period of time (usually, in a matter of minutes). So what about Django? If you have a python application codebase hosted on a GitHub, it would be enormously convenient to push it to the hosting environment right after you update the repository. And that is exactly what we're going to do using GitHub Actions, which is publicly available for free since late 2019.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. AWS Beanstalk environment
&lt;/h2&gt;

&lt;p&gt;Before we start configuring everything, we need to be sure we have our environment warm and ready.&lt;/p&gt;

&lt;p&gt;If you already have an AWS account, go to the Console and search for Beanstalk. If not, &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;create one here&lt;/a&gt;. You will be asked to add billing information in order to proceed.&lt;/p&gt;

&lt;p&gt;In the Beanstalk dashboard, make sure you have selected the most suitable region (where services to be deployed) in the right top corner. Check [Environments] and choose [Create a new environment]. Then, on the dialogue page, choose [Web server environment].&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2Fbeanstalk1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2Fbeanstalk1.jpg" alt="Creating beanstalk python environment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill in the details and focus on Platform section. Here we need to select Python platform (last version) and last Amazon Linux 2 available.&lt;/p&gt;

&lt;p&gt;Leave Sample application and hit &lt;strong&gt;[Create environment]&lt;/strong&gt;. In a few moments, you should be able to open your environment URL address and see a sample page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2Fsize%2Fw1000%2F2021%2F07%2Fbeanstalk-sample-page.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2Fsize%2Fw1000%2F2021%2F07%2Fbeanstalk-sample-page.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the name of the application and the environment displayed in the top left corner if you open the environment page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2Fbeanstalk-details.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2Fbeanstalk-details.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last thing to do here is to set up the database for Django.&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;[Configuration] → [Database]&lt;/strong&gt; and select preferred options:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2Fsize%2Fw1000%2F2021%2F07%2Fbeanstalk-db.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2Fsize%2Fw1000%2F2021%2F07%2Fbeanstalk-db.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It this example I picked a PostgreSQL since this my usual choice, but you can stick with MySQL or even Oracle if you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;! Alert:&lt;/strong&gt; &lt;em&gt;Database server created this way is suited for testing purposes only. It will be strictly attached to the environment. The database will be deleted if you remove or recreate the Beanstalk environment. In production, you should use RDS or Aurora, standalone servers inside your &lt;a href="https://aws.amazon.com/vpc/" rel="noopener noreferrer"&gt;VPC&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we've got a managed python environment and the database, we need to prepare a file storage for our future application builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. S3 configuration 🪣
&lt;/h2&gt;

&lt;p&gt;We need to set up a custom S3 bucket as a temporary storage for our builds before they will be pushed to the Beanstalk.&lt;/p&gt;

&lt;p&gt;Search for S3 in AWS console or switch to it from the menu. Then select &lt;strong&gt;[Create bucket]&lt;/strong&gt;. Type in some unique name and select your region.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2Fs3-new-bucket.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2Fs3-new-bucket.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leave the checkbox on &lt;em&gt;"Block all public access"&lt;/em&gt; and proceed with &lt;strong&gt;[Apply]&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The next thing we need to do is to generate programmatic access credentials which will be used by GitHub Action to deploy our Django app.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. IAM user access 🔐
&lt;/h2&gt;

&lt;p&gt;Search for IAM users in the AWS console and select &lt;strong&gt;[Add user]&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2FIAM-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2FIAM-1.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the permissions page, select [Attach existing policies directly]. Search for S3FullAccess* and select the checkbox. Repeat the same steps for &lt;em&gt;AdministratorAccess-AWSElasticBeanstalk&lt;/em&gt;* and hit &lt;strong&gt;[Add user]&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Finally, copy Access key ID and Secret from the last step. Make sure to save this info somewhere safe, since user secret will be shown only once.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2FIAM-3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2FIAM-3.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks like we're done with AWS and ready to proceed with GitHub!&lt;/p&gt;

&lt;h2&gt;
  
  
  4. GitHub workflow settings 🔡
&lt;/h2&gt;

&lt;p&gt;Go back to your Django project and create a folder called &lt;code&gt;.github&lt;/code&gt;. And one more folder inside it called workflows. GitHub Actions config should be placed inside and formatted with YAML syntax. See my example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Location: .github/workflows/custom_config.yml

name: CI-CD pipeline to AWS
env:
  EB_S3_BUCKET_NAME: "YOUR BUCKET NAME FROM Step 2"
  EB_APPLICATION_NAME: "YOUR APP NAME FROM Step 1"
  EB_ENVIRONMENT_NAME: "YOUR ENVIRONMENT NAME FROM Step 1"
  DEPLOY_PACKAGE_NAME: "django-app-${{ github.sha }}.zip"
  AWS_REGION_NAME: "YOUR AWS REGION ("us-east-1"/"eu-central-1" etc.)"

on:
  push:
    branches:
      - master #Use your own branch here (Might be staging or testing)
jobs:
  build:
    runs-on: ubuntu-latest
    steps: 
      - name: Git clone on our repo
        uses: actions/checkout@v2

      - name: Create zip deployment package
        run: zip -r ${{ env.DEPLOY_PACKAGE_NAME }} ./ -x *.git*

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with: 
          aws-access-key-id: ${{ secrets.aws_access_key_id }}
          aws-secret-access-key: ${{ secrets.aws_secret_access_key }}
          aws-region: ${{ env.AWS_REGION_NAME }}
      - name: Copying file to S3
        run: aws s3 cp ${{ env.DEPLOY_PACKAGE_NAME }} s3://${{ env.EB_S3_BUCKET_NAME }}/
      - name: Print nice message on success finish
        run: echo "CI part finished successfuly"
  deploy:
    runs-on: ubuntu-latest
    needs: [build]
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with: 
          aws-access-key-id: ${{ secrets.aws_access_key_id }}
          aws-secret-access-key: ${{ secrets.aws_secret_access_key }}
          aws-region: ${{ env.AWS_REGION_NAME }}

      - name: Create new EBL app ver
        run: |
          aws elasticbeanstalk create-application-version \
          --application-name ${{ env.EB_APPLICATION_NAME }} \
          --source-bundle S3Bucket="${{ env.EB_S3_BUCKET_NAME }}",S3Key="${{ env.DEPLOY_PACKAGE_NAME }}" \
          --version-label "${{ github.sha }}"

      - name: Deploy new app
        run: aws elasticbeanstalk update-environment --environment-name ${{ env.EB_ENVIRONMENT_NAME }} --version-label "${{ github.sha }}"
      - name: Print nice message on success finish
        run: echo "CD part finished successfuly"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go through the surface of the config blocks and check their purpose. More details can be found here.&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;[env]&lt;/strong&gt; block serves as a list of shortcuts for variables inside the config. We define our non-sensitive data here.&lt;/p&gt;

&lt;p&gt;2) &lt;strong&gt;[jobs]&lt;/strong&gt; block defines what actually will be done.&lt;/p&gt;

&lt;p&gt;3) &lt;strong&gt;[build]&lt;/strong&gt; block is just a job name and includes a list of "steps" to perform in our building process. For a pure Django app, this means only zipping the working directory and pushing it to the S3 bucket.&lt;/p&gt;

&lt;p&gt;4) &lt;strong&gt;[steps]&lt;/strong&gt; block lists names and actual commands to execute on the virtual machine.&lt;/p&gt;

&lt;p&gt;You might easily add another job name called testing and run ./manage.py test step within it.&lt;/p&gt;

&lt;p&gt;5) &lt;strong&gt;[needs]&lt;/strong&gt; block is a prerequisite for further execution. In our case, we're making sure that our file transfer was completed successfully before we send commands to the Beanstalk.&lt;/p&gt;

&lt;p&gt;6) &lt;strong&gt;[on]&lt;/strong&gt; block specifies the trigger event, like our "push", as well as the target branch.&lt;/p&gt;

&lt;p&gt;If you come up with several Beanstalk environments (QA, testing, staging, production etc.), you'd need to place new config.yml files for them and change env vars and branch accordingly.&lt;/p&gt;

&lt;p&gt;You might also notice that we didn't define &lt;code&gt;secrets.aws_access_key_id&lt;/code&gt; or &lt;code&gt;secrets.aws_secret_access_key&lt;/code&gt; yet. 🧊 Let's fix that!&lt;/p&gt;

&lt;p&gt;Open your GitHub repo or create one for your project and go to &lt;strong&gt;[Settings]&lt;/strong&gt; → &lt;strong&gt;[Secrets]&lt;/strong&gt; → &lt;strong&gt;[New repository secret]&lt;/strong&gt;. Paste your IAM user key and secret there like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2Fsecrets-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2F2021%2F07%2Fsecrets-1.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat the same step for &lt;code&gt;aws_secret_access_key and&lt;/code&gt; it's value. That's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Preparing Django settings 📦
&lt;/h2&gt;

&lt;p&gt;Remember, we've connected a Relational database to our Beanstalk environment at Step 1? But where are the connection parameters and db user credentials?&lt;/p&gt;

&lt;p&gt;With integrated database, Beanstalk exposes env variables, which can be used right away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# main_django_app settings.py
import os

ALLOWED_HOSTS = ['YOUR_ENVIRONMENT_HOST']

DATABASES = {
'default': {
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'NAME': os.environ['RDS_DB_NAME'],
    'USER': os.environ['RDS_USERNAME'],
    'PASSWORD': os.environ['RDS_PASSWORD'],
    'HOST': os.environ['RDS_HOSTNAME'],
    'PORT': os.environ['RDS_PORT'],
    }
}

# rest of the settings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to add your environment URL and main domain to allowed hosts list.&lt;/p&gt;

&lt;p&gt;At this point, we need to make sure that Beanstalk python platform will be ready to establish a connection with PostgreSQL. Let's tell it to install some extras for us.&lt;/p&gt;

&lt;p&gt;Create a folder in your &lt;a href="https://github.com/vlntsolo/django-cicd" rel="noopener noreferrer"&gt;project root&lt;/a&gt; called &lt;code&gt;.ebextensions&lt;/code&gt; and place there a file &lt;code&gt;01_packages.config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;packages: 
  yum: 
    amazon-linux-extras: []

commands:
  01_postgres_activate: 
    command: sudo amazon-linux-extras enable postgresql10
  02_postgres_install: 
    command: sudo yum install -y postgresql-devel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to add Django config to inform Beanstalk's WSGI server about our main app and settings. Create another file inside &lt;code&gt;.ebextensions&lt;/code&gt; folder called &lt;code&gt;django.config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;option_settings:
  aws:elasticbeanstalk:container:python:
    WSGIPath: main_django_app.wsgi:application
  aws:elasticbeanstalk:application:environment:
    DJANGO_SETTINGS_MODULE: main_django_app.settings
    "PYTHONPATH": "/var/app/current:$PYTHONPATH"
  aws:elasticbeanstalk:environment:proxy:staticfiles:
    /static: static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change &lt;em&gt;main_django_app&lt;/em&gt; to your Django application name.&lt;/p&gt;

&lt;p&gt;Last row &lt;code&gt;aws:elasticbeanstalk:environment:proxy:staticfiles0:&lt;/code&gt; tells which folder to serve through the reverse proxy and which relative URL to use.&lt;/p&gt;

&lt;p&gt;Finally, freeze your local python environment with &lt;code&gt;pip freeze &amp;gt; requirements.txt&lt;/code&gt; (it will be the primary source for Beanstalk to configure EC2 environment).&lt;/p&gt;

&lt;h2&gt;
  
  
  6. First run 🔥
&lt;/h2&gt;

&lt;p&gt;Push any changes to your specified branch and visit Actions tab. If your actions completed successfully, you will see the green mark. If something went wrong, open the logs and resolve the errors inside.&lt;/p&gt;

&lt;p&gt;Got errors after successful GitHub Actions run? Visit Elastic Beanstalk environment and check the Logs.&lt;/p&gt;

&lt;p&gt;Hopefully, you'll see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2Fsize%2Fw1000%2F2021%2F07%2Fdjango-demopage.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.valentine.click%2Fcontent%2Fimages%2Fsize%2Fw1000%2F2021%2F07%2Fdjango-demopage.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Composing postdeploy commands (hooks) 🔌
&lt;/h2&gt;

&lt;p&gt;There's still few commands that should be executed after deploy.&lt;/p&gt;

&lt;p&gt;1) We need to migrate our changes to the Beanstalk database (migration files created by makemigrations command should be a part of your codebase).&lt;/p&gt;

&lt;p&gt;2) We need to collect static files (no need to keep your static files in a codebase).&lt;/p&gt;

&lt;p&gt;This problem could be solved with Beanstalk postdeploy hooks. File names could be totally custom, but file extension is important. All .sh files will be executed in alphanumerical order.&lt;/p&gt;

&lt;p&gt;In your project root, create a folder &lt;code&gt;.platform&lt;/code&gt; with the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.platform/
└───hooks/
    └───postdeploy/
        -01_django.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

source /var/app/venv/*/bin/activate &amp;amp;&amp;amp; {

# collecting static files
python manage.py collectstatic --noinput;
# log which migrations have already been applied
python manage.py showmigrations;
# migrate the rest
python manage.py migrate --noinput;
# another command to create a superuser (write your own)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You've got the idea! Now, you can figure out how to create the superuser account.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚠️ Important notes:
&lt;/h3&gt;

&lt;p&gt;– &lt;strong&gt;Review IAM user access policy&lt;/strong&gt;. For the tutorial purpose, I've granted full access to S3 and Beanstalk. Make sure that you grant only required permissions for GitHub Actions client in order to prevent unpleasant consequences in case of secret key leakage.&lt;/p&gt;

&lt;p&gt;– &lt;strong&gt;Add lifecycle rules&lt;/strong&gt; to S3 bucket and Application versions storage. By default, S3 doesn't delete application versions or any other files from the buckets. Use lifecycle rules in both S3 and Beanstalk to keep only needed app versions.&lt;/p&gt;

&lt;p&gt;– AWS Beanstalk health checking system might not work properly ( severe health status) because of Django ALLOWED_HOSTS restriction in headers. I don't know the right way of handling this issue (you can either ignore status warnings or set &lt;code&gt;ALLOWED_HOSTS = ['*']&lt;/code&gt; at your own risk).&lt;/p&gt;

&lt;p&gt;– If you're going to use PostgreSQL, don't forget to install the package with &lt;code&gt;pip install psycopg2-binary&lt;/code&gt; and freeze the requirements.txt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful links:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://valentine.click/blog/how-to-split-django-settings-for-different-environments" rel="noopener noreferrer"&gt;How to split Django settings for different environments&lt;/a&gt;&lt;br&gt;
&lt;a href="https://valentine.click/blog/django-q-and-beanstalk" rel="noopener noreferrer"&gt;How to configure Django Q with Supervisor on AL2 Beanstalk&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-django.html" rel="noopener noreferrer"&gt;Deploying a Django application to Elastic Beanstalk&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions" rel="noopener noreferrer"&gt;Workflow syntax for GitHub Actions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope, you've enjoyed following this guide. Stay tuned and next time I might cover how to set up a full-stack Django-based project with truly seamless dev experience.&lt;/p&gt;

&lt;p&gt;This article was originally published &lt;a href="https://valentine.click/blog/django-full-ci-cd-flow-to-aws-with-github-actions" rel="noopener noreferrer"&gt;in my blog post&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>django</category>
      <category>aws</category>
      <category>devops</category>
      <category>python</category>
    </item>
    <item>
      <title>How to split Django settings for different environments?</title>
      <dc:creator>Valentyn Solonechnyi</dc:creator>
      <pubDate>Thu, 27 May 2021 21:17:17 +0000</pubDate>
      <link>https://dev.to/vlntsolo/how-to-split-django-settings-for-different-environments-18ad</link>
      <guid>https://dev.to/vlntsolo/how-to-split-django-settings-for-different-environments-18ad</guid>
      <description>&lt;p&gt;So it's time to put your code into production. That means you have to connect some third party stuff, probably some external database or SMTP server. At the same time it would be nice to keep an option to modify and test your codebase with your local database and double-check on some other staging or QA server. But how to achieve this with default &lt;code&gt;settings.py&lt;/code&gt; file?&lt;/p&gt;

&lt;p&gt;Searching for the solution I found several approaches of splitting the Django settings, but one that worked for me appeared to be a mixture. Here's how I've got it to work with AWS EC2, but it should work with any python environment where you can set your own env variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Short recipe
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Django settings as a module.&lt;/li&gt;
&lt;li&gt;Key environmental variables for production and/or staging servers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;__init__.py&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Directory view
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your_main_django_app
-settings
    --__init__.py
    --base.py
    --local.py
    --production.py
    --staging.py

-__init__.py
-asgi.py
-urls.py
-views.py
-wsgi.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step-by-step guide
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Set environmental variable with unique key or use one.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;I didn't set any env vars during development. But once I deployed my app to the staging server, it got a new env variable called &lt;code&gt;'RDS_DB_NAME'&lt;/code&gt;. I used it to differentiate between my local server and staging/production, but you can use your own unique key to perform the main check in the &lt;code&gt;__init__.py&lt;/code&gt; file (i.e.&lt;code&gt;'ENV_NAME' : 'Staging'&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Prepare base.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from pathlib import Path
import os

# The most important thing is to be build relative path
BASE_DIR = Path(__file__).resolve().parent.parent.parent

INSTALLED_APPS = []

#...other environmentally independent settings 
# (TEMPLATES, WSGI_APPLICATION, TIME_ZONE, STATIC_ROOT etc.)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Add environment-specific settings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since we have base.py, we can put the rest of the settings into their respective files: &lt;code&gt;local.py&lt;/code&gt;, &lt;code&gt;staging.py&lt;/code&gt;, &lt;code&gt;production.py&lt;/code&gt; (you've got the idea).&lt;/p&gt;

&lt;p&gt;By the way, don't forget to place your sensitive data (secrets, api keys etc.) into env variables instead of hard-coding them into your &lt;code&gt;production.py&lt;/code&gt;. Here's the example of retrieving them in your settings:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;'SECRET': os.environ.get('SECRET_KEY')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Tweak the import in &lt;strong&gt;init&lt;/strong&gt;.py with &lt;em&gt;if&lt;/em&gt; statement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Having all the settings in a separate module allows us not to specify local config directly each time we run &lt;code&gt;manage.py runserver&lt;/code&gt;, because Django loads settings.py by default. And our settings module/folder is simply an equivalent to it.&lt;/p&gt;

&lt;p&gt;Here's the &lt;strong&gt;&lt;strong&gt;init&lt;/strong&gt;.py&lt;/strong&gt; from the settings folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from .base import *
import os


if os.environ.get("ENV_NAME") == 'Production':
    from .production import *
elif os.environ.get("ENV_NAME") == 'Staging':
    from .staging import *
else:
    from .local import *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, whenever your Django app is initialized on the server, your environment variable called &lt;code&gt;"ENV_NAME"&lt;/code&gt; will determine the proper settings file. And if it's just your local dev server, no env variables will be accessible and your app will fall back to &lt;code&gt;local.py&lt;/code&gt; settings.&lt;/p&gt;

&lt;p&gt;*If you happened to use AWS Beanstalk for deploying python app, you might want to simplify your &lt;code&gt;__init__.py&lt;/code&gt; to something shorter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from .base import *
import os


if os.environ.get("RDS_DB_NAME") is None:
    from .local import *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we're only telling the server to check one env variable and fallback to local.py. What about the production configuration then? Well, Beanstalk allows you to specify preferred settings in an environmental variable called &lt;code&gt;DJANGO_SETTINGS_MODULE&lt;/code&gt;. Thus, you can just tune your Beanstalk app to load respective configuration like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DJANGO_SETTINGS_MODULE: "your_main_django_app.settings.production"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And that's all.&lt;/p&gt;

&lt;p&gt;Everyone gets its own settings and your &lt;code&gt;manage.py runserver&lt;/code&gt; works as before. Happy production.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was originally &lt;a href="https://valentine.click/blog/how-to-split-django-settings-for-different-environments"&gt;published in my blog post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>devops</category>
      <category>python</category>
    </item>
    <item>
      <title>How to configure Django Q with Supervisor on AL2 Beanstalk</title>
      <dc:creator>Valentyn Solonechnyi</dc:creator>
      <pubDate>Sat, 08 May 2021 20:20:50 +0000</pubDate>
      <link>https://dev.to/vlntsolo/how-to-configure-django-q-with-supervisor-on-al2-beanstalk-4gh1</link>
      <guid>https://dev.to/vlntsolo/how-to-configure-django-q-with-supervisor-on-al2-beanstalk-4gh1</guid>
      <description>&lt;p&gt;Working on the fintech project backend I decided to use Django Q package for scheduling and executing different tasks in the background. Our CD-CI flow was based on Beanstalk new Amazon Linux 2 (AL2) machine. The trick is that Beanstalk configuration files have to include all instructions to run your code from scratch on a brand new virtual machine. Why? That's how the AWS Beanstalk works: it manages the server for you and requires precise instructions on what to install/configure in case it has to run an update or recreate/scale the virtual servers from scratch. I hoped to google some decent bash script for AL2, but there was none. So I had to dive into DevOps thing again and write my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Postdeploy hooks to get Django-Q cluster running on Beanstalk's AL2
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Note #1&lt;/strong&gt;: &lt;em&gt;This guide is for Amazon Linux 2 EC2 instances which &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/04/aws-elastic-beanstalk-announces-general-availability-of-amazon-linux-2-based-docker-corretto-and-python-platforms/"&gt;were rolled&lt;/a&gt; out for Beanstalk python platform in April 2020. If you're looking for a way to set up Django Q cluster on an older version (Amazon Linux), here is the &lt;a href="https://gist.github.com/codeSamuraii/0e11ce6d585b3290b15a9ad163b9aa06"&gt;working gist&lt;/a&gt; for it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note #2&lt;/strong&gt;: &lt;em&gt;If you're not familiar with Django Q cluster (celery based task manager) I strongly recommend &lt;a href="https://django-q.readthedocs.io/en/latest/"&gt;reading the docs&lt;/a&gt;. Besides Beanstalk environment Django Q requires some message broker to schedule the tasks, so you'll need to set up a separate Redis server or Amazon SQS and connect it to your security group&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I assume you have your project configured to be deployed to Beanstalk. AWS Beanstalk allows you to place a custom number of bash scripts with .sh file extension into the following folder &lt;code&gt;.platform/hooks/postdeploy&lt;/code&gt;  in your root project directory. For convenience, I split it into 4 steps. Here's the &lt;a href="https://gist.github.com/vlntsolo/34261e6026ac0e303c40c6ece9961182"&gt;full gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/vlntsolo/34261e6026ac0e303c40c6ece9961182#file-01_set_env-sh"&gt;01_set_env.sh&lt;/a&gt; Grab the environmental variables and enable amazon-linux-extras&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;AL2 provides access to environmental variables we set in the Beanstalk dashboard through a specific file. Here we're grabbing them for further use. AL2 doesn't include supervisor. That's why we're also telling Beanstalk to enable extra extensions for us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/vlntsolo/34261e6026ac0e303c40c6ece9961182#file-02_django_migrate.sh"&gt;02_django_migrate.sh&lt;/a&gt; Just Django routine commands&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This doesn't have much to do with Django Q cluster, but as part of Django app deployment process these steps are necessary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/vlntsolo/34261e6026ac0e303c40c6ece9961182#file-03_django_q.sh"&gt;03_django_q.sh&lt;/a&gt; Django Q supervisor configuration&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now we got Django up and running, but we still need to launch Django Q cluster and make sure it will be alive even if the process crashes. Here we're preparing a configuration block which tells the supervisor (Linux process control system) where to find and how to launch Django Q cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/vlntsolo/34261e6026ac0e303c40c6ece9961182#file-04_supervisor_init.sh"&gt;04_supervisor_init.sh&lt;/a&gt; Supervisor daemon configuration&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The last but not least: we need to take care about the supervisor itself and add config for its daemon. Thus, we make sure that supervisor will be loaded after EC2 instance reboot. This is the last bash script file, but pay attention to this line: &lt;code&gt;sudo cp /var/app/current/supervisord.sample /etc/init.d/supervisord&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/var/app/current/&lt;/code&gt; - that's the default path to your project. In my root directory I placed a &lt;a href="https://gist.github.com/vlntsolo/34261e6026ac0e303c40c6ece9961182#file-supervisord-sample"&gt;supervisord sample config file&lt;/a&gt; (by Dan MacKinlay). You can place it wherever you want, just don't forget to change the path in the bash script to it.&lt;/p&gt;

&lt;p&gt;That's all, you're good to go. This guide doesn't cover full CI-CD flow with Django app and AWS, but I might be covering it in the next article. Stay tuned!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was originally &lt;a href="https://valentine.click/blog/django-q-and-beanstalk"&gt;published in my blog post&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>devops</category>
      <category>aws</category>
      <category>beanstalk</category>
    </item>
  </channel>
</rss>
