Coder Social home page Coder Social logo

allegro / marathon-consul Goto Github PK

View Code? Open in Web Editor NEW
189.0 14.0 31.0 24.56 MB

Integrates Marathon apps with Consul service discovery.

License: Apache License 2.0

Makefile 1.42% Go 98.20% Shell 0.32% Dockerfile 0.05%
marathon consul discovery hashicorp mesos

marathon-consul's Introduction

marathon-consul Build Status Coverage Status Go Report Card Download latest version

We no longer develop this project. If you'd like to take over and continue its maintenance, please fork this repository and let us know by Github issues.

Register Marathon Tasks as Consul Services for service discovery.

marathon-consul takes information provided by the Marathon event bus and forwards it to Consul agents. It also re-syncs all the information from Marathon to Consul on startup and repeats it with given interval.

Note: In the future release Event Bus (callbacks) will be considered deprecated and eventually removed in favor of Event Stream (SSE). Right now marathon-consul is supporting both solutions. SSE is provided as experimental feature, disabled by default (more).

Code

This project is based on

Differences

  • CiscoCloud/marathon-consul copies application information to Consul KV while allegro/marathon-consul registers tasks as Consul services (it is more similar to CiscoCloud/mesos-consul)
  • CiscoCloud/mesos-consul uses polling while allegro/marathon-consul uses Marathon's event bus to detect changes
  • CiscoCloud/marathon-consul is no longer developed (see comment)

Installation

Installing from source code

To simply compile and run the source code:

go run main.go [options]

To run the tests:

make test

To build the binary:

make build

Installing from binary distribution

Binary distribution of marathon-consul can be downloaded directly from the releases page. Download the build dedicated to your OS. After unpacking the archive, run marathon-consul binary. You can also add some options, for example:

marathon-consul --marathon-location=marathon.service.consul:8080 --sync-interval=5m --log-level=debug

Installing via APT package manager

If you are a Debian/Ubuntu user, you can easily install marathon-consul as a deb package using APT package manager. Both upstart and systemd service managers are supported. All releases are published as deb packages to our repository at Bintray.

To install marathon-consul with apt-get, simply follow the instructions:

# add our public key to apt
curl -s https://bintray.com/user/downloadSubjectPublicKey?username=allegro | sudo apt-key add -
# add the repository url
echo "deb http://dl.bintray.com/v1/content/allegro/deb /" | sudo tee /etc/apt/sources.list.d/marathon-consul.list
# update apt cache
sudo apt-get -y update
# install latest release of marathon-consul
sudo apt-get -qy install marathon-consul

Run it with service marathon-consul start. The configuration file is located at /etc/marathon-consul.d/config.json.

Installing with Docker

To build docker image run

make docker

Then you can run it with

docker run -d -P allegro/marathon-consul [options]

If you want to use SSL you will need to expose cert store to Docker. The Following line is only example, your cert store might be different depending on your system.

docker run '/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt' -P  allegro/marathon-consul

Usage

Marathon masters

  • marathon-consul should be installed on all Marathon masters

Mesos agents

  • Consul Agents should be available on every Mesos agent.
  • Tasks will be registered at the Mesos slave they run on.

Tagging tasks

  • Task labels are used by marathon-consul to register tasks in Consul.
  • Only tasks which are labeled as consul will be registered in Consul. If the consul label is left blank like "consul": "", the task will be registered with the app name by default. A different name can be provided as the label's value, e.g. "consul": "customName". As an exception to this rule, for backward compatibility with the 0.3.x branch, a value of true is resolved to the default name.
  "id": "my-new-app",
  "labels": {
    "consul": ""
  }
  • Only services with tag specified by consul-tag property will be maintained. By default, "consul-tag": "marathon" is automatically added when the task is registered.
  • Labels with a value of tag are converted to consul-tags, and appear in Consul as ServiceTags.
  • For example, we can set these tags in an app definition like:
  "id": "my-new-app",
  "labels": {
    "consul": "",
    "varnish": "tag",
    "metrics": "tag"
  }
  • If marathon-consul registers the app with Consul, we can then query Consul and see these tags appear:
curl -X GET http://localhost:8500/v1/catalog/service/my-new-app
...
"ServiceName": "my-new-app",
"ServiceTags": [
  "marathon",
  "varnish",
  "metrics",
  "marathon-task:my-new-app.6a95bb03-6ad3-11e6-beaf-080027a7aca0"
],

  • Every service registration contains an additional tag marathon-task specifying the Marathon task id related to this registration.
  • If there are multiple ports in use for the same app, note that only the first one will be registered by marathon-consul in Consul.

If you need to register your task under multiple ports, refer to Advanced usage section below.

Task healthchecks

  • At least one HTTP healthcheck should be defined for a task. The task is registered when Marathon marks it as alive.
  • The provided HTTP healthcheck will be transferred to Consul.
  • See this for more details.

Command healthchecks

Healthchecks commands are registered in Consul with a simple variable substitution.

  • $HOST is replaced by task hostname.
  • $PORT0, $PORT1... are replaced by port number defined in task.

Using a special shell syntax can break this variable substitution ($HOST, ${PORT0} ...)

