How to Build and Deploy Microservices With Python
How to Build and Deploy Microservices With Python
Microservices are small, independent applications that perform specific tasks, allowing for
flexible deployment and scaling. This modular approach to software design loosens the
coupling between components, enhancing flexibility and manageability throughout
development.
The article provides an overview of microservices, their functionality, and their creation using
Python. It also demonstrates deploying your microservices to Kinsta using a Dockerfile.
Unlike monolithic systems that scale entirely based on demand, microservices empower
scaling individual high-traffic components. This architecture facilitates easy fault management
and feature updates, countering monolithic limitations.
Flexibility and scalability — Decoupling individual services allows you to increase the
number of nodes running an instance of a particular service experiencing high traffic.
Code modularity — Each service can use a discrete technology stack, meaning you
can choose the best development tools for each one.
For this example, suppose you want to build an ecommerce web application. The website has
several components, including the product catalog, a list of orders, and a payment processing
system and logs, each of which you need to implement as an independent service.
Additionally, you need to establish a service-to-service communication method to transfer
data between these services, such as HTTP, efficiently.
Let’s build a microservice using Python to manage a product catalog. The microservice will
fetch product data from a specified source and return the data in JSON format.
Prerequisites
1. To get started, create a folder for your project called flask-microservice and current
directory into the project’s directory.
2. Next, run python3 --version to confirm that Python is installed on your computer
correctly.
3. Install virtualenv to create an isolated development environment for the Flask
microservice by running the command below:
virtualenv venv
5. Finally, activate the virtual environment using one of the following commands based on
your computer’s operating system:
# Windows:
.\venv\Scripts\activate
# Unix or macOS:
source venv/bin/activate
In the root directory, create a requirements.txt file and add these dependencies.
flask
requests
Next, create a new folder in the root directory and name it services. Inside this folder, create
a new file, products.py, and add the code below to set up a Flask server.
import requests
import os
from flask import Flask, jsonify
app = Flask(__name__)
port = int(os.environ.get('PORT', 5000))
@app.route("/")
def home():
return "Hello, this is a Flask Microservice"
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=port)
In the code above, a basic Flask server is set up. It initializes a Flask app, defines a single
route for the root URL ("/"), and when accessed, displays the message "Hello, this is
a Flask Microservice"
. The server runs on a specified port, obtained from an environment variable or defaults to
port 5000, and starts in debugging mode, making it ready to handle incoming requests.
With the server configured, create an API endpoint for a microservice that fetches product
data from a publicly available API. Add this code to the products.py file:
BASE_URL = "https://dummyjson.com"
@app.route('/products', methods=['GET'])
def get_products():
response = requests.get(f"{BASE_URL}/products")
if response.status_code != 200:
return jsonify({'error': response.json()['message']}), response.st
products = []
for product in response.json()['products']:
product_data = {
'id': product['id'],
'title': product['title'],
'brand': product['brand'],
'price': product['price'],
'description': product['description']
}
products.append(product_data)
return jsonify({'data': products}), 200 if products else 204
The code above creates an /products endpoint in the Flask server. When accessed via a
GET request, it fetches product data from a dummy API. If successful, it processes the
retrieved data, extracts product details, and returns the information in a JSON format. In case
of errors or no available data, it responds with an appropriate error message and status code.
4. Test the Microservice
At this point, you have successfully set up a simple microservice. To start the service, spin up
the development server, which will start running at http://localhost:5000.
Then you can make a GET request to the /products endpoint using the Postman client, you
should see a similar response to the screenshot below.
JWT is a widely used open standard that provides a safe and efficient way of transmitting
authentication information between clients and servers. They are compact, encrypted, and
digitally signed tokens you pass alongside HTTP requests. When you include a JWT with
each request, the server can quickly verify a user’s identity and permissions.
1. Add Python’s pyjwt package to your requirements.txt file and reinstall the
dependencies using pip install -r requirements.txt.
2. Because the service doesn’t have a dedicated database, create a users.json file in the
root directory of your project to store a list of authorized users. Paste the code below in
the file:
[
{
"id": 1,
"username": "admin",
"password": "admin"
}
]
Info
3. Then, in your services/products.py file, replace the import statements with the
following:
import requests
from flask import Flask, jsonify, request, make_response
import jwt
from functools import wraps
import json
import os
from jwt.exceptions import DecodeError
You are importing these modules to handle HTTP requests, create a Flask app, manage
JSON data, implement JWT-based authentication, and handle exceptions, enabling a
wide range of features within the Flask server.
4. Add the following code below the Flask app instance creation to generate a secret key
that will be used to sign the JWT tokens.
app.config['SECRET_KEY'] = os.urandom(24)
5. To verify JWTs, create a decorator function and add the following code above the API
routes in your Flask server code. This decorator function will authenticate and validate
users before they access protected routes.
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.cookies.get('token')
if not token:
return jsonify({'error': 'Authorization token is missing'}
try:
data = jwt.decode(token, app.config['SECRET_KEY'], algorit
current_user_id = data['user_id']
except DecodeError:
return jsonify({'error': 'Authorization token is invalid'}
return f(current_user_id, *args, **kwargs)
return decorated
This decorator function checks incoming HTTP requests for a JWT authorization token,
which should be in the request headers or cookies. If the token is missing or invalid, the
decorator sends an unauthorized status code message as a response.
Conversely, if a valid token is present, the decorator extracts the user ID after decoding
it. This process safeguards the protected API endpoints by granting access only to
authorized users.
6. Define an API endpoint for user authentication using the code below.
To authenticate and authorize users, the /auth API endpoint checks the credentials in
the POST request’s JSON payload against the list of allowed users. If the credentials are
valid, it generates a JWT token using the user’s ID and the app’s secret key and sets
the token as a cookie in the response. Users can now use this token to make
subsequent API requests.
After creating the /auth endpoint, use Postman to send an HTTP POST request to
http://localhost:5000/auth. In the request body, include the credentials of the
mock admin user you created.
If the request succeeds, the API will generate a JWT token, set it in Postman’s cookies,
and send an authenticated success response.
7. Finally, update the GET API endpoint to check for and verify the JWT token using the
code below:
@app.route('/products', methods=['GET'])
@token_required
def get_products(current_user_id):
headers = {'Authorization': f'Bearer {request.cookies.get("token")
response = requests.get(f"{BASE_URL}/products", headers=headers)
if response.status_code != 200:
return jsonify({'error': response.json()['message']}), respons
products = []
for product in response.json()['products']:
product_data = {
'id': product['id'],
'title': product['title'],
'brand': product['brand'],
'price': product['price'],
'description': product['description']
}
products.append(product_data)
return jsonify({'data': products}), 200 if products else 204
To containerize the microservice, you must create a Docker image from a Dockerfile that
specifies the dependencies required to run the application in a container. Create a Dockerfile
in the root directory of your project and add these instructions:
FROM python:3.9-alpine
WORKDIR /app
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "./services/products.py"]
FROM — Instructs Docker which base image to use. A base image is a pre-built instance
containing the software and dependencies to run the Flask application in a container.
WORKDIR — Sets the specified directory within the container as the working directory.
COPY requirements.txt ./ — Copies the dependencies in the requirements.txt
file into the container’s requirements.txt file.
RUN — Runs the specified command to install the dependencies the image requires.
COPY . . — Copies all the files from the project’s root directory to the working
directory inside the container.
EXPOSE — Specifies the port where the container will listen for requests. However,
Docker doesn’t publish the port to the host machine.
CMD — Specifies the default command to execute when the container starts.
Next, add a .dockerignore file in the root directory of your project to specify the files that the
Docker image should exclude. Limiting the image contents will reduce its final size and
associated build time.
/venv
/services/__pycache__/
.gitignore
Finally, once the image builds, you can run the microservice in a Docker container using the
following command:
This command will start a Docker container running the microservice and expose port 5000
on the container to port 5000 on the host machine, allowing you to make HTTP requests from
your web browser or Postman using the URL http://localhost:5000.
Follow these steps to configure your Flask microservice for deployment with MyKinsta:
1. First, create a new Procfile in the root directory and add the code below. It specifies the
command to run the Flask microservice on Kinsta’s Gunicorn WSGI HTTP Server for
Python applications.
web: gunicorn services.wsgi
gunicorn==20.1.*
3. Next, create a new services/wsgi.py file and add the code below.
4. Create a .gitignore file in the project root folder and add the following:
services/__pycache__
venv
5. Finally, create a new repository on GitHub and push your project files.
Once your repo is ready, follow these steps to deploy the Flask microservice to Kinsta:
1. Log in or create an account to view your MyKinsta dashboard.
2. Authorize Kinsta with your Git provider (Bitbucket, GitHub, or GitLab).
3. Click Applications on the left sidebar, then click Add application.
4. On the dashboard, click on Add Service, and select Application.
5. Select the repository and the branch you wish to deploy from.
6. Assign a unique name to your app and choose a data center location.
7. To configure the Build environment, select the option to use a Dockerfile to build the
container image.
8. Provide the path to your Dockerfile and the context.
9. Finally, review other information and click Create application.
Once the deployment process is successful, click the provided URL to test the microservice
by making HTTP requests in Postman. Make a GET request to the root endpoint.
Lastly, after successfully getting authenticated, make a GET request to the /products
endpoint to fetch data.
— HTTP API GET request to a microservice products endpoint.
Summary
As applications grow in size and complexity, adopting architecture patterns that allow
software systems to scale without straining available resources is crucial.
Kinsta simplifies the process of hosting your microservices. It allows you to effortlessly use
your preferred database and conveniently host both your application and database through a
unified dashboard.