Microservices vs a Monolithic Approach
Designing & Building Microservices
The course is part of this learning path
Interested in microservices, and how they can be used for increased agility and scalability?
Microservices is an architectural style and pattern that structures an application as a collection of coherent services. Each service is highly maintainable, testable, loosely coupled, independently deployable, and precisely focused.
This course takes a hands-on look at microservices using Python, Flask, and Docker. You'll learn how Flask can be used to quickly prototype and build microservices, as well as how to use Docker to host and deploy them.
We start by looking at various problems associated with monolithic architectures and how microservices address them. We then move on to designing and building a basic shopping cart system, focusing on each of the microservices that make up the overall system.
If you have any feedback relating to this course, feel free to get in touch with us at firstname.lastname@example.org.
- Obtain a solid understanding of microservices: the benefits, the challenges, and how using microservices differs from a monolithic approach
- Learn how to design and build microservices using Python and Flask
- Learn how to deploy microservices using Docker
This course is intended for anyone who wants to build and deploy microservices using Python, Flask, and Docker.
Python 3.x programming experience is required to get the most out of this course. If you want to follow along with this course, you'll need Python 3.7, an IDE (PyCharm Community Edition - free), and Docker (Docker Desktop).
The complete source code for the project demonstrated within the course is located here:
The repository contains the following 4 projects:
Create a new user ‘cloud academy’. Create a new database ‘userdev_db’ and grant the new user privileges over it. Configure a new virtual environment for the project and install pip packages from ‘requirements.txt’. Open ‘config.py’.
Import the os package from dotenv import load_dotenv. Dotenv adds .env support to your flask project. This is the file where we pass the environment variables. Make sure that this file exists and then load the environment configuration from it.
Now define the configurations by first creating a Config class. This class contains the configurations common to all environments like development, QA and production, etc. set SECRET_KEY string variable used to encrypt the session cookies and is a requirement for using Flask-Login.
To generate the value for SECRET_KEY, open the python console. import secrets and call token_urlsafe method with bytes mentioned. And copy the generated value to SECRET_KEY. To turn off the Flask-SQLALCHEMY event system, set SQLALCHEMY_TRACK_MODIFICATIONS to false.
Create a DevelopmentConfig class inheriting ‘Config’ class. It contains configuration settings used during development. ENV variable tells the application that this is a development environment. It shows unhandled exceptions and outputs the trace-back of those exceptions. It also automatically reloads the application when it is updated.
DEBUG helps to connect Flask Debugger with the app. Configure the database Uri using PyMysql, which is a python package used to connect our app with MySQL. SQLALCHEMY_ECHO helps us with debugging, by allowing SQLAlchemy to log its SQL statements.
Create a ProductionConfig class. It contains configuration settings being used in production. For now just write down the pass keyword to skip it. In .env file and set the configurations as Development. In __init__.py, import config, os, and Flask. from flask_login import LoginManager and from flask_sqlalchemy import SQLAlchemy.
Create a global object of SQLAlchemy and LoginManager. Start create_app() function to initialize the core application. Within that create a Flask app object. Load the environment configuration and use flask app config object to load the configuration.
Initialize db and login_manager plugins with our flask app. Configure app context with a python’s with statement. Later we use ‘app_context’ to register the blueprints and finally return the app object. In run.py from application import create_app We need to disable the Flask Session cookie and replace it with a custom session method.
For further code we can copy paste the code from https://flask-login.readthedocs.io/en/latest/ in the ‘Disable Session Cookie for APIs’ section. Configure the app entry point by its host and port.
Open .flaskenv and mention run.py as a value of FLASK_APP environment variable. In models.py import db from datetime, import datetime from flask_login, import UserMixin, and lastly from passlib.hash import sha256_crypt. Create a User class, inheriting from UserMixin and db.Model.
The first Column is id with type of integer type, also set it as primary_key. The next column is the username of type string, set unique to true and nullable to false. The email Column is of type string with unique equals True and nullable equals False. The First_name Column is of type string with unique equals False and nullable equals True. Same is the case of last_name. The password column is of string type with unique and nullable equals False. The is_admin column is of type boolean and set to False by default. The Authenticated column is a boolean column and is set to false by default.
Next is the api_key string column created upon the successful user login. Next is the date_added column, records the datetime when the user is registered. date_updated is the column of type datetime, save the datetime when user information is updated. The next function is the encode_api_key which creates the encrypted api_key for the user. encode_password encrypts the password. __repr__ (reper) is the special function that returns the string representation of the user object.
Lastly, to_json() method will return the User model properties in a presentable format suitable for JSON based API response. For migration, Import the db from our application. From flask_migrate import Migrate. Create a migrate object which will allow us to run migrations.
Open the embedded terminal and run the command ‘flask db init’ to create a migration repository. Create the first migration with ‘flask db migrate’ And finally apply the migration with ‘flask db upgrade’ to create database tables.
To define blueprint, from flask import Blueprint. Create the blueprint object. Next import the routes.py. Within app context, import the blueprint and register it. In routes.py, import user_api_blueprint, import db and login_manager plugin, import the User model, from flask import make_response, request and jsonify.
From flask_login import current_user, login_user, logout_user and login_required. From passlib.hash import sha256_crypt.
As a dependency to flask_login, you need to provide a user_loader callback. This callback is used to reload the user object from the user ID stored in the session. Sometimes you want to login users without using cookies, such as using header values or an API key passed as a query argument.
In these cases, you should use the request_loader callback. First, add the route for ‘/api/users’ endpoint with the ‘GET’ method. The function name is get_users() and within that function declare an empty list name data.
Next, write down a for loop that appends all users to data. Use jsonify to format data and return it. Run the application now and test the endpoint with POSTMAN Send the GET query on http://localhost:5001/api/users, if you may get the empty JSON due to the reason that we have no users in the database.
Now write down the endpoint ‘/api/user/create’ POST request. Under post_register function, save the incoming form request data in variables of first_name, last_name, email, and username. For password, first, convert the password to hash and save in password variable.
Create the user object and associate the above variable values with the user object properties like email, first_name, last_name, password, and username. Also, set authenticated to True. db.session.add will add the user object and db.session.commit save the user object to the database.
Create a dictionary object with a message and user information, jsonify it, and return the response. Run the application and test the API endpoint with POSTMAN. Send POST requests with form fields first_name, last_name, email, username, and password to http://localhost:5001/api/user/create.
After successful addition, you get a JSON response with user information. Next, create a route to log in the user. First, get the data received in request.form and save it in a variable ‘username’. Filter the username from the user model and create a user object.
If the user exists then verify his password hash with the password hash value of the user object. Afterwards, generate the encrypted api_key for the user and save it in the database. Call login_user method to log in the user. After login, return a message “Logged in” with the user's api_key. Otherwise, if the username is not found then return the ‘not logged in’ JSON response.
Add a logout route now. In the post_logout function, we first check if the user is already authenticated then use logout_user() method to logout the user and return the response ‘You are logged out. Otherwise, if the user is not logged in, authenticated already, return the message ‘You are not logged in’.
Now it's time to test login functionality. Use postman to hit the route http://localhost:5001/api/user/login with username and password to log in. After login success, hit the logout route on http://localhost:5001/api/user/logout with the username. Hit the send button and the user will log out with the message ‘you are logged out’. If you hit the logout endpoint again then the return message says ‘you are not logged in.’
Create an endpoint to verify if a username already exists. We will use it in the user registration form to check the availability of the username. Query the User model filter by the username and save the response in the item variable. If the username exists then return True otherwise return JSON response with the message ‘Cannot find username’ with HTTP 404.
To test this endpoint, open POSTMAN and hit the endpoint to check if the username exists. The next route is for logged-in users only. In get_user() function check if the user is authenticated then return a JSON response with user information. Otherwise return a JSON response of ‘Not, logged in’.
Now run POSTMAN and log in using the ‘/api/user/login’ endpoint, then using the same request window in POSTMAN test the ‘/api/user’ endpoint. Afterwards, logout and again go to ‘/api/user’ and you will get ‘Not logged in’ JSON response.
To access host machine MySQL from docker container, use the url ‘host.docker.internal’ instead of ‘localhost’. Open Dockerfile. Add Base image as python:3.7. Copy the requirements.txt to /userapp/. Change the working directory to /userapp. Run the command ‘pip install -r requirements.txt’. Copy all project files to docker. Specify ENTRYPOINT for the docker. Define CMD to run via ENTRYPOINT. Make sure that the required docker network is available. Input the ‘docker build’ command to generate a Docker image. Create and run the docker container. Check out the running container with ‘docker ps’.
Saqib is a member of the content creation team at Cloud Academy. He has over 15 years of experience in IT as a Programmer, with experience in Linux and AWS admin. He's also had DevOps engineer experience in various startups and in the telecommunications sector. He loves developing Unity3D games and participating in Game Jams.