This document describes the networking API released as of Marathon 1.5.
While Marathon continues to consume the legacy ports API that was shipped in versions 1.4.x and prior, all new applications should be declared using the new, non-deprecated networking API fields that are documented here. Applications using the old networking API fields will be automatically migrated to the new networking API in Marathon 1.5.x. As of version 1.5, Marathon will only produce responses in the new 1.5 networking API format.
See the Migrating to the 1.5 Networking API for more information on changes you may need to make to your applications.
If you are running Marathon within a DC/OS cluster, you can use virtual addresses (VIPs) to make ports management easier. VIPs simplify inter-app communication and implement a reliable service-oriented architecture. VIPs map traffic from a single virtual address to multiple IP addresses and ports.
Several command-line flags determine Marathon’s behavior with respect to networking.
default_network_name
is injected as the name
of a container
mode network when left blank by an application.local_port_min
and local_port_max
define a port range from which Marathon automatically allocates *service-port*s.Marathon apps and pods declare networks
the same way.
Three networking modes are supported:
host
NetworkingIn host
networking, an application shares the network namespace of the Mesos agent
process, typically the host network namespace.
container
NetworkingAn application should be allocated its own network namespace and IP address;
Mesos network isolators are responsible for providing backend support for this.
When using the Docker containerizer, this translates to a Docker “user” network.
Container networks are named, either explicitly by an application or else via --default_network_name
.
Unnamed container networks will fail to validate if --default_network_name
is not specified by the operator.
container/bridge
NetworkingSimilar to container
, an application should be allocated its own network namespace and IP address;
Mesos CNI provides a special mesos-bridge
that application containers are attached to.
When using the Docker containerizer, this translates to the Docker “default bridge” network.
Notes:
Network.name
parameter is only supported with container
networking.container
mode networks, with caveats:
container
mode networks (this limitation is imposed by Mesos).host
mode network. This is the default if an app definition does not declare a networks
field.container/bridge
network.host
network, a single container/bridge
network, or one or more container
networks.Marathon apps declare ports differently than pods. The following section is only relevant to Marathon apps.
Specifies a port within a container.
containerPort
is specified as a field of a port-mapping or endpoint when using container
or container/bridge
mode networking.
Specifies a port to allocate from the resources offered by a Mesos agent.
hostPort
is specified as a field of a port-mapping or endpoint when using container
or container/bridge
mode networking.
port
is specified as a field of a port-definition when using host
mode networking.
Note: Only host ports are made available to a task through environment variables.
When you create a new application in Marathon (either through the REST API or the front end), you may assign one or more service ports to it.
You can specify all valid port numbers as service ports, or you can use 0
to indicate that Marathon should allocate free service ports to the app automatically.
Marathon allocates service ports from the range defined by the --local_port_min
and --local_port_max
command line flags.
If you choose your own service port, you must ensure that it is unique across all of your applications.
See the port definition section for more information.
Note: Pods (endpoints) do not support service ports.
The following use cases explore how you can configure port mapping to meet your needs.
You can configure 3 defined ports: containerPort
, hostPort
, and servicePort
, as well as the label for VIP_0
in your port mapping.
All applications on Mesos are hosted in containers. Containers can run with a network configuration in bridge or host mode.
Let us assume an application called “ACME,” which opens port 8080
.
When in host mode, the container will have the host network interface defined in the container namespace. The container will not do any port mapping and the application will open up port 8080
on the host network interface. In this case, there is no container port and the hostPort
is 8080
.
If the container is run in bridge mode, it gets its own network interface and the containerPort
is 8080
. In bridge mode, the container will open a pseudo random port on the host and bridge/NAT communication to the container port.
For example, lets say the host port is 31000
. Now, the containerPort
is 8080
, but the hostPort
is 31000
. Clients connecting to this service will open port 31000
on the host the service is running on. The container will route traffic to the internal port in the container, 8080
.
Now, let’s say you have 3 instances of app ACME: 10.0.0.2:31000
, 10.0.0.2:31001
, and 10.0.0.3:31000
. There is a common need to have a port that will route with an algorithm to each of those instances. The algorithm used is determined by the configuration of the load balancer. This “port” is metadata specific to this application. All instances of the application will be hosted behind this port.
In Marathon, this is referred to as the service port. The service port is specified in the servicePort
parameter of the ACME app definition. Let us assume servicePort
is specified as 8080
.
The service port is metadata; Marathon does not do anything with this information except track it and provide it to a load balancer. The DevOps team setting up this service is expected to create a script or provide a means to read the servicePort
and configure the load balancer to route calls to that port (port 8080
in this case) to each of the instances of the application. All metadata is queryable from Marathon.
The Marathon-LB service, when configured, does exactly this. Marathon-LB will register all instances of an app and route to its configured servicePort
. Marathon-LB is an HAProxy service with scripts that will register all instances of an app and route to its configured servicePort
. In the configuration in this example, a client will connect to a load balancer at port 8080
(servicePort
), which will route (with an algorithm) to 10.0.0.2:31000
(hostPort
), which will in turn route to 8080
(containerPort
) of the internal application.
VIP_0
Label Use CaseVIP_0
, defined in portDefinitions
as a label, is like servicePort
in that it is informational and implementation-dependent. However, when used with DC/OS, services internal to DC/OS will make available to the cluster that DNS name and port as a route to services. An example configuration:
```app_def[‘portDefinitions’] = [{ “port”: 0, “protocol”: “tcp”, “name”: “acme”, “labels”: { “VIP_0”: “/acme:10000” } }]
This configuration will create a fully qualified domain name (FQDN) according to the following schema: `<vip-name>.marathon.l4lb.thisdcos.directory:<vip-port>`, which will load balance all instances of the application. Here, the FQDN would be `acme.marathon.l4lb.thisdcos.directory:10000`.
Marathon itself does nothing with this configuration. Marathon manages it as metadata for the application. A client in DC/OS could open a connection to `acme.marathon.l4lb.thisdcos.directory` at port `10000`, which would route to `10.0.0.2:31000`, which will in turn route to the `containerPort` of `8080`.
#### Declaring ports in an application
##### *endpoint*:
Endpoints are declared only by the containers of a Pod.
See the documentation for [pods](pods.html).
##### *port-definition*:
Port-definitions are used only with `host` mode networking.
A *port-definition* (specifically its `port` field) is interpreted through the lens of the `requirePorts` app field:
* When `requirePorts` is `false` (default), a port-definition's `port` is considered the *service-port* and a *host-port* is dynamically chosen by Marathon.
* When `requirePorts` is `true`, a port-definition's `port` is considered both a *host-port* and *service-port*, otherwise;
* The special `port` value of `0` tells Marathon to select any *host-port* from a Mesos resource offer and any *service-port* from the configured service port range.
##### *port-mapping*:
A *port-mapping* declares a *container-port* for an application, possibly linking that *container-port* to a *host-port* and *service-port*.
Marathon communicates *container-port*/*host-port* links (aka "mappings") to Mesos when launching instances of the application.
Port-mappings are used with both `container` and `container/bridge` networking.
Marathon ignores the value of `requirePorts` when interpreting a *port-mapping*.
* The special `containerPort` value of `0` tells Marathon to internally assign the (eventually) allocated *host-port* to `containerPort`.
* The special `hostPort` value of `0` tells Marathon to select any *host-port* from a Mesos resource offer.
* The special `servicePort` value of `0` tells Marathon to select any *service-port* from the configured *service-port* range.
### Port Definitions
#### Summary
* Review *port-definition*, *host-port*, and *service-port* in [Terminology](#Terminology).
* Location in app definition: `{ "portDefinitions": [ <port-definition>... ], "requirePorts": <bool>, ... }`
* Used in conjunction with `host` mode networking.
* `requirePorts` applies to `portDefinitions`.
* If no `portDefinitions` are defined (or defined as `null`) at create-time, default to `{ "portDefinitions": [ { "port": 0, "name": "default" } ], ... }`
* Specify an empty array (`[]`) to indicate NO ports are used by the app; no default is injected in this case.
* Ignored when used in conjunction with other networking modes.
* NOTE: Future versions of Marathon may fail to validate apps that declare `portDefinitions` with network modes other than `host`.
### Port Mappings
Summary:
* Review *port-mapping*, *container-port*, and *host-port* in [Terminology](#Terminology).
* Location in app definition: `{ "container": { "portMappings": [ <port-mapping>... ], ... }, ... }`
* Used in conjunction with `container` and `container/bridge` mode networking.
* When using `container/bridge` mode networking, an unspecified (`null`) value for `hostPort` is translated to `"hostPort": 0`.
* `requirePorts` does not apply to `portMappings`.
* If unspecified (`null`) at create-time, defaults to `{ "portMappings": [ { "containerPort": 0, "name": "default" } ], ... }`
* Specify an empty array (`[]`) to indicate NO ports are used by the app; no default is injected in this case.
* **NOTE:** When using `container/bridge` mode, the default *port-mapping* also sets `"hostPort: 0"`.
* Ignored when used in conjunction with other networking modes.
* **NOTE:** Future versions of Marathon may fail to validate apps that declare `container.portMappings` with network modes other than `container` or `container/bridge`.
* When used in conjunction with multiple container networks, each mapping entry that specifies a `hostPort` must also declare a `networkNames` value with a single item, identifying the network for which the mapping applies (a single `hostPort` may be mapped to only one container network, and `networkNames` defaults to all container networks for a pod or app).
## Downward API
### Per-Task Environment Variables
If a port is named `NAME`, it will be accessible via the environment variable `$PORT_NAME`.
Every *host-port* value is also exposed to the running application instance via environment variables `$PORT0`, `$PORT1`, etc.
Each Marathon application is given a single port by default, so `$PORT0` will normally be available, except for apps that specifically declare "no ports".
Variables are generated for all apps, regardless of whether the app uses the Mesos or Docker containerizer.
It is **highly recommended** to name the ports of an app to provide clarity with respect to the app configuration and intended use of each port.
When using `container` or `container/bridge` mode networking, be sure to bind your application to the `containerPort`s you have specified in your `portMapping`s.
If you have set `containerPort` to `0`, this will be the same as `hostPort` and you can use the `$PORTxxx` environment variables.
Additional [per-task enviroment variables](task-environment-vars.html) are also provided.
### Discovery Via Mesos
#### DiscoveryInfo and port `labels`:
* `labels` may be defined for items of `portDefinitions` as well as for items of `portMappings`. These labels are sent to Mesos via `DiscoveryInfo` protobufs at instance-launch time.
* Given a mapping or endpoint, we generate a port `DiscoveryInfo` for every combination of specified protocols and associated networks. (IE, if an `Endpoint` specifies 2 protocols and is associated with 3 container networks, then a total of 6 `DiscoveryInfo` protobufs would be generated for that single `Endpoint`).
* Marathon injects a `network-scope` label into the port `DiscoveryInfo` to disambiguate between a *host-port* and *container-port*.
* A scope value of `host` is used for *host-port* discovery.
* A scope value of `container` is used for *container-port* discovery.
* For *container-port* discovery, Marathon also injects a `network-name` label into the respective port `DiscoveryInfo`.
#### Virtual addresses
See the DC/OS documentation for [virtual addresses (VIPs)](https://docs.mesosphere.com/latest/networking/load-balancing-vips/).
## Examples
### `host` Mode
`host` mode networking is the default networking mode for all apps.
If your app uses Docker containers, it not necessary to `EXPOSE` ports in your `Dockerfile`.
#### Using `host` Mode
Host mode is enabled by default for all apps and all container types.
If you wish to be explicit, you can also specify it manually through the `networks` property:
```json
"networks": [ { "mode": "host" } ],
"container": {
"type": "MESOS",
"docker": {
"image": "my-image:1.0"
}
},
You can specify the ports that are available through the portDefinitions
array:
"portDefinitions": [
{"port": 0, "name": "http"}, {"port": 0, "name": "https"}, {"port": 0, "name": "mon"}
],
In this example, we specify three dynamically assigned host ports, which would then be available to our command via the environment variables $PORT_HTTP
, $PORT_HTTPS
and $PORT_MON
.
Marathon will also associate three dynamically selected service ports to these three host ports.
You can also specify specific service ports:
"portDefinitions": [
{"port": 2001, "name": "http"}, {"port": 2002, "name": "https"}, {"port": 3000, "name": "mon"}
],
In this case, host ports $PORT_HTTP
, $PORT_HTTPS
and $PORT_MON
remain dynamically assigned.
However, the three service ports for this application are now 2001
, 2002
and 3000
.
In this example, as with the previous one, it is necessary to use a service discovery solution such as HAProxy to proxy requests from service ports to host ports.
If you want the application’s service ports to be equal to its host ports, you can set requirePorts
to true
(requirePorts
is false
by default).
This will tell Marathon to only schedule this application on agents that have these ports available:
"portDefinitions": [
{"port": 2001, "name": "http"}, {"port": 2002, "name": "https"}, {"port": 3000, "name": "mon"}
],
"requirePorts" : true
The service and host ports (including the environment variables $PORT_HTTP
, $PORT_HTTPS
, and $PORT_MON
), are both now 2001
, 2002
and 3000
.
This property is useful if you don’t use a service discovery solution to proxy requests from service ports to host ports.
Each port-definition in a portDefinitions
array allows you to specify a protocol
, a name
and labels
for each definition.
When starting new tasks, Marathon will pass this metadata to Mesos.
Mesos will expose this information in the discovery
field of the task.
Custom network discovery solutions can consume this field.
Example port-definition requesting a dynamic tcp
port named http
with the label VIP_0
set to 10.0.0.1:80
:
"portDefinitions": [
{
"port": 0,
"protocol": "tcp",
"name": "http",
"labels": {"VIP_0": "10.0.0.1:80"}
}
],
The port
field is mandatory.
The protocol
, name
and labels
fields are optional.
You can reference the host-ports in the Dockerfile for our fictitious app as follows:
CMD ./my-app --http-port=$PORT_HTTP --https-port=$PORT_HTTPS --monitoring-port=$PORT_MON
Alternatively, if you are not using Docker, or had specified a cmd
in your Marathon application definition, it works in the same way:
"cmd": "./my-app --http-port=$PORT_HTTP --https-port=$PORT_HTTPS --monitoring-port=$PORT_MON"
container
and container/bridge
ModeBridge mode networking allows you to map host ports to ports inside your containers. It is particularly useful if you are using a container image with fixed port assignments that you can’t modify.
Note: It is not necessary to EXPOSE
ports in your Dockerfile when using Docker container images.
container/bridge
ModeSpecify container/bridge
mode through the networks
property:
"networks": [ { "mode": "container/bridge" } ],
"container": {
"type": "MESOS",
"docker": {
"image": "my-image:1.0"
}
},
mesos-bridge
CNI pluginIf you are not using DC/OS and want to enable container/bridge
mode with the Universal Container Runtime (UCR), several more steps are necessary to install and use the mesos-bridge
CNI plugin.
Prerequisites
Clone the CNI repository from https://github.com/containernetworking/cni and build according to their instructions.
Navigate to or create a /var/lib/mesos/cni
directory on each of your agent nodes as well as config
and plugins
subdirectories.
Copy the contents of the bin
folder created in the previous step to /var/lib/mesos/cni/plugins
on each agent node.
Create a file called mesos-bridge.json
, copy the following configuration into it, and add it to /var/lib/mesos/cni/config
.
{
"name": "mesos-bridge",
"type": "mesos-cni-port-mapper",
"excludeDevices": ["mesos-bridge"],
"chain": "MESOS-BRIDGE-PORT-MAPPER",
"delegate": {
"type": "bridge",
"bridge": "mesos-bridge",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.1.1.0/24",
"routes": [{
"dst": "0.0.0.0/0"
}]
}
}
}
Soft-link the mesos-cni-port-mapper
plugin to your plugins directory using the following command
$ sudo ln -sf /usr/libexec/mesos/mesos-cni-port-mapper /var/lib/mesos/cni/plugins/mesos-cni-port-mapper
container
ModeSpecify container
mode through the network
property:
"networks": [ { "mode": "container", "name": "someUserNetwork" } ],
"container": {
"type": "MESOS",
"docker": {
"image": "my-image:1.0"
}
}
If there is a --default_network_name
configured for Marathon, then specifying a network name for the container
network is optional:
container
networks with an unspecified (null
) name
will inherit the value of the --default_network_name
flag.
Port mappings are similar to passing -p
into the Docker command line and specify a relationship between a port on the host machine and a port inside the container.
In this case, the portMappings
array is used instead of the portDefinitions
array used in host mode.
Port mappings are specified inside a container
object:
"networks": [ { "mode": "container/bridge" } ],
"container": {
"type": "MESOS",
"docker": {
"image": "my-image:1.0"
},
"portMappings": [
{ "containerPort": 0, "hostPort": 0, "name": "http" },
{ "containerPort": 0, "hostPort": 0, "name": "https" },
{ "containerPort": 0, "hostPort": 0, "name": "mon" }
]
}
In this example, we specify 3 mappings.
A value of 0
will ask Marathon to dynamically assign a value for hostPort
.
In this case, setting containerPort
to 0
will cause it to have the same value as hostPort
.
These values are available inside the container as $PORT_HTTP
, $PORT_HTTPS
and $PORT_MON
respectively.
Alternatively, if our process running in the container had fixed ports, we might do something like the following:
"networks": [ { "mode": "container/bridge" } ],
"container": {
"type": "MESOS",
"docker": {
"image": "my-image:1.0"
},
"portMappings": [
{ "containerPort": 80, "hostPort": 0, "name": "http" },
{ "containerPort": 443, "hostPort": 0, "name": "https" },
{ "containerPort": 4000, "hostPort": 0, "name": "mon" }
]
}
In this case, Marathon will randomly allocate host ports and map these to ports 80
, 443
and 4000
respectively.
The $PORT_xxx
variables refer to the host ports.
In this case, $PORT_HTTP
will be set to the value of hostPort
for the first mapping, and so on.
You can also specify the protocol for these port mappings.
The default is tcp
:
"networks": [ { "mode": "container/bridge" } ],
"container": {
"type": "MESOS",
"docker": {
"image": "my-image:1.0"
},
"portMappings": [
{ "containerPort": 80, "hostPort": 0, "name": "http", "protocol": "tcp" },
{ "containerPort": 443, "hostPort": 0, "name": "https", "protocol": "tcp" },
{ "containerPort": 4000, "hostPort": 0, "name": "mon", "protocol": "udp" }
]
}
It is possible to specify multiple protocols by using a comma as a separator: udp,tcp
(note the lack of spaces).
By default, Marathon will create associated service ports for each of these declared mappings and dynamically assign them values.
Service ports are used by service discovery solutions and it is often desirable to set these to well known values.
You can assign well-known service-port values by defining a servicePort
for each mapping:
"networks": [ { "mode": "container/bridge" } ],
"container": {
"type": "MESOS",
"docker": {
"image": "my-image:1.0"
},
"portMappings": [
{ "containerPort": 80, "hostPort": 0, "name": "http", "protocol": "tcp", "servicePort": 2000 },
{ "containerPort": 443, "hostPort": 0, "name": "https", "protocol": "tcp", "servicePort": 2001 },
{ "containerPort": 4000, "hostPort": 0, "name": "mon", "protocol": "udp", "servicePort": 3000 }
]
},
In this example, the host ports $PORT_HTTP
, $PORT_HTTPS
and $PORT_MON
remain dynamically assigned.
However, the service ports for this application are now 2001
, 2002
and 3000
.
An external proxy, like HAProxy, may be configured to route from the service ports to the host ports.
If you set containerPort
to 0
, then you should specify ports in the Dockerfile for our fictitious app as follows:
CMD ./my-app --http-port=$PORT_HTTP --https-port=$PORT_HTTPS --monitoring-port=$PORT_MON
However, if you have defined non-zero containerPort
values, simply use the same values in the Dockerfile:
CMD ./my-app --http-port=80 --https-port=443 --monitoring-port=4000
Alternatively, you can specify a cmd
in your Marathon application definition, it works in the same way as before:
"cmd": "./my-app --http-port=$PORT_HTTP --https-port=$PORT_HTTPS --monitoring-port=$PORT_MON"
Or, if you’ve used fixed values:
"cmd": "./my-app --http-port=80 --https-port=443 --monitoring-port=4000"