...
Table of Contents | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
What are Microservices?
A microservice in PIPEFORCE is an application which runs inside a (Docker) container which in term runs inside the PIPEFORCE (Kubernetes) cluster. Such a microservice can communicate with other microservices inside the same namespace via messaging and REST. A namespace can “host” many such microservices:
...
PIPEFORCE offers a lot of toolings to develop, deploy and monitor your microservices in your namesace. This is called the Microservices Framework.
Microservice or App?
In general, you have three main possibilities to write a business application:
...
The app should contain:
Connections to external systems
Data mappings and transformations
Forms and lists
Stateful workflows
The microservice should contain:
Complex business specific logic
Designing a Microservice
Typically a microservice is a relatively small application which has the responsibility about a concrete and well-defined part of an overall business process. How you slice the microservices depends on your requirements.
...
See here for a good introduction how to design microservices: https://martinfowler.com/articles/break-monolith-into-microservices.html
Develop a Microservice
Developing a microservice typically means, developing a business application in the programming language of your choice.
...
Sync communication - Typically used with RESTful services inside PIPEFORCE.
Async communication - Typically used with RabbitMQ and messaging inside PIPEFORCE (preferred way).
Deploy / Start a Microservice
Once a microservice has been developed, it must be wrapped inside a (Docker) image in order to be able to deploy it into PIPEFORCE. The deployment cycle of a microservice in PIPEFORCE is always an 4-step task:
Build a (Docker) container image from the sources of your microservice.
Upload the container image to a container registry which is supported by PIPEFORCE (for example Docker Hub or the PIPEFORCE Container Registry).
Deploy the image from this registry into your PIPEFORCE namespace by using the command
service.start
.
Step 2-4 These steps are typically automated by using a CI/CD tool like Jenkins, CircleCI, Travis or similar.
...
service.start - Starts a new service by deploying an image.
service.stop - Stops a running service.
service.status - Returns the status of a service.
...
Default ENV variables
Every time a service is started using the service.start
command, also some implicit default variables will be automatically passed to this container and can be accessed via the environment variables inside the container. These implicit variables are:
ENV | Description | ||
---|---|---|---|
| The domain name used for the PIPEFORCE instance. For example | ||
| The cluster internal host name of the hub service in order to connect to. | ||
| The cluster internal host port in order to connect to. | ||
| The cluster internal url of the hub service in order to connect via REST for example. | ||
| The default dead letter queue used for RabbitMQ messaging. |
| The default messaging exchange used for RabbitMQ messagingIt is usually |
| The default messaging topic exchange used for RabbitMQ messaging. It is usually the | ||
| The default messaging topic used for RabbitMQ messaging. It is usually the same as the default exchange. | ||
| The messaging host to connect to in order to register a RabbitMQ listener. See below in documentation how to optionally pass messaging username and password as a secret if required. They aren’t provided by default. | ||
| The messaging host to connect to in order to register a RabbitMQ listener. | ||
| The namespace of the instance this services runs inside. | ||
| The external url to the PIPEFORCE webui portal in order to refer to from inside a microservice. | ||
| The name of this custom service inside PIPEFORCE. | ||
| The staging mode, this namespace is running in. Usually one of |
...
Note: Since any value of these ENV variables could change over time, you should never persist them in your microservice!
Custom ENV variables
Additionally to these default environment variables, you can also add your custom ones by using the parameter env
on the command service.start
:
Code Block | ||
---|---|---|
| ||
pipeline: - service.start: name: myservice image: myimage env: MY_ENV: "myCustomValue" |
...
Secret ENV variables
In case you would like to set pass secret values to environment your service ENV variables, you should create such secrets have to do these steps:
Create a secret of type
secret-text
and with the service name as prefix in the secret store
...
, like this:
<yourservice>_<YOUR_SECRET_NAME>
.Use the custom uri
...
$uri:secret:<yourservice>_<YOUR_SECRET_NAME>
in the env parameter.
...
language | yaml |
---|
...
For example, lets assume you would like to start a service myservice
and pass a secret with custom name MY_SERVICE
to it. For this you have to create a secret with name myservice_MY_SECRET
in the secret store and then refer to it like this:
Code Block | ||
---|---|---|
| ||
pipeline: - service.start: name: myservice image: myimage env: MY_SECRET_ENV: "$uri:secret:myservice_MY_SECRET" |
On startup of the service, the secret with name myservice_MY_SECRET
will be read from the secret store and passed to the container as environment variable MY_SECRET_ENV
. This way it is not required to store the secret in code.
...
Note: Only secrets having the same service name as prefix are allowed to be passed to this service. Any other secret cannot be passed to a service. This is for security reasons.
Info |
---|
Since the secret is stored in the environment variable in plain text, make sure to pass secrets only along to trustworthy microservices which belong to your stack! |
You can also define a default value for a secret. In case the secret could not be found in the secret store, the given default value will be used and passed as ENV variable value instead:
...
:
Code Block |
---|
$uri:secret:myservice_MY_SECRET:someDefaultValue |
Note: Since this default value is part of your source code, it should only be used in case it is not used in production. For example for demo logins or similar.
Shared secret ENV variables
In case you would like to share a single secret across multiple services, you must prefix it with SHARED_
instead. Secrets prefixed with SHARED_
can be passed to any microservice without security check.
Lets assume you have created a secret with name SHARED_MY_SECRET
, then you can pass it to any service like this example shows:
Code Block | ||
---|---|---|
| ||
pipeline:
- service.start:
name: myservice
image: myimage
env:
MY_SECRET_ENV: $uri:secret:SHARED_MY_SECRET |
Note |
---|
Important |
Monitoring a microservice
Logging
Everything you log into the standard output of your microservice container can be later viewed by using the command log.list and specifying the name under which you have deployed the service. Example:
...
Additionally you can use the log listing in the web portal to filter and search the logs of your microservice:
...
Service Status
The Services view in the web portal will show you the deployment status of your services. Here you can install new services, see their status and stop running services:
...
service.start - Starts a new service by deploying an image.
service.stop - Stops a running service.
service.status - Returns the status of a service.
Messaging in a Microservice
The preferred way each microservice inside PIPEFORCE can communicate with each other is by using messaging.
...
If you're interested in how to send and receive messages using pipelines, see Messaging and Events Framework.
How to connect a Microservice with the message broker?
In case you have deployed your microservice using the command service.start
, then these environment variables will be automatically provided inside your microservice and can be used to connect to the RabbitMQ message broker:
PIPEFORCE_MESSAGING_HOST
= The cluster-internal hostname of the messaging service.PIPEFORCE_MESSAGING_PORT
= The cluster-internal port of the messaging service.
Info |
---|
Note: This The values of these values ENVs can change at any time, so do not use store them as fixed value. |
Additionally to these variables, you need to set the PIPEFORCE_MESSAGING_USERNAME
and PIPEFORCE_MESSAGING_PASSWORD
along with your service.start
command using the custom uri prefix $uri:secret
. Here is how to do it:
Create a new secret in your secret store with name
pipeforce-<yourservice>_messaging-_username
and typesecret-text
and set the RabbitMQ username you would like to use to connect.Create a new secret in your secret store with name
pipeforce-<yourservice>_messaging-_password
and typesecret-text
and set the RabbitMQ password you would like to use to connect.
...
Code Block |
---|
pipeline: - service.start: name: "myservice" image: "some/image" env: PIPEFORCE_MESSAGING_USERNAME: "$uri:secret:pipeforce-myservice_messaging-username" PIPEFORCE_MESSAGING_PASSWORD: "$uri:secret:pipeforce-myservice_messaging-password" |
This way, the sensitive values will be passed to your container without the requirement to store them into code or refer from external systems.
...
Also remember to setup the dead leader queue in order to not lose any message as mentioned below.
Default Queue Naming
By default any microservice is responsible to setup and manage its own queues.
Each queue should contain always equal message types. So, for different messages, create additional queues.
...
Code Block |
---|
service_shoppingcart_orders_q |
Default Topic
PIEPFORCE PIPEFORCE automatically creates a default topic exchange on startup with this name: pipeforce.hub.default.topic
.
PIPEFORCE core services are configured in a way that any event which happens there or which is sent using the event.send
command is will also be send to this default topic.
In case a microservice wants to listen to a certain type of message with a given routing key, it needs to create a binding between the topic pipeforce.hub.default.topic
and the queue you want to “feed” this message into.
Note: The name of this default topic could change. Therefore, do not hard-code it into your microservice. Instead, use the value from the passed-in ENV variable PIPEFORCE_MESSAGING_DEFAULT_TOPIC
.
See here for more details about topics, routings and queues: https://www.rabbitmq.com/tutorials/tutorial-five-python.html
Default Dead Letter Queue
Additionally, a default Dead Letter Queue is automatically configured by PIPEFORCE : pipeforce_hub_default_dlq
those name is given by the ENV variable PIPEFORCE_MESSAGING_DEFAULT_DLQ
.
Any other queue can be configured in a way to forward messages to this queue if one these rules apply:
...
Code Block |
---|
x-dead-letter-exchange = ""
x-dead-letter-routing-key = "pipeforce_hub_default_dlq" |
How to set these arguments in your microservice depends on your selected programming language and the RabbitMQ client implementation you're using. See documentation for details: https://www.rabbitmq.com/dlx.html
...
Code Block | ||
---|---|---|
| ||
return QueueBuilder.durable(Constants.MY_QUEUE) .withArgument("x-dead-letter-exchange", "") .withArgument("x-dead-letter-routing-key", "pipeforce_hub_default_dlq) .build(); |
Default Message Keys
Here you can find the default message keys, PIPEFORCE will use for internal events and send messages with these keys to the default topic pipeforce.hub.default.topic
. You can subscribe to these keys using a binding between your queue and this default topic.
...