Deployment

Introduction

The last but not the least important step of building microservices with the minos framework is the deployment one, in which everything related with the microservice running will be explained. At this point, the minos.common.EntrypointLauncher class plays an important role, being the one which is able to orchestrate all the internal services within the microservice.

Command Line Interface

A common way to start a minos microservice is executing a command over the shell. To do that, the microservice must provide a command line interface. A good alternative to do that is using the typer library, that can be installed as follows:

Poetry

The typer library can be included with the following command:

poetry add typer

Then, to expose the microservice command and link it with the main function that will be defined on the src/cli.py file, the following lines are needed:

# pyproject.toml
...
[tool.poetry.scripts]
microservice = "src.cli:main"
...

Then, the way to activate the Poetry’s virtual environment can be done with:

poetry shell

And now, the microservice command should be available on the current shell. The next step is to add the CLI code.

PipEnv

[TODO: Include pipenv guide.]

Pip

[TODO: Include pip guide.]

After being set up the microservice command to be linked to the main function defined on the src/cli.py file, the next step is to properly add the src/cli.py file. Here is a standard template that can be used among most of the microservices created with the minos framework:

"""src/cli.py"""

import sys
from pathlib import (
    Path,
)
from typing import (
    Optional,
)

import typer
from minos.common import (
    EntrypointLauncher,
)

app = typer.Typer()


@app.command("start")
def start(
    file_path: Optional[Path] = typer.Argument(
        "config.yml", help="Microservice configuration file.", envvar="MINOS_CONFIGURATION_FILE_PATH",
    )
):
    """Start the microservice."""
    launcher = EntrypointLauncher.from_config(file_path, external_modules=[sys.modules["src.queries"]])
    launcher.launch()


@app.callback()
def callback():
    """Minos microservice CLI."""


def main():  # pragma: no cover
    """CLI's main function."""
    app()

The most interesting part here is the start function, that handles the microservice start command. The purpose of this function is to create an EntrypointLauncher instance from the configuration file (typically the config.yml) and then execute its launch() method, that orchestrates everything needed to start the microservice.

An important detail to notice is the purpose of the external_modules argument, which in this case receives a list containing the src.queries module. That is the way to “register” modules that must be linked to the minos dependency injection system. In this case, is needed to define a single query_repository instance among all the microservice and use it by the ExamQueryService.

Another way to call the src.cli:main function is directly by the module’s entrypoint i.e. the __main__.py file. In this case, it can be done as follows:

"""src/__main__.py"""

from .cli import (
    main,
)

if __name__ == "__main__":
    main()

Once everything related to the command line interface is explained, the next part is to talk about the containerization of the microservice.

Containerization

As in any microservice-based system, the containerization process is one of the most important parts of the deployment, being the standard way to deliver each piece of code in a self-contained way.

Docker

To create a dockerized image of the microservice the first step is to add a Dockerfile that defines the building steps. Here is a common template used by the minos microservices:

# Dockerfile

FROM ghcr.io/clariteia/minos-microservice:0.1.5 as development

COPY ./pyproject.toml ./poetry.lock ./
RUN poetry install --no-root
COPY . .
CMD ["poetry", "run", "microservice", "start"]

FROM development as build
RUN poetry export --without-hashes > req.txt && pip wheel -r req.txt --wheel-dir ./dist
RUN poetry build --format wheel

FROM python:3.9-slim as production
COPY --from=build /microservice/dist/ ./dist
RUN pip install --no-deps ./dist/*
COPY ./config.yml ./config.yml
ENTRYPOINT ["microservice"]
CMD ["start"]

To build the image, a command like this one can be used:

docker build -t exam-microservice:0.1.0

Then, the microservice can be started with the following command:

docker run -it exam-microservice:0.1.0

After being described how to deploy single microservices the next step is to orchestrate many of them.

Docker Compose

[TODO: Include kubernetes.]

Kubernetes

[TODO: Include kubernetes.]