usebindportip can fall back on the container ip / port

This commit is contained in:
Gérald Croës 2018-10-15 11:46:03 +02:00 committed by Traefiker Bot
parent 78a9d20691
commit 6d4cf0d892
3 changed files with 108 additions and 74 deletions

View file

@ -57,7 +57,9 @@ watch = true
exposedByDefault = true
# Use the IP address from the binded port instead of the inner network one.
# For specific use-case :)
#
# In case no IP address is attached to the binded port (or in case
# there is no bind), the inner network one will be used as a fallback.
#
# Optional
# Default: false
@ -427,3 +429,25 @@ Segment labels override the default behavior.
When running inside a container, Træfik will need network access through:
`docker network connect <network> <traefik-container>`
## usebindportip
The default behavior of Træfik is to route requests to the IP/Port of the matching container.
When setting `usebindportip` to true, you tell Træfik to use the IP/Port attached to the container's binding instead of the inner network IP/Port.
When used in conjunction with the `traefik.port` label (that tells Træfik to route requests to a specific port), Træfik tries to find a binding with `traefik.port` port to select the container. If it can't find such a binding, Træfik falls back on the internal network IP of the container, but still uses the `traefik.port` that is set in the label.
Below is a recap of the behavior of `usebindportip` in different situations.
| traefik.port label | Container's binding | Routes to |
|--------------------|----------------------------------------------------|----------------|
| - | - | IntIP:IntPort |
| - | ExtPort:IntPort | IntIP:IntPort |
| - | ExtIp:ExtPort:IntPort | ExtIp:ExtPort |
| LblPort | - | IntIp:LblPort |
| LblPort | ExtIp:ExtPort:LblPort | ExtIp:ExtPort |
| LblPort | ExtIp:ExtPort:OtherPort | IntIp:LblPort |
| LblPort | ExtIp1:ExtPort1:IntPort1 & ExtIp2:LblPort:IntPort2 | ExtIp2:LblPort |
!!! note
In the above table, ExtIp stands for "external IP found in the binding", IntIp stands for "internal network container's IP", ExtPort stands for "external Port found in the binding", and IntPort stands for "internal network container's port."

View file

