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

Network segments #3431

Merged
merged 14 commits into from
Sep 1, 2017
Merged

Network segments #3431

merged 14 commits into from
Sep 1, 2017

Conversation

kyhavlov
Copy link
Contributor

Adds the OSS components for network segments. This feature allows clients to be split up into named network segments, in order to separate their gossip layer from clients in different segments. The client's segment is stored in the consul-network-segment key in its node metadata.

Servers are always in all segments, and each segment can be configured with its own bind/advertise address and port (along with an option for an rpc listener if the segment's bind address is different from the default rpc addr).

Copy link
Contributor

@slackpad slackpad left a comment

Choose a reason for hiding this comment

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

Made an initial pass and noted a few things.

agent/agent.go Outdated
Leave() error
LANMembers() []serf.Member
LANSegmentMembers(name string) ([]serf.Member, error)
Copy link
Contributor

Choose a reason for hiding this comment

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

We should s/name/segment/ for this one so it's clearer what this does.

agent/agent.go Outdated

segments = append(segments, consul.NetworkSegment{
Name: segment.Name,
Bind: segment.Bind,
Copy link
Contributor

Choose a reason for hiding this comment

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

The Bind and Advertise values here won't be correct if they were empty any we used the default ones. Is that OK?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, fixed

agent/agent.go Outdated
@@ -2105,6 +2169,11 @@ func (a *Agent) loadMetadata(conf *Config) error {
a.state.metadata[key] = value
}

// The segment isn't reloadable so we only add it once.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did I add this? It seems like we call unloadMetadata anyway, so this extra check isn't necessary and we can just add it always.

var members []serf.Member
if wan {
members = s.agent.WANMembers()
} else {
} else if segment == "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be simplified to always just call LanSegmentMembers(segment) since that can handle the empty string case internally.

agent/config.go Outdated
// (Enterprise-only) NetworkSegment is the network segment for this client to join
Segment string `mapstructure:"segment"`

// Segments
Copy link
Contributor

Choose a reason for hiding this comment

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

Needs a doc comment.

@@ -49,6 +49,16 @@ func init() {
}
}

// (Enterprise-only)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should have a brief doc comment.

// Fire the event
return m.srv.serfLAN.UserEvent(eventName, args.Payload, false)
// Fire the event on all LAN segments
segments := m.srv.LANSegments()
Copy link
Contributor

Choose a reason for hiding this comment

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

How come you decided not to include the default segment in LANSegments(), which would eliminate the special cases? I think you had that way partway through development.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I removed it from the segments map on the server but I should have re-added it to the map that LANSegments() returns, fixed

} else {
mgr = m.srv.KeyManagerLAN()
segments := m.srv.LANSegments()
Copy link
Contributor

Choose a reason for hiding this comment

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

Doh this doesn't special case for the default segment, see comment above we should probably just put the default segment in here and avoid the special cases.

command/agent.go Outdated
}

if !cfg.Server && len(cfg.Segments) > 0 {
cmd.UI.Error("Cannot define segments on clients")
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe "Segments can only be defined on servers" to be more symmetric with the other message.

Copy link
Contributor

@slackpad slackpad left a comment

Choose a reason for hiding this comment

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

Made a detailed pass through everything. Nice job on the guide - I think that's super easy to follow. Noted some things and then I think we are good to merge this.

@@ -2105,6 +2169,8 @@ func (a *Agent) loadMetadata(conf *Config) error {
a.state.metadata[key] = value
}

a.state.metadata[structs.MetaSegmentKey] = conf.Segment
Copy link
Contributor

Choose a reason for hiding this comment

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

Now I remember why I did that - since the segment isn't reloadable, this would let them change their metadata about it. I think this is fine as-is vs. adding code complexity (my thing didn't work since the map is empty). We explicitly document what can be reloaded.

agent/config.go Outdated
return fmt.Errorf("Cannot exceed network segment limit of %d", SegmentLimit)
}

takenPorts := make(map[int]string, len(conf.Segments))
Copy link
Contributor

