From Code to Cloud: Automating Django App Deployment with GitHub Actions and Azure Web Apps
Deploying applications seamlessly and securely is every developer's dream. With GitHub Actions and Azure Web Apps, that dream can become a reality. But as they say, no good deployment happens without a fair share of challenges. In this article, I’ll take you on a journey through building a CI/CD pipeline for deploying a Django-based application containerized with Docker to Azure Web Apps.
You’ll discover the workflow, the roadblocks we hit along the way, and how we tackled them to ensure smooth sailing.
The Big Picture
The goal was simple: automate the deployment of a Django application to Azure Web Apps. The app was containerized with Docker, and the pipeline was designed to:
Build the Docker image.
Push it to Docker Hub.
Deploy it to an Azure Web App.
The tools in play:
GitHub Actions: To automate the workflow.
Docker: To containerize the application.
Azure Web Apps: For hosting the app.
Azure Service Principal: For secure deployments.
Sounds straightforward, right? Well, the devil is in the details. Let’s dive in.
The Workflow in Action
Here’s the GitHub Actions workflow YAML file that powered the entire process:
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions
name: Build and deploy container app to Azure Web App - djangok8ssneh
on:
push:
branches:
- master
workflow_dispatch:
jobs:
build:
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to registry
uses: docker/login-action@v2
with:
registry: https://index.docker.io/v1/
username: ${{ secrets.AzureAppService_ContainerUsername_392caf8095fc4c21aa31160e3232eb77 }}
password: ${{ secrets.AzureAppService_ContainerPassword_89b68f62a5d849868b696eea8d35bf54 }}
- name: Build and push container image to registry
uses: docker/build-push-action@v3
with:
push: true
tags: index.docker.io/${{ secrets.AzureAppService_ContainerUsername_392caf8095fc4c21aa31160e3232eb77 }}/djangok8s:${{ github.sha }}
file: ./Dockerfile
deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
steps:
- name: Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
with:
app-name: 'djangok8ssneh'
slot-name: 'production'
publish-profile: ${{ secrets.AzureAppService_PublishProfile_aede91b4efc14e9bb4a65b3ecca7794e }}
images: 'index.docker.io/${{ secrets.AzureAppService_ContainerUsername_392caf8095fc4c21aa31160e3232eb77 }}/djangok8s:${{ github.sha }}'
The Challenges and How We Overcame Them
1. Docker Push Access Denied
When the pipeline first attempted to push the Docker image, we encountered a dreaded error:
push access denied, repository does not exist or may require authorization
Root Cause:
The repository
djangok8s
didn’t exist on Docker Hub.The PAT (Personal Access Token) lacked the required permissions.
Solution:
Created the Repository: Logged in to Docker Hub and manually created the
djangok8s
repository.Generated a New PAT: Ensured the PAT had write access and updated it in GitHub Secrets.
2. Azure Web App Deployment Fails with ENOTFOUND
The deployment step failed with:
Error: getaddrinfo ENOTFOUND <app-name>.scm.azurewebsites.net
Root Cause: The .
scm.azurewebsites.net
endpoint for Kudu (Azure's deployment engine) wasn’t resolving due to DNS propagation issues.
Solution:
Restarted the Web App:
az webapp restart --name django-todo-app --resource-group my-resource-group
Verified DNS Resolution: Ran
nslookup
to ensure the endpoint was reachable.
3. Authorization Failure for Azure Web App
Deployment failed due to incorrect Azure service principal credentials.
Solution:
Regenerated the service principal:
az ad sp create-for-rbac --name "github-action-sp" --role contributor --scopes /subscriptions/<subscription-id>
Updated the
AZURE_CLIENT_ID
,AZURE_CLIENT_SECRET
, andAZURE_TENANT_ID
secrets in GitHub.
4. Excessive Image Tags
Each pipeline run generated a new image tag based on the commit SHA, cluttering Docker Hub.
Solution:
Added a stable
latest
tag alongside the SHA tag:docker tag ${{ secrets.DOCKER_USERNAME }}/djangok8s:${{ github.sha }} ${{ secrets.DOCKER_USERNAME }}/djangok8s:latest docker push ${{ secrets.DOCKER_USERNAME }}/djangok8s:latest
Scheduled periodic cleanups for unused tags.
Best Practices from the Experience
Secure Secrets Management:
Store sensitive information like Docker credentials and Azure service principal in GitHub Secrets.
Rotate credentials regularly.
Traceable Builds:
Use commit SHAs as image tags for easy debugging and traceability.
Always tag a stable version (
latest
) for production use.
Fail Fast with Debugging:
- Add debugging steps in the workflow to identify issues early, such as checking DNS resolution or listing Docker images.
Automate Cleanup:
- Periodically clean up unused Docker images to optimize storage on Docker Hub.
Conclusion
With a well-configured GitHub Actions workflow, deploying a Django app to Azure Web Apps becomes a breeze. The challenges along the way taught valuable lessons in debugging, secret management, and workflow optimization. By leveraging best practices, you can build a robust CI/CD pipeline that ensures smooth deployments.
What challenges have you faced while automating deployments? Let’s discuss in the comments!