@ -337,21 +337,22 @@ func (p *Provider) getPortBinding(container dockerData) (*nat.PortBinding, error
func (p *Provider) getIPPort(container dockerData) (string, string, error) {
var ip, port string
usedBound := false
if p.UseBindPortIP {
portBinding, err := p.getPortBinding(container)
if err != nil {
return "", "", fmt.Errorf("unable to find a binding for the container %q: ignoring server", container.Name)
log.Infof("Unable to find a binding for container %q, falling back on its internal IP/Port.", container.Name)
} else if (portBinding.HostIP == "0.0.0.0") || (len(portBinding.HostIP) == 0) {
log.Infof("Cannot determine the IP address (got %q) for %q's binding, falling back on its internal IP/Port.", portBinding.HostIP, container.Name)
} else {
ip = portBinding.HostIP
port = portBinding.HostPort
usedBound = true
}
}
if portBinding.HostIP == "0.0.0.0" {
return "", "", fmt.Errorf("cannot determine the IP address (got 0.0.0.0) for the container %q: ignoring server", container.Name)
}
ip = portBinding.HostIP
port = portBinding.HostPort
} else {
if !usedBound {
ip = p.getIPAddress(container)
port = getPort(container)
}
@ -359,6 +360,7 @@ func (p *Provider) getIPPort(container dockerData) (string, string, error) {
if len(ip) == 0 {
return "", "", fmt.Errorf("unable to find the IP address for the container %q: the server is ignored", container.Name)
}
return ip, port, nil
}

View file

@ -1391,6 +1391,31 @@ func TestDockerGetIPPort(t *testing.T) {
ip, port string
expectsError bool
}{
{
desc: "label traefik.port not set, no binding, falling back on the container's IP/Port",
container: containerJSON(
ports(nat.PortMap{
"8080/tcp": {},
}),
withNetwork("testnet", ipv4("10.11.12.13"))),
ip: "10.11.12.13",
port: "8080",
},
{
desc: "label traefik.port not set, single binding with port only, falling back on the container's IP/Port",
container: containerJSON(
withNetwork("testnet", ipv4("10.11.12.13")),
ports(nat.PortMap{
"80/tcp": []nat.PortBinding{
{
HostPort: "8082",
},
},
}),
),
ip: "10.11.12.13",
port: "80",
},
{
desc: "label traefik.port not set, binding with ip:port should create a route to the bound ip:port",
container: containerJSON(
@ -1406,6 +1431,52 @@ func TestDockerGetIPPort(t *testing.T) {
ip: "1.2.3.4",
port: "8081",
},
{
desc: "label traefik.port set, no binding, falling back on the container's IP/traefik.port",
container: containerJSON(
labels(map[string]string{
label.TraefikPort: "80",
}),
withNetwork("testnet", ipv4("10.11.12.13"))),
ip: "10.11.12.13",
port: "80",
},
{
desc: "label traefik.port set, single binding with ip:port for the label, creates the route",
container: containerJSON(
labels(map[string]string{
label.TraefikPort: "443",
}),
ports(nat.PortMap{
"443/tcp": []nat.PortBinding{
{
HostIP: "5.6.7.8",
HostPort: "8082",
},
},
}),
withNetwork("testnet", ipv4("10.11.12.13"))),
ip: "5.6.7.8",
port: "8082",
},
{
desc: "label traefik.port set, no binding on the corresponding port, falling back on the container's IP/label.port",
container: containerJSON(
labels(map[string]string{
label.TraefikPort: "80",
}),
ports(nat.PortMap{
"443/tcp": []nat.PortBinding{
{
HostIP: "5.6.7.8",
HostPort: "8082",
},
},
}),
withNetwork("testnet", ipv4("10.11.12.13"))),
ip: "10.11.12.13",
port: "80",
},
{
desc: "label traefik.port set, multiple bindings on different ports, uses the label to select the correct (first) binding",
container: containerJSON(
@ -1454,69 +1525,6 @@ func TestDockerGetIPPort(t *testing.T) {
ip: "5.6.7.8",
port: "8082",
},
{
desc: "label traefik.port set, single binding with ip:port for the label, creates the route",
container: containerJSON(
labels(map[string]string{
label.TraefikPort: "443",
}),
ports(nat.PortMap{
"443/tcp": []nat.PortBinding{
{
HostIP: "5.6.7.8",
HostPort: "8082",
},
},
}),
withNetwork("testnet", ipv4("10.11.12.13"))),
ip: "5.6.7.8",
port: "8082",
},
{
desc: "label traefik.port not set, single binding with port only, server ignored",
container: containerJSON(
ports(nat.PortMap{
"80/tcp": []nat.PortBinding{
{
HostPort: "8082",
},
},
}),
withNetwork("testnet", ipv4("10.11.12.13"))),
expectsError: true,
},
{
desc: "label traefik.port not set, no binding, server ignored",
container: containerJSON(
withNetwork("testnet", ipv4("10.11.12.13"))),
expectsError: true,
},
{
desc: "label traefik.port set, no binding on the corresponding port, server ignored",
container: containerJSON(
labels(map[string]string{
label.TraefikPort: "80",
}),
ports(nat.PortMap{
"443/tcp": []nat.PortBinding{
{
HostIP: "5.6.7.8",
HostPort: "8082",
},
},
}),
withNetwork("testnet", ipv4("10.11.12.13"))),
expectsError: true,
},
{
desc: "label traefik.port set, no binding, server ignored",
container: containerJSON(
labels(map[string]string{
label.TraefikPort: "80",
}),
withNetwork("testnet", ipv4("10.11.12.13"))),
expectsError: true,
},
}
for _, test := range testCases {
@ -1529,7 +1537,7 @@ func TestDockerGetIPPort(t *testing.T) {
dData.SegmentLabels = segmentProperties[""]
provider := &Provider{
Network: "webnet",
Network: "testnet",
UseBindPortIP: true,
}