Git Product home page Git Product logo

vulcand's Introduction

Vulcand

  • HTTP proxy that uses Etcd as a configuration backend.
  • Changes to configuration take effect immediately without restarting the service.

Vulcan diagram

Status

Hardening: Testing, benchmarking. Not usable for production yet.

Discussions

Mailing list: https://groups.google.com/d/forum/vulcan-proxy

Installation

Deps: go>=1.2, Etcd

Install:

make install
make run

Concepts

Host

Incoming requests are matched by their hostname first. Hostname is defined by incoming 'Host' header. E.g. curl http://example.com/alice will be matched by the host example.com first.

Location

Hosts contain one or several locations. Each location defines a path - simply a regular expression that will be matched against request's url. Location contains link to an upstream and vulcand will use the endpoints from this upstream to serve the request.

E.g. location loc1 will serve the request curl http://example.com/search because it matches the path /search and host example.com:

Vulcan diagram

Upstream

Upstream is a collection of endpoints. Upstream can be assigned to multiple locations at the same time. This is convenient as sometimes one endpoint serves multiple purposes and locations.

Endpoint

Endpoint is a final destination of the incoming request, each endpoint is defined by <schema>://<host>:<port>, e.g. http://localhost:5000

Middleware

Vulcand supports pluggable middlewares. Middlewares can intercept or transform the request to any location. Examples of the supported middlewares are rate limits and connection limits. You can add or remove middlewares using command line, API or directly via backends.

Etcd

Vulcan supports reading and updating configuration based on the changes in Etcd. Vulcans watch etcd prefix that is supplied when running an instance and configure themselves. Important All examples bellow assume that Vulcand is configured to listen on /vulcand prefix, which is a default prefix.

Upstreams and endpoints

# Upserts upstream and adds an endpoint to it
etcdctl set /vulcand/upstreams/up1/endpoints/e1 http://localhost:5000

Hosts and locations

# Upsert a host "localhost" and add a location to it that matches path "/home" and uses endpoints from upstream "up1"
etcdctl set /vulcand/hosts/localhost/locations/loc1/path "/home"
etcdctl set /vulcand/hosts/localhost/locations/loc1/upstream up1

The best part is that you can update upstream using the same command. Let's add another upstream and switch traffic to it:

# create a new upstream with endpoint http://localhost:5003
etcdctl set /vulcand/upstreams/up2/endpoints/e3 http://localhost:5003

# redirect the traffic of the location "loc1" to the endpoints of the upstream "up2"
etcdctl set /vulcand/hosts/localhost/locations/loc1/upstream up2

Note that you can add and remove endpoints to the existing upstream, and vulcan will start redirecting the traffic to them automatically:

# Add a new endpoint to the existing upstream
etcdctl set /vulcand/upstreams/up1/endpoints/e2 http://localhost:5001

Rate and connection limits

Vulcan supports setting rate and connection limits.

# Update or set rate limit the request to location "loc1" to 1 request per second per client ip with bursts up to 3 requests per second.
# Note the priority here - middlewares with lower priorities will be executed earlier.
etcdctl set /vulcand/hosts/localhost/locations/loc1/middlewares/ratelimit/rl1 '{"Type": "ratelimit", "Middleware":{"Requests":1, "PeriodSeconds":1, "Burst":3, "Variable": "client.ip"}}'
# Update or set the connection limit to 3 simultaneous connections per client ip at a time
# Note the priority here - middlewares with lower priorities will be executed earlier.
etcdctl set /vulcand/hosts/localhost/locations/loc1/middlewares/connlimit/rl1 '{"Type": "connlimit", "Middleware":{"Requests":1, "PeriodSeconds":1, "Burst":3, "Variable": "client.ip"}}'

Command line

Command line is the most convenient way to start working with vulcan, here are some examples.

Status

Displays the configuration and stats about the daemon

$ vulcanctl status

