Prework Assignment - Flask App with Redis, MongoDB, and Nginx
Overview This project is a simple Flask application integrated with Redis and MongoDB, deployed using Docker Swarm. It features an Nginx reverse proxy and security measures to handle rate limiting and secure sensitive data.
Setup
creating the virtual env:
python3 -m venv venv
activating the virtual env
source venv/bin/activate
installing dependencies
pip install -r requirements.txt
running the tests: REDIS_ENDPOINT=localhost pytest
The deployment strategy leverages Docker Swarm for scalable and resilient deployments. While the current CI/CD pipeline automates testing, version control, and intent checks, it does not yet automatically deploy updates.
Docker Swarm is used to manage and deploy multiple services as a single stack, ensuring scalability and fault tolerance. This setup includes:
- Flask App (web service): Running the Flask application.
- Redis: Used for caching and quick data access.
- Nginx: Acting as a reverse proxy with load balancing.
- Initialize Docker Swarm:
docker swarm init
- Build the Docker Image:
This command builds the Docker image for the Flask application and tags it as
docker build -t prework-flask-app .
prework-flask-app
. - Deploy the Stack:
Deploys the services defined in
docker stack deploy -c docker-compose.yml prework_stack
docker-compose.yml
to Docker Swarm as a stack namedprework_stack
. - View Service Logs:
Streams real-time logs from the
docker service logs prework_stack_web -f
prework_stack_web
service. - Inspect Service Details:
Provides detailed information about the
docker service inspect --pretty prework_stack_web
prework_stack_web
service, including its replicas, networks, and current status.
Docker Swarm is configured for rolling updates, which ensures minimal downtime during application updates. The stack deploys multiple replicas of services, and updates are handled by deploying new instances before terminating the old ones, ensuring continuous availability.
This application is secured by routing all traffic through Nginx, which acts as a reverse proxy. The application's internal ports are not exposed directly, ensuring that it can only be accessed via Nginx on port 80.
- Rate Limiting: A rate limit is set at 20 requests per second per IP address to mitigate denial-of-service (DoS) attacks. If requests exceed this rate, they are delayed or dropped.
- Proxy Setup: Nginx forwards requests to the Flask app running on port 8000 within the Docker network, preserving important headers such as
X-Real-IP
andX-Forwarded-For
.
This setup enhances security by preventing direct access to the Flask application, ensuring that all traffic is funneled through Nginx, where it can be monitored and controlled.
Environment variables are managed through a .env
file, ensuring that sensitive information like database credentials, API keys, and other secrets are not hardcoded into the application. This file is not included in version control and is securely managed. For even greater security, sensitive information is managed through Docker Swarm secrets, which are securely injected into the running containers without being exposed in the code or Docker images.
The project's dependencies are listed in the requirements.txt
file, which has been frozen using pip freeze
. This ensures that all the libraries and their specific versions are consistent across different environments, reducing the chances of compatibility issues.
Automated tests run as part of the CI/CD pipeline to catch issues early. This project includes several checks to ensure code quality, security, and correctness.
- Action: Runs
flake8
to lint the codebase. - Outcome: Identifies and reports any style violations, ensuring that the code adheres to PEP 8 standards.
- Action: Runs
black --check
to verify code formatting. - Outcome: Detects and reports any formatting issues, ensuring that the code is consistently formatted.
- Action: Runs
bandit -r
with a focus on medium and higher severity issues to scan the codebase for security issues. - Outcome: Reports potential security vulnerabilities
- Action: Runs
mypy
to check for type errors. - Outcome: Identifies type-related issues, ensuring that the code adheres to its type annotations.
Represents the production-ready code. Only stable releases are merged here.
Active development happens here. Feature and bug branches are merged into dev after testing.
These branches are created from dev for specific features or bug fixes. They are merged back into dev once complete and tested.
- Development Branch (Dev):
- The version includes a
-dev
suffix and is automatically incremented with each push to thedev
branch. - This allows for continuous integration and testing during development without affecting the production version.
- The version includes a
- Master Branch:
- When merging from
dev
tomaster
, the-dev
suffix is stripped, and the version is incremented for production releases. - This ensures that production versions are clean and ready for deployment.
- When merging from
The application currently uses Redis for caching and MongoDB for data persistence. Redis is backed by Docker volumes to ensure data persists even after container restarts, providing quick, in-memory storage for the application's state.
- Redis: Extend its use for session management, token handling, and caching user-specific data to improve application performance.
- MongoDB: Implement MongoDB as the primary database for storing user data, application data, and other persistent records. This would be particularly useful for structured data that requires long-term storage and retrieval.
- Hybrid Approach: Leverage Redis for fast access to temporary data and MongoDB for long-term data storage. Redis can handle frequent, small transactions (like session tokens), while MongoDB can manage more complex datasets.
The application is designed with fault tolerance in mind by deploying multiple replicas via Docker Swarm. However, further resilience could be achieved by implementing automatic failover strategies, where a backup service takes over if the primary service fails. Additionally, using distributed data storage solutions like MongoDB with replication could further safeguard data integrity.
- The rate limiting configuration in Nginx is basic and may need tuning for different traffic patterns.
- MongoDB certificate handling is basic; further security enhancements could be made.
During development, debugging is made easier by:
- Exposing Ports: The Docker Compose file has commented out port mappings, which can be uncommented for easier local development.
- Debug Mode: The Flask app's
debug
mode is enabled during development, providing detailed error messages and an interactive debugger.