Choose a reason for hiding this comment

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

Realized we also need to make sure the names are unique, otherwise we will overlay one on the other when we create them.

agent/config.go Outdated
return &result, nil
}

func ValidateSegments(conf *Config) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

In OSS we should probably bail with an error if they set the segment on a client, and if they set up any segments for servers. I wonder if it's worth splitting an agent/segment_stub.go here in OSS and creating ValidateSegments() into there that does those checks, and then moving this into agent/segment.go in Enterprise to represent the two behaviors (you'd have to move the test for this over into Enterprise as well).

)

const (
errSegmentsNotSupported = "network segments are not supported in this version of Consul"
Copy link
Contributor

Choose a reason for hiding this comment

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

If you pull out an agent-level segment.go and stub, you could move this error to a common place (maybe structs)?

raftState = "raft/"
snapshotsRetained = 2
serfLANSnapshot = "serf/local.snapshot"
serfLANSegmentSnapshot = "serf/local-segment-%s.snapshot"
Copy link
Contributor

Choose a reason for hiding this comment

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

This could get moved to Enterprise agent/consul/segment.go.

@@ -87,6 +87,25 @@ populate the query before it is executed. All of the string fields inside the
doesn't match, or an invalid index is given, then `${match(N)}` will return an
empty string.

- `${agent.segment}` has the network segment (Enterprise only) of the agent that
Copy link
Contributor

Choose a reason for hiding this comment

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

Enterprise-only (hyphen for consistency with other places)

# Partial LAN Connectivity
## Configuring Network Segments

[//]: # ( ~> The network area functionality described here is available only in )
Copy link
Contributor

Choose a reason for hiding this comment

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

Change "network area" to "network segment" and change version to 0.9.3.

By default, all Consul agents in one datacenter are part of a shared gossip pool over the LAN;
this means that the partial connectivity caused by segmented networks would cause health flapping
as nodes failed to communicate. In this guide we will cover the Network Segments feature, added
in [Consul Enterprise](https://www.hashicorp.com/products/consul/) version 0.9.3, which allows users
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to link this network segments guide from a new landing page on https://www.consul.io/docs/enterprise/index.html..


Once the servers have been configured with the correct segment info, the clients only need to specify
their own segment in the [Agent Config](/docs/agent/options.html) and join by connecting to another
agent within the same segment.
Copy link
Contributor

Choose a reason for hiding this comment

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

We should add something like "If joining to a Consul server, client will need to provide the server's port for their segment along with the address of the server when performing the join (eg. consul agent -retry-join "consul.domain.internal:1234").

client1 4c29819c 192.168.0.5 dc1
```

With this metadata key, we can construct a prepared query that can be used for DNS to return
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd link to "/api/query.html" for "prepared query".

return nil, nil
members, err := client.Agent().MembersOpts(consulapi.MembersOpts{})
if err != nil {
return nil, fmt.Errorf("Error retrieving members: %s", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Just return err here, since you prepend the same thing above.

@@ -18,6 +18,7 @@ increases both scalability and resilience. Features include:
- [Redundancy Zones](/docs/enterprise/redundancy/index.html)
- [Advanced Federation for Complex Network
Topologies](/docs/enterprise/federation/index.html)
- [Network Segments](/docs/guides/segments.html)
Copy link
Contributor

Choose a reason for hiding this comment

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

ping @KFishner this is fine for now but do you want to write a marketing landing thing for later?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

@slackpad slackpad left a comment

Choose a reason for hiding this comment

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

One tiny nit then LGTM.

func getSegmentMembers(client *consulapi.Client) ([]*consulapi.AgentMember, error) {
members, err := client.Agent().MembersOpts(consulapi.MembersOpts{})
if err != nil {
return nil, fmt.Errorf("Error retrieving members: %s", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

@kyhavlov just return err here since we add the same string above

@kyhavlov kyhavlov merged commit 220db48 into master Sep 1, 2017
@kyhavlov kyhavlov deleted the network-segments-oss branch September 1, 2017 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants