-
Notifications
You must be signed in to change notification settings - Fork 303
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
Ingress HTTP/2 Support #146
Conversation
agau4779
commented
Mar 8, 2018
•
edited
Loading
edited
- Adds HTTP2 annotation to Service
- Creates alpha HealthCheck and BackendService resources when the protocol is HTTP/2, by using a composite BackendService type
- Tests for HealthCheck/BackendServices using HTTP/2
/assign @nicksardo @bowei |
pkg/backends/backends.go
Outdated
@@ -431,7 +457,7 @@ func (b *Backends) edgeHop(be *compute.BackendService, igs []*compute.InstanceGr | |||
newBackends := getBackendsForIGs(addIGs, bm) | |||
be.Backends = append(originalIGBackends, newBackends...) | |||
|
|||
if err := b.cloud.UpdateGlobalBackendService(be); err != nil { | |||
if err := b.cloud.UpdateAlphaGlobalBackendService(be); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alpha cannot be used on the beta, ga paths
pkg/backends/backends.go
Outdated
@@ -388,7 +414,7 @@ func getBackendsForNEGs(negs []*computealpha.NetworkEndpointGroup) []*computealp | |||
|
|||
// edgeHop checks the links of the given backend by executing an edge hop. | |||
// It fixes broken links. | |||
func (b *Backends) edgeHop(be *compute.BackendService, igs []*compute.InstanceGroup) error { | |||
func (b *Backends) edgeHop(be *computealpha.BackendService, igs []*compute.InstanceGroup) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably keep the alpha types off of the ga paths.
pkg/backends/backends.go
Outdated
if be == nil { | ||
namedPort := &compute.NamedPort{ | ||
Name: b.namer.NamedPort(p.NodePort), | ||
Port: p.NodePort, | ||
} | ||
glog.V(2).Infof("Creating backend service for port %v named port %v", p.NodePort, namedPort) | ||
be, err = b.create(namedPort, hcLink, p, beName) | ||
be, err = b.createAlpha(namedPort, hcLink, p, beName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can't call alpha on the GA, beta paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to keep the alpha API by itself, alpha APIs have no SLO and are whitelist only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it need to be feature gated? or always turning it on is okay.
@freehan - we're going to Feature Gate this. |
/ok-to-test |
pkg/backends/backends.go
Outdated
} | ||
|
||
// If NEG is enabled, do not link backend service to instance groups. | ||
if p.NEGEnabled { | ||
return b.Update(be) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems wrong. It will always update backend service. Please only update when necessary
Move this back to above
pkg/backends/backends.go
Outdated
beIGs := sets.String{} | ||
for _, beToIG := range be.Backends { | ||
beIGs.Insert(beToIG.Group) | ||
if be.isAlpha { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have to distinguish between Alpha and GA backend services here?
Or consider have some helper function with BackendService struct
IIUC, the URLs are the same except for the API version part.
pkg/backends/backends.go
Outdated
func (b *Backends) edgeHopAndUpdate(be *BackendService, igs []*compute.InstanceGroup) error { | ||
addIGs := getInstanceGroupsToAdd(be, igs) | ||
if len(addIGs) == 0 { | ||
return b.Update(be) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no Instance Group needs to be added, why do you need to update?
edgeHop is mostly for linking backends and backend service
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, good point. Removing
pkg/backends/backends.go
Outdated
if existingProtocol != string(p.Protocol) || existingHCName != expectedHCName || existingDescription != p.Description() { | ||
glog.V(2).Infof("Updating backend protocol %v (%v) for change in protocol (%v) or health check", beName, existingProtocol, string(p.Protocol)) | ||
if be.isAlpha { | ||
be.Alpha.Protocol = string(p.Protocol) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
worth wraping this into a separate function that ensures protocol, hclink and description in a be
pkg/backends/backends.go
Outdated
for _, bm := range []BalancingMode{Rate, Utilization} { | ||
// Generate backends with given instance groups with a specific mode | ||
if be.isAlpha { | ||
originalIGBackends := []*computealpha.Backend{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrap this into a separate function to generate corresponding backends?
@@ -90,6 +90,14 @@ type Backends struct { | |||
namer *utils.Namer | |||
} | |||
|
|||
// BackendService embeds both the GA and alpha compute BackendService types | |||
type BackendService struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add some helper functions to this struct. For instance,
- Ensure protocol, healthcheck and description, (return if there is difference)
- Ensure Backends (check if current backend instance groups are super set)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pkg/backends/backends.go
Outdated
} | ||
|
||
// Update calls either the GA or Alpha update path depending on Protocol | ||
func (b *Backends) Update(be *BackendService) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the log line here to log it is updating backend service
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this function has to be public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opted to add new logging for update
here - f6ca1c7
pkg/healthchecks/healthchecks.go
Outdated
if newHC.ForNEG { | ||
if newHC.isHttp2() { | ||
glog.V(2).Infof("Updating health check with protocol %v", newHC.Type) | ||
return h.cloud.UpdateAlphaHealthCheck(newHC.ToAlphaComputeHealthCheck()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably need to copy the same pattern for NEG health checks. Need to ensure existing settings are preserved.
@nicksardo Do you think it is necessary for HTTP2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, settings should absolutely be preserved as they are done today. IIRC, we change some parameters for NEGs, but never the path value.
We'll also need to consider the case of NEGs + HTTP2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@agau4779 need to rethink the logic here.
- Need to handle PortSpecification + HTTP2
- Need to preserve whatever user healthcheck configuration even if protocol or NEG setting changes.
ea42fc7
to
6aa47f9
Compare
pkg/backends/backends.go
Outdated
|
||
// If the Protocol is empty, this means this is a alpha BackendService and | ||
// Protocol is expected to be HTTP2 | ||
if beGa.Protocol == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any possibility that Protocol is empty and GetAlphaGlobalBackendService keeps returning error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetGlobalBackendService
gets the GA BackendService but returns an empty string in the Protocol if the Protocol is not HTTP or HTTPS - so here, we're anticipating that the user created an Alpha BackendService with an HTTP2 protocol (currently alpha-only), so calling the GA path will return a blank protocol.
GetAlphaGlobalBackendService
may run into an isForbidden
error if the user is no longer Alpha whitelisted - in which case, might cause the Ingress controller to be stuck forever. Will add this warning in a code comment.
pkg/backends/backends.go
Outdated
if err := b.cloud.CreateGlobalBackendService(bs); err != nil { | ||
return nil, err | ||
|
||
bsAlpha := &computealpha.BackendService{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move to the following else block
pkg/backends/backends.go
Outdated
be.HealthChecks = []string{hcLink} | ||
be.Description = p.Description() | ||
if err = b.cloud.UpdateGlobalBackendService(be); err != nil { | ||
existingProtocol := be.Ga.Protocol |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
existingProtocol := be.GetProtocol()
existingProtocol := be.GetDescription()
pkg/backends/backends.go
Outdated
} | ||
|
||
if existingProtocol != string(p.Protocol) || existingHCName != expectedHCName || existingDescription != p.Description() { | ||
glog.V(2).Infof("Updating backend protocol %v (%v) for change in protocol (%v) or health check", beName, existingProtocol, string(p.Protocol)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove this since b.update() has a log already.
pkg/backends/backends.go
Outdated
|
||
if existingProtocol != string(p.Protocol) || existingHCName != expectedHCName || existingDescription != p.Description() { | ||
glog.V(2).Infof("Updating backend protocol %v (%v) for change in protocol (%v) or health check", beName, existingProtocol, string(p.Protocol)) | ||
be.ensureProtocol(p, hcLink) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needUpdate := be.ensureProtocol(p.Protocol)
needUpdate = needUpdate | be.ensureHealthCheck(hcLink)
needUpdate = needUpdate | be.ensureDescription(p.Description())
if needUpdate {
if err = b.update(be); err != nil {
return err
}
}
or
if be.ensureConfig(p, hcLink) {
if err = b.update(be); err != nil {
return err
}
}
ensureConfig
returns true if existing parameter does not match expected. Move all comparison between existing vs. expected into the helper functions.
pkg/backends/backends.go
Outdated
@@ -277,8 +351,11 @@ func (b *Backends) ensureBackendService(p ServicePort, igs []*compute.InstanceGr | |||
|
|||
// Check that the backend service has the correct protocol and health check link | |||
existingHCLink := "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
existingHCLink := be.GetHealthCheckLink()
pkg/backends/backends.go
Outdated
} | ||
|
||
// generateBackends generates backends with given instance groups with a specific mode | ||
func (be *BackendService) generateBackends(bm BalancingMode, addIGs []*compute.InstanceGroup) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to ensureBackends?
pkg/backends/backends.go
Outdated
} | ||
|
||
igLinks := sets.String{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/igLinks/expectedIGs
pkg/healthchecks/healthchecks.go
Outdated
if newHC.ForNEG { | ||
if newHC.isHttp2() { | ||
glog.V(2).Infof("Updating health check with protocol %v", newHC.Type) | ||
return h.cloud.UpdateAlphaHealthCheck(newHC.ToAlphaComputeHealthCheck()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@agau4779 need to rethink the logic here.
- Need to handle PortSpecification + HTTP2
- Need to preserve whatever user healthcheck configuration even if protocol or NEG setting changes.
pkg/backends/backends.go
Outdated
Alpha *computealpha.BackendService | ||
Ga *compute.BackendService | ||
// Flag to indicate we want to use the Alpha compute type | ||
isAlpha bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we have Alpha == nil denote that alpha compute type is used?
is there a possibility of isAlpha == true, but Alpha == nil?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is not - when getting an Alpha BackendService, or promoting an GA BackendService to Alpha, isAlpha is set to true. Looks like it's redundant if we can just check BackendService.Alpha == nil
- removing.
pkg/backends/backends.go
Outdated
// If NEG is enabled, do not link backend service to instance groups. | ||
if p.NEGEnabled { | ||
var errs []string | ||
for _, bm := range []BalancingMode{Rate, Utilization} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move the original comments here.
pkg/backends/backends.go
Outdated
} | ||
|
||
// ensureProtocol updates the BackendService Protocol with the expected value | ||
func (be *BackendService) ensureProtocol(p ServicePort, hcLink string) (needsUpdate bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hcLink not used right?
pkg/backends/backends.go
Outdated
} | ||
|
||
if be.Alpha != nil { | ||
be.Alpha.Protocol = string(p.Protocol) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, if the existing backend-service is using HTTP2. If the new protocol set to HTTP. Then it will set protocol on the alpha object. Hence, during update, it will call Alpha API to update backend service.
Try to avoid calling alpha API as much as possible. SLA is different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, then should we convert it to a Ga backend service in this function?
e.g.
if p.Protocol != annotations.ProtocolHTTP2 {
be.Ga = toV1BackendService(be.Alpha)
be.Alpha = nil
}
pkg/healthchecks/healthchecks.go
Outdated
@@ -146,9 +147,12 @@ func (h *HealthChecks) create(hc *HealthCheck) error { | |||
} | |||
|
|||
func (h *HealthChecks) update(oldHC, newHC *HealthCheck) error { | |||
if newHC.ForNEG { | |||
if newHC.isHttp2() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
merge NEG and isHttp2 conditions
Please also unit test the |
…ompare InstanceGroups by comparable name instead of full URL
a2272b3
to
501b19b
Compare
pkg/backends/backends.go
Outdated
return nil, err | ||
} | ||
} else { | ||
bsAlpha := toAlphaBackendService(bs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toAlphaBackendService
is mostly for testing.
Are you confident about its reliability for production use?
pkg/backends/backends.go
Outdated
|
||
// Using the HTTP2 Protocol requires that the BackendService is alpha. | ||
// If the BackendService wasn't originally alpha, convert it to Alpha. | ||
if be.Alpha == nil && p.Protocol == annotations.ProtocolHTTP2 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably do this: ensureProtocol
just ensures the protocol for both GA and Alpha objects if exists.
In update
, pick the api to call.
52b9572
to
76405ba
Compare
} | ||
|
||
if be.Alpha != nil { | ||
be.Alpha.HealthChecks = []string{hcLink} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible that alpha backend-service points to GA health check link? Or reverse (GA backend-service point to Alpha Health check)
IIRC, this is actually okay. But need to confirm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't be possible - GetHealthCheckLink
performs a check - if be.Alpha != nil && len(be.Alpha.HealthChecks) == 1
then hcLink will return an Alpha healthcheck link; otherwise a Ga link is returned.
The compute API currently returns the alpha version of the Healthcheck link through the Alpha API and the GA version of the link through the GA API (gcloud [alpha] compute backend-services describe [service-name]
), so I agree that it should be ok.
existingHCLink = be.HealthChecks[0] | ||
} | ||
needUpdate := be.ensureProtocol(p) | ||
needUpdate = needUpdate || be.ensureHealthCheckLink(hcLink) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
quick question:
GA health checks supports HTTP2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GA Healthchecks do not support HTTP2. HTTP2 healthchecks are created through the Alpha API.
// This is to preserve the existing health check setting as much as possible. | ||
// WARNING: if a service backend is converted from IG mode to NEG mode, | ||
// the existing health check setting will be preserve, although it may not suit the customer needs. | ||
func mergeHealthcheckForNEG(oldHC, newHC *HealthCheck) *HealthCheck { | ||
func mergeHealthcheck(oldHC, newHC *HealthCheck) *HealthCheck { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems incorrect. Does it preserve configs if oldHC is HTTPS or HTTP2?
|
||
// edgeHop checks the links of the given backend by executing an edge hop. | ||
// It fixes broken links and updates the Backend accordingly. | ||
func (b *Backends) edgeHop(be *BackendService, igs []*compute.InstanceGroup) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cannot think of a better way to avoid duplicate code.
Do you think it is worthwhile to hide the complexity behind ensureInstanceGroupBackends
? And do something clever behind the scene and unit test it thoroughly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to only use GA backend service to ensure backends?
Will it overwrite the HTTP2 config?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If GA Backend-service set to HTTP2, you probably gets a validation error.
But if you get a GA backend-service, and update its backend without touching protocol. What will happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't use the GA BackendService API. Verified that setting the Protocol on an HTTP or HTTPS BackendService to HTTP2 causes a validation error, and that modifying any other attribute on an HTTP2 BackendService causes the BackendService's Protocol to default to HTTP. The Sync loop eventually calls backends.go#Ensure then causes it to revert back to HTTP2.
LGTM @bowei @nicksardo do you want another look? |
/lgtm |
Automatic merge from submit-queue (batch tested with PRs 62208, 62114, 62144, 60460, 62214). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. add unreleased tag to http2 test **What this PR does / why we need it**: [HTTP2](kubernetes/ingress-gce#146) isn't yet in an Ingress release. Add Unreleased label to disable this test for gci-gke-ingress until Ingress gets a new release. **Release note**: ```release-note NONE ```