/perfect-stack

Ideal docker microservice Infrastructure with multiple languages, databases and unit, integration and behaviour tests.

Primary LanguageJavaScript

Microservice stack

The purpose of this microservice-stack is to illustrate how a scalable and multilanguage docker microservice stack would work on a real environment.

It doesn't mean it is perfect or that you have to use this exact combination of services to build your own.

Specially taking into account that some metodologies and technologies improve over the years / months.

Stack overview

Microservices (internal)

Services we have implemented for the purpose of this demo

  • User (python flask): Microservice that allow us to create users and store them in postgres
  • Slack (nodejs express): Microservice that sends a notification on Slack everytime we create an user
  • Gateway (nodejs express): API layer
  • BDD service to test behaviours in our stack, using behave for python, written in Gherkin language.

Microservices (external)

Services available on dockerhub that we use as docker containers (check docker-compose.yml)

  • Postgres: Database used by user to store users
  • Redis: Database used by slack to store all the messages we send out
  • RabbitMQ: Internal messagging system between microservices (not currently used, but implemented on services)

Getting started

Build services

docker-compose build

Run services

Run all services available in docker-compose, including databases

docker-compose up -d

Run queue consumers (rabbitmq)

Some services have a consumer service that will consume messages from rabbitmq and process asynchronous messages.

docker-compose up user-queue

Run tests

  • Python: docker-compose run user py.test

  • Slack NodeJS: docker-compose run slack npm test

  • Gateway NodeJS: docker-compose run gateway npm test

  • BDDs (Python behave): docker-compose run bdd

bdd

Test create new user using REST

curl -X POST http://localhost:8101/user -d '{"name": "foo", "email": "bar"}'  -H  'Content-Type: application/json'

Test Post a slack message

curl -X POST http://localhost:8102/ -d '{"channel": "#user", "message": "User <id> created"}'  -H  'Content-Type: application/json'

Heatlh checks

Run gateway and go to http://localhost/health to see the status of all the services. You will get something like:

    {
        "slack": {
            "time": 24,
            "response": "pong"
        },
        "user": {
            "time": 50,
            "response": "pong"
        }
    }

API Layer

The API layer is in charge or redirecting the external API requests to the correct microservice, potentially, this can be extended adding authentication, rate limiting, metrics, etc.

Sample: How POST request works

If we send the following request to our API layer: curl -X POST localhost:8100/user -H 'content-type: application/json' -d '{"foo": "bar"}'

User makes POST request -> API -> User -> API -> User

The API Layer (nodejs) will redirect this request and the json body to User (python) , it will process the data and return a response back to the API layer, which will be returned to the user

Missing stuff

  • Authentication, potentially implemented in the API layer
  • RPC sample
  • Websocket sample
  • Frontend
  • Web api documentation
  • Monitoring services (prometheus, zipkin)

Conclussions and assumptions

Multilanguage is fun, but try to keep it minimal

You will have to use similar implementations on multiple microservices (eg: rabbitmq integration). In order to minimize errors and potentially reuse the same code (you can build your own libraries), it's better to stick to similar languages.

Use your favourite technologies and languages

Apart from docker and docker compose, the fact I use languages like javascript or python or services like redis, postgres or rabbitmq, doesn't mean you have to use the same. I only use these technologies because they are very familiar to me and easy to implement. But a good alternative to rabbitmq for example, would be to use Kafka or SQS service on Amazon .

Wrappers, wrappers, wrappers

It's common on a stack (specially when building tooling), to wrap common utils on classes, like creating a class for rabbitmq to simplify the messaging, trying to avoid specific rabbitmq logic. On this stack I didn't bother wrapping any shared logic, as it's very early stage. Be careful with wrappers specially when doing things on different languages.

Microservices are not magic bullets

Sometimes people say starting your stack by building microservices is not a good idea, because you don't understand what they will do at the beginning and you end up having unnecessary barriers in some places of the codebase. Ideally, you would build a single monolithic application and then split functionality in microservices along the way.

Build your monitoring before start a big project

Monitoring is very useful to ensure everything works as expected and also to debug if something gets wrong. Prometheus is a monitoring server tool that record endpoint latency, database connections, CPU and memory usage, etc. https://prometheus.io/ Zipkin is a tracing tool very useful to track the lifecycle of a message by passin headers on REST or queues. http://zipkin.io/

Orchestration

This setup, is useful for small projects or development environment. On a real enviroment like Prod you will probably use an orchestrator to manage all your containers. I've used Rancher on AWS, but I strongly recommend Kubernetes, it's by far the best orchestrator available.