From 27b03f6f9076e05839961530006185998660f321 Mon Sep 17 00:00:00 2001 From: James Phillips Date: Mon, 22 Feb 2016 10:45:52 -0800 Subject: [PATCH 1/6] Adds initial content for official Consul image. --- consul/README-short.txt | 1 + consul/content.md | 261 ++++++++++++++++++++++++++++++++++++++++ consul/license.md | 1 + consul/logo.png | Bin 0 -> 33689 bytes update.sh | 1 + 5 files changed, 264 insertions(+) create mode 100644 consul/README-short.txt create mode 100644 consul/content.md create mode 100644 consul/license.md create mode 100644 consul/logo.png diff --git a/consul/README-short.txt b/consul/README-short.txt new file mode 100644 index 000000000..71c626fc5 --- /dev/null +++ b/consul/README-short.txt @@ -0,0 +1 @@ +Consul is a datacenter runtime that provides service discovery, configuration, and orchestration. diff --git a/consul/content.md b/consul/content.md new file mode 100644 index 000000000..431a54b6f --- /dev/null +++ b/consul/content.md @@ -0,0 +1,261 @@ +# Consul + +Consul is a distributed, highly-available, and multi-datacenter aware tool for service discovery, configuration, and orchestration. Consul enables rapid deployment, configuration, and maintenance of service-oriented architectures at massive scale. For more information, please see: + +- [Consul documentation](https://www.consul.io/) +- [Consul on GitHub](https://github.com/hashicorp/consul) + +%%LOGO%% + +# Consul and Docker + +Consul has several moving parts so we'll start with a brief introduction to Consul's architecture and then detail how Consul interacts with Docker. Please see the [Consul Architecture](https://www.consul.io/docs/internals/architecture.html) guide for more detail on all these concepts. + +Each host in a Consul cluster runs the Consul agent, a long running daemon that can be started in client or server mode. Each cluster has at least 1 agent in server mode, and usually 3 or 5 for high availability. The server agents participate in a [consensus protocol](https://www.consul.io/docs/internals/consensus.html), maintain a centralized view of the the cluster's state, and respond to queries about the cluster from other agents in the cluster. The rest of the agents in client mode participate in a [gossip protocol](https://www.consul.io/docs/internals/gossip.html) to discover other agents and check them for failures, and they forward queries about the cluster to the server agents. + +Applications running on a given host communicate only with their local Consul agent, using its HTTP APIs or DNS interface. Services on the host are also registered with the local Consul agent, which shares the information with the Consul servers. Doing the most basic DNS-based service discovery using Consul, an application queries for `foo.service.consul` and gets a randomly shuffled subset of all the hosts providing service "foo". This allows services to locate each other and balance the load without any intermediate proxies. Several HTTP APIs are also available for applications doing a deeper integration with Consul's service discovery capabilities, as well as its other features such as the key/value store. + +These concepts also apply when running Consul in Docker. Typically, you'll run a single Consul agent container on each host, running alongside the Docker daemon. You'll also need to configure some of the agents as servers (at least 3 for a basic HA setup). Consul should always be run with `--net=host` in Docker because Consul's consensus and gossip protocols are sensitive to delays and packet loss, so the extra layers involved with other networking types are usually undesirable and unnecessary. We will talk more about this below. + +We don't cover Consul's multi-datacenter capability here, but as long as `--net=host` is used, there should be no special considerations for Docker. + +# Using the Container + +We chose Alpine as a lightweight base with a reasonably small surface area for security concerns, but with enough functionality for development, interactive debugging, and useful health, watch, and exec scripts running under Consul in the container. + +Consul always runs under [dumb-init](https://github.com/Yelp/dumb-init), which handles reaping zombie processes and forwards signals on to all processes running in the container. We also use [gosu](https://github.com/tianon/gosu) to run Consul as a non-root "consul" user for better security. These binaries are all built by HashiCorp and signed with our [GPG key](https://www.hashicorp.com/security.html), so you can verify the signed package used to build a given base image. + +An entry point script is provided that provides several common configurations: + +- `dev` yields a fully in-memory Consul server agent suitable for development (this is the default if you run the container with no arguments) +- `client` yields a Consul agent in client mode +- `server` yields a Consul agent in server mode +- any other command gets `exec`-ed inside the container under `dumb-init` + +The container exposes `VOLUME /consul/data`, which is a path were Consul will place its persisted state. This isn't used in any way when running in the `dev` configuration. For client agents, this stores some information about the cluster and the client's health checks, in case the container is restarted. For server agents, this stores the client information plus snapshots and data related to the consensus algorithm and Consul's catalog of services. For servers it is highly desirable to keep this volume's data around when restarting the containers to recover from outage scenarios. + +Additionally, these entry points run consul with two [configuration directories](https://www.consul.io/docs/agent/options.html#_config_dir). There's a common directory `/consul/config/local` as well as one specific to client or server mode (`/consul/config/client` and `/consul/config/server`). The mode-specific directories contain files that are shipped with the container and provide good default options suggested by HashiCorp. You can override any of these settings by composing a new container or binding a volume to `/consul/config/local`, which is loaded last when starting up. Alternatively, when binding a volume is not an option, e.g. when runnning Consul as a Nomad job, configuration can be added by passing the configuration JSON via environment variable `CONSUL_LOCAL_CONFIG`. + +Since Consul is almost always run with `--net=host` in Docker, some care is required when configuring Consul's IP addresses. Consul has the concept of its cluster address as well as its client address. The cluster address is the address at which other Consul agents may contact a given agent. The client address is the address where other processes on the host contact Consul in order to make HTTP or DNS requests. You will typically need to tell Consul what its cluster address is when starting so that it binds to the correct interface and advertises a workable interface to the rest of the Consul agents. You'll see this in the examples below as the `-bind=` argument to Consul. + +The entry point also includes a small utility to look up a bind address by interface name. To use this, set the `CONSUL_BIND_INTERFACE` environment variable to the name of the interface you'd like Consul to bind to and a `-bind=` argument will be computed and passed to Consul at startup. + +## Running Consul for Development + +```console +$ docker run hashicorp/consul +``` + +This runs a completely in-memory Consul server agent with default bridge networking and no services exposed on the host, which is useful for development but should not be used in production. For example, if that server is running at internal address 172.17.0.2, you can run a three node cluster for development by starting up two more instances and telling them to join the first node. + +```console +$ docker run -d hashicorp/consul dev -join=172.17.0.2 +... server 2 starts +$ docker run -d hashicorp/consul dev -join=172.17.0.2 +... server 3 starts +``` + +Then, choosing any one of the container IDs, we can query for all the members in the cluster by running a Consul CLI command: + +```console +$ docker exec -t c9caabfd4c2a consul members +Node Address Status Type Build Protocol DC +579db72c1ae1 172.17.0.3:8301 alive server 0.6.3 2 dc1 +7e185aebe4e6 172.17.0.3:8301 left server 0.6.3 2 dc1 +93fe2309ef19 172.17.0.4:8301 alive server 0.6.3 2 dc1 +c9caabfd4c2a 172.17.0.2:8301 alive server 0.6.3 2 dc1 +``` + +Remember that Consul doesn't use the data volume in this mode - once the container stops all of your state will be wiped out, so please don't use this mode for production. Running completely on the bridge network with the development server is useful for testing multiple instances of Consul on a single machine, which is normally difficult to do because of port conflicts. + +The dev entry point also starts a version of Consul's web UI on port 8500. This can be added to the other Consul configurations by supplying the `-ui` option to Consul on the command line. The web assets are bundled inside the Consul binary in the container. + +## Running Consul Agent in Client Mode + +```console +$ docker run -d --net=host consul client -bind= -retry-join= +==> Starting Consul agent... +==> Starting Consul agent RPC... +==> Consul agent running! + Node name: 'linode' + Datacenter: 'dc1' + Server: false (bootstrap: false) + Client Addr: 127.0.0.1 (HTTP: 8500, HTTPS: -1, DNS: 8600, RPC: 8400) + Cluster Addr: (LAN: 8301, WAN: 8302) + Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false + Atlas: +... +``` + +This runs a Consul client agent sharing the host's network and advertising the external IP address to the rest of the cluster. Note that the agent defaults to binding its client interfaces to 127.0.0.1, which is the host's loopback interface. This would be a good configuration to use if other containers on the host also use `--net=host`, and it also exposes the agent to processes running directly on the host outside a container, such as HashiCorp's Nomad. + +The `-retry-join` parameter specifies the external IP of one other agent in the cluster to use to join at startup. There are several ways to control how an agent joins the cluster, see the [agent configuration](https://www.consul.io/docs/agent/options.html) guide for more details on the `-join`, `-retry-join`, and `-atlas-join` options. + +At startup, the agent will read config JSON files from `/consul/config/client` then `/consul/config/local`. Data will be persisted in the `/consul/data` volume. + +Here are some example queries on a host with an external IP of 66.175.220.234: + +```console +$ curl http://localhost:8500/v1/health/service/consul?pretty +[ + { + "Node": { + "Node": "linode", + "Address": "66.175.220.234", +... +``` + +```console +$ dig @localhost -p 8600 consul.service.consul +; <<>> DiG 9.9.5-3ubuntu0.7-Ubuntu <<>> @localhost -p 8600 consul.service.consul +; (2 servers found) +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61616 +;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 +;; WARNING: recursion requested but not available + +;; QUESTION SECTION: +;consul.service.consul. IN A + +;; ANSWER SECTION: +consul.service.consul. 0 IN A 66.175.220.234 +... +``` + +If you want to expose the Consul interfaces to other containers via a different network, such as the bridge network, use the `-client` option for Consul: + +```console +docker run -d --net=host consul client -bind= -client= -retry-join= +==> Starting Consul agent... +==> Starting Consul agent RPC... +==> Consul agent running! + Node name: 'linode' + Datacenter: 'dc1' + Server: false (bootstrap: false) + Client Addr: (HTTP: 8500, HTTPS: -1, DNS: 8600, RPC: 8400) + Cluster Addr: (LAN: 8301, WAN: 8302) + Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false + Atlas: +... +``` + +With this configuration, Consul's client interfaces will be bound to the bridge IP and available to other containers on that network, but not on the host network. Note that we still keep the cluster address out on the host network for performance. Consul will also accept the `-client=0.0.0.0` option to bind to all interfaces. + +## Running Consul Agent in Server Mode + +```console +$ docker run -d --net=host consul server -bind= -retry-join= -bootstrap-expect= +``` + +This runs a Consul server agent sharing the host's network. All of the network considerations and behavior we covered above for the client agent also apply to the server agent. A single server on its own won't be able to form a quorum and will be waiting for other servers to join. + +Just like the client agent, the `-retry-join` parameter specifies the external IP of one other agent in the cluster to use to join at startup. There are several ways to control how an agent joins the cluster, see the [agent configuration](https://www.consul.io/docs/agent/options.html) guide for more details on the `-join`, `-retry-join`, and `-atlas-join` options. The server agent also consumes a `-bootstrap-expect` option that specifies how many server agents to watch for before bootstrapping the cluster for the first time. This provides an easy way to get an orderly startup with a new cluster. See the [agent configuration](https://www.consul.io/docs/agent/options.html) guide for more details on the `-bootstrap` and `-bootstrap-expect` options. + +At startup, the agent will read config JSON files from `/consul/config/server` then `/consul/config/local`. Data will be persisted in the `/consul/data` volume. + +Once the cluster is bootstrapped and quorum is achieved, you must use care to try to keep the minimum number of servers operating in order to avoid an outage state for the cluster. The deployment table in the [consensus](https://www.consul.io/docs/internals/consensus.html) guide outlines the number of servers required for different configurations. There's also an [adding/removing servers](https://www.consul.io/docs/guides/servers.html) guide that describes that process, which is relevant to Docker configurations as well. The [outage recovery](https://www.consul.io/docs/guides/outage.html) guide has steps to perform if servers are permanently lost. In general it's best to restart or replace servers one at a time, making sure servers are healthy before proceeding to the next server. + +## Exposing Consul's DNS Server on Port 53 + +By default the dev, client, and server modes started by the endpoint will expose Consul's DNS server on port 8600. Because this is cumbersome to configure with facilities like `resolv.conf`, you may want to expose DNS on port 53 using port arguments on your run command: + +```console +$ docker run --net=host -p 53:8600/tcp -p 53:8600/udp hashicorp/consul +``` + +If you are binding Consul's client interfaces to the host's loopback address, then you should be able to configure your host's `resolv.conf` to route DNS requests to Consul by including "127.0.0.1" as the primary DNS server. Due to Docker's built-in DNS server, you can't point to this directly from inside your containers; Docker will issue an error message if you attempt to do this. + +If you are binding Consul's client interfaces to the bridge or other network, you can use the `--dns` option in your *other containers* in order for them to use Consul's DNS server, mapped to port 53. Here's an example: + +```console +$ docker run -d --net=host -p 53:8600/tcp -p 53:8600/udp consul client -bind= +``` + +Now start another container and point it at Consul's DNS, using the bridge address of the host: + +```console +$ docker run -i --dns= -t ubuntu sh -c "apt-get install -y dnsutils && dig consul.service.consul" +... +;; ANSWER SECTION: +consul.service.consul. 0 IN A 66.175.220.234 +... +``` + +## Service Discovery with Containers + +There are several approaches you can use to register services running in containers with Consul. For manual configuration, your containers can use the local agent's APIs to register and deregister themselves, see the [Agent API](https://www.consul.io/docs/agent/http/agent.html) for more details. Another strategy could be to create a derived Consul container for each host type which includes JSON config files for Consul to parse at startup, see [Services](https://www.consul.io/docs/agent/services.html) for more information. Both of these approaches are fairly cumbersome, and the configured services may fall out of sync if containers die or additional containers are started. + +If you run your containers under [HashiCorp's Nomad](https://www.nomadproject.io/) scheduler, it has [first class support for Consul](https://www.nomadproject.io/docs/jobspec/servicediscovery.html). The Nomad agent runs on each host alongside the Consul agent. When jobs are scheduled on a given host, the Nomad agent automatically takes care of syncing the Consul agent with the service information. This is very easy to manage, and even services on hosts running outside of Docker containers can be managed by Nomad and registered with Consul. You can find out more about running Docker under Nomad in the [Docker Driver](https://www.nomadproject.io/docs/drivers/docker.html) guide. + +Another option is the open source [Registrator](http://gliderlabs.com/registrator/latest/) project from Glider Labs. Registrator works by running a Registrator instance on each host, alongside the Consul agent. Registrator monitors the Docker daemon for container stop and start events, and handles service registration with Consul using the container names and exposed ports as the service information. Here's a complete example. + +Run a development Consul server: + +```console +$ docker run -d --net=host hashicorp/consul dev -bind=66.175.220.234 # <- use your own external IP +``` + +Now start a companion Registrator instance: + +```console +$ docker run -d --net=host --volume=/var/run/docker.sock:/tmp/docker.sock gliderlabs/registrator:latest consul://localhost:8500 +``` + +Now run some services: + +```console +$ docker run -d -P redis +$ docker run -d -P redis +$ docker run -d -P redis +``` + +And query for them using Consul's API: + +```console +$ curl http://localhost:8500/v1/catalog/service/redis?pretty +[ + { + "Node": "linode", + "Address": "66.175.220.234", + "ServiceID": "linode:boring_noyce:6379", + "ServiceName": "redis", + "ServiceTags": [], + "ServiceAddress": "66.175.220.234", + "ServicePort": 32775, + "ServiceEnableTagOverride": false, + "CreateIndex": 8, + "ModifyIndex": 8 + }, + { + "Node": "linode", + "Address": "66.175.220.234", + "ServiceID": "linode:kickass_swartz:6379", + "ServiceName": "redis", + "ServiceTags": [], + "ServiceAddress": "66.175.220.234", + "ServicePort": 32776, + "ServiceEnableTagOverride": false, + "CreateIndex": 7, + "ModifyIndex": 7 + }, + { + "Node": "linode", + "Address": "66.175.220.234", + "ServiceID": "linode:sick_euclid:6379", + "ServiceName": "redis", + "ServiceTags": [], + "ServiceAddress": "66.175.220.234", + "ServicePort": 32777, + "ServiceEnableTagOverride": false, + "CreateIndex": 6, + "ModifyIndex": 6 + } +] +``` + +Note that in the example above that the service address is the same as the external address of the host, and that the service ports are the mapped ports exposed on the host (32775, 32776, 32777). There are configuration options that control which IP and port Registrator gives to Consul, which may need to be adjusted depending on your network configuration. See Registrator's [Run Reference](http://gliderlabs.com/registrator/latest/user/run/) for more details. + +## Running Health Checks in Docker Containers + +Consul has the ability to execute health checks inside containers. If the Docker daemon is exposed to the Consul agent and the `DOCKER_HOST` environment variable is set, then checks can be configured with the Docker container ID to execute in. See the [health checks](https://www.consul.io/docs/agent/checks.html) guide for more details. diff --git a/consul/license.md b/consul/license.md new file mode 100644 index 000000000..d565b3812 --- /dev/null +++ b/consul/license.md @@ -0,0 +1 @@ +View [license information](https://raw.githubusercontent.com/hashicorp/consul/master/LICENSE) for the software contained in this image. diff --git a/consul/logo.png b/consul/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d5985627f151c69bdd840fcf537830aa0d6dbf3d GIT binary patch literal 33689 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 Date: Fri, 4 Mar 2016 10:51:07 -0800 Subject: [PATCH 2/6] Adds a description of the new client interface environment variable. --- consul/content.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consul/content.md b/consul/content.md index 431a54b6f..5b91d3da1 100644 --- a/consul/content.md +++ b/consul/content.md @@ -38,7 +38,7 @@ Additionally, these entry points run consul with two [configuration directories] Since Consul is almost always run with `--net=host` in Docker, some care is required when configuring Consul's IP addresses. Consul has the concept of its cluster address as well as its client address. The cluster address is the address at which other Consul agents may contact a given agent. The client address is the address where other processes on the host contact Consul in order to make HTTP or DNS requests. You will typically need to tell Consul what its cluster address is when starting so that it binds to the correct interface and advertises a workable interface to the rest of the Consul agents. You'll see this in the examples below as the `-bind=` argument to Consul. -The entry point also includes a small utility to look up a bind address by interface name. To use this, set the `CONSUL_BIND_INTERFACE` environment variable to the name of the interface you'd like Consul to bind to and a `-bind=` argument will be computed and passed to Consul at startup. +The entry point also includes a small utility to look up a client or bind address by interface name. To use this, set the `CONSUL_CLIENT_INTERFACE` and/or `CONSUL_BIND_INTERFACE` environment variables to the name of the interface you'd like Consul to use and a `-client=` and/or `-bind=` argument will be computed and passed to Consul at startup. ## Running Consul for Development From 1e44dd8e480229b177945079e057a923cdb8e306 Mon Sep 17 00:00:00 2001 From: James Phillips Date: Wed, 23 Mar 2016 08:57:10 -0700 Subject: [PATCH 3/6] Updates DNS section. --- consul/content.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/consul/content.md b/consul/content.md index 5b91d3da1..e0abdb84c 100644 --- a/consul/content.md +++ b/consul/content.md @@ -164,24 +164,26 @@ By default the dev, client, and server modes started by the endpoint will expose $ docker run --net=host -p 53:8600/tcp -p 53:8600/udp hashicorp/consul ``` -If you are binding Consul's client interfaces to the host's loopback address, then you should be able to configure your host's `resolv.conf` to route DNS requests to Consul by including "127.0.0.1" as the primary DNS server. Due to Docker's built-in DNS server, you can't point to this directly from inside your containers; Docker will issue an error message if you attempt to do this. +If you are binding Consul's client interfaces to the host's loopback address, then you should be able to configure your host's `resolv.conf` to route DNS requests to Consul by including "127.0.0.1" as the primary DNS server. This would expose Consul's DNS to all applications running on the host, but due to Docker's built-in DNS server, you can't point to this directly from inside your containers; Docker will issue an error message if you attempt to do this. You must configure Consul to listen on a non-localhost address that is reachable from within other containers. -If you are binding Consul's client interfaces to the bridge or other network, you can use the `--dns` option in your *other containers* in order for them to use Consul's DNS server, mapped to port 53. Here's an example: +Once you bind Consul's client interfaces to the bridge or other network, you can use the `--dns` option in your *other containers* in order for them to use Consul's DNS server, mapped to port 53. Here's an example: ```console -$ docker run -d --net=host -p 53:8600/tcp -p 53:8600/udp consul client -bind= +$ docker run -d --net=host -p 53:8600/tcp -p 53:8600/udp consul client -bind= ``` Now start another container and point it at Consul's DNS, using the bridge address of the host: ```console -$ docker run -i --dns= -t ubuntu sh -c "apt-get install -y dnsutils && dig consul.service.consul" +$ docker run -i --dns= -t ubuntu sh -c "apt-get install -y dnsutils && dig consul.service.consul" ... ;; ANSWER SECTION: consul.service.consul. 0 IN A 66.175.220.234 ... ``` +In the example above, adding the bridge address to the host's `/etc/resolv.conf` file should expose it to all containers without running with the `--dns` option. + ## Service Discovery with Containers There are several approaches you can use to register services running in containers with Consul. For manual configuration, your containers can use the local agent's APIs to register and deregister themselves, see the [Agent API](https://www.consul.io/docs/agent/http/agent.html) for more details. Another strategy could be to create a derived Consul container for each host type which includes JSON config files for Consul to parse at startup, see [Services](https://www.consul.io/docs/agent/services.html) for more information. Both of these approaches are fairly cumbersome, and the configured services may fall out of sync if containers die or additional containers are started. From fa399a8c09a62e14d30d8118a21454fdc8d8390a Mon Sep 17 00:00:00 2001 From: James Phillips Date: Thu, 21 Apr 2016 13:59:03 -0700 Subject: [PATCH 4/6] Updates docs based on latest entrypoint changes. --- consul/content.md | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/consul/content.md b/consul/content.md index e0abdb84c..42f9d7935 100644 --- a/consul/content.md +++ b/consul/content.md @@ -25,16 +25,11 @@ We chose Alpine as a lightweight base with a reasonably small surface area for s Consul always runs under [dumb-init](https://github.com/Yelp/dumb-init), which handles reaping zombie processes and forwards signals on to all processes running in the container. We also use [gosu](https://github.com/tianon/gosu) to run Consul as a non-root "consul" user for better security. These binaries are all built by HashiCorp and signed with our [GPG key](https://www.hashicorp.com/security.html), so you can verify the signed package used to build a given base image. -An entry point script is provided that provides several common configurations: +Running the Consul container with no arguments will give you a Consul server in [development mode](https://www.consul.io/docs/agent/options.html#_dev). The provided entry point script will also look for Consul subcommands and run `consul` as the correct user and that subcommand. For example, you can excecute `docker run consul members` and it will run the `consul members` using the container. The adds some special configration options as detailed in the sections below when running the `agent` subcommand. Any other command gets `exec`-ed inside the container under `dumb-init`. -- `dev` yields a fully in-memory Consul server agent suitable for development (this is the default if you run the container with no arguments) -- `client` yields a Consul agent in client mode -- `server` yields a Consul agent in server mode -- any other command gets `exec`-ed inside the container under `dumb-init` +The container exposes `VOLUME /consul/data`, which is a path were Consul will place its persisted state. This isn't used in any way when running in development mode. For client agents, this stores some information about the cluster and the client's health checks, in case the container is restarted. For server agents, this stores the client information plus snapshots and data related to the consensus algorithm and Consul's catalog of services. For servers it is highly desirable to keep this volume's data around when restarting the containers to recover from outage scenarios. -The container exposes `VOLUME /consul/data`, which is a path were Consul will place its persisted state. This isn't used in any way when running in the `dev` configuration. For client agents, this stores some information about the cluster and the client's health checks, in case the container is restarted. For server agents, this stores the client information plus snapshots and data related to the consensus algorithm and Consul's catalog of services. For servers it is highly desirable to keep this volume's data around when restarting the containers to recover from outage scenarios. - -Additionally, these entry points run consul with two [configuration directories](https://www.consul.io/docs/agent/options.html#_config_dir). There's a common directory `/consul/config/local` as well as one specific to client or server mode (`/consul/config/client` and `/consul/config/server`). The mode-specific directories contain files that are shipped with the container and provide good default options suggested by HashiCorp. You can override any of these settings by composing a new container or binding a volume to `/consul/config/local`, which is loaded last when starting up. Alternatively, when binding a volume is not an option, e.g. when runnning Consul as a Nomad job, configuration can be added by passing the configuration JSON via environment variable `CONSUL_LOCAL_CONFIG`. +The container has a Consul configuration directory set up at `/consul/config` and the agent will load any configuration files placed here by binding a volume or by composing a new image and adding files. Alternatively, configuration can be added by passing the configuration JSON via environment variable `CONSUL_LOCAL_CONFIG`. Since Consul is almost always run with `--net=host` in Docker, some care is required when configuring Consul's IP addresses. Consul has the concept of its cluster address as well as its client address. The cluster address is the address at which other Consul agents may contact a given agent. The client address is the address where other processes on the host contact Consul in order to make HTTP or DNS requests. You will typically need to tell Consul what its cluster address is when starting so that it binds to the correct interface and advertises a workable interface to the rest of the Consul agents. You'll see this in the examples below as the `-bind=` argument to Consul. @@ -43,15 +38,15 @@ The entry point also includes a small utility to look up a client or bind addres ## Running Consul for Development ```console -$ docker run hashicorp/consul +$ docker run consul ``` This runs a completely in-memory Consul server agent with default bridge networking and no services exposed on the host, which is useful for development but should not be used in production. For example, if that server is running at internal address 172.17.0.2, you can run a three node cluster for development by starting up two more instances and telling them to join the first node. ```console -$ docker run -d hashicorp/consul dev -join=172.17.0.2 +$ docker run -d consul agent -dev -join=172.17.0.2 ... server 2 starts -$ docker run -d hashicorp/consul dev -join=172.17.0.2 +$ docker run -d consul agent -dev -join=172.17.0.2 ... server 3 starts ``` @@ -68,12 +63,12 @@ c9caabfd4c2a 172.17.0.2:8301 alive server 0.6.3 2 dc1 Remember that Consul doesn't use the data volume in this mode - once the container stops all of your state will be wiped out, so please don't use this mode for production. Running completely on the bridge network with the development server is useful for testing multiple instances of Consul on a single machine, which is normally difficult to do because of port conflicts. -The dev entry point also starts a version of Consul's web UI on port 8500. This can be added to the other Consul configurations by supplying the `-ui` option to Consul on the command line. The web assets are bundled inside the Consul binary in the container. +Development mode also starts a version of Consul's web UI on port 8500. This can be added to the other Consul configurations by supplying the `-ui` option to Consul on the command line. The web assets are bundled inside the Consul binary in the container. ## Running Consul Agent in Client Mode ```console -$ docker run -d --net=host consul client -bind= -retry-join= +$ docker run -e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' -d --net=host consul agent -bind= -retry-join= ==> Starting Consul agent... ==> Starting Consul agent RPC... ==> Consul agent running! @@ -91,7 +86,9 @@ This runs a Consul client agent sharing the host's network and advertising the e The `-retry-join` parameter specifies the external IP of one other agent in the cluster to use to join at startup. There are several ways to control how an agent joins the cluster, see the [agent configuration](https://www.consul.io/docs/agent/options.html) guide for more details on the `-join`, `-retry-join`, and `-atlas-join` options. -At startup, the agent will read config JSON files from `/consul/config/client` then `/consul/config/local`. Data will be persisted in the `/consul/data` volume. +Note also we've set [`leave_on_terminate`](https://www.consul.io/docs/agent/options.html#leave_on_terminate) using the `CONSUL_LOCAL_CONFIG` environment variable. This is recommended for clients since they should usually be removed from the cluster when the Consul agent terminates. + +At startup, the agent will read config JSON files from `/consul/config`. Data will be persisted in the `/consul/data` volume. Here are some example queries on a host with an external IP of 66.175.220.234: @@ -126,7 +123,7 @@ consul.service.consul. 0 IN A 66.175.220.234 If you want to expose the Consul interfaces to other containers via a different network, such as the bridge network, use the `-client` option for Consul: ```console -docker run -d --net=host consul client -bind= -client= -retry-join= +docker run -d --net=host consul agent -bind= -client= -retry-join= ==> Starting Consul agent... ==> Starting Consul agent RPC... ==> Consul agent running! @@ -145,14 +142,16 @@ With this configuration, Consul's client interfaces will be bound to the bridge ## Running Consul Agent in Server Mode ```console -$ docker run -d --net=host consul server -bind= -retry-join= -bootstrap-expect= +$ docker run -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' -d --net=host consul agent -server -bind= -retry-join= -bootstrap-expect= ``` This runs a Consul server agent sharing the host's network. All of the network considerations and behavior we covered above for the client agent also apply to the server agent. A single server on its own won't be able to form a quorum and will be waiting for other servers to join. Just like the client agent, the `-retry-join` parameter specifies the external IP of one other agent in the cluster to use to join at startup. There are several ways to control how an agent joins the cluster, see the [agent configuration](https://www.consul.io/docs/agent/options.html) guide for more details on the `-join`, `-retry-join`, and `-atlas-join` options. The server agent also consumes a `-bootstrap-expect` option that specifies how many server agents to watch for before bootstrapping the cluster for the first time. This provides an easy way to get an orderly startup with a new cluster. See the [agent configuration](https://www.consul.io/docs/agent/options.html) guide for more details on the `-bootstrap` and `-bootstrap-expect` options. -At startup, the agent will read config JSON files from `/consul/config/server` then `/consul/config/local`. Data will be persisted in the `/consul/data` volume. +Note also we've set [`skip_leave_on_interrupt`](https://www.consul.io/docs/agent/options.html#skip_leave_on_interrupt) using the `CONSUL_LOCAL_CONFIG` environment variable. This is recommended for servers to and will be defaulted to `true` in Consul 0.7 and later. + +At startup, the agent will read config JSON files from `/consul/config`. Data will be persisted in the `/consul/data` volume. Once the cluster is bootstrapped and quorum is achieved, you must use care to try to keep the minimum number of servers operating in order to avoid an outage state for the cluster. The deployment table in the [consensus](https://www.consul.io/docs/internals/consensus.html) guide outlines the number of servers required for different configurations. There's also an [adding/removing servers](https://www.consul.io/docs/guides/servers.html) guide that describes that process, which is relevant to Docker configurations as well. The [outage recovery](https://www.consul.io/docs/guides/outage.html) guide has steps to perform if servers are permanently lost. In general it's best to restart or replace servers one at a time, making sure servers are healthy before proceeding to the next server. @@ -161,7 +160,7 @@ Once the cluster is bootstrapped and quorum is achieved, you must use care to tr By default the dev, client, and server modes started by the endpoint will expose Consul's DNS server on port 8600. Because this is cumbersome to configure with facilities like `resolv.conf`, you may want to expose DNS on port 53 using port arguments on your run command: ```console -$ docker run --net=host -p 53:8600/tcp -p 53:8600/udp hashicorp/consul +$ docker run --net=host -p 53:8600/tcp -p 53:8600/udp consul ``` If you are binding Consul's client interfaces to the host's loopback address, then you should be able to configure your host's `resolv.conf` to route DNS requests to Consul by including "127.0.0.1" as the primary DNS server. This would expose Consul's DNS to all applications running on the host, but due to Docker's built-in DNS server, you can't point to this directly from inside your containers; Docker will issue an error message if you attempt to do this. You must configure Consul to listen on a non-localhost address that is reachable from within other containers. @@ -169,7 +168,7 @@ If you are binding Consul's client interfaces to the host's loopback address, th Once you bind Consul's client interfaces to the bridge or other network, you can use the `--dns` option in your *other containers* in order for them to use Consul's DNS server, mapped to port 53. Here's an example: ```console -$ docker run -d --net=host -p 53:8600/tcp -p 53:8600/udp consul client -bind= +$ docker run -d --net=host -p 53:8600/tcp -p 53:8600/udp consul agent -bind= ``` Now start another container and point it at Consul's DNS, using the bridge address of the host: @@ -195,7 +194,7 @@ Another option is the open source [Registrator](http://gliderlabs.com/registrato Run a development Consul server: ```console -$ docker run -d --net=host hashicorp/consul dev -bind=66.175.220.234 # <- use your own external IP +$ docker run -d --net=host consul agent -dev -bind=66.175.220.234 # <- use your own external IP ``` Now start a companion Registrator instance: From 0d6947e1465e02f1e1b642465c9fc6da1a86c125 Mon Sep 17 00:00:00 2001 From: James Phillips Date: Thu, 21 Apr 2016 16:31:12 -0700 Subject: [PATCH 5/6] Makes sure -d is set where appropriate and cleans up examples. --- consul/content.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/consul/content.md b/consul/content.md index 42f9d7935..16807f389 100644 --- a/consul/content.md +++ b/consul/content.md @@ -38,7 +38,7 @@ The entry point also includes a small utility to look up a client or bind addres ## Running Consul for Development ```console -$ docker run consul +$ docker run -d --name=dev-consul consul ``` This runs a completely in-memory Consul server agent with default bridge networking and no services exposed on the host, which is useful for development but should not be used in production. For example, if that server is running at internal address 172.17.0.2, you can run a three node cluster for development by starting up two more instances and telling them to join the first node. @@ -50,10 +50,10 @@ $ docker run -d consul agent -dev -join=172.17.0.2 ... server 3 starts ``` -Then, choosing any one of the container IDs, we can query for all the members in the cluster by running a Consul CLI command: +Then we can query for all the members in the cluster by running a Consul CLI command in the first container: ```console -$ docker exec -t c9caabfd4c2a consul members +$ docker exec -t dev-consul consul members Node Address Status Type Build Protocol DC 579db72c1ae1 172.17.0.3:8301 alive server 0.6.3 2 dc1 7e185aebe4e6 172.17.0.3:8301 left server 0.6.3 2 dc1 @@ -68,7 +68,7 @@ Development mode also starts a version of Consul's web UI on port 8500. This can ## Running Consul Agent in Client Mode ```console -$ docker run -e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' -d --net=host consul agent -bind= -retry-join= +$ docker run -d --net=host -e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' consul agent -bind= -retry-join= ==> Starting Consul agent... ==> Starting Consul agent RPC... ==> Consul agent running! @@ -86,7 +86,7 @@ This runs a Consul client agent sharing the host's network and advertising the e The `-retry-join` parameter specifies the external IP of one other agent in the cluster to use to join at startup. There are several ways to control how an agent joins the cluster, see the [agent configuration](https://www.consul.io/docs/agent/options.html) guide for more details on the `-join`, `-retry-join`, and `-atlas-join` options. -Note also we've set [`leave_on_terminate`](https://www.consul.io/docs/agent/options.html#leave_on_terminate) using the `CONSUL_LOCAL_CONFIG` environment variable. This is recommended for clients since they should usually be removed from the cluster when the Consul agent terminates. +Note also we've set [`leave_on_terminate`](https://www.consul.io/docs/agent/options.html#leave_on_terminate) using the `CONSUL_LOCAL_CONFIG` environment variable. This is recommended for clients to and will be defaulted to `true` in Consul 0.7 and later, so this will no longer be necessary. At startup, the agent will read config JSON files from `/consul/config`. Data will be persisted in the `/consul/data` volume. @@ -142,14 +142,14 @@ With this configuration, Consul's client interfaces will be bound to the bridge ## Running Consul Agent in Server Mode ```console -$ docker run -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' -d --net=host consul agent -server -bind= -retry-join= -bootstrap-expect= +$ docker run -d --net=host -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' consul agent -server -bind= -retry-join= -bootstrap-expect= ``` This runs a Consul server agent sharing the host's network. All of the network considerations and behavior we covered above for the client agent also apply to the server agent. A single server on its own won't be able to form a quorum and will be waiting for other servers to join. Just like the client agent, the `-retry-join` parameter specifies the external IP of one other agent in the cluster to use to join at startup. There are several ways to control how an agent joins the cluster, see the [agent configuration](https://www.consul.io/docs/agent/options.html) guide for more details on the `-join`, `-retry-join`, and `-atlas-join` options. The server agent also consumes a `-bootstrap-expect` option that specifies how many server agents to watch for before bootstrapping the cluster for the first time. This provides an easy way to get an orderly startup with a new cluster. See the [agent configuration](https://www.consul.io/docs/agent/options.html) guide for more details on the `-bootstrap` and `-bootstrap-expect` options. -Note also we've set [`skip_leave_on_interrupt`](https://www.consul.io/docs/agent/options.html#skip_leave_on_interrupt) using the `CONSUL_LOCAL_CONFIG` environment variable. This is recommended for servers to and will be defaulted to `true` in Consul 0.7 and later. +Note also we've set [`skip_leave_on_interrupt`](https://www.consul.io/docs/agent/options.html#skip_leave_on_interrupt) using the `CONSUL_LOCAL_CONFIG` environment variable. This is recommended for servers to and will be defaulted to `true` in Consul 0.7 and later, so this will no longer be necessary. At startup, the agent will read config JSON files from `/consul/config`. Data will be persisted in the `/consul/data` volume. @@ -160,7 +160,7 @@ Once the cluster is bootstrapped and quorum is achieved, you must use care to tr By default the dev, client, and server modes started by the endpoint will expose Consul's DNS server on port 8600. Because this is cumbersome to configure with facilities like `resolv.conf`, you may want to expose DNS on port 53 using port arguments on your run command: ```console -$ docker run --net=host -p 53:8600/tcp -p 53:8600/udp consul +$ docker run -d --net=host -p 53:8600/tcp -p 53:8600/udp consul ``` If you are binding Consul's client interfaces to the host's loopback address, then you should be able to configure your host's `resolv.conf` to route DNS requests to Consul by including "127.0.0.1" as the primary DNS server. This would expose Consul's DNS to all applications running on the host, but due to Docker's built-in DNS server, you can't point to this directly from inside your containers; Docker will issue an error message if you attempt to do this. You must configure Consul to listen on a non-localhost address that is reachable from within other containers. @@ -174,7 +174,7 @@ $ docker run -d --net=host -p 53:8600/tcp -p 53:8600/udp consul agent -bind= -t ubuntu sh -c "apt-get install -y dnsutils && dig consul.service.consul" +$ docker run -i --dns= -t ubuntu sh -c "apt-get update && apt-get install -y dnsutils && dig consul.service.consul" ... ;; ANSWER SECTION: consul.service.consul. 0 IN A 66.175.220.234 From 725e8c07fc426cf724379c5ad55f09b9cdfc04ec Mon Sep 17 00:00:00 2001 From: James Phillips Date: Fri, 22 Apr 2016 17:22:24 -0700 Subject: [PATCH 6/6] Removes examples using non-official images; adds ContainerPilot ref. --- consul/content.md | 68 +---------------------------------------------- 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/consul/content.md b/consul/content.md index 16807f389..25e0dd758 100644 --- a/consul/content.md +++ b/consul/content.md @@ -189,73 +189,7 @@ There are several approaches you can use to register services running in contain If you run your containers under [HashiCorp's Nomad](https://www.nomadproject.io/) scheduler, it has [first class support for Consul](https://www.nomadproject.io/docs/jobspec/servicediscovery.html). The Nomad agent runs on each host alongside the Consul agent. When jobs are scheduled on a given host, the Nomad agent automatically takes care of syncing the Consul agent with the service information. This is very easy to manage, and even services on hosts running outside of Docker containers can be managed by Nomad and registered with Consul. You can find out more about running Docker under Nomad in the [Docker Driver](https://www.nomadproject.io/docs/drivers/docker.html) guide. -Another option is the open source [Registrator](http://gliderlabs.com/registrator/latest/) project from Glider Labs. Registrator works by running a Registrator instance on each host, alongside the Consul agent. Registrator monitors the Docker daemon for container stop and start events, and handles service registration with Consul using the container names and exposed ports as the service information. Here's a complete example. - -Run a development Consul server: - -```console -$ docker run -d --net=host consul agent -dev -bind=66.175.220.234 # <- use your own external IP -``` - -Now start a companion Registrator instance: - -```console -$ docker run -d --net=host --volume=/var/run/docker.sock:/tmp/docker.sock gliderlabs/registrator:latest consul://localhost:8500 -``` - -Now run some services: - -```console -$ docker run -d -P redis -$ docker run -d -P redis -$ docker run -d -P redis -``` - -And query for them using Consul's API: - -```console -$ curl http://localhost:8500/v1/catalog/service/redis?pretty -[ - { - "Node": "linode", - "Address": "66.175.220.234", - "ServiceID": "linode:boring_noyce:6379", - "ServiceName": "redis", - "ServiceTags": [], - "ServiceAddress": "66.175.220.234", - "ServicePort": 32775, - "ServiceEnableTagOverride": false, - "CreateIndex": 8, - "ModifyIndex": 8 - }, - { - "Node": "linode", - "Address": "66.175.220.234", - "ServiceID": "linode:kickass_swartz:6379", - "ServiceName": "redis", - "ServiceTags": [], - "ServiceAddress": "66.175.220.234", - "ServicePort": 32776, - "ServiceEnableTagOverride": false, - "CreateIndex": 7, - "ModifyIndex": 7 - }, - { - "Node": "linode", - "Address": "66.175.220.234", - "ServiceID": "linode:sick_euclid:6379", - "ServiceName": "redis", - "ServiceTags": [], - "ServiceAddress": "66.175.220.234", - "ServicePort": 32777, - "ServiceEnableTagOverride": false, - "CreateIndex": 6, - "ModifyIndex": 6 - } -] -``` - -Note that in the example above that the service address is the same as the external address of the host, and that the service ports are the mapped ports exposed on the host (32775, 32776, 32777). There are configuration options that control which IP and port Registrator gives to Consul, which may need to be adjusted depending on your network configuration. See Registrator's [Run Reference](http://gliderlabs.com/registrator/latest/user/run/) for more details. +Other open source options include [Registrator](http://gliderlabs.com/registrator/latest/) from Glider Labs and [ContainerPilot](https://www.joyent.com/containerpilot) from Joyent. Registrator works by running a Registrator instance on each host, alongside the Consul agent. Registrator monitors the Docker daemon for container stop and start events, and handles service registration with Consul using the container names and exposed ports as the service information. ContainerPilot manages service registration using tooling running inside the container to register services with Consul on start, manage a Consul TTL health check while running, and deregister services when the container stops. ## Running Health Checks in Docker Containers