Heimdall is an API Gateway for Mesos and Marathon. It routes requests through filters dynamically loaded from Marathon before forwarding them to their intended services.
When the application starts, it will attempt to register itself as a Marathon subscriber. Once registered as a callback, Heimdall will start listening for change events from Marathon, reloading its dynamic routing config on each change.
Dynamic routing configuration is done through Marathon labels. There are 4 labels used to decide how a request gets routed for an app:
heimdall.host
(required) - routes a request only if its host matches this valueheimdall.path
(required) - path prefix the request must match (the prefix is stripped from the forwarded request)heimdall.filters
- a JSON list of plugs through which to filter the request before sending it back offheimdall.opts
- a JSON object of options that gets passed into each plug
Filters can be viewed as a pipeline of plugs (simple functions that take a
request and return a changed request or a response),
with the final plug forwarding the request to the place it actually needs
to go. The pipeline is built in the order that the plugs are listed in the
config. In other words, if heimdal.filters
is: ["Plugs.First", "Plugs.Second"]
the pipeline would have the following flow:
Request -> Plugs.First -> Plugs.Second -> Heimdall.Plug.ForwardRequest -> Microservice
Ideally, Heimdall should forward requests to a load balancer that knows how to
send each request to an actual instance of the microservice it's trying to get
to. Some recommended solutions for this are traefik,
Minuteman, or your own
custom nginx config. The default forward url (ie the location of the load
balancer) is an OTP application setting, :default_forward_url
(see config/dev.exs
for an example of the config). If you wish to set the forward location on
a per service basis, you can set forward_url
in heimdall.opts
(this gets passed to
Heimdall.Plug.ForwardRequest
)
The package can be installed and used as an OTP application in your project:
- Add
heimdall
to your list of dependencies inmix.exs
:
```elixir
def deps do
[{:heimdall, "~> 0.1.0"}]
end
```
- Ensure
heimdall
is started before your application:
```elixir
def application do
[applications: [:heimdall]]
end
```
- Create a plug through which to filter requests
```elixir
defmodule Plug.TestPlug do
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
conn
|> put_req_header("some-header", "a header value")
end
end
```
- Add heimdall labels to your Marathon app config
``` json
{
"id": "/test-app",
"cmd": null,
"cpus": 1,
...
"labels": {
"heimdall.path": "/test",
"heimdall.options": "{\"forward_url\": \"http://localhost:8081/test\"}",
"heimdall.host": "localhost",
"heimdall.filters": "[\"Plug.TestPlug\", \"AnotherModule.SomeOtherPlug\"]",
},
...
}
```
-
Start your server with
mix server
. NOTE: Marathon must be available, and Heimdall must be configured to reach it before you can start your server. Seeconfig/dev.exs
for a list of configurations. -
Your app can now be reached at
localhost:4000/test
. This will run any Plugs configured in yourheimdall.filters
before forwarding the request to your app.