Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advertise driver-specific addresses #2709

Merged
merged 21 commits into from
Jul 3, 2017
Merged

Conversation

schmichael
Copy link
Member

@schmichael schmichael commented Jun 15, 2017

Summary

  • DriverNetwork struct returned by Driver.Prestart and Driver.Start containing the PortMap and IP set by the Driver.
  • NOMAD_{HOST,DRIVER}_{ADDR,IP,PORT}_<label> env vars
  • service.address_mode to determine what IP to advertise

DriverNetwork

Ideally this would only need to be returned by Prestart, but Docker doesn't assign a container an IP until the container starts.

I could have implemented it such that Prestart returns PortMap and Start returns IP/AutoAdvertise, but here's why I didn't:

  • DriverNetwork is a really nice way to wrap up all of the container network metadata for passing through TaskRunner to Consul ServiceClient. Even if the Driver didn't use it I'd probably keep it around in TaskRunner and Consul.
  • In theory other drivers may work differently and provide the IP in Prestart. I couldn't actually find where Docker documented their behavior, so it's conceivable they'll even change someday.
  • This mirrors Docker's own Inspect function: it returns networking information whether it's populated or not.

The last point raises an interesting question: Should I just create Driver.Inspect and have it return `DriverNetwork? TaskRunner would just call it as needed and we wouldn't need to persist DriverNetwork.

Might make for a good followup PR.

NOMAD_{HOST,DRIVER}_{ADDR,IP,PORT}_<label> env vars

This was the Cambrian explosion of networking env vars. My work revealed something interesting: NOMAD_ADDR_<label> isn't reliable. If a port_map is set it concatenates the host IP with the driver port. This can only ever be right by pure happenstance.

So I marked it as deprecated and introduced explicit env vars.

The big caveat here is that the DRIVER_IP (and therefore ADDR) env vars can't be populated until the driver returns them -- which right now only happens after Start. This makes it only really useful for script checks. Everything else happens before Start is called.

In theory if other drivers return an IP from Prestart then it would be available in Start (eg args interpolation).

I tried to document this well, but it feels like a pretty big footgun.

service.address_mode

Turned out as we hoped! auto, host, or driver where auto means host unless you're using a Docker networking plugin.

Checks always use host IP+Port as we discussed. A bit of a gotcha, but I think it's always the right choice. If it needs to be configurable for esoteric setups we can add that later.

Naming (Driver vs Plugin)

As always I'm willing to changes any names! That being said I went with driver everywhere as it just made sense. It's the network defined by the driver. I think using "plugin" right now would just leave people hunting around for what these "plugins" are -- or conflate it with Docker plugins which is kind of true now but would probably lead to confusion in the future?

@schmichael schmichael force-pushed the f-advertise-docker-ips branch 2 times, most recently from 6dfd590 to 43a326c Compare June 20, 2017 16:42
@schmichael schmichael requested a review from dadgar June 20, 2017 16:58
@schmichael schmichael changed the title [WIP] Advertise driver-specific addresses Advertise driver-specific addresses Jun 20, 2017
@@ -2467,6 +2498,11 @@ func (s *Service) Copy() *Service {
// Canonicalize interpolates values of Job, Task Group and Task in the Service
// Name. This also generates check names, service id and check ids.
func (s *Service) Canonicalize(job string, taskGroup string, task string) {
// Default to AddressModeAuto
if s.AddressMode == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you actually move this to the api pkg. The defaulting should be happening there now.

Tags []string
PortLabel string `mapstructure:"port"`
AddressMode string `mapstructure:"address_mode"`
Checks []ServiceCheck
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Canonicalize below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

// This should only happen if there's been a coding error (such
// as not calling InspetContainer after CreateContainer). Code
// defensively in case the Docker API changes subtly.
d.logger.Printf("[WARN] driver.docker: no network settings for container %s", c.ID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May want to bump the log level

// Don't auto-advertise bridge IPs
if name != "bridge" {
auto = true
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just break?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -276,7 +276,7 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
waitCh: make(chan *dstructs.WaitResult, 1),
}
go h.run()
return h, nil
return &StartResponse{Handle: h}, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this not need to expose the port map?

Copy link
Member Author

@schmichael schmichael Jun 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've only ever propagated the port map for the Docker driver despite rkt and qemu also having port maps. Should I go ahead and add that as part of this PR? Should Just Work but would be a new feature (or bugfix depending on perspective).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From offline discussion: going to have rkt and qemu return their port maps.

const (
AddressModeAuto = "auto"
AddressModeHost = "host"
AddressModeDriver = "driver" //FIXME plugin? I forget what we decided because like 1000 other things have gone wrong since then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove FIXME

// allocations to tasks with the driver's IP address if on exists.
// Service's will advertise this address if address_mode=true or
// address_mode=auto and the driver determines it should be used.
DriverAddrPrefix = "NOMAD_DRIVER_ADDR_"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

// Deprecated: Use NOMAD_HOST_ADDR_ or NOMAD_DRIVER_ADDR_ instead as
// this environment variable will only ever use the host IP. If a port
// map is used this variable will be set to the Host IP and Driver's
// Port which likely won't work in either context.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it always be host_ip:host_port and don't say deprecated

AddrPrefix = "NOMAD_ADDR_"

// IpPrefix is the prefix for passing the IP of a port allocation to a task.
// IpPrefix is the prefix for passing the IP of a port allocation to a
// task. This may not be the host's address depending on task
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always host IP

IpPrefix = "NOMAD_IP_"

// PortPrefix is the prefix for passing the port allocation to a task.
PortPrefix = "NOMAD_PORT_"

// HostAddrPrefix is the prefix for passing both dynamic and static
// port allocations to tasks with the host's IP address for cases where
// the task advertises a different address.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove these

Ideally DriverNetwork would be fully populated in Driver.Prestart, but
Docker doesn't assign the container's IP until you start the container.

However, it's important to setup the port env vars before calling
Driver.Start, so Prestart should populate that.
Also make NOMAD_ADDR_* use host ip:port for consistency. NOMAD_PORT_*
varies based on port map and the driver IP isn't exposed as an env var
as the only place it can be used is in script checks anyway.
@schmichael
Copy link
Member Author

@dadgar Ready for round 2.

return resp, nil
}

func (d *DockerDriver) detectIP(c *docker.Container) (string, bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put a comment on what this does and the fields it returns

d.logger.Printf("[ERROR] driver.docker: no network settings for container %s", c.ID)
return "", false
}
ip, ipName := "", ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add blank spaces between blocks. Here and line 643

// buildNetworkEnv env vars in the given map.
//
// Auto: NOMAD_{IP,PORT,ADDR}_<label>
// Host: NOMAD_HOST_{IP,PORT,ADDR}_<label>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update comment

@@ -2430,6 +2450,12 @@ func (sc *ServiceCheck) Hash(serviceID string) string {
return fmt.Sprintf("%x", h.Sum(nil))
}

const (
AddressModeAuto = "auto"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments

`NOMAD_DRIVER_IP_<label>` environment variable for use in script checks.

The ports specified in the `port_map` are exposed via the
`NOMAD_DRIVER_PORT_<label>` environment variables. Unlike the IP, ports are
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update

@schmichael schmichael merged commit 6b3ae9a into master Jul 3, 2017
@schmichael schmichael deleted the f-advertise-docker-ips branch July 3, 2017 21:04
schmichael added a commit that referenced this pull request Jul 3, 2017
schmichael added a commit that referenced this pull request Aug 4, 2017
Looks like I accidently dropped them when combining env var listings in
PR #2709
schmichael added a commit that referenced this pull request Aug 17, 2017
Looks like I accidently dropped them when combining env var listings in
PR #2709
@github-actions
Copy link

I'm going to lock this pull request because it has been closed for 120 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 31, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants