From efdab227752c13183a4f6a29007fa80aa57e10b3 Mon Sep 17 00:00:00 2001 From: Adam Burnett Date: Wed, 17 Jun 2015 16:19:53 -0400 Subject: [PATCH 01/91] fix where providing a SERVICE_NAME for a container with multiple ports exposed would cause services to overwrite each other --- bridge/bridge.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index 4901c0901..b6fffabd0 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -176,10 +176,7 @@ func (b *Bridge) add(containerId string, quiet bool) { func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { container := port.container defaultName := strings.Split(path.Base(container.Config.Image), ":")[0] - if isgroup { - defaultName = defaultName + "-" + port.ExposedPort - } - + // not sure about this logic. kind of want to remove it. hostname, err := os.Hostname() if err != nil { @@ -208,6 +205,9 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { service.Origin = port service.ID = hostname + ":" + container.Name[1:] + ":" + port.ExposedPort service.Name = mapDefault(metadata, "name", defaultName) + if isgroup { + service.Name += "-" + port.ExposedPort + } var p int if b.config.Internal == true { service.IP = port.ExposedIP From 640360bf7cf44a62d0bb6ee81e2c7bd377ed4914 Mon Sep 17 00:00:00 2001 From: Amalia Hawkins Date: Thu, 6 Aug 2015 14:04:19 -0400 Subject: [PATCH 02/91] Synchornize etcd cluster in registrator on service registration --- etcd/etcd.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/etcd/etcd.go b/etcd/etcd.go index eb6c61ef7..1698e6333 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -52,6 +52,8 @@ type EtcdAdapter struct { } func (r *EtcdAdapter) Ping() error { + r.syncEtcdCluster() + var err error if r.client != nil { rr := etcd.NewRawRequest("GET", "version", nil, nil) @@ -67,7 +69,22 @@ func (r *EtcdAdapter) Ping() error { return nil } +func (r *EtcdAdapter) syncEtcdCluster() { + var result bool + if r.client != nil { + result = r.client.SyncCluster() + } else { + result = r.client2.SyncCluster() + } + + if !result { + log.Println("etcd: sync cluster was unsuccessful") + } +} + func (r *EtcdAdapter) Register(service *bridge.Service) error { + r.syncEtcdCluster() + path := r.path + "/" + service.Name + "/" + service.ID port := strconv.Itoa(service.Port) addr := net.JoinHostPort(service.IP, port) @@ -86,6 +103,8 @@ func (r *EtcdAdapter) Register(service *bridge.Service) error { } func (r *EtcdAdapter) Deregister(service *bridge.Service) error { + r.syncEtcdCluster() + path := r.path + "/" + service.Name + "/" + service.ID var err error From 1fd16237b7af18135e0f77283420a46e18dd80bb Mon Sep 17 00:00:00 2001 From: Jeff Lindsay Date: Mon, 10 Aug 2015 14:25:07 -0500 Subject: [PATCH 03/91] note on docker hub tags --- README.md | 3 +++ docs/index.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 1c98effda..afb8aefe8 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,9 @@ Get the latest release, master, or any version of Registrator via [Docker Hub](h $ docker pull gliderlabs/registrator:latest +Latest tag always points to the latest release. There is also a `:master` tag +and version tags to pin to specific releases. + ## Using Registrator The quickest way to see Registrator in action is our diff --git a/docs/index.md b/docs/index.md index 56b7ccc70..6de1709bd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -19,6 +19,9 @@ Get the latest release, master, or any version of Registrator via [Docker Hub](h $ docker pull gliderlabs/registrator:latest +Latest tag always points to the latest release. There is also a `:master` tag +and version tags to pin to specific releases. + ## Using Registrator The quickest way to see Registrator in action is our From 7c79bed6f74fced89a8ec1e58c09bbe3a055f1ee Mon Sep 17 00:00:00 2001 From: Jeff Lindsay Date: Mon, 10 Aug 2015 17:55:11 -0500 Subject: [PATCH 04/91] link to boot2docker --- docs/user/quickstart.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/user/quickstart.md b/docs/user/quickstart.md index 3f854203a..c0eafd3e7 100644 --- a/docs/user/quickstart.md +++ b/docs/user/quickstart.md @@ -16,7 +16,8 @@ container that will automatically get added to Consul. ## Before Starting We're going to need a host running Docker, which could just be a local -boot2docker VM, and a shell with the `docker` client pointed to that host. +[boot2docker](http://boot2docker.io/) VM, and a shell with the `docker` client +pointed to that host. We'll also need to have Consul running, which can just be running in a container. Let's run a single instance of Consul in server bootstrap mode: From 25208b3dcd74e779d7116ea77f1326dcc861ea99 Mon Sep 17 00:00:00 2001 From: Jeff Lindsay Date: Wed, 12 Aug 2015 10:07:37 -0500 Subject: [PATCH 05/91] analytics --- Makefile | 1 + mkdocs.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 1f01ed9c5..dfb18b6d5 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ release: glu hubtag gliderlabs/$(NAME) $(VERSION) docs: + boot2docker ssh "sync; sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'" || true docker run --rm -it -p 8000:8000 -v $(PWD):/work gliderlabs/pagebuilder mkdocs serve circleci: diff --git a/mkdocs.yml b/mkdocs.yml index c4d784ab4..c6bd3f07e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,6 +3,7 @@ site_url: https://gliderlabs.com/registrator repo_url: https://github.com/gliderlabs/registrator dev_addr: 0.0.0.0:8000 theme_dir: /pagebuilder/theme +google_analytics: ['UA-58928488-1', 'auto'] pages: - 'Readme': index.md - 'User Guide': From 1e14d478d9571e2745654d313bafaa664b8fa7dc Mon Sep 17 00:00:00 2001 From: Ash McKenzie Date: Sat, 15 Aug 2015 01:17:48 +1000 Subject: [PATCH 06/91] Default to tcp for PortType if not provided --- bridge/util.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/bridge/util.go b/bridge/util.go index b151e533c..9a7975909 100644 --- a/bridge/util.go +++ b/bridge/util.go @@ -56,7 +56,7 @@ func serviceMetaData(config *dockerapi.Config, port string) map[string]string { } func servicePort(container *dockerapi.Container, port dockerapi.Port, published []dockerapi.PortBinding) ServicePort { - var hp, hip string + var hp, hip, ep, ept string if len(published) > 0 { hp = published[0].HostPort hip = published[0].HostIP @@ -64,13 +64,19 @@ func servicePort(container *dockerapi.Container, port dockerapi.Port, published if hip == "" { hip = "0.0.0.0" } - p := strings.Split(string(port), "/") + exposedPort := strings.Split(string(port), "/") + ep = exposedPort[0] + if len(exposedPort) == 2 { + ept = exposedPort[1] + } else { + ept = "tcp" // default + } return ServicePort{ HostPort: hp, HostIP: hip, - ExposedPort: p[0], + ExposedPort: ep, ExposedIP: container.NetworkSettings.IPAddress, - PortType: p[1], + PortType: ept, ContainerID: container.ID, ContainerHostname: container.Config.Hostname, container: container, From c716421cc6b60df71143c926b0ae4fb70e59b785 Mon Sep 17 00:00:00 2001 From: Ash McKenzie Date: Sat, 15 Aug 2015 01:18:41 +1000 Subject: [PATCH 07/91] Allow DEV_RUN_OPTS to be used when calling make dev --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dfb18b6d5..42445777f 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,12 @@ NAME=registrator VERSION=$(shell cat VERSION) +DEV_RUN_OPTS ?= consul: dev: docker build -f Dockerfile.dev -t $(NAME):dev . docker run --rm \ -v /var/run/docker.sock:/tmp/docker.sock \ - $(NAME):dev /bin/registrator consul: + $(NAME):dev /bin/registrator $(DEV_RUN_OPTS) build: mkdir -p build From 18dcd458402507301188d0b51505d6fbce2dd960 Mon Sep 17 00:00:00 2001 From: Matt Good Date: Mon, 17 Aug 2015 16:14:58 -0700 Subject: [PATCH 08/91] Add new version checker Checks for new versions with the "usage" service and automatically displays a standard version message when the "--version" flag is passed. --- registrator.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/registrator.go b/registrator.go index 0d35eb69a..df3227cbe 100644 --- a/registrator.go +++ b/registrator.go @@ -3,17 +3,19 @@ package main import ( "errors" "flag" - "fmt" "log" "os" "time" dockerapi "github.com/fsouza/go-dockerclient" + "github.com/gliderlabs/pkg/usage" "github.com/gliderlabs/registrator/bridge" ) var Version string +var versionChecker = usage.NewChecker("registrator", Version) + var hostIp = flag.String("ip", "", "IP for ports mapped to the host") var internal = flag.Bool("internal", false, "Use internal ports instead of published ones") var refreshInterval = flag.Int("ttl-refresh", 0, "Frequency with which service TTLs are refreshed") @@ -37,7 +39,7 @@ func assert(err error) { func main() { if len(os.Args) == 2 && os.Args[1] == "--version" { - fmt.Println(Version) + versionChecker.PrintVersion() os.Exit(0) } log.Printf("Starting registrator %s ...", Version) From 029e2e491e284325e7f97fe6fa41e00b065b6bf9 Mon Sep 17 00:00:00 2001 From: Marcelo Salazar R Date: Wed, 19 Aug 2015 15:28:22 -0300 Subject: [PATCH 09/91] Adding retries to backend service in the startup Signed-off-by: Marcelo Salazar R --- bridge/bridge.go | 21 ++++++++++++++++++--- bridge/types.go | 2 ++ registrator.go | 9 +++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index 4901c0901..4ee20b933 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "sync" + "time" dockerapi "github.com/fsouza/go-dockerclient" ) @@ -32,10 +33,24 @@ func New(docker *dockerapi.Client, adapterUri string, config Config) *Bridge { log.Fatal("Unrecognized adapter:", adapterUri) } adapter := factory.New(uri) - err = adapter.Ping() - if err != nil { - log.Fatalf("%s: %s", uri.Scheme, err) + + attempt := 0 + for config.RetryAttempts == -1 || attempt <= config.RetryAttempts { + log.Printf("Connecting to backend (%v/%v)", attempt, config.RetryAttempts) + + err = adapter.Ping() + if err == nil { + break + } + + if err != nil && attempt == config.RetryAttempts { + log.Fatalf("%s: %s", uri.Scheme, err) + } + + time.Sleep(time.Duration(config.RetryInterval) * time.Millisecond) + attempt++ } + log.Println("Using", uri.Scheme, "adapter:", uri) return &Bridge{ docker: docker, diff --git a/bridge/types.go b/bridge/types.go index 6560f4683..da9e65e8c 100644 --- a/bridge/types.go +++ b/bridge/types.go @@ -25,6 +25,8 @@ type Config struct { RefreshTtl int RefreshInterval int DeregisterCheck string + RetryAttempts int + RetryInterval int } type Service struct { diff --git a/registrator.go b/registrator.go index df3227cbe..32ef2c133 100644 --- a/registrator.go +++ b/registrator.go @@ -23,6 +23,8 @@ var refreshTtl = flag.Int("ttl", 0, "TTL for services (default is no expiry)") var forceTags = flag.String("tags", "", "Append tags for all registered services") var resyncInterval = flag.Int("resync", 0, "Frequency with which services are resynchronized") var deregister = flag.String("deregister", "always", "Deregister exited services \"always\" or \"on-success\"") +var retryAttempts = flag.Int("retry-attempts", 0, "Max retry attempts to establish a connection with the backend. Use -1 for infinite retries") +var retryInterval = flag.Int("retry-interval", 2000, "Interval (in millisecond) between retry-attempts.") func getopt(name, def string) string { if env := os.Getenv(name); env != "" { @@ -49,12 +51,17 @@ func main() { if *hostIp != "" { log.Println("Forcing host IP to", *hostIp) } + if (*refreshTtl == 0 && *refreshInterval > 0) || (*refreshTtl > 0 && *refreshInterval == 0) { assert(errors.New("-ttl and -ttl-refresh must be specified together or not at all")) } else if *refreshTtl > 0 && *refreshTtl <= *refreshInterval { assert(errors.New("-ttl must be greater than -ttl-refresh")) } + if *retryInterval <= 0 { + assert(errors.New("-retry-interval must be greater than 0")) + } + docker, err := dockerapi.NewClient(getopt("DOCKER_HOST", "unix:///tmp/docker.sock")) assert(err) @@ -69,6 +76,8 @@ func main() { RefreshTtl: *refreshTtl, RefreshInterval: *refreshInterval, DeregisterCheck: *deregister, + RetryAttempts: *retryAttempts, + RetryInterval: *retryInterval, }) // Start event listener before listing containers to avoid missing anything From d3863e3c5016f382776712fb2c57bc4716a7aa7b Mon Sep 17 00:00:00 2001 From: Marcelo Salazar R Date: Wed, 19 Aug 2015 15:48:51 -0300 Subject: [PATCH 10/91] Added retry parameters documentation Signed-off-by: Marcelo Salazar R --- docs/user/run.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/user/run.md b/docs/user/run.md index 86f10a059..c7aa891dd 100644 --- a/docs/user/run.md +++ b/docs/user/run.md @@ -35,6 +35,8 @@ Option | Description ------ | ----------- `-internal` | Use exposed ports instead of published ports `-ip ` | Force IP address used for registering services +`-retry-attempts` | Max retry attempts to establish a connection with the backend +`-retry-interval` | Interval (in millisecond) between retry-attempts `-tags ` | Force comma-separated tags on all registered services `-deregister ` | Deregister existed services "always" or "on-success". Default: always `-ttl ` | TTL for services. Default: 0, no expiry (supported backends only) @@ -52,6 +54,8 @@ argument. For registry backends that support TTL expiry, Registrator can both set and refresh service TTLs with `-ttl` and `-ttl-refresh`. +If you want unlimited retry-attempts use `-retry-attempts -1`. + The `-resync` options controls how often Registrator will query Docker for all containers and reregister all services. This allows Registrator and the service registry to get back in sync if they fall out of sync. From ec8ac843b366438b21f391fa6e7295890c364de6 Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Wed, 19 Aug 2015 19:51:58 -0500 Subject: [PATCH 11/91] Upgrade to alpine:3.2 and go 1.4 go 1.4 is now required (miekg/dns#197) --- CHANGELOG.md | 2 +- Dockerfile | 2 +- Dockerfile.dev | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 307089ed9..a17524653 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ All notable changes to this project will be documented in this file. ### Removed ### Changed - +- Upgraded base image to alpine:3.2 and go 1.4 ## [v6] - 2015-08-07 ### Fixed diff --git a/Dockerfile b/Dockerfile index b793d38a7..238b04426 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM gliderlabs/alpine:3.1 +FROM gliderlabs/alpine:3.2 ENTRYPOINT ["/bin/registrator"] COPY . /go/src/github.com/gliderlabs/registrator diff --git a/Dockerfile.dev b/Dockerfile.dev index 1d7934626..dff9429a3 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM gliderlabs/alpine:3.1 +FROM gliderlabs/alpine:3.2 CMD ["/bin/registrator"] ENV GOPATH /go From c12423bc4fb439a3ec4c3ddc6e11c21e67ba1603 Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Thu, 20 Aug 2015 11:45:10 -0500 Subject: [PATCH 12/91] Refactor bridge for better testability bridge.New no longer attempts to ping an adapter, caller must now use the bridge Ping method. A few simple tests have been added to the bridge pkg --- CHANGELOG.md | 3 +++ bridge/bridge.go | 21 +++++++++++---------- bridge/bridge_test.go | 23 +++++++++++++++++++++++ bridge/types_test.go | 25 +++++++++++++++++++++++++ registrator.go | 5 ++++- 5 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 bridge/bridge_test.go create mode 100644 bridge/types_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a17524653..4d9e86253 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,14 @@ All notable changes to this project will be documented in this file. ### Fixed ### Added +- bridge.Ping - calls adapter.Ping ### Removed ### Changed - Upgraded base image to alpine:3.2 and go 1.4 +- bridge.New returns an error instead of calling log.Fatal +- bridge.New will not attempt to ping an adapter. ## [v6] - 2015-08-07 ### Fixed diff --git a/bridge/bridge.go b/bridge/bridge.go index 4901c0901..979a0f22e 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -1,6 +1,7 @@ package bridge import ( + "errors" "log" "net" "net/url" @@ -22,28 +23,28 @@ type Bridge struct { config Config } -func New(docker *dockerapi.Client, adapterUri string, config Config) *Bridge { +func New(docker *dockerapi.Client, adapterUri string, config Config) (*Bridge, error) { uri, err := url.Parse(adapterUri) if err != nil { - log.Fatal("Bad adapter URI:", adapterUri) + return nil, errors.New("bad adapter uri: " + adapterUri) } factory, found := AdapterFactories.Lookup(uri.Scheme) if !found { - log.Fatal("Unrecognized adapter:", adapterUri) - } - adapter := factory.New(uri) - err = adapter.Ping() - if err != nil { - log.Fatalf("%s: %s", uri.Scheme, err) + return nil, errors.New("unrecognized adapter: " + adapterUri) } + log.Println("Using", uri.Scheme, "adapter:", uri) return &Bridge{ docker: docker, config: config, - registry: adapter, + registry: factory.New(uri), services: make(map[string][]*Service), deadContainers: make(map[string]*DeadContainer), - } + }, nil +} + +func (b *Bridge) Ping() error { + return b.registry.Ping() } func (b *Bridge) Add(containerId string) { diff --git a/bridge/bridge_test.go b/bridge/bridge_test.go new file mode 100644 index 000000000..7aa67e82e --- /dev/null +++ b/bridge/bridge_test.go @@ -0,0 +1,23 @@ +package bridge + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewError(t *testing.T) { + bridge, err := New(nil, "", Config{}) + assert.Nil(t, bridge) + assert.Error(t, err) +} + +func TestNewValid(t *testing.T) { + Register(new(fakeFactory), "fake") + // Note: the following is valid for New() since it does not + // actually connect to docker. + bridge, err := New(nil, "fake://", Config{}) + + assert.NotNil(t, bridge) + assert.NoError(t, err) +} diff --git a/bridge/types_test.go b/bridge/types_test.go new file mode 100644 index 000000000..fe2265e8e --- /dev/null +++ b/bridge/types_test.go @@ -0,0 +1,25 @@ +package bridge + +import "net/url" + +type fakeFactory struct{} + +func (f *fakeFactory) New(uri *url.URL) RegistryAdapter { + + return &fakeAdapter{} +} + +type fakeAdapter struct{} + +func (f *fakeAdapter) Ping() error { + return nil +} +func (f *fakeAdapter) Register(service *Service) error { + return nil +} +func (f *fakeAdapter) Deregister(service *Service) error { + return nil +} +func (f *fakeAdapter) Refresh(service *Service) error { + return nil +} diff --git a/registrator.go b/registrator.go index df3227cbe..995d443c3 100644 --- a/registrator.go +++ b/registrator.go @@ -62,7 +62,7 @@ func main() { assert(errors.New("-deregister must be \"always\" or \"on-success\"")) } - b := bridge.New(docker, flag.Arg(0), bridge.Config{ + b, err := bridge.New(docker, flag.Arg(0), bridge.Config{ HostIp: *hostIp, Internal: *internal, ForceTags: *forceTags, @@ -71,6 +71,9 @@ func main() { DeregisterCheck: *deregister, }) + assert(err) + assert(b.Ping()) + // Start event listener before listing containers to avoid missing anything events := make(chan *dockerapi.APIEvents) assert(docker.AddEventListener(events)) From 02bda14e22b8ebe9815bd0e04f9ef31a4ec0fdcb Mon Sep 17 00:00:00 2001 From: Marcelo Salazar R Date: Fri, 21 Aug 2015 22:20:16 -0300 Subject: [PATCH 13/91] Removed unused attributes --- bridge/types.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/bridge/types.go b/bridge/types.go index da9e65e8c..6560f4683 100644 --- a/bridge/types.go +++ b/bridge/types.go @@ -25,8 +25,6 @@ type Config struct { RefreshTtl int RefreshInterval int DeregisterCheck string - RetryAttempts int - RetryInterval int } type Service struct { From f04e7894a8240b457f55e90cb836a5758c2b0d97 Mon Sep 17 00:00:00 2001 From: Jeff Lindsay Date: Wed, 26 Aug 2015 10:40:55 -0500 Subject: [PATCH 14/91] prebump --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 9c0be88a7..6fb293940 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v6 +v7-dev From 70807eacc4a63937f8263b57eb594c2c9a9b0f21 Mon Sep 17 00:00:00 2001 From: Nathan Black Date: Tue, 1 Sep 2015 16:36:16 -0500 Subject: [PATCH 15/91] Adding documentation link --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index afb8aefe8..418ca8a93 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ supports pluggable service registries, which currently includes [Consul](http://www.consul.io/), [etcd](https://github.com/coreos/etcd) and [SkyDNS 2](https://github.com/skynetservices/skydns/). +Full documentation available at http://gliderlabs.com/registrator + ## Getting Registrator Get the latest release, master, or any version of Registrator via [Docker Hub](https://registry.hub.docker.com/u/gliderlabs/registrator/): From 5eab11529aae69b6beab7b02da6e98d096759ff3 Mon Sep 17 00:00:00 2001 From: Andy Shinn Date: Mon, 7 Sep 2015 17:39:49 -0700 Subject: [PATCH 16/91] update wording for Weave product family --- README.md | 3 +-- docs/index.md | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 418ca8a93..53b841f5e 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,7 @@ Releases](https://gliderlabs.com/registrator/latest/dev/releases.). ## Sponsors and Thanks -Ongoing support of this project is made possible by [Weave](http://weave.works), -the Docker SDN. Big thanks to Michael Crosby for +Ongoing support of this project is made possible by [Weave](http://weave.works), the easiest way to connect, observe and control your containers. Big thanks to Michael Crosby for [skydock](https://github.com/crosbymichael/skydock) and the Consul mailing list for inspiration. diff --git a/docs/index.md b/docs/index.md index 6de1709bd..3ddeccde6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -47,8 +47,7 @@ and [Staging Releases](dev/releases.md). ## Sponsors and Thanks -Ongoing support of this project is made possible by [Weave](http://weave.works), -the Docker SDN. Big thanks to Michael Crosby for +Ongoing support of this project is made possible by [Weave](http://weave.works), the easiest way to connect, observe and control your containers. Big thanks to Michael Crosby for [skydock](https://github.com/crosbymichael/skydock) and the Consul mailing list for inspiration. From 60744fddf4813b2b48acbb84c92d528ad4a132c8 Mon Sep 17 00:00:00 2001 From: Adam Burnett Date: Tue, 8 Sep 2015 14:31:00 -0400 Subject: [PATCH 17/91] updating documentation & CHANGELOG --- CHANGELOG.md | 1 + docs/user/services.md | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d9e86253..15c7bac29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. - Upgraded base image to alpine:3.2 and go 1.4 - bridge.New returns an error instead of calling log.Fatal - bridge.New will not attempt to ping an adapter. +- Specifying a SERVICE_NAME for containers exposing multiple ports will now result in a named service per port. #194 ## [v6] - 2015-08-07 ### Fixed diff --git a/docs/user/services.md b/docs/user/services.md index 4c440bc2c..f893c27d4 100644 --- a/docs/user/services.md +++ b/docs/user/services.md @@ -70,7 +70,9 @@ internal exposed port to differentiate from each other. For example, an image `nginx-80` and `nginx-443`. You can override this default name with label or environment variable -`SERVICE_NAME` or `SERVICE_x_NAME`, where `x` is the internal exposed port. +`SERVICE_NAME` or `SERVICE_x_NAME`, where `x` is the internal exposed port. Note +that if a container has multiple exposed ports then setting `SERVICE_NAME` will +still result in multiple services named `SERVICE_NAME-`. ## IP and Port From dd3ab2e63ca1eec4dc750189b213f6b3d191492f Mon Sep 17 00:00:00 2001 From: John Days Date: Thu, 10 Sep 2015 19:13:46 +0100 Subject: [PATCH 18/91] Fix specific port names not overriding port suffix --- bridge/bridge.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index a60eb5bf4..5439c38c5 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -206,7 +206,7 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { service.Origin = port service.ID = hostname + ":" + container.Name[1:] + ":" + port.ExposedPort service.Name = mapDefault(metadata, "name", defaultName) - if isgroup { + if isgroup && metadata["name"] == "" { service.Name += "-" + port.ExposedPort } var p int From c4e31cc24df7245a54a7d3da1d695622728e8206 Mon Sep 17 00:00:00 2001 From: John Days Date: Thu, 10 Sep 2015 19:41:39 +0100 Subject: [PATCH 19/91] Actually check metadata from port. Fix ENV variable order dependency --- bridge/bridge.go | 4 ++-- bridge/util.go | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index 5439c38c5..ff4808566 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -195,7 +195,7 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { port.HostIP = b.config.HostIp } - metadata := serviceMetaData(container.Config, port.ExposedPort) + metadata, metadataFromPort := serviceMetaData(container.Config, port.ExposedPort) ignore := mapDefault(metadata, "ignore", "") if ignore != "" { @@ -206,7 +206,7 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { service.Origin = port service.ID = hostname + ":" + container.Name[1:] + ":" + port.ExposedPort service.Name = mapDefault(metadata, "name", defaultName) - if isgroup && metadata["name"] == "" { + if isgroup && !metadataFromPort["name"] { service.Name += "-" + port.ExposedPort } var p int diff --git a/bridge/util.go b/bridge/util.go index 9a7975909..828510928 100644 --- a/bridge/util.go +++ b/bridge/util.go @@ -30,16 +30,20 @@ func combineTags(tagParts ...string) []string { return tags } -func serviceMetaData(config *dockerapi.Config, port string) map[string]string { +func serviceMetaData(config *dockerapi.Config, port string) (map[string]string, map[string]bool) { meta := config.Env for k, v := range config.Labels { meta = append(meta, k + "=" + v) } metadata := make(map[string]string) + metadataFromPort := make(map[string]bool) for _, kv := range meta { kvp := strings.SplitN(kv, "=", 2) if strings.HasPrefix(kvp[0], "SERVICE_") && len(kvp) > 1 { key := strings.ToLower(strings.TrimPrefix(kvp[0], "SERVICE_")) + if metadataFromPort[key] { + continue + } portkey := strings.SplitN(key, "_", 2) _, err := strconv.Atoi(portkey[0]) if err == nil && len(portkey) > 1 { @@ -47,12 +51,13 @@ func serviceMetaData(config *dockerapi.Config, port string) map[string]string { continue } metadata[portkey[1]] = kvp[1] + metadataFromPort[portkey[1]] = true } else { metadata[key] = kvp[1] } } } - return metadata + return metadata, metadataFromPort } func servicePort(container *dockerapi.Container, port dockerapi.Port, published []dockerapi.PortBinding) ServicePort { From 81d8f6a6a1892fb4df5fd1cffe0134662569c4e5 Mon Sep 17 00:00:00 2001 From: Matt Good Date: Tue, 15 Sep 2015 13:07:24 -0700 Subject: [PATCH 20/91] Use exit status to determine if container was killed Instead of using the "kill" and "stop" events, this uses the exit status to check whether the container was terminated via a signal. This will be more reliable since the "kill" event can also be sent for non-fatal signals such as SIGHUP. Fixes #248 --- bridge/bridge.go | 26 +++++++++++++++++++++----- registrator.go | 2 -- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index a60eb5bf4..610e672f0 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -58,7 +58,7 @@ func (b *Bridge) Remove(containerId string) { } func (b *Bridge) RemoveOnExit(containerId string) { - b.remove(containerId, b.config.DeregisterCheck == "always" || b.didExitCleanly(containerId)) + b.remove(containerId, b.shouldRemove(containerId)) } func (b *Bridge) Refresh() { @@ -177,7 +177,7 @@ func (b *Bridge) add(containerId string, quiet bool) { func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { container := port.container defaultName := strings.Split(path.Base(container.Config.Image), ":")[0] - + // not sure about this logic. kind of want to remove it. hostname, err := os.Hostname() if err != nil { @@ -269,7 +269,13 @@ func (b *Bridge) remove(containerId string, deregister bool) { delete(b.services, containerId) } -func (b *Bridge) didExitCleanly(containerId string) bool { +// bit set on ExitCode if it represents an exit via a signal +var dockerSignaledBit = 128 + +func (b *Bridge) shouldRemove(containerId string) bool { + if b.config.DeregisterCheck == "always" { + return true + } container, err := b.docker.InspectContainer(containerId) if _, ok := err.(*dockerapi.NoSuchContainer); ok { // the container has already been removed from Docker @@ -277,9 +283,19 @@ func (b *Bridge) didExitCleanly(containerId string) bool { // so its exit code is not accessible log.Printf("registrator: container %v was removed, could not fetch exit code", containerId[:12]) return true - } else if err != nil { + } + + switch { + case err != nil: log.Printf("registrator: error fetching status for container %v on \"die\" event: %v\n", containerId[:12], err) return false + case container.State.Running: + log.Printf("registrator: not removing container %v, still running", containerId[:12]) + return false + case container.State.ExitCode == 0: + return true + case container.State.ExitCode&dockerSignaledBit == dockerSignaledBit: + return true } - return !container.State.Running && container.State.ExitCode == 0 + return false } diff --git a/registrator.go b/registrator.go index 95702a727..f7bdd3621 100644 --- a/registrator.go +++ b/registrator.go @@ -145,8 +145,6 @@ func main() { go b.Add(msg.ID) case "die": go b.RemoveOnExit(msg.ID) - case "stop", "kill": - go b.Remove(msg.ID) } } From f38bf72670ac93ff8056d9a6f770f1d2c914c144 Mon Sep 17 00:00:00 2001 From: Denis Parchenko Date: Wed, 30 Sep 2015 12:35:43 +0300 Subject: [PATCH 21/91] Fix releases link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 53b841f5e..51ed82872 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ discussing in [Slack](http://glider-slackin.herokuapp.com/). Also check out our Developer Guide on [Contributing Backends](https://gliderlabs.com/registrator/latest/dev/backends) and [Staging -Releases](https://gliderlabs.com/registrator/latest/dev/releases.). +Releases](https://gliderlabs.com/registrator/latest/dev/releases). ## Sponsors and Thanks From ffed69c8e1aed42374eaa0fab5dd5265f4319827 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 24 Oct 2015 03:40:13 -0400 Subject: [PATCH 22/91] Align SPONSORS text --- SPONSORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPONSORS b/SPONSORS index 47c141492..c70a20d8b 100644 --- a/SPONSORS +++ b/SPONSORS @@ -1,2 +1,2 @@ -DigitalOcean http://digitalocean.com +DigitalOcean http://digitalocean.com Weaveworks http://weave.works From 9321a0d02ebc9e9df2e307e6cf0dcbe7dc35b5bf Mon Sep 17 00:00:00 2001 From: Matthew Good Date: Sun, 22 Nov 2015 14:21:21 -0800 Subject: [PATCH 23/91] Add more detailed usage regarding options placement Go's "flag" module only parses options up until the first non-option argument, so additional arguments are left unparsed in "flag.Args()". We only expect one argument, but additional arguments were ignored, leading to some confusion about options that were ignored. This updates the Usage() message with the syntax showing the options before the registry URI, as well as more detail if the registry argument is missing, or additional arguments are found. --- registrator.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/registrator.go b/registrator.go index 95702a727..25f0af41a 100644 --- a/registrator.go +++ b/registrator.go @@ -3,8 +3,10 @@ package main import ( "errors" "flag" + "fmt" "log" "os" + "strings" "time" dockerapi "github.com/fsouza/go-dockerclient" @@ -46,8 +48,26 @@ func main() { } log.Printf("Starting registrator %s ...", Version) + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " %s [options] \n\n", os.Args[0]) + flag.PrintDefaults() + } + flag.Parse() + if flag.NArg() != 1 { + if flag.NArg() == 0 { + fmt.Fprint(os.Stderr, "Missing required argument for registry URI.\n\n") + } else { + fmt.Fprintln(os.Stderr, "Extra unparsed arguments:") + fmt.Fprintln(os.Stderr, " ", strings.Join(flag.Args()[1:], " ")) + fmt.Fprint(os.Stderr, "Options should come before the registry URI argument.\n\n") + } + flag.Usage() + os.Exit(2) + } + if *hostIp != "" { log.Println("Forcing host IP to", *hostIp) } From 3181e58ae642b9963711ebf1fe7cb431a3f684b3 Mon Sep 17 00:00:00 2001 From: Matt Robenolt Date: Sun, 14 Jun 2015 20:12:17 -0700 Subject: [PATCH 24/91] Cleanup dangling services When a service was previously registered into the service registry and registrator exits without unregistering, registrator now queries the backend to see which services were registered, and checks against it's internal list to determine which should be unregistered. --- bridge/bridge.go | 70 +++++++++++++++++++++++++++++++++++++------- bridge/types.go | 3 ++ consul/consul.go | 21 +++++++++++++ consulkv/consulkv.go | 4 +++ etcd/etcd.go | 4 +++ registrator.go | 3 ++ skydns2/skydns2.go | 4 +++ 7 files changed, 99 insertions(+), 10 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index a60eb5bf4..256606040 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -7,6 +7,7 @@ import ( "net/url" "os" "path" + "regexp" "strconv" "strings" "sync" @@ -14,6 +15,8 @@ import ( dockerapi "github.com/fsouza/go-dockerclient" ) +var serviceIDPattern = regexp.MustCompile(`^(.+?):([a-zA-Z0-9][a-zA-Z0-9_.-]+):[0-9]+(?::udp)?$`) + type Bridge struct { sync.Mutex registry RegistryAdapter @@ -98,8 +101,7 @@ func (b *Bridge) Sync(quiet bool) { log.Printf("Syncing services on %d containers", len(containers)) - // NOTE: This assumes reregistering will do the right thing, i.e. nothing. - // NOTE: This will NOT remove services. + // NOTE: This assumes reregistering will do the right thing, i.e. nothing.. for _, listing := range containers { services := b.services[listing.ID] if services == nil { @@ -113,6 +115,47 @@ func (b *Bridge) Sync(quiet bool) { } } } + + // Clean up services that were registered previously, but aren't + // acknowledged within registrator + if b.config.Cleanup { + log.Println("Cleaning up dangling services") + + extServices, err := b.registry.Services() + if err != nil { + log.Println("cleanup failed:", err) + return + } + + Outer: + for _, extService := range extServices { + matches := serviceIDPattern.FindStringSubmatch(extService.ID) + if len(matches) != 3 { + // There's no way this was registered by us, so leave it + continue + } + serviceHostname := matches[1] + if serviceHostname != Hostname { + // ignore because registered on a different host + continue + } + serviceContainerName := matches[2] + for _, listing := range b.services { + for _, service := range listing { + if service.Name == extService.Name && serviceContainerName == service.Origin.container.Name[1:] { + continue Outer + } + } + } + log.Println("dangling:", extService.ID) + err := b.registry.Deregister(extService) + if err != nil { + log.Println("deregister failed:", extService.ID, err) + continue + } + log.Println(extService.ID, "removed") + } + } } func (b *Bridge) add(containerId string, quiet bool) { @@ -179,15 +222,14 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { defaultName := strings.Split(path.Base(container.Config.Image), ":")[0] // not sure about this logic. kind of want to remove it. - hostname, err := os.Hostname() - if err != nil { + hostname := Hostname + if hostname == "" { hostname = port.HostIP - } else { - if port.HostIP == "0.0.0.0" { - ip, err := net.ResolveIPAddr("ip", hostname) - if err == nil { - port.HostIP = ip.String() - } + } + if port.HostIP == "0.0.0.0" { + ip, err := net.ResolveIPAddr("ip", hostname) + if err == nil { + port.HostIP = ip.String() } } @@ -283,3 +325,11 @@ func (b *Bridge) didExitCleanly(containerId string) bool { } return !container.State.Running && container.State.ExitCode == 0 } + +var Hostname string + +func init() { + // It's ok for Hostname to ultimately be an empty string + // An empty string will fall back to trying to make a best guess + Hostname, _ = os.Hostname() +} diff --git a/bridge/types.go b/bridge/types.go index 6560f4683..b1611127e 100644 --- a/bridge/types.go +++ b/bridge/types.go @@ -16,6 +16,7 @@ type RegistryAdapter interface { Register(service *Service) error Deregister(service *Service) error Refresh(service *Service) error + Services() ([]*Service, error) } type Config struct { @@ -25,6 +26,7 @@ type Config struct { RefreshTtl int RefreshInterval int DeregisterCheck string + Cleanup bool } type Service struct { @@ -52,5 +54,6 @@ type ServicePort struct { PortType string ContainerHostname string ContainerID string + ContainerName string container *dockerapi.Container } diff --git a/consul/consul.go b/consul/consul.go index 8c1bc5eac..2e1252164 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -96,3 +96,24 @@ func (r *ConsulAdapter) Deregister(service *bridge.Service) error { func (r *ConsulAdapter) Refresh(service *bridge.Service) error { return nil } + +func (r *ConsulAdapter) Services() ([]*bridge.Service, error) { + services, err := r.client.Agent().Services() + if err != nil { + return []*bridge.Service{}, err + } + out := make([]*bridge.Service, len(services)) + i := 0 + for _, v := range services { + s := &bridge.Service{ + ID: v.ID, + Name: v.Service, + Port: v.Port, + Tags: v.Tags, + IP: v.Address, + } + out[i] = s + i++ + } + return out, nil +} diff --git a/consulkv/consulkv.go b/consulkv/consulkv.go index 6947a7666..3be48e561 100644 --- a/consulkv/consulkv.go +++ b/consulkv/consulkv.go @@ -68,3 +68,7 @@ func (r *ConsulKVAdapter) Deregister(service *bridge.Service) error { func (r *ConsulKVAdapter) Refresh(service *bridge.Service) error { return nil } + +func (r *ConsulKVAdapter) Services() ([]*bridge.Service, error) { + return []*bridge.Service{}, nil +} diff --git a/etcd/etcd.go b/etcd/etcd.go index 1698e6333..67b84cbe9 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -123,3 +123,7 @@ func (r *EtcdAdapter) Deregister(service *bridge.Service) error { func (r *EtcdAdapter) Refresh(service *bridge.Service) error { return r.Register(service) } + +func (r *EtcdAdapter) Services() ([]*bridge.Service, error) { + return []*bridge.Service{}, nil +} diff --git a/registrator.go b/registrator.go index 95702a727..08f85c2f2 100644 --- a/registrator.go +++ b/registrator.go @@ -25,6 +25,8 @@ var resyncInterval = flag.Int("resync", 0, "Frequency with which services are re var deregister = flag.String("deregister", "always", "Deregister exited services \"always\" or \"on-success\"") var retryAttempts = flag.Int("retry-attempts", 0, "Max retry attempts to establish a connection with the backend. Use -1 for infinite retries") var retryInterval = flag.Int("retry-interval", 2000, "Interval (in millisecond) between retry-attempts.") +var cleanup = flag.Bool("cleanup", false, "Remove dangling services") + func getopt(name, def string) string { if env := os.Getenv(name); env != "" { @@ -76,6 +78,7 @@ func main() { RefreshTtl: *refreshTtl, RefreshInterval: *refreshInterval, DeregisterCheck: *deregister, + Cleanup: *cleanup, }) assert(err) diff --git a/skydns2/skydns2.go b/skydns2/skydns2.go index 90c1cebec..6f2cb2c4d 100644 --- a/skydns2/skydns2.go +++ b/skydns2/skydns2.go @@ -65,6 +65,10 @@ func (r *Skydns2Adapter) Refresh(service *bridge.Service) error { return r.Register(service) } +func (r *Skydns2Adapter) Services() ([]*bridge.Service, error) { + return []*bridge.Service{}, nil +} + func (r *Skydns2Adapter) servicePath(service *bridge.Service) string { return r.path + "/" + service.Name + "/" + service.ID } From d1bec88ec139aecb9311c0eaa0bba7a0ff7777b9 Mon Sep 17 00:00:00 2001 From: nirped Date: Fri, 4 Dec 2015 17:41:33 +0200 Subject: [PATCH 25/91] Support for Docker multi host networking When using the Docker multi-host networking, IPAddress under NetworkSettings is set to an empty string and the container IP can be retrieved from NetworkSettings.Networks. At this point it is assumed a single Docker network is associated with the container --- bridge/util.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bridge/util.go b/bridge/util.go index 9a7975909..2bf9df255 100644 --- a/bridge/util.go +++ b/bridge/util.go @@ -71,11 +71,20 @@ func servicePort(container *dockerapi.Container, port dockerapi.Port, published } else { ept = "tcp" // default } + + // Nir: support docker NetworkSettings + eip = container.NetworkSettings.IPAddress + if eip == "" { + for _, network := range container.NetworkSettings.Networks { + eip = network.IPAddress + } + } + return ServicePort{ HostPort: hp, HostIP: hip, ExposedPort: ep, - ExposedIP: container.NetworkSettings.IPAddress, + ExposedIP: eip, PortType: ept, ContainerID: container.ID, ContainerHostname: container.Config.Hostname, From dc2faa6fe424b964671eea0b812ee9c4a93b06db Mon Sep 17 00:00:00 2001 From: nirped Date: Fri, 4 Dec 2015 17:47:08 +0200 Subject: [PATCH 26/91] Update util.go --- bridge/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridge/util.go b/bridge/util.go index 2bf9df255..d22e42a85 100644 --- a/bridge/util.go +++ b/bridge/util.go @@ -56,7 +56,7 @@ func serviceMetaData(config *dockerapi.Config, port string) map[string]string { } func servicePort(container *dockerapi.Container, port dockerapi.Port, published []dockerapi.PortBinding) ServicePort { - var hp, hip, ep, ept string + var hp, hip, ep, ept, eip string if len(published) > 0 { hp = published[0].HostPort hip = published[0].HostIP From 7f183c74a522f38755bb106a1b906b5bd385809e Mon Sep 17 00:00:00 2001 From: Jonathan Rowlands Date: Sat, 5 Sep 2015 00:38:49 -0400 Subject: [PATCH 27/91] Using NewVersionedClientFromEnv to create docker client --- registrator.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/registrator.go b/registrator.go index 08f85c2f2..4b0a82cb5 100644 --- a/registrator.go +++ b/registrator.go @@ -27,7 +27,6 @@ var retryAttempts = flag.Int("retry-attempts", 0, "Max retry attempts to establi var retryInterval = flag.Int("retry-interval", 2000, "Interval (in millisecond) between retry-attempts.") var cleanup = flag.Bool("cleanup", false, "Remove dangling services") - func getopt(name, def string) string { if env := os.Getenv(name); env != "" { return env @@ -64,7 +63,12 @@ func main() { assert(errors.New("-retry-interval must be greater than 0")) } - docker, err := dockerapi.NewClient(getopt("DOCKER_HOST", "unix:///tmp/docker.sock")) + dockerHost := os.Getenv("DOCKER_HOST") + if dockerHost == "" { + os.Setenv("DOCKER_HOST", "unix:///tmp/docker.sock") + } + + docker, err := dockerapi.NewClientFromEnv() assert(err) if *deregister != "always" && *deregister != "on-success" { From ed13635c5a374ad4a2e9e790c2b8e8d236cd5384 Mon Sep 17 00:00:00 2001 From: John Meichle Date: Sat, 28 Mar 2015 13:19:05 -0400 Subject: [PATCH 28/91] Initial basic zookeeper backend for registrator --- CHANGELOG.md | 1 + docs/user/backends.md | 19 +++++++ modules.go | 1 + zookeeper/zookeeper.go | 115 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 zookeeper/zookeeper.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 15c7bac29..51c30ed30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file. - Panic from invalid skydns2 URI. ### Added +- Basic zookeeper adapter - Optional periodic resyncing of services from containers - More error logging for registries - Support for services on containers with `--net=host` diff --git a/docs/user/backends.md b/docs/user/backends.md index 07a737b05..29503b00b 100644 --- a/docs/user/backends.md +++ b/docs/user/backends.md @@ -104,3 +104,22 @@ SkyDNS requires the service ID to be a valid DNS hostname, so this backend requi override service ID to a valid DNS name. Example: $ docker run -d --name redis-1 -e SERVICE_ID=redis-1 -p 6379:6379 redis + +## Zookeeper Store + +The Zookeeper backend lets you publish ephemeral znodes into zookeeper. This mode is enabled by specifying a zookeeper path. The zookeeper backend supports publishing a json znode body complete with defined service attributes/tags as well as the service name and container id. Example URIs: + + $ registrator zookeeper://zookeeper.host/basepath + $ registrator zookeeper://192.168.1.100:9999/basepath + +Within the base path specified in the zookeeper URI, registrator will create the following path tree containing a JSON entry for the service: + + / = + +The JSON will contain all infromation about the published container service. As an example, the following container start: + + docker run -i -p 80 -e 'SERVICE_80_NAME=www' -t ubuntu:14.04 /bin/bash + +Will result in the zookeeper path and JSON znode body: + + /basepath/www/80 = {"Name":"www","IP":"192.168.1.123","PublicPort":49153,"PrivatePort":80,"ContainerID":"9124853ff0d1","Tags":[],"Attrs":{}} diff --git a/modules.go b/modules.go index 833e5f999..4ac74c313 100644 --- a/modules.go +++ b/modules.go @@ -5,4 +5,5 @@ import ( _ "github.com/gliderlabs/registrator/consulkv" _ "github.com/gliderlabs/registrator/etcd" _ "github.com/gliderlabs/registrator/skydns2" + _ "github.com/gliderlabs/registrator/zookeeper" ) diff --git a/zookeeper/zookeeper.go b/zookeeper/zookeeper.go new file mode 100644 index 000000000..26e7c9e55 --- /dev/null +++ b/zookeeper/zookeeper.go @@ -0,0 +1,115 @@ +package zookeeper + +import ( + "encoding/json" + "log" + "net/url" + "strconv" + "time" + + "github.com/gliderlabs/registrator/bridge" + "github.com/samuel/go-zookeeper/zk" +) + +func init() { + bridge.Register(new(Factory), "zookeeper") +} + +type Factory struct{} + +func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter { + c, _, err := zk.Connect([]string{uri.Host}, (time.Second * 10)) + if err != nil { + panic(err) + } + exists, _, err := c.Exists(uri.Path) + if err != nil { + log.Println("zookeeper: error checking if base path exists:", err) + } + if !exists { + c.Create(uri.Path, []byte{}, 0, zk.WorldACL(zk.PermAll)) + } + return &ZkAdapter{client: c, path: uri.Path} +} + +type ZkAdapter struct { + client *zk.Conn + path string +} + +type ZnodeBody struct { + Name string + IP string + PublicPort int + PrivatePort int + ContainerID string + Tags []string + Attrs map[string]string +} + +func (r *ZkAdapter) Register(service *bridge.Service) error { + privatePort, _ := strconv.Atoi(service.Origin.ExposedPort) + acl := zk.WorldACL(zk.PermAll) + + exists, _, err := r.client.Exists(r.path + "/" + service.Name) + if err != nil { + log.Println("zookeeper: error checking if exists: ", err) + } else { + if !exists { + _, err := r.client.Create(r.path+"/"+service.Name, []byte{}, 0, acl) + if err != nil { + log.Println("zookeeper: failed to create base service node: ", err) + } else { + zbody := &ZnodeBody{Name: service.Name, IP: service.IP, PublicPort: service.Port, PrivatePort: privatePort, Tags: service.Tags, Attrs: service.Attrs, ContainerID: service.Origin.ContainerHostname} + body, err := json.Marshal(zbody) + if err != nil { + log.Println("zookeeper: failed to json encode service body: ", err) + } else { + path := r.path + "/" + service.Name + "/" + service.Origin.ExposedPort + _, err = r.client.Create(path, body, 1, acl) + if err != nil { + log.Println("zookeeper: failed to register service: ", err) + } + } // json encode error check + } // create service path error check + } // service path exists + } // service path exists error check + return err +} + +func (r *ZkAdapter) Ping() error { + _, _, err := r.client.Exists("/") + if err != nil { + log.Println("zookeeper: error on ping check for Exists(/): ", err) + return err + } + return nil +} + +func (r *ZkAdapter) Deregister(service *bridge.Service) error { + basePath := r.path + "/" + service.Name + // Delete the service-port znode + servicePortPath := basePath + "/" + service.Origin.ExposedPort + err := r.client.Delete(servicePortPath, -1) // -1 means latest version number + if err != nil { + log.Println("zookeeper: failed to deregister service port entry: ", err) + } + // Check if all service-port znodes are removed. + children, _, err := r.client.Children(basePath) + if len(children) == 0 { + // Delete the service name znode + err := r.client.Delete(basePath, -1) + if err != nil { + log.Println("zookeeper: failed to delete service path: ", err) + } + } + return err +} + +func (r *ZkAdapter) Refresh(service *bridge.Service) error { + return r.Register(service) +} + +func (r *ZkAdapter) Services() ([]*bridge.Service, error) { + return []*bridge.Service{}, nil +} From ceae5da0980f16c803ccb8419e9c81f4aad6426c Mon Sep 17 00:00:00 2001 From: Etki Date: Sat, 23 Jan 2016 12:41:28 +0300 Subject: [PATCH 29/91] Small docs refurbishment --- docs/user/run.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/user/run.md b/docs/user/run.md index c7aa891dd..66824225f 100644 --- a/docs/user/run.md +++ b/docs/user/run.md @@ -31,17 +31,17 @@ hostname (`-h $HOSTNAME`) and using the `-ip` Registrator option below. ## Registrator Options -Option | Description ------- | ----------- -`-internal` | Use exposed ports instead of published ports -`-ip ` | Force IP address used for registering services -`-retry-attempts` | Max retry attempts to establish a connection with the backend -`-retry-interval` | Interval (in millisecond) between retry-attempts -`-tags ` | Force comma-separated tags on all registered services -`-deregister ` | Deregister existed services "always" or "on-success". Default: always -`-ttl ` | TTL for services. Default: 0, no expiry (supported backends only) -`-ttl-refresh ` | Frequency service TTLs are refreshed (supported backends only) -`-resync ` | Frequency all services are resynchronized. Default: 0, never +Option | Since | Description +------ | ----- | ----------- +`-internal` | | Use exposed ports instead of published ports +`-ip ` | | Force IP address used for registering services +`-retry-attempts ` | v7 | Max retry attempts to establish a connection with the backend +`-retry-interval ` | v7 | Interval (in millisecond) between retry-attempts +`-tags ` | v5 | Force comma-separated tags on all registered services +`-deregister ` | v6 | Deregister existed services "always" or "on-success". Default: always +`-ttl ` | | TTL for services. Default: 0, no expiry (supported backends only) +`-ttl-refresh ` | | Frequency service TTLs are refreshed (supported backends only) +`-resync ` | v6 | Frequency all services are resynchronized. Default: 0, never If the `-internal` option is used, Registrator will register the docker0 internal IP and port instead of the host mapped ones. From 0c22941773494f42f7f7147747bef6428dd78666 Mon Sep 17 00:00:00 2001 From: Damir Murat Date: Thu, 28 Jan 2016 09:39:34 +0100 Subject: [PATCH 30/91] Note for ignoring individual service on container --- docs/user/services.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/user/services.md b/docs/user/services.md index f893c27d4..d7a187c1e 100644 --- a/docs/user/services.md +++ b/docs/user/services.md @@ -53,6 +53,9 @@ These can be implicitly set from the Dockerfile or explicitly set with `docker r You can also tell Registrator to ignore a container by setting a label or environment variable for `SERVICE_IGNORE`. +If you need to ignore individual service on some container, you can use +`SERVICE__IGNORE=true`. + ## Service Name Service names are what you use in service discovery lookups. By default, the From fd3ec079ab95a84b535b695840c15cbb871ea4cf Mon Sep 17 00:00:00 2001 From: Cole Brumley Date: Sun, 7 Feb 2016 21:22:56 -0600 Subject: [PATCH 31/91] Add support for Consul unix sockets --- consul/consul.go | 8 ++++++-- consulkv/consulkv.go | 15 ++++++++++++--- docs/user/backends.md | 2 ++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/consul/consul.go b/consul/consul.go index 2e1252164..41569c191 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -13,7 +13,9 @@ import ( const DefaultInterval = "10s" func init() { - bridge.Register(new(Factory), "consul") + f := new(Factory) + bridge.Register(f, "consul") + bridge.Register(f, "consul-unix") } func (r *ConsulAdapter) interpolateService(script string, service *bridge.Service) string { @@ -26,7 +28,9 @@ type Factory struct{} func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter { config := consulapi.DefaultConfig() - if uri.Host != "" { + if uri.Scheme == "consul-unix" { + config.Address = strings.TrimPrefix(uri.String(), "consul-") + } else if uri.Host != "" { config.Address = uri.Host } client, err := consulapi.NewClient(config) diff --git a/consulkv/consulkv.go b/consulkv/consulkv.go index 3be48e561..7d5cc12b3 100644 --- a/consulkv/consulkv.go +++ b/consulkv/consulkv.go @@ -5,27 +5,34 @@ import ( "net" "net/url" "strconv" + "strings" "github.com/gliderlabs/registrator/bridge" consulapi "github.com/hashicorp/consul/api" ) func init() { - bridge.Register(new(Factory), "consulkv") + f := new(Factory) + bridge.Register(f, "consulkv") + bridge.Register(f, "consulkv-unix") } type Factory struct{} func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter { config := consulapi.DefaultConfig() - if uri.Host != "" { + path := uri.Path + if uri.Scheme == "consulkv-unix" { + spl := strings.SplitN(uri.Path, ":", 2) + config.Address, path = "unix://"+spl[0], spl[1] + } else if uri.Host != "" { config.Address = uri.Host } client, err := consulapi.NewClient(config) if err != nil { log.Fatal("consulkv: ", uri.Scheme) } - return &ConsulKVAdapter{client: client, path: uri.Path} + return &ConsulKVAdapter{client: client, path: path} } type ConsulKVAdapter struct { @@ -46,9 +53,11 @@ func (r *ConsulKVAdapter) Ping() error { } func (r *ConsulKVAdapter) Register(service *bridge.Service) error { + log.Println("Register") path := r.path[1:] + "/" + service.Name + "/" + service.ID port := strconv.Itoa(service.Port) addr := net.JoinHostPort(service.IP, port) + log.Printf("path: %s", path) _, err := r.client.KV().Put(&consulapi.KVPair{Key: path, Value: []byte(addr)}, nil) if err != nil { log.Println("consulkv: failed to register service:", err) diff --git a/docs/user/backends.md b/docs/user/backends.md index 29503b00b..5ce7b2f4e 100644 --- a/docs/user/backends.md +++ b/docs/user/backends.md @@ -9,6 +9,7 @@ See also [Contributing Backends](../dev/backends.md). ## Consul consul://
: + consul-unix:// Consul is the recommended registry since it specifically models services for service discovery with health checks. @@ -63,6 +64,7 @@ SERVICE_CHECK_TTL=30s ## Consul KV consulkv://
:/ + consulkv-unix://:/ This is a separate backend to use Consul's key-value store instead of its native service catalog. This behaves more like etcd since it has similar semantics, but From 3f3391ca21ea904713f558ac5e2a3f84b499d182 Mon Sep 17 00:00:00 2001 From: Jude Date: Mon, 8 Feb 2016 18:02:41 +0100 Subject: [PATCH 32/91] Remove services from registrator if corresponding container is not running --- bridge/bridge.go | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index de941a32f..e5ea3592b 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -119,8 +119,32 @@ func (b *Bridge) Sync(quiet bool) { // Clean up services that were registered previously, but aren't // acknowledged within registrator if b.config.Cleanup { - log.Println("Cleaning up dangling services") + // Remove services if its corresponding container is not running + log.Println("Listing non-exited containers") + filters := map[string][]string{"status": {"created", "restarting", "running", "paused"}} + nonExitedContainers, err := b.docker.ListContainers(dockerapi.ListContainersOptions{Filters: filters}) + if err != nil && quiet { + log.Println("error listing nonExitedContainers, skipping sync") + return + } else if err != nil && !quiet { + log.Fatal(err) + } + for listingId, _ := range b.services { + found := false + for _, container := range nonExitedContainers { + if listingId == container.ID { + found = true + break + } + } + // This is a container that does not exist + if !found { + log.Printf("stale: Removing service %s because it does not exist", listingId) + go b.Remove(listingId) + } + } + log.Println("Cleaning up dangling services") extServices, err := b.registry.Services() if err != nil { log.Println("cleanup failed:", err) @@ -220,7 +244,7 @@ func (b *Bridge) add(containerId string, quiet bool) { func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { container := port.container defaultName := strings.Split(path.Base(container.Config.Image), ":")[0] - + // not sure about this logic. kind of want to remove it. hostname := Hostname if hostname == "" { @@ -249,7 +273,7 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { service.ID = hostname + ":" + container.Name[1:] + ":" + port.ExposedPort service.Name = mapDefault(metadata, "name", defaultName) if isgroup && !metadataFromPort["name"] { - service.Name += "-" + port.ExposedPort + service.Name += "-" + port.ExposedPort } var p int if b.config.Internal == true { From 1a55d6685750d6558a9c1eec0e5c4f75c1716f28 Mon Sep 17 00:00:00 2001 From: Matthew Hook Date: Thu, 11 Feb 2016 12:16:13 +1300 Subject: [PATCH 33/91] Change default port for etc2 backend to default 2379 --- etcd/etcd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etcd/etcd.go b/etcd/etcd.go index 67b84cbe9..9b440d197 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -25,7 +25,7 @@ func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter { if uri.Host != "" { urls = append(urls, "http://"+uri.Host) } else { - urls = append(urls, "http://127.0.0.1:4001") + urls = append(urls, "http://127.0.0.1:2379") } res, err := http.Get(urls[0] + "/version") From 39e5331493f3c164d9af6e2150e8c8499aa54669 Mon Sep 17 00:00:00 2001 From: Matthew Hook Date: Thu, 11 Feb 2016 12:21:06 +1300 Subject: [PATCH 34/91] Update docs for etcd backend for default port --- docs/user/backends.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user/backends.md b/docs/user/backends.md index 5ce7b2f4e..ee99cd2d6 100644 --- a/docs/user/backends.md +++ b/docs/user/backends.md @@ -83,7 +83,7 @@ Using the prefix from the Registry URI, service definitions are stored as: Etcd works similar to Consul KV, except supports service TTLs. It also currently doesn't support service attributes/tags. -If no address and port is specified, it will default to `127.0.0.1:4001`. +If no address and port is specified, it will default to `127.0.0.1:2379`. Using the prefix from the Registry URI, service definitions are stored as: @@ -96,7 +96,7 @@ Using the prefix from the Registry URI, service definitions are stored as: SkyDNS 2 uses etcd, so this backend writes service definitions in a format compatible with SkyDNS 2. The path may not be omitted and must be a valid DNS domain for SkyDNS. -If no address and port is specified, it will default to `127.0.0.1:4001`. +If no address and port is specified, it will default to `127.0.0.1:2379`. Using a Registry URI with the domain `cluster.local`, service definitions are stored as: From 149948870c2ab422f9808aedef0e868493df4919 Mon Sep 17 00:00:00 2001 From: Jude Date: Thu, 11 Feb 2016 13:53:25 +0100 Subject: [PATCH 35/91] On sync, log error instead of exiting --- bridge/bridge.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index e5ea3592b..eea98f5e6 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -123,11 +123,9 @@ func (b *Bridge) Sync(quiet bool) { log.Println("Listing non-exited containers") filters := map[string][]string{"status": {"created", "restarting", "running", "paused"}} nonExitedContainers, err := b.docker.ListContainers(dockerapi.ListContainersOptions{Filters: filters}) - if err != nil && quiet { - log.Println("error listing nonExitedContainers, skipping sync") + if err != nil { + log.Println("error listing nonExitedContainers, skipping sync", err) return - } else if err != nil && !quiet { - log.Fatal(err) } for listingId, _ := range b.services { found := false From 1c200b138fa8b5fe16683cccab247ef319ddf61d Mon Sep 17 00:00:00 2001 From: Jude Date: Thu, 11 Feb 2016 13:54:22 +0100 Subject: [PATCH 36/91] Make removing 'stale' service compatible with deregister flag --- bridge/bridge.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index eea98f5e6..aa466549d 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -138,7 +138,7 @@ func (b *Bridge) Sync(quiet bool) { // This is a container that does not exist if !found { log.Printf("stale: Removing service %s because it does not exist", listingId) - go b.Remove(listingId) + go b.RemoveOnExit(listingId) } } From 484bbf1f1a854893fa8efe535c2778aa365535e3 Mon Sep 17 00:00:00 2001 From: Maxim Sidorenko Date: Tue, 16 Feb 2016 23:43:09 +0200 Subject: [PATCH 37/91] *servicePort method add support hostip for overlay network --- bridge/util.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bridge/util.go b/bridge/util.go index 6dd32ea03..4647d96f9 100644 --- a/bridge/util.go +++ b/bridge/util.go @@ -61,7 +61,7 @@ func serviceMetaData(config *dockerapi.Config, port string) (map[string]string, } func servicePort(container *dockerapi.Container, port dockerapi.Port, published []dockerapi.PortBinding) ServicePort { - var hp, hip, ep, ept, eip string + var hp, hip, ep, ept, eip, nm string if len(published) > 0 { hp = published[0].HostPort hip = published[0].HostIP @@ -69,6 +69,15 @@ func servicePort(container *dockerapi.Container, port dockerapi.Port, published if hip == "" { hip = "0.0.0.0" } + + //for overlay networks + //detect if container use overlay network, than set HostIP into NetworkSettings.Network[string].IPAddress + //better to use registrator with -internal flag + nm = container.HostConfig.NetworkMode + if nm != "bridge" || nm != "default" || nm != "host" { + hip = container.NetworkSettings.Networks[nm].IPAddress + } + exposedPort := strings.Split(string(port), "/") ep = exposedPort[0] if len(exposedPort) == 2 { From ab486640a330a228f6860a54c2603f38291fdcd6 Mon Sep 17 00:00:00 2001 From: jgeiger Date: Tue, 16 Feb 2016 20:57:57 -0700 Subject: [PATCH 38/91] gofmt bridge --- bridge/bridge.go | 2 +- bridge/extpoints.go | 1 - bridge/util.go | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index 4fae58621..673605df6 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -249,7 +249,7 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { service.ID = hostname + ":" + container.Name[1:] + ":" + port.ExposedPort service.Name = mapDefault(metadata, "name", defaultName) if isgroup && !metadataFromPort["name"] { - service.Name += "-" + port.ExposedPort + service.Name += "-" + port.ExposedPort } var p int if b.config.Internal == true { diff --git a/bridge/extpoints.go b/bridge/extpoints.go index bf82346e2..dd46dcf31 100644 --- a/bridge/extpoints.go +++ b/bridge/extpoints.go @@ -139,4 +139,3 @@ func (ep *adapterFactoryExt) All() map[string]AdapterFactory { } return all } - diff --git a/bridge/util.go b/bridge/util.go index 6dd32ea03..10b2905f7 100644 --- a/bridge/util.go +++ b/bridge/util.go @@ -33,7 +33,7 @@ func combineTags(tagParts ...string) []string { func serviceMetaData(config *dockerapi.Config, port string) (map[string]string, map[string]bool) { meta := config.Env for k, v := range config.Labels { - meta = append(meta, k + "=" + v) + meta = append(meta, k+"="+v) } metadata := make(map[string]string) metadataFromPort := make(map[string]bool) @@ -74,9 +74,9 @@ func servicePort(container *dockerapi.Container, port dockerapi.Port, published if len(exposedPort) == 2 { ept = exposedPort[1] } else { - ept = "tcp" // default + ept = "tcp" // default } - + // Nir: support docker NetworkSettings eip = container.NetworkSettings.IPAddress if eip == "" { @@ -84,7 +84,7 @@ func servicePort(container *dockerapi.Container, port dockerapi.Port, published eip = network.IPAddress } } - + return ServicePort{ HostPort: hp, HostIP: hip, From 733dffbcc798ad80769c5f2a0b8918eeacad3f12 Mon Sep 17 00:00:00 2001 From: Mark van Holsteijn Date: Fri, 26 Feb 2016 10:49:18 +0100 Subject: [PATCH 39/91] added Consul TCP Health Check --- consul/consul.go | 7 ++++++- docs/user/backends.md | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/consul/consul.go b/consul/consul.go index 41569c191..b681b89ec 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -80,10 +80,15 @@ func (r *ConsulAdapter) buildCheck(service *bridge.Service) *consulapi.AgentServ check.Script = r.interpolateService(script, service) } else if ttl := service.Attrs["check_ttl"]; ttl != "" { check.TTL = ttl + } else if tcp := service.Attrs["check_tcp"]; tcp != "" { + check.TCP = fmt.Sprintf("%s:%d", service.IP, service.Port) + if timeout := service.Attrs["check_timeout"]; timeout != "" { + check.Timeout = timeout + } } else { return nil } - if check.Script != "" || check.HTTP != "" { + if check.Script != "" || check.HTTP != "" || check.TCP != "" { if interval := service.Attrs["check_interval"]; interval != "" { check.Interval = interval } else { diff --git a/docs/user/backends.md b/docs/user/backends.md index ee99cd2d6..9d9ef827f 100644 --- a/docs/user/backends.md +++ b/docs/user/backends.md @@ -33,6 +33,21 @@ SERVICE_80_CHECK_TIMEOUT=1s # optional, Consul default used otherwise It works for services on any port, not just 80. If its the only service, you can also use `SERVICE_CHECK_HTTP`. +### Consul TCP Check + +This feature is only available when using Consul 0.6 or newer. Containers +specifying these extra metadata in labels or environment will be used to +register an TCP health check with the service. + +```bash +SERVICE_443_CHECK_TCP=true +SERVICE_443_CHECK_INTERVAL=15s +SERVICE_443_CHECK_TIMEOUT=3s # optional, Consul default used otherwise +``` + +It works for services on any port, not just 80. If its the only service, +you can also use `SERVICE_CHECK_HTTP`. + ### Consul Script Check This feature is tricky because it lets you specify a script check to run from From 3b65f7d38bf52882da7ca43285b6db3bfc50d0ea Mon Sep 17 00:00:00 2001 From: Mark van Holsteijn Date: Mon, 29 Feb 2016 17:29:15 +0100 Subject: [PATCH 40/91] removed sentence that was copied from HTTP health check --- docs/user/backends.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/user/backends.md b/docs/user/backends.md index 9d9ef827f..e7c037d72 100644 --- a/docs/user/backends.md +++ b/docs/user/backends.md @@ -45,9 +45,6 @@ SERVICE_443_CHECK_INTERVAL=15s SERVICE_443_CHECK_TIMEOUT=3s # optional, Consul default used otherwise ``` -It works for services on any port, not just 80. If its the only service, -you can also use `SERVICE_CHECK_HTTP`. - ### Consul Script Check This feature is tricky because it lets you specify a script check to run from From 73bf16f8d77f1accf4081257e2f5202af6e1e63e Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Tue, 23 Feb 2016 23:06:05 -0600 Subject: [PATCH 41/91] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51c30ed30..5d055d27e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file. - bridge.New returns an error instead of calling log.Fatal - bridge.New will not attempt to ping an adapter. - Specifying a SERVICE_NAME for containers exposing multiple ports will now result in a named service per port. #194 +- Etcd uses port 2379 instead of 4001 #340 ## [v6] - 2015-08-07 ### Fixed From b29dbe954489ad35520dbc30711a8f954bce8122 Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Sat, 5 Mar 2016 00:34:11 -0600 Subject: [PATCH 42/91] Add image size to readme. Closes #290 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 51ed82872..f748087e5 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Service registry bridge for Docker, sponsored by [Weave](http://weave.works). [![Circle CI](https://circleci.com/gh/gliderlabs/registrator.png?style=shield)](https://circleci.com/gh/gliderlabs/registrator) [![Docker Hub](https://img.shields.io/badge/docker-ready-blue.svg)](https://registry.hub.docker.com/u/gliderlabs/registrator/) +[![ImageLayers Size](https://img.shields.io/imagelayers/image-size/gliderlabs/registrator/latest.svg)](https://imagelayers.io/?images=gliderlabs%2Fregistrator:latest) [![IRC Channel](https://img.shields.io/badge/irc-%23gliderlabs-blue.svg)](https://kiwiirc.com/client/irc.freenode.net/#gliderlabs)

From 876e287d037323eb77d6140dd48e998454373f8d Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Sat, 5 Mar 2016 16:29:08 -0600 Subject: [PATCH 43/91] Release prep --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d055d27e..11a882371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,20 @@ All notable changes to this project will be documented in this file. ## [Unreleased][unreleased] ### Fixed +- Providing a SERVICE_NAME for a container with multiple ports exposed would cause services to overwrite each other +- dd3ab2e Fix specific port names not overriding port suffix ### Added - bridge.Ping - calls adapter.Ping +- Consul TCP Health Check +- Support for Consul unix sockets +- Basic Zookeper backend +- Support for Docker multi host networking +- Default to tcp for PortType if not provided +- Sync etcd cluster on service registration +- Support hostip for overlay network +- Cleanup dangling services +- Startup backend service connection retry ### Removed @@ -15,6 +26,8 @@ All notable changes to this project will be documented in this file. - bridge.New will not attempt to ping an adapter. - Specifying a SERVICE_NAME for containers exposing multiple ports will now result in a named service per port. #194 - Etcd uses port 2379 instead of 4001 #340 +- Setup Docker client from environment +- Use exit status to determine if container was killed ## [v6] - 2015-08-07 ### Fixed From a4a4b0dd6ef48207391465d28abc590749d73d43 Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Sat, 5 Mar 2016 16:40:08 -0600 Subject: [PATCH 44/91] Add image size to docs --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 3ddeccde6..757c11e92 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,6 +4,7 @@ Service registry bridge for Docker, sponsored by [Weave](http://weave.works). [![Circle CI](https://circleci.com/gh/gliderlabs/registrator.png?style=shield)](https://circleci.com/gh/gliderlabs/registrator) [![Docker Hub](https://img.shields.io/badge/docker-ready-blue.svg)](https://registry.hub.docker.com/u/gliderlabs/registrator/) +[![ImageLayers Size](https://img.shields.io/imagelayers/image-size/gliderlabs/registrator/latest.svg)](https://imagelayers.io/?images=gliderlabs%2Fregistrator:latest) [![IRC Channel](https://img.shields.io/badge/irc-%23gliderlabs-blue.svg)](https://kiwiirc.com/client/irc.freenode.net/#gliderlabs)

From 55a38b9135c7cb911a241633d3febc90d60430c5 Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Sat, 5 Mar 2016 16:40:30 -0600 Subject: [PATCH 45/91] bump --- CHANGELOG.md | 12 +++++++++++- VERSION | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11a882371..4d541ef2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file. ## [Unreleased][unreleased] ### Fixed + +### Added + +### Removed + +### Changed + +## [v7] - 2016-03-05 +### Fixed - Providing a SERVICE_NAME for a container with multiple ports exposed would cause services to overwrite each other - dd3ab2e Fix specific port names not overriding port suffix @@ -73,6 +82,7 @@ All notable changes to this project will be documented in this file. - Dropped Godeps for now -[unreleased]: https://github.com/gliderlabs/registrator/compare/v6...HEAD +[unreleased]: https://github.com/gliderlabs/registrator/compare/v7...HEAD +[v7]: https://github.com/gliderlabs/registrator/compare/v6...v7 [v6]: https://github.com/gliderlabs/registrator/compare/v5...v6 [v5]: https://github.com/gliderlabs/registrator/compare/v0.4.0...v5 diff --git a/VERSION b/VERSION index 6fb293940..02a819f21 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v7-dev +v7 From 0d0b505c6744910175f92af4a01a6e028c85283e Mon Sep 17 00:00:00 2001 From: Will Rouesnel Date: Sun, 11 Oct 2015 14:55:40 +0200 Subject: [PATCH 46/91] Use ExposedPorts for --net=host --- bridge/bridge.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index 673605df6..8a6f3021c 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -179,7 +179,8 @@ func (b *Bridge) add(containerId string, quiet bool) { ports := make(map[string]ServicePort) // Extract configured host port mappings, relevant when using --net=host - for port, published := range container.HostConfig.PortBindings { + for port, _ := range container.Config.ExposedPorts { + published := []dockerapi.PortBinding{ {"0.0.0.0", port.Port()}, } ports[string(port)] = servicePort(container, port, published) } From fd0069dfb96c5142dca3a0a1c521a06cde898d27 Mon Sep 17 00:00:00 2001 From: Martin Englund Date: Wed, 16 Mar 2016 16:08:34 -0700 Subject: [PATCH 47/91] document how to run when consul uses and acl --- docs/user/run.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/user/run.md b/docs/user/run.md index 66824225f..439fdbd0d 100644 --- a/docs/user/run.md +++ b/docs/user/run.md @@ -60,6 +60,23 @@ The `-resync` options controls how often Registrator will query Docker for all containers and reregister all services. This allows Registrator and the service registry to get back in sync if they fall out of sync. +## Consul ACL token + +If consul is configured to require an ACL token, Registrator needs to know about it, +or you will see warnings in the consul docker container + + [WARN] consul.catalog: Register of service 'redis' on 'hostname' denied due to ACLs + +The ACL token is passed in through docker in an environment variable called `CONSUL_HTTP_TOKEN`. + + $ docker run -d \ + --name=registrator \ + --net=host \ + --volume=/var/run/docker.sock:/tmp/docker.sock \ + -e CONSUL_HTTP_TOKEN= \ + gliderlabs/registrator:latest \ + consul://localhost:8500 + ## Registry URI ://
[/] From dec131aec1fbbc8b66d736de2db5a84653afd3fd Mon Sep 17 00:00:00 2001 From: Randall Leeds Date: Wed, 6 Apr 2016 17:39:33 -0700 Subject: [PATCH 48/91] add support for consul https health checks Fix #365 --- consul/consul.go | 5 +++++ docs/user/backends.md | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/consul/consul.go b/consul/consul.go index b681b89ec..451072b5a 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -74,6 +74,11 @@ func (r *ConsulAdapter) buildCheck(service *bridge.Service) *consulapi.AgentServ if timeout := service.Attrs["check_timeout"]; timeout != "" { check.Timeout = timeout } + } else if path := service.Attrs["check_https"]; path != "" { + check.HTTP = fmt.Sprintf("https://%s:%d%s", service.IP, service.Port, path) + if timeout := service.Attrs["check_timeout"]; timeout != "" { + check.Timeout = timeout + } } else if cmd := service.Attrs["check_cmd"]; cmd != "" { check.Script = fmt.Sprintf("check-cmd %s %s %s", service.Origin.ContainerID[:12], service.Origin.ExposedPort, cmd) } else if script := service.Attrs["check_script"]; script != "" { diff --git a/docs/user/backends.md b/docs/user/backends.md index e7c037d72..180376273 100644 --- a/docs/user/backends.md +++ b/docs/user/backends.md @@ -33,6 +33,18 @@ SERVICE_80_CHECK_TIMEOUT=1s # optional, Consul default used otherwise It works for services on any port, not just 80. If its the only service, you can also use `SERVICE_CHECK_HTTP`. +### Consul HTTPS Check + +This feature is only available when using Consul 0.5 or newer. Containers +specifying these extra metedata in labels or environment will be used to +register an HTTPS health check with the service. + +```bash +SERVICE_443_CHECK_HTTPS=/health/endpoint/path +SERVICE_443_CHECK_INTERVAL=15s +SERVICE_443_CHECK_TIMEOUT=1s # optional, Consul default used otherwise +``` + ### Consul TCP Check This feature is only available when using Consul 0.6 or newer. Containers From e2462696df38495f509a5db12e0e5663db57e1d5 Mon Sep 17 00:00:00 2001 From: Cyprien DIOT Date: Thu, 14 Apr 2016 16:00:51 +0200 Subject: [PATCH 49/91] adding TLS support for consul backend --- consul/consul.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/consul/consul.go b/consul/consul.go index b681b89ec..ad813c14c 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -5,15 +5,17 @@ import ( "log" "net/url" "strings" - + "os" "github.com/gliderlabs/registrator/bridge" consulapi "github.com/hashicorp/consul/api" + "github.com/hashicorp/go-cleanhttp" ) const DefaultInterval = "10s" func init() { f := new(Factory) + bridge.Register(f, "consul-tls") bridge.Register(f, "consul") bridge.Register(f, "consul-unix") } @@ -30,6 +32,23 @@ func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter { config := consulapi.DefaultConfig() if uri.Scheme == "consul-unix" { config.Address = strings.TrimPrefix(uri.String(), "consul-") + } else if uri.Scheme == "consul-tls" { + tlsConfigDesc := &consulapi.TLSConfig { + Address: uri.Host, + CAFile: os.Getenv("CONSUL_CACERT"), + CertFile: os.Getenv("CONSUL_TLSCERT"), + KeyFile: os.Getenv("CONSUL_TLSKEY"), + InsecureSkipVerify: false, + } + tlsConfig, err := consulapi.SetupTLSConfig(tlsConfigDesc) + if err != nil { + log.Fatal("Cannot set up Consul TLSConfig", err) + } + config.Scheme = "https" + transport := cleanhttp.DefaultPooledTransport() + transport.TLSClientConfig = tlsConfig + config.HttpClient.Transport = transport + config.Address = uri.Host } else if uri.Host != "" { config.Address = uri.Host } From 04f52e0a9e07e35fc930e00822747a4a87affae5 Mon Sep 17 00:00:00 2001 From: jmeichle Date: Tue, 19 Apr 2016 18:59:53 -0400 Subject: [PATCH 50/91] Have the zookeeper backend use the host port for the service paths, allow publishing services if the base service path already exists, and allow publishing into the root of zookeeper. (#367) --- zookeeper/zookeeper.go | 43 ++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/zookeeper/zookeeper.go b/zookeeper/zookeeper.go index 26e7c9e55..a2ac08d59 100644 --- a/zookeeper/zookeeper.go +++ b/zookeeper/zookeeper.go @@ -49,30 +49,33 @@ type ZnodeBody struct { func (r *ZkAdapter) Register(service *bridge.Service) error { privatePort, _ := strconv.Atoi(service.Origin.ExposedPort) + publicPortString := strconv.Itoa(service.Port) acl := zk.WorldACL(zk.PermAll) - - exists, _, err := r.client.Exists(r.path + "/" + service.Name) + basePath := r.path + "/" + service.Name + if (r.path == "/") { + basePath = r.path + service.Name + } + exists, _, err := r.client.Exists(basePath) if err != nil { log.Println("zookeeper: error checking if exists: ", err) } else { if !exists { - _, err := r.client.Create(r.path+"/"+service.Name, []byte{}, 0, acl) + _, err := r.client.Create(basePath, []byte{}, 0, acl) if err != nil { - log.Println("zookeeper: failed to create base service node: ", err) - } else { - zbody := &ZnodeBody{Name: service.Name, IP: service.IP, PublicPort: service.Port, PrivatePort: privatePort, Tags: service.Tags, Attrs: service.Attrs, ContainerID: service.Origin.ContainerHostname} - body, err := json.Marshal(zbody) - if err != nil { - log.Println("zookeeper: failed to json encode service body: ", err) - } else { - path := r.path + "/" + service.Name + "/" + service.Origin.ExposedPort - _, err = r.client.Create(path, body, 1, acl) - if err != nil { - log.Println("zookeeper: failed to register service: ", err) - } - } // json encode error check + log.Println("zookeeper: failed to create base service node at path '" + basePath + "': ", err) + } + } // create base path for the service name if it missing + zbody := &ZnodeBody{Name: service.Name, IP: service.IP, PublicPort: service.Port, PrivatePort: privatePort, Tags: service.Tags, Attrs: service.Attrs, ContainerID: service.Origin.ContainerHostname} + body, err := json.Marshal(zbody) + if err != nil { + log.Println("zookeeper: failed to json encode service body: ", err) + } else { + path := basePath + "/" + service.IP + ":" + publicPortString + _, err = r.client.Create(path, body, 1, acl) + if err != nil { + log.Println("zookeeper: failed to register service at path '" + path + "': ", err) } // create service path error check - } // service path exists + } // json znode body creation check } // service path exists error check return err } @@ -88,8 +91,12 @@ func (r *ZkAdapter) Ping() error { func (r *ZkAdapter) Deregister(service *bridge.Service) error { basePath := r.path + "/" + service.Name + if (r.path == "/") { + basePath = r.path + service.Name + } + publicPortString := strconv.Itoa(service.Port) + servicePortPath := basePath + "/" + service.IP + ":" + publicPortString // Delete the service-port znode - servicePortPath := basePath + "/" + service.Origin.ExposedPort err := r.client.Delete(servicePortPath, -1) // -1 means latest version number if err != nil { log.Println("zookeeper: failed to deregister service port entry: ", err) From 6f4147f7b5cea3a966d6f9b586df161fc0b69f39 Mon Sep 17 00:00:00 2001 From: r0p0s3c Date: Wed, 27 Apr 2016 10:43:52 -0400 Subject: [PATCH 51/91] Fix servicePort HostIP detection logic (#407) --- bridge/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridge/util.go b/bridge/util.go index bcca0508e..3b450faff 100644 --- a/bridge/util.go +++ b/bridge/util.go @@ -74,7 +74,7 @@ func servicePort(container *dockerapi.Container, port dockerapi.Port, published //detect if container use overlay network, than set HostIP into NetworkSettings.Network[string].IPAddress //better to use registrator with -internal flag nm = container.HostConfig.NetworkMode - if nm != "bridge" || nm != "default" || nm != "host" { + if nm != "bridge" && nm != "default" && nm != "host" { hip = container.NetworkSettings.Networks[nm].IPAddress } From 2f327944bf5e5cd284c8191ec34ae9afe6a37104 Mon Sep 17 00:00:00 2001 From: Selim Ekizoglu Date: Sat, 9 Jul 2016 17:07:49 +0300 Subject: [PATCH 52/91] Add consul initial check status to service attributes --- consul/consul.go | 3 +++ docs/user/backends.md | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/consul/consul.go b/consul/consul.go index b681b89ec..957cb0dfd 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -69,6 +69,9 @@ func (r *ConsulAdapter) Register(service *bridge.Service) error { func (r *ConsulAdapter) buildCheck(service *bridge.Service) *consulapi.AgentServiceCheck { check := new(consulapi.AgentServiceCheck) + if status := service.Attrs["check_initial_status"]; status != "" { + check.Status = status + } if path := service.Attrs["check_http"]; path != "" { check.HTTP = fmt.Sprintf("http://%s:%d%s", service.IP, service.Port, path) if timeout := service.Attrs["check_timeout"]; timeout != "" { diff --git a/docs/user/backends.md b/docs/user/backends.md index e7c037d72..47d488157 100644 --- a/docs/user/backends.md +++ b/docs/user/backends.md @@ -73,6 +73,14 @@ healthy. SERVICE_CHECK_TTL=30s ``` +### Consul Initial Health Check Status + +By default when a service is registered against Consul, the state is set to "critical". You can specify the initial health check status. + +```bash +SERVICE_CHECK_INITIAL_STATUS=passing +``` + ## Consul KV consulkv://
:/ From 8e5de9c4d8101babaaa7be2aa9fb329afd4c5bcc Mon Sep 17 00:00:00 2001 From: Cyprien DIOT Date: Tue, 12 Jul 2016 19:25:06 +0200 Subject: [PATCH 53/91] Adding documentation for the consul-tls backend scheme --- docs/user/backends.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/user/backends.md b/docs/user/backends.md index e7c037d72..f6c7bc44c 100644 --- a/docs/user/backends.md +++ b/docs/user/backends.md @@ -10,6 +10,7 @@ See also [Contributing Backends](../dev/backends.md). consul://
: consul-unix:// + consul-tls://
: Consul is the recommended registry since it specifically models services for service discovery with health checks. @@ -18,6 +19,11 @@ If no address and port is specified, it will default to `127.0.0.1:8500`. Consul supports tags but no arbitrary service attributes. +When using the `consul-tls` scheme, registrator communicates with Consul through TLS. You must set the following environment variables: + * `CONSUL_CACERT` : CA file location + * `CONSUL_TLSCERT` : Certificate file location + * `CONSUL_TLSKEY` : Key location + ### Consul HTTP Check This feature is only available when using Consul 0.5 or newer. Containers From 8e57bf0da2380a5dea8bdef8314c5b7fd86afa4b Mon Sep 17 00:00:00 2001 From: Cyprien DIOT Date: Wed, 13 Jul 2016 19:20:18 +0200 Subject: [PATCH 54/91] Ordering consul backend schemes in alphabetical order --- consul/consul.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consul/consul.go b/consul/consul.go index ad813c14c..2f8480cbc 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -15,8 +15,8 @@ const DefaultInterval = "10s" func init() { f := new(Factory) - bridge.Register(f, "consul-tls") bridge.Register(f, "consul") + bridge.Register(f, "consul-tls") bridge.Register(f, "consul-unix") } From 7534d64959ba14d34e5ec2b2823450adafdeb8de Mon Sep 17 00:00:00 2001 From: Yann DEGAT Date: Tue, 2 Aug 2016 09:18:36 +0200 Subject: [PATCH 55/91] Add caution note on resync parameter Closes gliderlabs/registrator#449 --- docs/user/run.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/user/run.md b/docs/user/run.md index 66824225f..63af2cca4 100644 --- a/docs/user/run.md +++ b/docs/user/run.md @@ -58,7 +58,9 @@ If you want unlimited retry-attempts use `-retry-attempts -1`. The `-resync` options controls how often Registrator will query Docker for all containers and reregister all services. This allows Registrator and the service -registry to get back in sync if they fall out of sync. +registry to get back in sync if they fall out of sync. Use this option with caution +as it will notify all the watches you may have registered on your services, and +may rapidly flood your system (e.g. consul-template makes extensive use of watches). ## Registry URI From ac47f6f39adca743cc05095c7f5bf8e7de946e60 Mon Sep 17 00:00:00 2001 From: Yann DEGAT Date: Mon, 5 Sep 2016 10:30:49 +0200 Subject: [PATCH 56/91] Fix build issues Close #468 --- Dockerfile | 6 +++--- Dockerfile.dev | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 238b04426..297102cdf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ -FROM gliderlabs/alpine:3.2 +FROM gliderlabs/alpine:3.3 ENTRYPOINT ["/bin/registrator"] COPY . /go/src/github.com/gliderlabs/registrator -RUN apk-install -t build-deps go git mercurial \ +RUN apk-install -t build-deps build-base go git mercurial \ && cd /go/src/github.com/gliderlabs/registrator \ && export GOPATH=/go \ && go get \ - && go build -ldflags "-X main.Version $(cat VERSION)" -o /bin/registrator \ + && go build -ldflags "-X main.Version=$(cat VERSION)" -o /bin/registrator \ && rm -rf /go \ && apk del --purge build-deps diff --git a/Dockerfile.dev b/Dockerfile.dev index dff9429a3..a9de6a4a2 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,9 +1,9 @@ -FROM gliderlabs/alpine:3.2 +FROM gliderlabs/alpine:3.3 CMD ["/bin/registrator"] ENV GOPATH /go -RUN apk-install go git mercurial +RUN apk-install build-base go git mercurial COPY . /go/src/github.com/gliderlabs/registrator RUN cd /go/src/github.com/gliderlabs/registrator \ && go get \ - && go build -ldflags "-X main.Version dev" -o /bin/registrator + && go build -ldflags "-X main.Version=dev" -o /bin/registrator From 916c8b6e1ca88b11ced0579e8a38db53db73ee52 Mon Sep 17 00:00:00 2001 From: Yann DEGAT Date: Mon, 29 Aug 2016 11:41:32 +0200 Subject: [PATCH 57/91] Add cleanup parameter in the doc - add cleanup parameter in the doc - alphabetically reorder the parameters list --- docs/user/run.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/user/run.md b/docs/user/run.md index 63af2cca4..e4269e343 100644 --- a/docs/user/run.md +++ b/docs/user/run.md @@ -33,15 +33,16 @@ hostname (`-h $HOSTNAME`) and using the `-ip` Registrator option below. Option | Since | Description ------ | ----- | ----------- +`-cleanup` | v7 | Cleanup dangling services +`-deregister ` | v6 | Deregister existed services "always" or "on-success". Default: always `-internal` | | Use exposed ports instead of published ports `-ip ` | | Force IP address used for registering services +`-resync ` | v6 | Frequency all services are resynchronized. Default: 0, never `-retry-attempts ` | v7 | Max retry attempts to establish a connection with the backend `-retry-interval ` | v7 | Interval (in millisecond) between retry-attempts `-tags ` | v5 | Force comma-separated tags on all registered services -`-deregister ` | v6 | Deregister existed services "always" or "on-success". Default: always `-ttl ` | | TTL for services. Default: 0, no expiry (supported backends only) `-ttl-refresh ` | | Frequency service TTLs are refreshed (supported backends only) -`-resync ` | v6 | Frequency all services are resynchronized. Default: 0, never If the `-internal` option is used, Registrator will register the docker0 internal IP and port instead of the host mapped ones. From 3a5c9d7434e8ec184a313db7a8769e0ace4d2c82 Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Thu, 15 Sep 2016 00:26:36 -0500 Subject: [PATCH 58/91] Fixes logic for appending port to SERVICE_NAME Fixes registrator to follow the following documentation. http://gliderlabs.com/registrator/latest/user/services/#service-name --- bridge/bridge.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index e321cf868..0557c6446 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -215,14 +215,20 @@ func (b *Bridge) add(containerId string, quiet bool) { return } - for _, port := range ports { + servicePorts := make(map[string]ServicePort) + for key, port := range ports { if b.config.Internal != true && port.HostPort == "" { if !quiet { log.Println("ignored:", container.ID[:12], "port", port.ExposedPort, "not published on host") } continue } - service := b.newService(port, len(ports) > 1) + servicePorts[key] = port + } + + isGroup := len(servicePorts) > 1 + for _, port := range servicePorts { + service := b.newService(port, isGroup) if service == nil { if !quiet { log.Println("ignored:", container.ID[:12], "service on port", port.ExposedPort) From e4460f06339732e0677024d9d97c326fdb29fe4e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 17 Sep 2016 16:50:50 -0600 Subject: [PATCH 59/91] Add an issue template --- ISSUE_TEMPLATE.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 ISSUE_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..65a39f6cb --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,18 @@ +- What version of docker are you running? +- What version of registrator are you running? +- Did you build a custom version of registrator? If so, what is that image? +- What is the exact command you are running registrator with? +- What is the exact command you are running your container with? +- If relevant, `Dockerfile` for application that is having issues. + +Description of the problem: + +How reproducible: + +Steps to Reproduce: + +Actual Results: + +Expected Results: + +Additional info: From 79e2e73f4d5364c3bee6ea6016c79b9b7334efe4 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 20 Sep 2016 13:55:41 -0600 Subject: [PATCH 60/91] Update ISSUE_TEMPLATE.md --- ISSUE_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 65a39f6cb..ab1a08da1 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -3,6 +3,7 @@ - Did you build a custom version of registrator? If so, what is that image? - What is the exact command you are running registrator with? - What is the exact command you are running your container with? +- A log capture of all the docker events before, during, and after the issue. - If relevant, `Dockerfile` for application that is having issues. Description of the problem: From 990e6d05962c04129f67d8acc00f43cc73ab8b23 Mon Sep 17 00:00:00 2001 From: Rene MT Date: Fri, 21 Oct 2016 18:28:41 +0200 Subject: [PATCH 61/91] Adds 'useIpFromLabel' parameter (#480) * Adds capability to read Ranger network IP for registration in Consul from container label * Adds 'useRancherContainerIP' command argument * Adds support for reading container IP used for registration from a label assigned to a container * Updated documentation for new parameter 'useIpFromLabel' --- bridge/bridge.go | 18 ++++++++++++++++++ bridge/types.go | 1 + docs/user/run.md | 5 +++-- registrator.go | 2 ++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index 2387b769f..90e920404 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -281,6 +281,7 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { service.Name += "-" + port.ExposedPort } var p int + if b.config.Internal == true { service.IP = port.ExposedIP p, _ = strconv.Atoi(port.ExposedPort) @@ -290,6 +291,23 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { } service.Port = p + if b.config.UseIpFromLabel != "" { + containerIp := container.Config.Labels[b.config.UseIpFromLabel] + if containerIp != "" { + slashIndex := strings.LastIndex(containerIp, "/") + if slashIndex > -1 { + service.IP = containerIp[:slashIndex] + } else { + service.IP = containerIp + } + log.Println("using container IP " + service.IP + " from label '" + + b.config.UseIpFromLabel + "'") + } else { + log.Println("Label '" + b.config.UseIpFromLabel + + "' not found in container configuration") + } + } + if port.PortType == "udp" { service.Tags = combineTags( mapDefault(metadata, "tags", ""), b.config.ForceTags, "udp") diff --git a/bridge/types.go b/bridge/types.go index b1611127e..601b826ab 100644 --- a/bridge/types.go +++ b/bridge/types.go @@ -22,6 +22,7 @@ type RegistryAdapter interface { type Config struct { HostIp string Internal bool + UseIpFromLabel string ForceTags string RefreshTtl int RefreshInterval int diff --git a/docs/user/run.md b/docs/user/run.md index 669107d2d..b9ce293d2 100644 --- a/docs/user/run.md +++ b/docs/user/run.md @@ -43,6 +43,7 @@ Option | Since | Description `-tags ` | v5 | Force comma-separated tags on all registered services `-ttl ` | | TTL for services. Default: 0, no expiry (supported backends only) `-ttl-refresh ` | | Frequency service TTLs are refreshed (supported backends only) +`-useIpFromLabel