gliderlabs / registrator

Service registry bridge for Docker with pluggable adapters
http://gliderlabs.com/registrator
MIT License
4.66k stars 912 forks source link

Possible bug (or by-design behavior) of using IPv6 addresses to register TCP health check to `Consul`? #691

Open yaobinwen-mvs opened 3 years ago

yaobinwen-mvs commented 3 years ago

Overview

Description of the problem

I'm using registrator to register my Docker containers to Consul. The Docker containers have TCP health checks. When an IPv6 address is used to register the containers, the health checks always fail with the error (Note that 32789 is the published port):

dial tcp: address 2601:547:980:b3e0::172f:32789: too many colons in address

How reproducible

Always reproducible.

Steps to Reproduce

  1. Run the Shell script I put in Reproduce script section in a terminal.
  2. In the browser, open http://localhost:8500/.
  3. Open the redis-service page to show the "Health Checks".

Actual Results

  1. The "Serf Health Status" is shown as normal (in green).
  2. The "Service 'redis-service' check" fails with output dial tcp: address 2601:547:980:b3e0::172f:32789: too many colons in address.
    • Note: When the service is just started up, the Output area may be still empty. Need to wait about a few minutes to have the error message show up.

Expected Results

The IPv6 address should be properly wrapped before the port number is appended.

Additional info

  1. This issue can be worked around by wrapping the IPv6 address with [] in the -ip argument.
  2. Initially, I thought this is an issue in Consul so I posted a question here. After discussing with Consul's product manager, we thought it looks more like a bug in Registrator.
  3. In that Consul post, the product manager pointed out the lines that possibly cause the bug:
      } else if tcp := service.Attrs["check_tcp"]; tcp != "" {
        check.TCP = fmt.Sprintf("%s:%d", service.IP, service.Port)
  1. He also pointed out a possible fix:

I think this is something that Registrator could easily fix by using net.JoinHostPort instead of the current IP and port formatting function.

  1. If the user is supposed to properly wrap the IPv6 address, maybe it would be better to explicitly mention this in the IP and Port documentation.

Reproduce script

The script that reproduces the bug is listed below. Please update the variable IPv6_Addr to use your IPv6 address.

#!/bin/sh

# This script reproduces the issue that `registrator` doesn't seem to form the
# TCP health check link appropriately for IPv6 addresses.
#
# Pre-conditions:
# 1). `docker` is installed.

# The IPv6 address that's used to register services.
IPv6_Addr="2601:547:980:b3e0::172f"

# Echo the commands and exit immediately on failure.
set -ex

# Start Consul.
# `boot2docker` is officially deprecated as of 2020-06-22.
# See: https://github.com/boot2docker/boot2docker
NUM=$(docker ps --filter "name=consul" --filter "status=running" --quiet | wc -l)
if [ $NUM -eq 0 ];
then
  docker run -d --name=consul --net=host consul
fi

# Make sure Consul is accessible.
curl http://localhost:8500/v1/catalog/services

# Pull in `registrator:master`.
docker pull gliderlabs/registrator:master

# Start `registrator`.
NUM=$(docker ps --filter "name=registrator" --filter "status=running" --quiet | wc -l)
if [ $NUM -eq 0 ];
then
  docker run -d \
    --name=registrator \
    --net=host \
    --volume=/var/run/docker.sock:/tmp/docker.sock \
    gliderlabs/registrator:master \
      -ip "${IPv6_Addr}" \
      consul://localhost:8500
fi

# Start a `redis` container for testing.
docker run -d \
  --env SERVICE_CHECK_TCP="true" \
  --env SERVICE_CHECK_INTERVAL="10s" \
  --env SERVICE_CHECK_TIMEOUT="1s" \
  --env SERVICE_ID="redis-service-id" \
  --env SERVICE_NAME="redis-service" \
  --name=redis \
  --publish-all \
  redis