Sync

  • The scheduled Marathon-consul sync may run in two modes:
    • Only on node that is the current Marathon-leader, marathon-leader parameter should be set to hostname:port the current node appears in the Marathon cluster. This mode is enabled by default and the marathon-leader property is set to the hostname resolved by OS. Note that there is a difference between marathon-leader and marathon-location: marathon-leader is used for node leadership detection (should be set to cluster-wide node name), while marathon-location is used for connection purpose (may be set to localhost)
    • On every node, sync-force parameter should be set to true
  • If marathon-consul fails on startup sync and you see following error "Can't get Consul services: No Consul client available in agents cache" it may be caused by empty consul agents cache. If this occurs try configuring --consul-local-agent-host to Consul Master or Consul agent.

Options

Argument Default Description
config-file Path to a JSON file to read configuration from. Note: Will override options set earlier on the command line
consul-auth false Use Consul with authentication
consul-auth-password The basic authentication password
consul-auth-username The basic authentication username
consul-enable-tag-override false Disable the anti-entropy feature for all services
consul-ignored-healthchecks A comma separated blacklist of Marathon health check types that will not be migrated to Consul, e.g. command,tcp
consul-local-agent-host Consul Agent hostname or IP that should be used for startup sync and service listing operations
consul-name-separator . Separator used to create default service name for Consul
consul-get-services-retry 3 Number of retries on failure when performing requests to Consul. Each retry uses different cached agent
consul-max-agent-failures 3 Max number of consecutive request failures for agent before removal from cache
consul-port 8500 Consul port
consul-ssl false Use HTTPS when talking to Consul
consul-ssl-ca-cert Path to a CA certificate file, containing one or more CA certificates to use to validate the certificate sent by the Consul server to us
consul-ssl-cert Path to an SSL client certificate to use to authenticate to the Consul server
consul-ssl-verify true Verify certificates when connecting via SSL
consul-tag marathon Common tag name added to every service registered in Consul, should be unique for every Marathon-cluster connected to Consul
consul-timeout 3s Time limit for requests made by the Consul HTTP client. A Timeout of zero means no timeout
consul-token The Consul ACL token
events-queue-size 1000 Size of events queue
event-max-size 4096 Maximum size of event to process (bytes)
listen :4000 Accept connections at this address
log-file Save logs to file (e.g.: /var/log/marathon-consul.log). If empty logs are published to STDERR
log-format text Log format: JSON, text
log-level info Log level: panic, fatal, error, warn, info, or debug
marathon-location localhost:8080 Marathon URL
marathon-password Marathon password for basic auth
marathon-protocol http Marathon protocol (http or https)
marathon-ssl-verify true Verify certificates when connecting via SSL
marathon-timeout 30s Time limit for requests made by the Marathon HTTP client. A Timeout of zero means no timeout
marathon-username Marathon username for basic auth
marathon-leader Marathon cluster-wide node name (defaults to :8080), the some leader specific calls will be made only if the specified node is the current Marathon-leader. Set to * to always act like a Leader.
metrics-interval 30s Metrics reporting interval
metrics-location Graphite URL (used when metrics-target is set to graphite)
metrics-prefix default Metrics prefix (default is resolved to .<app_name>
metrics-target stdout Metrics destination stdout or graphite (empty string disables metrics)
sentry-dsn Sentry DSN. If it's not set sentry will be disabled
sentry-env Sentry environment
sentry-level error Sentry alerting level (info
sentry-timeout 1s Sentry hook initialization timeout
sse-retries 0 Number of times to recover SSE stream.
sse-retry-backoff 0s Configuration of initial time between retries to recover SSE stream.
sync-enabled true Enable Marathon-consul scheduled sync
sync-force false Force leadership-independent Marathon-consul sync (run always)
sync-interval 15m0s Marathon-consul sync interval
workers-pool-size 10 Number of concurrent workers processing events

Endpoints

Endpoint Description
/health healthcheck - returns OK

Advanced usage

Register under multiple ports

If you need to map your Marathon task into multiple service registrations in Consul, you can configure marathon-consul via Marathon's portDefinitions:

  "id": "my-new-app",
  "labels": {
    "consul": "",
    "common-tag": "tag"
  },
  "portDefinitions": [
    {
      "labels": {
        "consul": "my-app-custom-name"
      }
    },
    {
      "labels": {
        "consul": "my-app-other-name",
        "specific-tag": "tag"
      }
    }
  ]

This configuration would result in two service registrations:

curl -X GET http://localhost:8500/v1/catalog/service/my-app-custom-name
...
"ServiceName": "my-app-custom-name",
"ServiceTags": [
  "marathon",
  "common-tag",
  "marathon-task:my-new-app.6a95bb03-6ad3-11e6-beaf-080027a7aca0"
],
"ServicePort": 31292,
...

curl -X GET http://localhost:8500/v1/catalog/service/my-app-other-name
...
"ServiceName": "my-app-other-name",
"ServiceTags": [
  "marathon",
  "common-tag",
  "specific-tag",
  "marathon-task:my-new-app.6a95bb03-6ad3-11e6-beaf-080027a7aca0"
],
"ServicePort": 31293,
...

If any port definition contains the consul label, then advanced configuration mode is enabled. As a result, only the ports containing this label are registered, under the name specified as the label's value – with empty value resolved to default name. Names don't have to be unique – you can have multiple registrations under the same name, but on different ports, perhaps with different tags. Note that the consul label still needs to be present in the top-level application labels, even though its value won't have any effect.

Tags configured in the top-level application labels will be added to all registrations. Tags configured in the port definition labels will be added only to corresponding registrations.

All registrations share the same marathon-task tag.

Migration to version 1.x.x

Until 1.x.x marathon-consul would register services in Consul with registration id equal to related Marathon task id. Since 1.x.x registration ids are different and an additional tag, marathon-task, is added to each registration.

If you update marathon-consul from version 0.x.x to 1.x.x, expect the synchronization phase during the first startup to reregister all healthy services managed by marathon-consul to the new format. Unhealthy services will get deregistered in the process.

SSE Support

While using SSE please consider:

  • SSE is using Web module config for queues, event sizes, in the future will be moved to sse module,
  • SSE is using marathon-leader config for determining current leader, when this value match leader returned by marathon (/v2/leader endpoint) then SSE is started on this instance,
  • when enabled SSE is spawning its own own set of workers and separated dispatcher,
  • be advised to disable marathon callback subscription when enabling SSE, otherwise it might result in doubling registers and deregisers.

HTTP callbacks support

Marathon-Consul does not support HTTP callbacks. Marathon deprecated support for HTTP callbacks in 1.4. This mechanism is no longer available starting from Marathon 1.5.

Known limitations

The following section describes known limitations in marathon-consul.

  • In Marathon when a deployment changing the application's service name (by changing its labels) is being stopped, it changes app's configuration anyway. This means we loose the link between the app and the services registered with the old name in Consul. Later on, if another deployment takes place, new services are registered with a new name, the old ones are not being deregistered though. A scheduled sync is required to wipe them out.

Release

To release new version of marathon-consul follow steps:

  1. Commit all changes you need for release to master branch.
  2. git checkout master
  3. git checkout -b release/<version> e.g., git checkout -b release/1.3.1
  4. make version v=<version> e.g., make version v=1.3.1
  5. git push
  6. Create pull request from branch release/<version> to master.
  7. Once pull request gets merged put tag on this commit (remember to update your master with git pull) git tag <version> -f e.g., git tag 1.3.1 -f. Create annotated tag with release notes in it.
  8. Travis will automatically prepare github release from tag on master. Go there and update release notes.
  9. Copy github release to bintray.

License

Marathon-consul is released under the Apache 2.0 license (see LICENSE)

marathon-consul's People

Contributors

adamdubiel avatar alfredbroda avatar brianhicks avatar chemicl avatar chrisaubuchon avatar cynial avatar dankraw avatar fortrieb avatar guilhem avatar janisz avatar kamilchm avatar marcust avatar medzin avatar mhamann avatar misiek08 avatar ojagodzinski avatar pbetkier avatar pszymczyk avatar slonka avatar strzelecki-maciek avatar tokenrain avatar tomez avatar wendigo avatar wind0r avatar zanes2016 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

marathon-consul's Issues

Not handling properly deployment events for apps with custom parent groups in Marathon

Currently RestartedConsulApps/StoppedConsulApps/RenamedConsulApps api of deployment events search for the apps in /Plan/Original/Apps of the event - this is will catch only apps that are created in root of the deployment plan.
When application has a different parent group, for example /com.example.tech/applicationName we should look in /Plan/Original/Groups[{group_index}]/Apps where {group_index} points a record having id: /com.example.tech.

Health check invalid port number

Health check are always created in consul with port number from $PORT0 marathon variable. This is only valid for health checks with Port Type: Port Index.
For health checks with Port Type: Port Number port number should be taken from key "port" from marathon health check definition.

Eg.: marathon health check definition with Port Type: Port Number with static port 8123:

"healthChecks": [
    {
      "path": "/v1/version",
      "protocol": "HTTP",
      "gracePeriodSeconds": 60,
      "intervalSeconds": 10,
      "timeoutSeconds": 5,
      "maxConsecutiveFailures": 3,
      "ignoreHttp1xx": false,
      "port": 8123
    }
  ]

So there is "port": 8123 instead of "portIndex": 0,.

This check in consul looks like:

  {
    "ModifyIndex": 5488518,
    "CreateIndex": 5488430,
    "Node": "dev-mesos-p2",
    "CheckID": "service:ops_mesos-dns.cd314424-6b12-11e6-8d24-0242f3fd597e",
    "Name": "Service 'mesos-dns' check",
    "Status": "critical",
    "Notes": "",
    "Output": "Get http://10.14.30.74:31313/v1/version: dial tcp 10.14.30.74:31313: getsockopt: connection refused",
    "ServiceID": "ops_mesos-dns.cd314424-6b12-11e6-8d24-0242f3fd597e",
    "ServiceName": "mesos-dns"
  }

Port number should be 8123 instead of 31313.
Port for check was taken from $PORT0 instead of static port key from check definition.

Consul does not support dots in service names for DNS queries

Services names must not contain a dot (.) otherwise you could not use the Consul DNS API. For example
a Marathon App with id "/demo/hello/world" is registered by default as Consul Service "demo.hello.world"

From Consul DNS API documentation (https://www.consul.io/docs/agent/services.html):
"DNS-compliant service and tag names may contain any alpha-numeric characters, as well as dashes. Dots are not supported because Consul internally uses them to delimit service tags."

It would be nice if you could implement a different naming strategy, e.g. use dashes instead of dots.

Current workaround for us is to specify a custom consul service name.

Add doc string for public methods

Use golint as a style check.

➜  marathon-consul git:(master) golint ./... 
main.go:14:5: exported var VERSION should have comment or be unexported
apps/app.go:8:6: exported type HealthCheck should have comment or be unexported
apps/app.go:18:6: exported type AppWrapper should have comment or be unexported
apps/app.go:22:6: exported type AppsResponse should have comment or be unexported
apps/app.go:22:6: type name will be used as apps.AppsResponse by other packages, and that stutters; consider calling this Response
apps/app.go:26:6: exported type App should have comment or be unexported
apps/app.go:33:1: comment on exported type AppId should be of the form "AppId ..." (with optional leading article)
apps/app.go:36:6: type AppId should be AppID
apps/app.go:42:1: exported method AppId.ConsulServiceName should have comment or be unexported
apps/app.go:46:1: exported method App.IsConsulApp should have comment or be unexported
apps/app.go:51:1: exported method App.ConsulServiceName should have comment or be unexported
apps/app.go:61:1: exported function ParseApps should have comment or be unexported
apps/app.go:68:1: exported function ParseApp should have comment or be unexported
apps/task.go:7:6: exported type Task should have comment or be unexported
apps/task.go:16:1: comment on exported type TaskId should be of the form "TaskId ..." (with optional leading article)
apps/task.go:18:6: type TaskId should be TaskID
apps/task.go:24:6: exported type HealthCheckResult should have comment or be unexported
apps/task.go:28:6: exported type TasksResponse should have comment or be unexported
apps/task.go:32:1: exported function ParseTasks should have comment or be unexported
apps/task.go:39:1: exported function ParseTask should have comment or be unexported
apps/task.go:45:1: exported method Task.IsHealthy should have comment or be unexported
config/config.go:17:6: exported type Config should have comment or be unexported
config/config.go:35:1: exported function New should have comment or be unexported
consul/agents.go:15:6: exported type Agents should have comment or be unexported
consul/agents.go:21:6: exported type ConcurrentAgents should have comment or be unexported
consul/agents.go:27:1: exported function NewAgents should have comment or be unexported
consul/agents.go:34:1: exported method ConcurrentAgents.GetAnyAgent should have comment or be unexported
consul/agents.go:45:28: method getRandomAgentIpAddress should be getRandomAgentIPAddress
consul/agents.go:47:17: should omit 2nd value from range; this loop is equivalent to `for ipAddress := range ...`
consul/agents.go:54:1: exported method ConcurrentAgents.RemoveAgent should have comment or be unexported
consul/agents.go:68:1: exported method ConcurrentAgents.GetAgent should have comment or be unexported
consul/config.go:5:6: exported type ConsulConfig should have comment or be unexported
consul/config.go:5:6: type name will be used as consul.ConsulConfig by other packages, and that stutters; consider calling this Config
consul/config.go:17:6: exported type Auth should have comment or be unexported
consul/consul.go:13:6: exported type ConsulServices should have comment or be unexported
consul/consul.go:13:6: type name will be used as consul.ConsulServices by other packages, and that stutters; consider calling this Services
consul/consul.go:17:13: interface method parameter serviceId should be serviceID
consul/consul.go:21:6: exported type Consul should have comment or be unexported
consul/consul.go:26:6: exported type ServicesProvider should have comment or be unexported
consul/consul.go:28:1: exported function New should have comment or be unexported
consul/consul.go:35:1: exported method Consul.GetServices should have comment or be unexported
consul/consul.go:42:45: should drop = nil from declaration of var services; it is the zero value
consul/consul.go:78:1: exported method Consul.GetAllServices should have comment or be unexported
consul/consul.go:119:1: exported method Consul.Register should have comment or be unexported
consul/consul.go:157:1: exported method Consul.Deregister should have comment or be unexported
consul/consul.go:157:29: method parameter serviceId should be serviceID
consul/consul.go:168:29: method parameter serviceId should be serviceID
consul/consul.go:183:1: exported method Consul.GetAgent should have comment or be unexported
consul/consul_stub.go:8:6: exported type ConsulStub should have comment or be unexported
consul/consul_stub.go:8:6: type name will be used as consul.ConsulStub by other packages, and that stutters; consider calling this Stub
consul/consul_stub.go:15:1: exported function NewConsulStub should have comment or be unexported
consul/consul_stub.go:19:1: exported function NewConsulStubWithTag should have comment or be unexported
consul/consul_stub.go:28:1: exported method ConsulStub.GetAllServices should have comment or be unexported
consul/consul_stub.go:43:1: exported method ConsulStub.GetServices should have comment or be unexported
consul/consul_stub.go:63:1: exported method ConsulStub.Register should have comment or be unexported
consul/consul_stub.go:66:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
consul/consul_stub.go:76:1: exported method ConsulStub.Deregister should have comment or be unexported
consul/consul_stub.go:76:33: method parameter serviceId should be serviceID
consul/consul_stub.go:79:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
consul/consul_stub.go:85:1: exported method ConsulStub.RegisteredServicesIds should have comment or be unexported
consul/consul_stub.go:94:1: exported method ConsulStub.GetAgent should have comment or be unexported
consul/consul_test_server.go:10:1: exported function CreateConsulTestServer should have comment or be unexported
consul/consul_test_server.go:16:1: exported function ConsulClientAtServer should have comment or be unexported
consul/consul_test_server.go:16:6: func name will be used as consul.ConsulClientAtServer by other packages, and that stutters; consider calling this ClientAtServer
events/deployment_info.go:9:6: exported type DeploymentEvent should have comment or be unexported
events/deployment_info.go:15:6: exported type Plan should have comment or be unexported
events/deployment_info.go:20:6: exported type Deployments should have comment or be unexported
events/deployment_info.go:21:2: struct field Id should be ID
events/deployment_info.go:26:6: exported type CurrentStep should have comment or be unexported
events/deployment_info.go:30:6: exported type Action should have comment or be unexported
events/deployment_info.go:32:2: struct field AppId should be AppID
events/deployment_info.go:76:1: exported method DeploymentEvent.StoppedConsulApps should have comment or be unexported
events/deployment_info.go:80:1: exported method DeploymentEvent.RestartedConsulApps should have comment or be unexported
events/deployment_info.go:84:1: exported method DeploymentEvent.RenamedConsulApps should have comment or be unexported
events/deployment_info.go:185:6: range var appId should be appID
events/deployment_info.go:185:13: should omit 2nd value from range; this loop is equivalent to `for appId := range ...`
events/deployment_info.go:193:1: exported function ParseDeploymentEvent should have comment or be unexported
events/events.go:10:2: exported var ErrNoEvent should have comment or be unexported
events/events.go:13:6: exported type Event should have comment or be unexported
events/events.go:18:6: exported type BaseEvent should have comment or be unexported
events/events.go:22:1: exported function EventType should have comment or be unexported
events/task_health_change.go:8:6: exported type TaskHealthChange should have comment or be unexported
events/task_health_change.go:17:1: exported function ParseTaskHealthChange should have comment or be unexported
marathon/config.go:5:6: exported type Config should have comment or be unexported
marathon/marathon.go:16:6: exported type Marathoner should have comment or be unexported
marathon/marathon.go:23:6: exported type Marathon should have comment or be unexported
marathon/marathon.go:30:6: exported type LeaderResponse should have comment or be unexported
marathon/marathon.go:34:1: exported function New should have comment or be unexported
marathon/marathon.go:58:1: exported method Marathon.App should have comment or be unexported
marathon/marathon.go:58:23: method parameter appId should be appID
marathon/marathon.go:69:1: exported method Marathon.Apps should have comment or be unexported
marathon/marathon.go:79:1: exported method Marathon.Tasks should have comment or be unexported
marathon/marathon.go:85:2: var trimmedAppId should be trimmedAppID
marathon/marathon.go:94:1: exported method Marathon.Leader should have comment or be unexported
marathon/marathon.go:142:17: should omit type string from declaration of var statusCode; it will be inferred from the right-hand side
marathon/marathon_stub.go:8:6: exported type MarathonerStub should have comment or be unexported
marathon/marathon_stub.go:15:1: exported method MarathonerStub.Apps should have comment or be unexported
marathon/marathon_stub.go:19:1: exported method MarathonerStub.App should have comment or be unexported
marathon/marathon_stub.go:22:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
marathon/marathon_stub.go:27:1: exported method MarathonerStub.Tasks should have comment or be unexported
marathon/marathon_stub.go:27:31: method parameter appId should be appID
marathon/marathon_stub.go:30:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
marathon/marathon_stub.go:35:1: exported method MarathonerStub.Leader should have comment or be unexported
marathon/marathon_stub.go:39:1: exported function MarathonerStubWithLeaderForApps should have comment or be unexported
marathon/marathon_stub.go:45:1: exported function MarathonerStubForApps should have comment or be unexported
metrics/config.go:5:6: exported type Config should have comment or be unexported
metrics/metrics.go:22:1: exported function Mark should have comment or be unexported
metrics/metrics.go:27:1: exported function Time should have comment or be unexported
metrics/metrics.go:32:1: exported function UpdateGauge should have comment or be unexported
metrics/metrics.go:37:1: exported function Init should have comment or be unexported
metrics/metrics.go:66:1: exported function TargetName should have comment or be unexported
sync/config.go:5:6: exported type Config should have comment or be unexported
sync/sync.go:15:6: exported type Sync should have comment or be unexported
sync/sync.go:21:1: exported function New should have comment or be unexported
sync/sync.go:25:1: exported method Sync.StartSyncServicesJob should have comment or be unexported
sync/sync.go:53:1: exported method Sync.SyncServices should have comment or be unexported
sync/sync.go:127:12: should omit 2nd value from range; this loop is equivalent to `for node := range ...`
sync/sync.go:138:3: var serviceId should be serviceID
sync/sync_test.go:157:49: method parameter instanceId should be instanceID
sync/sync_test.go:161:41: method parameter serviceId should be serviceID
sync/sync_test.go:364:30: method parameter appId should be appID
sync/sync_test.go:387:33: method parameter serviceId should be serviceID
utils/apps.go:8:1: exported function ConsulApp should have comment or be unexported
utils/apps.go:12:1: exported function ConsulAppWithUnhealthyInstances should have comment or be unexported
utils/apps.go:16:1: exported function NonConsulApp should have comment or be unexported
utils/net.go:8:1: exported function HostToIPv4 should have comment or be unexported
web/event_handler.go:16:6: exported type EventHandler should have comment or be unexported
web/event_handler.go:21:1: exported function NewEventHandler should have comment or be unexported
web/event_handler.go:28:1: exported method EventHandler.Handle should have comment or be unexported
web/event_handler.go:91:2: var appId should be appID
web/event_handler.go:130:6: func findTaskById should be findTaskByID
web/event_handler.go:130:35: don't use underscores in Go names; func parameter tasks_ should be tasks
web/event_handler.go:283:6: func replaceTaskIdWithId should be replaceTaskIDWithID
web/event_handler_test.go:824:37: func parameter taskId should be taskID
web/health_handler.go:8:1: exported function HealthHandler should have comment or be unexported

Refactor consul service dependency

Extract domain service object that could be used instead of passing consul.Service. This will give us ability to decouple marathon instances ID and consul service ID.

Easy migration between service names

In order to have some backwards compatibility when migrating from any existing discovery mechanism, i would like marathon-consul to register app instances under multiple names:

  • one is Marathon app-id
  • second one is custom name (if provided) as in #27

This feature of course can be switched on/off.

Manual trigger of sync

Expose endpoint for triggering sync. Operator should be able to execute sync manually when he notices differences between Marathon and Consul states to resolve the issue faster.

Differences may occur e.g. when marathon-consul doesn't receive events from Marathon.

Things to consider:

  • usability: when running multiple instances of marathon-consul as a cluster how to know which instance to call? Currently sync may be executed on the leader only.
  • safety: would it be safe if manual sync and scheduled sync were running simultaneously? If not, how to introduce mutual exclusion?

Retry obtaining application details

When marathon-consul get tash health change event. It ask marathon for application details to check if and how task should be registered in consul and get task details (ip and port). When marathon is under heavy load it sends events but API returns stale data. And we do not register application due to missing details. We need to retry getting app information to prevent this happen. web/event_handler.go#L119

Error while receiving deployment event from Marathon

After registering to an event bus in Marathon I get the following error:

{"EventType":"deployment_success","level":"debug","msg":"Received event","time":"2016-01-25T14:18:50Z"}
{"error":"Cannot handle deployment_success","level":"debug","msg":"Returning 400 due to malformed request","time":"2016-01-25T14:18:50Z"}

We are running Marathon: 0.13.0

Comparison to CiscoCloud/marathon-consul

I noticed that this project is based on CiscoCloud's but..
What are the main differences between allegro/marathon-consul and CiscoCloud/marathon-consul and when shall one use one over the other?

Specify custom service name

I would like to be able to specify custom service name, instead of using marathon app id. Currently you need to add consul:true label to be visible to marathon-consul. We could change this syntax to: consul:custom_service_name. true could be a protected keyword for when you don't want any custom name (and to maintain backwards compatibility).

Handle dependecies

Currently we do not specify version of dependencies we are using. Consider using some type of package manager or vendor directory to make builds independent of machine state and dependencies changes.

Event based notification

Hi All,
I was under the impression that this version of marathon-consul was event based. As it was the ciscocloud version was based on polling.

I am a bit confused as I have to use two flags (--sync-interval=30s --sync-force=true) to actually get it to recognise that services are registering/deregistering. In reality I want to have a 1s value but I am unsure if this is correct...

I cannot see any difference (in functionality) with the old ciscocloud version.

Any advice for me?

Event-sourced requests to Consul should not be performed using random Agent

Example scenario:

  • we get TASK_FAILED
  • we check if there is a service registered with the same ID and properly labeled -> we call GetServices using random agent that may be down -> we have an error, the service is not being deregistered.

We should use random agents only for sync, we could remove invalid agents from cache.

error=Can't get Consul services: No Consul client available in agents cache

Hello,

I apologize ahead of time. I am new to Mesos/Marathon and even newer with allegro/marathon-consul. I have read the README and based my command on the example therein:

./marathon-consul --marathon-location=our-marathonservers.service.consul=8181 --sync-interval=2m --sync-force=true --log-level=debug
  • My infrastructure has a fully-working consul fabric
  • All servers in the infrastructure are resolvable by hostname.node.consul.
  • The services they avail are resolvable by servicename.node.consul.
  • All servers have consul accessible via localhost:8500,8600

I presumed that config-file meant a consul configuration. I pointed it to the configuration file that already exists in the host. This is the same command with the config-file specified:

./marathon-consul --config-file=/etc/consul/config.json --marathon-location=marathon.service.consul=8181 --sync-interval=2m --sync-force=true --log-level=debug

Running either commands above gives this error:

error=Can't get Consul services: No Consul client available in agents cache

Could someone please help me? Thank you.

Use marathon labels filter API

When we obtain apps from marathon we download all apps and then filter it on our side. We could use

GET /v2/apps?label={labelSelectorQuery}

endpoint and download only apps that are interesting for us. This functions should be changed.

Create integration tests

Right now we have only synthetic tests that check how marathon-consul behave when it gets specific event form Marathon and how it interacts with Marathon and Consul then. Before merging commit or releasing we go thru manual testing but we don't have prepared test cases.

We need set of integration test that will launch Mesos, Marathon, Consul and marathon-consul and try to deploy, change, delete tasks/apps/groups on Marathon and see the result in Consul.

We need to prepare:

  1. Multiplatform environment to run integration tests (we can use https://github.com/dankraw/marathon-consul-playground)
  2. Integration tests (If we use Vagrant then this could be just golang test that will launch vagrant and m-c and perform rest calls to marathon and check effect in consul)

Can we include all labels: values in the service definition written to consul...

I am trying to recreate the functionality between ciscocloud/marathonpconsul and ciscocloud/haproxy-consul

In the original setup the full service definition as described in marathon was inserted straight into kv's in consul.

I can see this function:

func (c _Consul) marathonTaskToConsulService(task *apps.Task, app *apps.App) (_consulapi.AgentServiceRegistration, error) {
IP, err := utils.HostToIPv4(task.Host)
if err != nil {
return nil, err
}
serviceAddress := IP.String()

return &consulapi.AgentServiceRegistration{
    ID:      task.ID.String(),
    Name:    app.ConsulServiceName(),
    Port:    task.Ports[0],
    Address: serviceAddress,
--->    Tags:    c.marathonLabelsToConsulTags(app.Labels), <----
    Checks:  c.marathonToConsulChecks(task, app.HealthChecks, serviceAddress),
}, nil

}

I would have assumed that it would have picked up all these labels from my service definition:

"ports": [10000],
"labels": {
"consul": "basic-3",
"HAPROXY_HTTP": "true",
"HTTP_PORT_IDX_0_NAME": "basic-3",
"HTTP_PORT_IDX_10000_NAME": "basic-3"
},

but it doesn't...

Additionally I wanted to pull in the ports.

Any advice?

Revert marathon tag label semantics

With the current implementation, tags that need to be passed to consul are filtered by their value. this feels slightly odd and I was wondering whether having a labels called consul_tags that includes an array of strings would make more sense.

Would altering https://github.com/allegro/marathon-consul/blob/master/consul/consul.go#L268
and https://github.com/allegro/marathon-consul/blob/master/apps/app.go#L32 be enough for this or would it require more substantial changes?

Drop not supported events

Currently we are using only 2 events status_update_event and health_status_changed_event. Marathon has about 30 implementations of MarathonEvent and all that events could be sent to marathon-consul. Some events are big and Marathon doesn't allow filtering events befor sent (see #3154). There is no point in pushing all events into event queue. Better drop all unwanted events before they gets to queue.

Support multiple ports

Most of our marathon apps have only one portMapping but sometimes we have an app with multiple ports where one of the ports will be used for (http) health checks.

In newer versions of marathon you can specify labels for each port in the "portMappings". Maybe this is something we can use to register services in consul?

Example marathon app:

{
  "id": "/hello-world",
  "cpus": 0.1,
  "mem": 256,
  "instances": 1,
  "container": {
    "type": "DOCKER",
    "docker": {
      "image": "my-hello-world:latest",
      "network": "BRIDGE",
      "portMappings": [
        {
          "containerPort": 8080,
          "hostPort": 0,
          "protocol": "tcp",
          "name": "api",
          "labels": { 
              "consul": "helloworld-api"
          }
        },
        {
          "containerPort": 8081,
          "hostPort": 0,
          "protocol": "tcp",
          "name": "actuator",
          "labels": {
              "consul": "helloworld-health"
          }
        }
      ]
   }
  },
  "healthChecks": [
    {
      "path": "/health",
      "protocol": "HTTP",
      "portIndex": 1,
      "gracePeriodSeconds": 300,
      "intervalSeconds": 10,
      "timeoutSeconds": 10,
      "maxConsecutiveFailures": 3,
      "ignoreHttp1xx": false
    }
  ]
}

Disabling command healthchecks registration

Currently marathon-consul migrates all healthchecks from marathon/mesos to consul. I would like to have a possibility to disable migration for healthchecks type of command.

Adding configurable tag name to Consul servies when registering

We should allow setting a configurable tag name instead of "marathon" for all Consul services.
This will allow coexist multiple mesos/marathon clusters ("marathon-cluster1", "marathon-cluster2") with a single consul cluster.
In other words, single marathon-consul instance will filter and manage only its Consul services.

Allow tasks to register without health checks

A Consul service definition does not have to have a healthcheck - https://www.consul.io/docs/agent/services.html

A service definition must include a name and *may optionally* provide an id, tags, address, port, check, and enableTagOverride. (emphasis mine)

I'm interested in using marathon-consul in a RPC context where availability and health checks are looked after at the RPC layer and all I need is a relatively up to date list of my Marathon task endpoints.

In my testing, if a task doesn't have a health check but is labeled correctly, it won't show up in Consul.

Could this behavior be changed to allow for tasks / services without health checks?

Synchro vs events processing race / synchro cache invalidation

There is a possibility that during the time the scheduled synchro takes place,
an event from Marathon (like TASK_KILLED) is received and processed (causing service deregistration), but the synchro later on overrides it effect (service registration again). We end up with a service that should not be registered any more.

This is caused because synchro creates a local cache of Marathon apps and Consul services at the beginning of the process, and uses it to apply a diff result to Consul. This diff may already be stale if the synchro takes some time.

Register all marathon apps in consul

I would actually like to have all services in marathon added to consul but I see the MARATHON_CONSUL_LABEL value is constant and there is no configuration for it.

Agents cache smart cleanup

We should not remove agents from cache after first request failure. We might get Unexpected response code: 500 (rpc error: rpc error: No cluster leader) and few seconds later the clients works fine.

An agent should be removed from cache after a specified number of consecutive failures (timeout, connection refused), if it responds (even with status code 500) it should not be removed. We may consider doing some priority queue for search failing clients last.

APT repository not signed?

Following the APT repository instructions gives me the following output when I run apt-get update:

W: The repository 'http://dl.bintray.com/v1/content/allegro/deb  Release' is not signed.
N: Data from such a repository can't be authenticated and is therefore potentially dangerous to use.
N: See apt-secure(8) manpage for repository creation and user configuration details.

Which in turn fails my project because the package cannot be authenticated.

Health check URL is url encoded in consul

My health check has following path in marathon: "/status/ping?serviceName=name"

When registered in consul it is: "/status/ping%3FserviceName=name" and results in 404 thus critical state

ERRO[0060] An error occured while performing sync error=Can't get Consul services: No Consul client available in agents cache

Anyone seen this error before?

INFO[0000] Starting marathon-consul Version=0.3.6
INFO[0000] Sending metrics to stdout
INFO[0000] Listening Port=:4000
INFO[0000] Marathon-consul sync job started Force=false Interval=1m0s Leader=
DEBU[0000] Asking Marathon for leader Location=localhost:8080
DEBU[0000] Sending GET request to marathon Location=localhost:8080 Protocol=http Uri=/v2/leader
INFO[0000] Marathon-consul sync leader mode set to resolved hostname Leader=mesos-master1:8080
DEBU[0000] Node has leadership Node=mesos-master1:8080
INFO[0000] Syncing services started
DEBU[0000] Asking Marathon for apps Location=localhost:8080
DEBU[0000] Sending GET request to marathon Location=localhost:8080 Protocol=http Uri=/v2/apps?embed=apps.tasks&label=consul
ERRO[0060] An error occured while performing sync error=Can't get Consul services: No Consul client available in agents cache

Async events processing

HttpEventActor try to be smart and if specific subscriber is not responding then rate to this subscriber is limited.

When consul is not responding (e.g., there is no leader, hos is unreachable, timeout occurs) we pass this error to Marathon returning 500. In the end rate to marathon-consul is limited and no events are sent hence service is not de/registered.

E.g.:

echo '{
          "slaveId":"85e59460-a99e-4f16-b91f-145e0ea595bd-S0",
          "taskId":"web",
          "taskStatus":"TASK_LOST",
          "message":"Command terminated with signal Terminated",
          "appId":"/test/app",
          "host":"localhost",
          "ports":[
            31372
          ],
          "version":"2015-12-07T09:02:48.981Z",
          "eventType":"status_update_event",
          "timestamp":"2015-12-07T09:33:40.898Z"
}' |  \
  http POST http://localhost:4000/events \
  cache-control:no-cache \
  content-type:application/json \
HTTP/1.1 500 Internal Server Error
Content-Length: 115
Content-Type: text/plain; charset=utf-8
Date: Wed, 20 Apr 2016 12:20:01 GMT

Put http://127.0.0.1:8500/v1/agent/service/deregister/web: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

To solve this issue we should return 202 Accepted and process events asynchronous. As a first step we could change 500/400 responses to 200, but it will not prevent rate limit on consul timeout.
Changes are required in handler. Remember to check metrics and error handling.

Dockerfile

Hi Guys, I was using the original ciscocloud/marathon-consul in a docker image (for coreos) and used the Dockerfile supplied with the project. I have created a Dockerfile by pulling down the distro. I would prefer to build from source, I am a bit lost how to create a Dockerfile for your project as I have no real experience with golang... Can you anyone help please?

Ok, managed to hack the ciscocloud/marathon-consul file, very simple just used the Makefile and removed the test tag from the build target.

The launch.sh is again from ciscocloud/marathon-consul

===== DOCKERFILE =====
FROM gliderlabs/alpine:3.2
MAINTAINER Brian Hicks [email protected]

RUN apk add --update ca-certificates bash
COPY launch.sh /launch.sh

COPY . /go/src/github.com/allegro/marathon-consul
RUN apk add make go git mercurial
&& cd /go/src/github.com/allegro/marathon-consul
&& export GOPATH=/go
&& make build
&& mv /go/src/github.com/allegro/marathon-consul/bin/marathon-consul /bin/marathon-consul
&& rm -rf /go
&& apk del --purge go git mercurial

ENTRYPOINT ["/launch.sh"]

Human-readable time durations in config files

It would be nice if we had human-readable time durations in config files instead of the nanosecond integers, for example:

"Sync": {
  "Interval": 900000000000
}

would become

"Sync": {
  "Interval": "15m"
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.