[hosts]
  │
  └host(name=localhost)
    │
    └location(id=loc1, path=/hello)
      │
      └upstream(id=u1)
        │
        └endpoint(id=e1, url=http://localhost:5001)

Host

Host command supports adding or removing host

# Add host with name 'example.com'
$ vulcanctl host add --name example.com

# Remove host with name 'example.com'
$ vulcanctl host rm --name example.com

Upstream

Upstream command adds or removes upstreams

# Add upstream  with id 'u1'
$ vulcanctl upstream add --id u1

# Adds upstream with auto generated id
$ vulcanctl upstream add 

# Remove upstream with id 'u1'
$ vulcanctl upstream rm --id u1

# "Drain" - wait till there are no more active connections from the endpoints of the upstream 'u1'
# or timeout after 10 seconds if there are remaining connections
vulcanctl upstream drain -id u1 -timeout 10

Endpoint

Endpoint command adds or removed endpoints to the upstream.

# add endpoint with id 'e2' and url 'http://localhost:5002' to upstream with id 'u1'
$ vulcanctl endpoint add --id e1 --up u1 --url http://localhost:5000 

# in case if id is omitted, etcd will auto generate it
$ vulcanctl endpoint add --up u1 --url http://localhost:5001 

# removed endpoint with id 'e1' from upstream 'u1'
$ vulcanctl endpoint rm --up u1 --id e1 

Location

Location adds or removes location to the host

# add location with id 'id1' to host 'example.com', use path '/hello' and upstream 'u1'
$ vulcanctl location add --host example.com --id loc1 --path /hello --up u1 

# add location with auto generated id to host 'example.com', use path '/hello2' and upstream 'u1'
$ vulcanctl location add --host example.com --path /hello2 --up u1 

# remove location with id 'loc1' from host 'example.com'
$ vulcanctl location rm --host example.com --id loc1 

# update upstream of the location 'loc1' in host 'example.com' to be 'u2'
# this redirects the traffic gracefully from endpoints in the previous upstream
# to endpoints of the upstream 'u2', see drain for connection draining
$ vulcanctl location set_upstream --host example.com --id loc1 --up u2

Rate limit

Rate add or removes rate limit restrictions on the location

# limit access per client ip to 10 requests per second in 
# location 'loc1' in host 'example.com'
$ vulcanctl ratelimit add --variable client.ip --host example.com --loc loc1 --requests 10

# limit access per custom http header value 'X-Account-Id' to 100 requests per second 
# to location 'loc1' in host 'example.com'
$ vulcanctl ratelimit add --variable request.header.X-Account-Id --host example.com --loc loc1 --requests 10

# remove rate limit restriction with id 'r1' from host 'example.com' location 'loc1'
$ vulcanctl ratelimit rm --id r1  --host example.com --loc 'loc1'

Connection limit

Control simultaneous connections for a location.

# limit access per client ip to 10 simultaneous connections for
# location 'loc1' in host 'example.com'
$ vulcanctl connlimit add --id c1 -host example.com -loc loc1 -connections 10

# limit access per custom http header value 'X-Account-Id' to 100 simultaneous connections
# to location 'loc1' in host 'example.com'
$ vulcanctl connlimit add --variable request.header.X-Account-Id --host example.com --loc loc1 --connections 10

# remove connection limit restriction with id 'c1' from host 'example.com' location 'loc1'
$ vulcanctl connlimit rm --id c1  --host example.com --loc 'loc1'

HTTP API

Vulcan's HTTP API is the best way to configure one or several instances of Vulcan at the same time.
Essentially it's a tiny wrapper around the Etcd backend, that writes to the Etcd. Multiple Vulcand instances listening to the same prefix would detect changes simultaneously and reload configuration.

Status

GET /v1/status

Check status of the Vulcan instance.

Returns:

200 OK
{
   "Status": "ok"
}

Host

GET /v1/hosts

Retrieve the existing hosts.

Example response:

{
  "Hosts": [
    {
      "Name": "localhost",
      "Locations": [
        {
          "Hostname": "localhost",
          "Path": "/home",
          "Id": "loc1",
          "Upstream": {
            "Id": "up1",
            "Endpoints": [
              {
                "Id": "e1",
                "Url": "http://localhost:5000",
                "UpstreamId": "up1",
                "Stats": {
                  "Successes": 0,
                  "Failures": 0,
                  "FailRate": 0,
                  "PeriodSeconds": 10
                }
              }
            ]
          },
          "Middlewares": [
            {
              "Id": "rl1",
              "Priority": 0,
              "Type": "ratelimit",
              "Middleware": {
                "PeriodSeconds": 1,
                "Burst": 3,
                "Variable": "client.ip",
                "Requests": 1
              }
            }
          ]
        }
      ]
    }
  ]
}

POST 'application/json' /v1/hosts

Add a host to the proxy.

{
  "Name": "localhost"
}
Parameter Description
name Hostname

DELETE /v1/hosts/<name>

Delete a host.

Upstream

GET /v1/upstreams

Retrieve the existing upstreams. Example response:

{
  "Upstreams": [
    {
      "Id": "up1",
      "Endpoints": [
        {
          "Id": "e1",
          "Url": "http://localhost:5000",
          "UpstreamId": "up1",
          "Stats": null
        },
        {
          "Id": "e2",
          "Url": "http://localhost:5001",
          "UpstreamId": "up1",
          "Stats": null
        }
      ]
    }
  ]
}

POST 'application/json' /v1/upstreams

{"Id": "up1"}

Add upstream to the proxy

Parameter Description
id Optional upstream id, will be generated if omitted.

DELETE /v1/upstreams/<id>

Delete an upstream.

GET /v1/upstreams/drain?timeout=3

Wait till there are no more connections to any endpoints to the upstream.

Parameter Description
timeout Timeout in form 1s for the amount of seconds to wait before time out.

Example response:

{
  "Connections": 0
}

Endpoint

GET /v1/upstreams/<id>/endpoints

Retrieve the endpoints of the upstream. Example response:

{
  "Endpoints": [
    {
      "Id": "e1",
      "Url": "http://localhost:5000",
      "UpstreamId": "up1"
    }
  ]
}

GET /v1/upstreams/<id>/endpoints/<endpoint-id>

Retrieve the particular endpoint with id endpoint-id

POST /v1/upstreams/<id>/endpoints

{
  "Id": "e4",
  "Url": "http://localhost:5004",
  "UpstreamId": "up1"
}

Add endpoint to the upstream

Parameter Description
id Optional endppint id, will be generated if omitted.
url Required valid endpoint url

Example response:

{
  "Id": "e4",
  "Url": "http://localhost:5004",
  "UpstreamId": "up1",
  "Stats": null
}

DELETE /v1/upstreams/<id>/endpoints/<endpoint-id>

Delete an endpoint.

Location

GET /v1/hosts/<hostname>/locations

Retrieve the locations of the host.

Example response:

{
  "Locations": [
    {
      "Hostname": "localhost",
      "Path": "/home",
      "Id": "loc1",
      "Upstream": {
        "Id": "up1",
        "Endpoints": [
          {
            "Id": "e1",
            "Url": "http://localhost:5000",
            "UpstreamId": "up1",
            "Stats": null
          }
        ]
      },
      "Middlewares": []
    }
  ]
}

GET /v1/hosts/<hostname>/locations/<location-id>

Retrieve the particular location in the host hostname with id location-id

{
  "Hostname": "localhost",
  "Path": "/home",
  "Id": "loc1",
  "Upstream": {
    "Id": "up1",
    "Endpoints": [
      {
        "Id": "e1",
        "Url": "http://localhost:5000",
        "UpstreamId": "up1",
        "Stats": null
      },
      {
        "Id": "e2",
        "Url": "http://localhost:5001",
        "UpstreamId": "up1",
        "Stats": null
      },
      {
        "Id": "e3",
        "Url": "http://localhost:5003",
        "UpstreamId": "up1",
        "Stats": null
      },
      {
        "Id": "e4",
        "Url": "http://localhost:5004",
        "UpstreamId": "up1",
        "Stats": null
      }
    ]
  },
  "Middlewares": [
    {
      "Id": "rl1",
      "Priority": 0,
      "Type": "ratelimit",
      "Middleware": {
        "PeriodSeconds": 1,
        "Burst": 3,
        "Variable": "client.ip",
        "Requests": 1
      }
    },
    {
      "Id": "cl1",
      "Priority": 0,
      "Type": "connlimit",
      "Middleware": {
        "Connections": 3,
        "Variable": "client.ip"
      }
    }
  ]
}

POST 'application/json' /v1/hosts/<hostname>/locations

Add a location to the host

{
  "Id": "loc2",
  "Hostname": "localhost",
  "Path": "/home",
  "Upstream": {
    "Id": "up1"
  }
}
Parameter Description
Id Optional location id, will be generated if omitted.
Path Required regular expression for path matchng
Upstream.Id Required id of the existing upstream
Hostname Required hostname

DELETE /v1/hosts/<hostname>/locations/<location-id>

Delete a location

PUT /v1/hosts/<hostname>/locations/<location-id>

Update location's upstream. Gracefully Redirects all the traffic to the endpoints of the new upstream.

Parameter Description
upstream Required id of the existing upstream

Rate limit

GET /v1/hosts/<hostname>/locations/<location-id>/middlewares/ratelimit/<rate-id>

Retrieve the particular rate of location in the host hostname with id location-id and rate id rate-id

{
  "Id": "rl1",
  "Priority": 0,
  "Type": "ratelimit",
  "Middleware": {
    "PeriodSeconds": 1,
    "Burst": 3,
    "Variable": "client.ip",
    "Requests": 1
  }
}

POST 'application/json' /v1/hosts/<hostname>/locations/limits/rates

Add a rate limit to the location, will take effect immediately.

{
  "Id": "rl1",
  "Priority": 0,
  "Type": "ratelimit",
  "Middleware": {
    "PeriodSeconds": 1,
    "Burst": 3,
    "Variable": "client.ip",
    "Requests": 1
  }
}
Parameter Description
Id Optional rate id, will be generated if omitted.
Requests Required amount of allowed requests
PeriodSeconds Required period in seconds for counting the requests
Burst Required allowed burst of the requests (additional requests exceeding the rate)
Variable Variable for rate limiting e.g. client.ip or request.header.My-Header

DELETE /v1/hosts/<hostname>/locations/<location-id>/limits/rates/<rate-id>

Delete a rate limit from the location.

PUT /v1/hosts/<hostname>/locations/<location-id>/limits/rates/<rate-id>

Update location's rate limit. Takes effect immdediatelly.

{
  "Id": "rl1",
  "Priority": 0,
  "Type": "ratelimit",
  "Middleware": {
    "PeriodSeconds": 1,
    "Burst": 3,
    "Variable": "client.ip",
    "Requests": 1
  }
}

Connection limit

GET /v1/hosts/<hostname>/locations/<location-id>/middlewares/connlimit/<conn-id>

Retrieve the particular connection limit of location in the host hostname with id location-id and connection limit id conn-id

Example response:

{
  "Id": "cl1",
  "Priority": 0,
  "Type": "connlimit",
  "Middleware": {
    "Connections": 3,
    "Variable": "client.ip"
  }
}

POST 'application/json' /v1/hosts/<hostname>/locations/limits/connections

Add a connection limit to the location, will take effect immediately.

{
  "Id": "cl1",
  "Priority": 0,
  "Type": "connlimit",
  "Middleware": {
    "Connections": 3,
    "Variable": "client.ip"
  }
}
Parameter Description
Id Optional limit id, will be generated if omitted.
Connections Required maximum amount of allowed simultaneous connections
Variable Variable for limiting e.g. client.ip or request.header.My-Header

DELETE /v1/hosts/<hostname>/locations/<location-id>/middlewares/connlimit/<conn-id>

Delete a connection limit from the location.

PUT /v1/hosts/<hostname>/locations/<location-id>/limits/connections/<conn-id>

Update location's connection limit. Takes effect immdediatelly.

{
  "Id": "cl1",
  "Priority": 0,
  "Type": "connlimit",
  "Middleware": {
    "Connections": 3,
    "Variable": "client.ip"
  }
}

Docker

Here's how you build vulcan in Docker:

docker build -t mailgun/vulcan .

Starting the daemon:

docker run -p 8182:8182 -p 8181:8181 mailgun/vulcan /opt/vulcan/vulcand -apiInterface="0.0.0.0" -interface="0.0.0.0" --etcd=http://10.0.3.1:4001

Don't forget to map the ports and bind to the proper interfaces, otherwise vulcan won't be reachable from outside the container.

Using the vulcanctl from container:

docker run mailgun/vulcan /opt/vulcan/vulcanctl status  --vulcan 'http://10.0.3.1:8182'

Make sure you've specified --vulcan flag to tell vulcanctl where the running vulcand is. I've used lxc bridge interface in the example above.

vulcand's People

Contributors

klizhentas avatar mailgun-ci avatar pquerna avatar shawnps avatar thedrow avatar

Watchers

 avatar  avatar

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.