Skip to content
This repository has been archived by the owner on Jun 14, 2018. It is now read-only.

Eureka service discovery adapter #994

Merged
merged 36 commits into from
Aug 15, 2017
Merged

Eureka service discovery adapter #994

merged 36 commits into from
Aug 15, 2017

Conversation

ijsnellf
Copy link
Contributor

@ijsnellf ijsnellf commented Aug 5, 2017

Basic Eureka support. Forthcoming:

  • More documentation describing how Eureka data is consumed
  • Integration tests (after config store work becomes more settled)

Note that TCP services will not work with Eureka until headless TCP services are supported in our proxy generation logic.

@istio istio deleted a comment from istio-testing Aug 5, 2017
@istio istio deleted a comment from istio-testing Aug 5, 2017
@codecov
Copy link

codecov bot commented Aug 5, 2017

Codecov Report

Merging #994 into master will increase coverage by 0.41%.
The diff coverage is 77.08%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #994      +/-   ##
==========================================
+ Coverage   71.65%   72.07%   +0.41%     
==========================================
  Files          43       47       +4     
  Lines        5067     5307     +240     
==========================================
+ Hits         3631     3825     +194     
- Misses       1278     1304      +26     
- Partials      158      178      +20
Impacted Files Coverage Δ
platform/eureka/client.go 56.81% <56.81%> (ø)
platform/eureka/servicediscovery.go 70.49% <70.49%> (ø)
platform/eureka/conversion.go 84.21% <84.21%> (ø)
platform/eureka/controller.go 92.5% <92.5%> (ø)
platform/kube/controller.go 61.45% <0%> (+3.43%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update de4446f...5b27021. Read the comment docs.

Copy link
Member

@rshriram rshriram left a comment

Choose a reason for hiding this comment

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

@ijsnellf can you add a readme under platform/eureka documenting how you map the eureka fields to our service model, limitations and any todos, requirements expected from end users?

@GregHanson can you do the same for Consul ?

We don't need elaborate docs. Just enough to get other devs started, if they want to contribute.

@ijsnellf ijsnellf changed the title [WIP] Eureka support Eureka support Aug 7, 2017
Copy link
Member

@cmluciano cmluciano left a comment

Choose a reason for hiding this comment

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

One comment on health

I'd like to see this use a client library like fargo in the future.

"hostName": "foo.bar.local",
"app": "FOO_BAR_LOCAL",
"ipAddr": "10.0.0.2",
"status": "UP",
Copy link
Member

Choose a reason for hiding this comment

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

This normally indicates health as posted by local healthchecks or those embedded in clients like ribbon. Since this is updated by each application instance, I think we should filter out instances with status: "down".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that sounds reasonable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cmluciano: filter out everything except for "UP", I assume? Possible values: UP, DOWN, STARTING, OUT_OF_SERVICE, UNKNOWN

Copy link
Member

Choose a reason for hiding this comment

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

Yes everything except for up

@@ -0,0 +1,161 @@
package eureka
Copy link
Member

Choose a reason for hiding this comment

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

missing copyright

"time"

"reflect"

Copy link
Member

Choose a reason for hiding this comment

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

kind of surprised this passed this goimports test, I believe "time" and "reflect" should be sorted alphabetically

Copy link
Contributor

Choose a reason for hiding this comment

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

Please remove extra line between the imports. Lines between imports are used to group imports together.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, they got added randomly. Surprised that goimports doesn't remove them automatically.


type application struct {
Name string `json:"name"`
Instances []*instance `json:"instance"`
Copy link
Member

Choose a reason for hiding this comment

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

json:"instance" or json:"instances"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Eureka idiosyncrasies. instance is correct.

@@ -191,7 +191,7 @@ func TestConvertService(t *testing.T) {
}

if len(out.Ports) != 1 {
t.Errorf("covnertService() incorrect # of ports => %v, want %v",
t.Errorf("convertService() incorrect # of ports => %v, want %v",
Copy link
Member

Choose a reason for hiding this comment

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

Nit: should this be moved to a new PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Technically, but if it has to be it won't happen.

Hostname string `json:"hostName"`
App string `json:"app"`
IPAddress string `json:"ipAddr"`
Status string `json:"status"`
Copy link
Member

Choose a reason for hiding this comment

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

I forget, does status mean health? If so do we already drop non-"UP" instances?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. We filter out the non-"UP" instances in the conversion logic (in conversion.go).

const statusUp = "UP"

const (
basePath = "/eureka/v2"
Copy link
Member

Choose a reason for hiding this comment

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

Should note in docs that this is hardcoded to Eureka v2

Choose a reason for hiding this comment

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

Can this be made configurable in the future at least?

Copy link
Member

Choose a reason for hiding this comment

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

+1. Can't we use a config file or something to take in all these params?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. We might move to a Eureka library in the future as well.

Copy link
Member

Choose a reason for hiding this comment

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

I was wrong in my original interpretation of this. The v2 endpoint has no relation to the v2 release of Eureka.

data: readFile(t, "testdata/eureka-apps.json"),
apps: []*application{
{
Name: appName("foo.bar.local"),
Copy link
Member

Choose a reason for hiding this comment

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

Nit: This is fine for testing, but I don't think it is common to place a full DNS entry in the Eureka application name.

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, makes sense. The app name shouldn't matter at all for any of the actual logic, but I can change it so it is less confusing.

if !reflect.DeepEqual(apps, cachedApps) {
cachedApps = apps
// TODO: feed with real events.
// The handlers are being feed dummy events. This is sufficient with simplistic
Copy link
Member

Choose a reason for hiding this comment

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

s/feed/fed

@spencergibb
Copy link

Can I ask how heartbeats are being handled (haven't had a chance to review yet)?

@spencergibb
Copy link

I probably just answered my own question in my mind, clients still handle registration and heartbeats, this is just another client, is that right?

@cmluciano
Copy link
Member

@spencergibb Yes client libraries embedded in the application will normally do this. Is pilot responsible for handling health-checks on each application?

@rshriram
Copy link
Member

No. We just do discovery. Registration and heartbeats are application/platform's responsibility

@ijsnellf
Copy link
Contributor Author

Thanks for the comments everyone, I think I addressed them all. Should be ready to merge, pending an approval.

Copy link
Member

@rshriram rshriram left a comment

Choose a reason for hiding this comment

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

This looks good for a first cut. Bunch of nits and a few important TODOs (esp the reading port protocols from a file).

continue
}

if !reflect.DeepEqual(apps, cachedApps) {
Copy link
Member

Choose a reason for hiding this comment

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

+1. Since we don't control how the Eureka server handles the order of registered instances, we should make this comparison deterministic on our end by sorting the apps before caching/comparison.


// Convert Eureka applications to services. If provided, only convert applications in the hostnames whitelist,
// otherwise convert all.
func convertServices(apps []*application, hostnames map[string]bool) map[string]*model.Service {
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason to have this special case for filtering hostnames in the white list?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only for convenience. There are several places where we filter hostnames.

}

func convertPorts(instance *instance) model.PortList {
out := make(model.PortList, 0, 2)
Copy link
Member

Choose a reason for hiding this comment

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

add a comment stating that eureka instances have only two ports


service.Ports = append(service.Ports, port)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

don't you need to delete the service from the map if it has no ports attached to it? Is this possible in Eureka?

},
services: map[string]*model.Service{
"foo.bar.local": makeService("foo.bar.local", []int{5000}, nil),
"foo.biz.local": makeService("foo.biz.local", []int{5000}, nil),
Copy link
Member

Choose a reason for hiding this comment

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

may be have the protocol also here, for completeness ?

}
services := convertServices(apps, nil)

// TODO: canonical ordering?
Copy link
Member

Choose a reason for hiding this comment

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

I think this is a requirement else Envoy will go crazy. Use stable sort or whatever and make sure that the return value is deterministic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, missed that I hadn't dealt with that TODO

return out
}

const protocolMetadata = "istio.protocol" // metadata key for port protocol
Copy link
Member

Choose a reason for hiding this comment

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

I understand that Eureka does not support protocols. But this would require changes to existing eureka clients or require people to update instance info out of band.

As part of the effort to read Eureka configuration from a file (e.g., the server's IPs, API base URL, etc., one can also imagine that the user provides a list of ports and their protocols - which can be used during runtime to lookup the protocols associated with a port). I leave it up to you to do this in this PR or in a follow up PR to add the ability to read this info from a statically supplied config file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The metadata can be supplied via a config file for the Eureka clients. Do you think that is reasonable? It would have to be a config file change on each Eureka client in your env.

See: https://github.com/Netflix/eureka/wiki/Overriding-Default-Configurations#statically-set-via-configuration

For the alternative that you are suggesting, are you imagining a config file that the Istio discovery server would read?

Copy link
Member

Choose a reason for hiding this comment

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

Yes.. Because odds are, someone already has bunch of apps running on VMs or wherever. For these folks, do you think its easy to update the Eureka server with metadata info out of band and will this info remain in tact despite heartbeats etc. from apps?

}

type instance struct {
Hostname string `json:"hostName"`
Copy link
Member

Choose a reason for hiding this comment

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

According to the docs, hostName is used during access (not sure if its AWS specific), while there is also a virtualHostName and secureVirtualHostName(). Question is, when I say http://reviews.foo.local/getReviews, is reviews.foo.local coming from hostName or virtualHostName ?
@cmluciano ?

Copy link
Member

@cmluciano cmluciano Aug 14, 2017

Choose a reason for hiding this comment

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

Talked to @seh about these:

  1. hostName should refer to the machine where the application resides.
  2. virtualHostName is similar to a VIP (Virtual IP) that a client could add

Status string `json:"status"`
Port *port `json:"port,omitempty"`
SecurePort *port `json:"securePort,omitempty"`
Metadata metadata `json:"metadata,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

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

According to docs, https://github.com/Netflix/eureka/wiki/Eureka-REST-operations, the instance also has a vipAddress. Is this equivalent to our service VIP address in K8S ? If so, may be worthwhile to include it?

Copy link
Contributor Author

@ijsnellf ijsnellf Aug 14, 2017

Choose a reason for hiding this comment

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

Eureka's VIPs aren't the same as k8s service addresses. VIP address can be used to obtain hostname/port pairs using the Eureka client, but the requests themselves are made to the hostname, not the VIP address.

See: https://github.com/Netflix/eureka/wiki/Understanding-eureka-client-server-communication#eureka-client-operations

We may have it in the future, but until then our linters get mad because it is an unused variable, so I've left it out. :)

Hostname string `json:"hostName"`
App string `json:"app"`
IPAddress string `json:"ipAddr"`
Status string `json:"status"`
Copy link
Member

Choose a reason for hiding this comment

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

Please add a TODO to parse the datacenter info from instance (https://github.com/Netflix/eureka/wiki/Eureka-REST-operations) - this is needed to populate Envoy's AZ info per instance.

@rshriram
Copy link
Member

@jhspaybar would appreciate any feedback you may have :).

@rshriram rshriram changed the title Eureka support Eureka adapter Aug 14, 2017
Copy link
Contributor

@kyessenov kyessenov left a comment

Choose a reason for hiding this comment

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

Code-wise I think it's alright. We need a short explanation of how we map Eureka model into our data model (things like two-ports, hostnames, labels). Also, an integration test for a dummy eureka setup would be great.

@rshriram rshriram merged commit 3dccc73 into istio:master Aug 15, 2017
@rshriram rshriram changed the title Eureka adapter Eureka service discovery adapter Aug 15, 2017
Metadata metadata `json:"metadata,omitempty"`
}

type port struct {
Copy link

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

FWIW, pivotal doesn't support the older eureka servers. Maybe a documentation not on minimum supported eureka version?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @seh. I created an issue: #1378

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

8 participants