Harnessing Jupyter Notebook for Microservices Management

MatteoPignotti
Nethive Engineering
7 min readMar 8, 2024

In the current era, where microservices architecture is revolutionizing the design and development of applications, significant challenges related to deployment, interaction, and troubleshooting in these complex ecosystems are emerging. It is precisely in this context of growing complexity that I discovered, the unexpected potential of a well-known tool: Jupyter Notebook. During my work with a series of clients, I faced the challenge of deploying and managing a complex cluster of microservices. Operating through a Linux terminal, without the aid of a graphical interface, I quickly realized how difficult it could be to monitor and resolve issues in real time in such an intricate environment.

The need to quickly isolate problems, track requests across various services, and effectively manage system logs was evident. Jupyter Notebook, once for me a tool confined to the world of data analysis, turned out to be an unexpected and versatile solution. By setting up an SSH tunnel, I was able to bring Jupyter into my local browser, transforming it into a useful console for managing and monitoring microservices. The user-friendly interface, combined with its ability to execute code in real time and display results interactively, proved to be exceptionally useful. Not only could I perform functionality tests on services and receive immediate feedback, but I could also document the process, making it simpler for me and my team to follow and understand the workflow.

This article explores how using this tool can significantly simplify the management of troubleshooting in a microservices environment. Through practical examples and personal reflections, the following sections aim to provide a new perspective on the use of familiar development tools in unexpected contexts, demonstrating how innovation can emerge from the creative adaptation of existing technologies. On this journey, we will discover how a simple adaptation of well-known tools can offer revolutionary solutions to complex problems, illuminating the path towards a more effective and intuitive management of microservices.

Usage

Practical Implementation: Jupyter at the Service of Microservices

After a brief introduction, let’s delve into a more detailed description of its practical implementation.

  • Creating a Custom Dockerfile: The first step involves setting up a custom Dockerfile tailored to your needs. Below is an example of the configuration I used:
# Use the Jupyter Data Science Docker Image as a base
FROM jupyter/datascience-notebook:latest

# Set the root user (necessary for package installation)
USER root

# Install additional packages
RUN pip install docker requests pyrad networkx matplotlib langchain mysql.connector

# Copy the required scripts into the container
COPY ./script/jupyter /home/jovyan/work

# Switch back to the default user
USER $NB_UI
  • Building and Pushing the Docker Image: Next, proceed with building the Docker image and uploading it to a Docker registry.
docker build -t jupyter-test .

# Log in to Docker Hub (or another registry)
docker login

# Tag the image with your Docker Hub username (or your registry prefix)
docker tag jupyter-test username/jupyter-test

# Upload the image to Docker Hub
docker push username/jupyter-test
  • Deploying in the Cluster: The final step is deploying the image in the cluster, using the following commands:
docker run -p 8888:8888 --user root -e GR

Case study and practical example

Monitoring Container Status

In the context of microservices troubleshooting, having a clear view of the status of each service is crucial. Instead of using the classic docker ps command, writing a Python script to monitor and report the status of all containers in a given Docker network can be very useful. This approach offers an immediate overview of the services that require attention.

  • docker.from_env(): This function connects to the Docker daemon running on the host system using default environment variables.
  • client.containers.list(all=True): Retrieves a list of all containers, including those not running.
  • container.attrs: This data structure contains detailed properties of the container, such as network settings and status.

The output from the monitoring script is key to effective troubleshooting. As shown in the attached image, the script does more than list containers; it provides immediate visual feedback on the status of each service. Running containers are simply tagged with “- running”, while those that have encountered problems are distinctly marked with “- crash” in red and bold. This approach greatly enhances readability over the classic output of docker ps.

The coloring and emphasis on the text “crash” draw attention to the most critical information, allowing operators to quickly identify issues without manually filtering through a list of containers. In a production environment where speed of response is crucial, this type of simplified visualization can significantly speed up monitoring operations.

Visualization of Microservices Status

Maintaining a microservices architecture requires a clear understanding of the dependencies and status of each component. To this end, I leveraged the power of Jupyter Notebook to create a visual representation that simplifies monitoring and rapid problem identification.

