Cloud Manager supports the twin mechanisms of connectors and bindings to simplify application configuration. Services expose one or more connectors, and applications bind to these connectors using bindings. Each binding specifies a set of related environment variables that are set at application deployment and restart time. The value of these variables is evaluated from the settings of the connector it binds to.
A deployment or service can expose one or more connectors, each defining a set of key-value pairs used to export connection information to applications. They are defined with a JSON specification, and typically include information, such as: credentials (username/password, key/secret), service location (host/IP), access URL, and other connection settings. When a connector is created it can be assigned an optional type and tags. This allows for the automatic generation of binging specs (see below).
Some examples of connectors include:
A binding is a collection of settings for environment variables whose values are resolved from the connector that it binds to. These settings constitute the specification (spec) of the binding, define as a JSON value.
A binding is matched with a connector by specifying a selector of the form service-name/connector-name
. In Cloud Manager UI, the the service/connector of a binding can also selected via dropdown/combobox widgets.
It is preferred to make the actual settings in the binding spec depend on stack used to implement the application. This allows the app to be configured in a completely portable way — rather than requiring the app to be aware and pick-up platform specific settings.
When a binding is created, and the auto
option provided, its specification is generated automatically. The type and/or tags of the matching connector, together with the stack of th app, is used determine which environment variable to set by the binding.
To create a connector for a deployment with the Cloud Manager UI navigate to panel Environment > Connector
of the deployment dashboard. Press button Add Connector
.
The following information is specified for each connector:
key--value
pairs. When using the CLI, it can also be specified as a comma-separated list of key--value
pairs.Image depicts a the UI modal to create or edit a connector.
Connector can also be created with the CLI tool using command connector add
. The option --spec
provides a JSON specification for the connector.
Because entering the spec with JSON from the command-line tends to be a bit cumbersome (as double-quotes need to be escaped), it is often preferred to use a comma-separated list of settings. The option --spec
defines the specification of the connection as JSON or a comma-separated list of key--value
pairs. The option --type
define the type of connector, and option --tag
define a comma-separated list of tags. Both are used as optional hint to allow auto-generation of binding specifications.
ei connector add mysql superheros/root --type=mysql \
--spec=password:^^mysql-credentials.password,username:root,uri:mysql://\${host}/superheros
In the example above, the connector spec defines several variables, including: password
looked up from secret mysql-credentials
(syntax prefix ^^
resolves a secret data item, and prefix ^
resolves a configmap data item), username
defined as root
, and uri
referring to variable ${host}
(set automatically to the IP of the service endpoint) and database superheros
.
External connectors are used to connect to external services not running in the same cluster/space as the the binding applications. They are defined at the space level. This allow deployments running in the same space share the definitions.
Possible use cases for external connector, include:
To create an external connector with Cloud Manager UI use panel Environment > (X)Connector
of the space dashboard. Press button Add Connector
.
External connector can also be created with the CLI tool using command connector create
with option ‘-x’.
I define below a connector to an external SMTP service, named mailer
, managed by AWS. The command connector add
is used as before, but now the option -x
is used to specify that it is an external connector. The connector is created in the scope of the current space. Alternatively, the option -n
can be used to specify another space.
ei connector add mailer -x --type=mail --tags=smtp \
--spec=host:email-smtp.us-east-1.amazonaws.com,port:587,from:web@acme.com,username:^^mail.key,password:^^mail.secret
The type mail
and tag smtp
are used to hint Cloud Manager how to create binding specs. For a Spring Boot application this sets SPRING_MAIL_*
variables. Notice that the connector spec refers to a secret name mail
, so we need to define it. Below, I use command secret create
for this.
MAIL_KEY=XXXXX
MAIL_SECRET=YYYYY
ei secret create mail --encode --data=key:$MAIL_KEY,secret:$MAIL_SECRET
Table below, summarizes some of the stacks and services supported by Cloud Manager.
Stack | Service | Service Type/Tags | Environment variables |
---|---|---|---|
Spring Boot | MySQL|MariaDB | mysql | SPRING_DATASOURCE_{URI|USERNAME|PASSWORD} |
Quarkus | MySQL|MariaDB | mysql | QUARKUS_DATASOURCE_{URI|USERNAME|PASSWORD} |
Note that we can use the connectors abstraction for any kind of service, and the binding abstraction for any stack. However, if the service-stack pair is currently not supported by Cloud Manager you will need to provide the spec of the binding manually. Alternatively, you can fallback to use plain environment variables settings.
TIP: If you find that there is currently no support for your preferred stack and service types in Cloud Manager, you can email <support@einnovator.org>
to ask to be included in the next/future release.
You may also need to configure additional properties for a service beyond what the auto-generated binding spec provides. You can do this by modifying the binding spec (via the UI or CLI), or by complementing these settings with plain environment variables defined at the level of deployment or at the level of the Space*. For example, you have URL and credentials for a DB be defined in a stack-aware binding, and use a variable to define other connection settings.
To bind to an external service connector is similar to a deployment connector. Cloud Manager matches the selector of the binding with the connector name.
ei binding add superheros mailer
ei superheros restart
A binding is a collection of settings for environment variables whose value are resolved from a connector. The appropriate environment variables to set depend on the stack used to implement the application. Below, I use command binding add
to create a new binding for the app superheros
. The selector mysql/superheros/root
specifies that we are binding to the connector named superheros/root
of service mysql
.
ei binding add superheros mysql/superheros/root --auto
ei deploy restart supeheros
The option --auto
ask for the binding specification to be generated automatically based on the stack of the app and the service type. Because we specified --stack=BOOT
for the app early on, and the connector type is mysql
, the environment variables to set are SPRING_DATASOURCE_*
. The genereate binding spec is show below:
{"spring":{"datasource":{"url":"jdbc:${uri}","username":"${username}","password":"${password}"}}}
Once the app is restarted it will be running and fetching/storing data in the MySQL DB, rather than the embedded DB. You can confirm this my looking at the logs of the app (the table schema is auto-generated again), using the UI or with command ei deploy log superheros -l300
.
Message-brokers are ideal to implement asynchronous inter-process communication in microservice architectures. Below, I use RabbitMQ messaging-broker to illustrate how apps can easily be configured to send-receive messages with connectors and bindings.
RabbitMQ is most conveniently installed in a Kubernetes cluster using Helm charts. You can use helm
command from your lapatop to perform the installation. Alternatively, you can use the command install
of Cloud Manager CLI, has done before for MySQL.
Check that you have at least one solution for RabbitMQ in Cloud Manager marketplace by typing ei market rabbitmq
. Confirm that it shown under a catalog of type HELM
. If not, you can add a catalog known to have it, as follows:
ei catalog create bitami --type=helm --url=https://charts.bitnami.com/bitnami
To perform the installation with ei
, assuming devops tools are installed in the cluster, type the command below.
ei install rabbitmq
ei ps
TIP: To use ei
to install Helm charts, the devops tools image need to be running as a pod in the cluster. This can be done via the Cloud Manager UI in panel Cluster > Settings > Runtime > Devops Tools
, or by specifying the option --tools
when importing the cluster.
To create connectors to solutions installed from Helm charts you also need to make the solution managed by Cloud Manager. This is done with command deploy attach
as shown below:
ei deploy attach rabbitmq
Below, I use command connector add
to create a connector the service rabbitmq
with name default
. The connector name default
is somewhat special, as it can be omitted when defining bindings.
ei connector add rabbitmq default --type=amqp --spec=password:^^rabbitmq.rabbitmq-password,username:user,host:\${host}
The option --spec
defines a comma-separated list of key-value pairs for the specification for the connector. Here, exporting the variables: password
looked up from data in rabbitmq-password
in secret rabbitmq
, username
defined as user
, and host
referring to built-in variable ${host}
. The virtual host and ports are left unspecified, so they defaults are assumed. The option --type
is an optional hint to allow auto-generation of binding specifications.
Next, I create a binding for the app to connect to rabbitmq
the service. The option --auto
hints Cloud Manager to generate the specification automatically. Because the app stack in Spring Boot, the environment variables SPRING_RABBIT_
will be configured automatically.
ei binding add superheros rabbitmq --auto
ei deploy restart superheros
Command restart
is used to restart the app and update the settings. Confirm that the environment variables are set automatically from the binding by inspeciting the deployment manifest. For this, you can use Cloud Manager UI panel Deployment > Instances > MetaData
, command kubectl describe superheros
, or command ei deploy manifest superheros
.
In addition to backing services, connectors and bindings can also be used to connect to other microservices, including: third-party reusable middleware services, such as a SSO Gateway or a Payments Gateway, and other custom services part of an overall architecture. Below, I illustrate these two scenarios by showing how to deploy and connect to EInnovator SSO Gateway, and discuss how to make the sample app API be accessible by other services.
To install EInnovator SSO Gateway I use the command install
— the same procedure used before to install the backing services. The catalog name is einnovator
and solution name einnovator-sso
. The option --name
is used to define a shorter name for the deployment.
ei install einnovator/einnovator-sso --name=sso
TIP: The EInnovator SSO Gateway will try to automatically bind to a AMQP service in the same space if you defined a connector named default
, and tries to declare a queue and exchange. To prevent this run command ei connector rm sso amqp
, or change the name of the RabbitMQ connector. You should also restart the SSO Gateway after this with command ei deploy restart sso
.
Command connector add
is used to define a connector for the SSO Gateway named app
. The type ei-sso
is used to hint Cloud Manager on how to generate stack-aware bindings. The --spec
option defines a comma-separated list of variables to export, including: the clientId
and clientSecret
, and the service URL from the implicit variable ${url}
.
ei connector add sso app --type=ei-sso --spec=clientId:application,clientSecret:application\$123,url:\${url}
The specified clientId
and clientSecret
are default ones, setup automatically by the SSO Gateway for development purposes. For production environments, you should create new client app credentials for security reasons. (see below)
Without additional configuration, the implicit variable ${url}
will take the value http://host-ip
— where host-ip
is the private IP address of the service. This is good enough if the connecting applications are using only the API of the SSO Gateway. For OAuth2 authentication with code grant or to use the UI, the SSO Gateway needs to accessible outside the cluster from a web-browser. The most convenient way to do this (although not the only one supported by Kubernetes), is to add a DNS route to the app. (see below)
To bind the sample app to the SSO Gateway I use command binding add
with option --auto
. Cloud Manager will generate the binding spec to set the environment variables SSO_SERVER
, SSO_CLIENTID
, and SSO_CLIENTSECRET
.
ei binding add superheros sso/app --auto
ei restart
ei connector refresh sso app
ei binding refresh superheros sso/app
ei restart superheros
In addition to a UI, the sample app Superheros implements also a REST API for managing superhero resources with CRUD operations. To make this service API easily accessible by other services, we can define a default connector.
ei connector add superheros default --spec=url:\${url}
As was the case for the app
connector of SSO Gateway, the implicit variable ${url}
will take the value http://host-ip
. To access the UI of the superheros app, and/or to access the API via a DNS host.domain
, add one (or more) route(s) to the app.
ei route add superheros superheros
ei route add superheros heros
Client apps can then be configured to connect to the REST API (using the DNS route address) by defining a binding with a custom spec.
ei binding add myotherapp superheros --spec=superheros_server:\${url}
The variable superheros_server
is some custom variable, assumed to be recognized by myotherapp
.
Note: When service API is accessed via the private IP address (rather than a DNS host.domain
), the connector-binding approach can be considered an overkill since Kubernetes exposes automatically this information in environment variables KUBERNETES_*_HOST
. However, this requires the app to be modified to pickup this Kubernetes specific variables and detect that is running in a cluster rather than a (localhost) dev environment.
Comments and Discussion