Using the networkx library together with matplotlib, I wrote a script that draws a graph of the microservices, assigning a different color to each node depending on the corresponding service’s status. The code queries the status of each Docker container and then generates a map that visually illustrates the microservices chain.

The output of this script is a directed graph showing the microservices chain with colored nodes: blue nodes represent active services (status “running”), while red nodes indicate stopped services (status “down”).

In critical scenarios where every second counts, graphical visualization becomes an essential tool. This approach allows operators to quickly identify problems by focusing on visual elements like colored nodes, rather than manually analyzing log files or scrolling through textual outputs. This immediacy proves crucial not only for real-time troubleshooting but also for post-incident reviews, facilitating event reconstruction and failure point identification.

The method I developed adapts to various microservices infrastructures. Its flexibility allows for real-time updates of visualizations, a key aspect in both incident resolution and effective presentations during team meetings.

This example of translating the dynamic state of a distributed system into an understandable visualization demonstrates how data analysis tools, traditionally used in other contexts, can bring concrete benefits to IT systems management.

Benefits of Graphical Visualization

  • Quick Visual Recognition: Distinct colors allow for the immediate identification of each service’s status, making the problem identification process faster and more intuitive.
  • Understanding Dependencies: The visualization shows how services are connected, helping to understand the impact that a non-functioning service can have on others.
  • Improved Documentation: Maintaining a visual representation can enhance documentation, providing a clear representation of the system’s status at any given time.
  • Ease of Sharing: Visualizations can be easily shared with team members or included in reports, improving communication and collaboration.

Integration and Functionality Testing

Beyond monitoring and visualizing the status of microservices, Jupyter stands out as an excellent tool for orchestrating and conducting functionality tests. These tests are crucial for verifying that interactions between microservices occur correctly and that events are properly propagated through the service chain.

Example of Account Creation Test

Consider the following example where Jupyter is used to test the account creation process in a microservice. The test includes sending HTTP requests to create and retrieve account information, aiming to confirm that the operations were successfully executed.

In this scenario, Jupyter serves as an interactive console, sending requests to the account microservice and verifying if the operation was successful through the response received. With the ability to execute HTTP requests directly from the notebook, we are able to simulate real-time interactions between the frontend and backend microservices.

This immediate verification of integration allows for the rapid isolation and resolution of any dysfunctions in critical business processes.

This scenario illustrates how Jupyter acts as an interactive console, allowing the sending of requests to the account management microservice and the verification of the responses received. Thanks to the capability of directly executing HTTP requests from the notebook, we can simulate real-time interactions between the frontend and backend microservices.

Integration with Database and Verification of Results

A crucial aspect of these tests involves verifying the impact of operations on the databases of various microservices. Using MySQL connectors in Jupyter, it’s possible to write tests that directly query databases to confirm that the results of operations match expectations. This approach allows verifying not only the correct execution of requests but also the consistency of data at the database level.

Monitoring Regressions in New Versions

When releasing new versions of microservices, tests in Jupyter prove fundamental for identifying any regressions. Through repeatable and automatable tests, we can quickly assess the impact of new releases on critical business processes and ensure that changes do not introduce errors or unexpected behaviors.

Example of Log Parsing

An additional dimension of functionality tests is log analysis, which can be effectively managed through Jupyter. Imagine having a unique TraceId for each chain of calls to our microservices. The following script uses Jupyter to examine the logs of all containers in a given Docker network, searching for a specific TraceId:

This approach not only identifies and orders microservices interactions based on a TraceId but also highlights any errors in the flow. In a Jupyter environment, we can run and modify these scripts in real-time, adapting our tests to the rapid changes that often characterize microservices environments. This flexibility is essential for keeping integration and functionality tests aligned with the latest code and configuration changes.

Conclusion

The adoption of Jupyter in the field of microservices is not only a demonstration of technological innovation but also represents a paradigm shift in the management and analysis of complex systems. Through its dynamic and interactive environment, Jupyter enables integration and functionality testing with unprecedented speed and efficiency, ensuring an immediate response to emerging problems and continuous improvement in software quality.

This platform not only facilitates diagnosis and problem resolution through intuitive service status visualizations but also allows for the execution of complex tests and detailed documentation of every process stage. In this way, Jupyter promotes a culture of transparency, collaboration, and collective learning within development teams.

--

--