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

Ingress HTTP/2 Support #146

Merged
merged 9 commits into from
Apr 2, 2018
Merged

Ingress HTTP/2 Support #146

merged 9 commits into from
Apr 2, 2018

Conversation

agau4779
Copy link
Contributor

@agau4779 agau4779 commented Mar 8, 2018

  • 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

@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Mar 8, 2018
@agau4779
Copy link
Contributor Author

agau4779 commented Mar 8, 2018

/assign @nicksardo @bowei

@@ -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 {
Copy link
Member

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

@@ -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 {
Copy link
Member

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.

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)
Copy link
Member

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.

Copy link
Member

@bowei bowei left a 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.

Copy link
Contributor

@freehan freehan left a 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.

@agau4779
Copy link
Contributor Author

agau4779 commented Mar 9, 2018

@freehan - we're going to Feature Gate this.

@nicksardo
Copy link
Contributor

/ok-to-test

@k8s-ci-robot k8s-ci-robot removed the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Mar 13, 2018
}

// If NEG is enabled, do not link backend service to instance groups.
if p.NEGEnabled {
return b.Update(be)
Copy link
Contributor

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

beIGs := sets.String{}
for _, beToIG := range be.Backends {
beIGs.Insert(beToIG.Group)
if be.isAlpha {
Copy link
Contributor

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.

func (b *Backends) edgeHopAndUpdate(be *BackendService, igs []*compute.InstanceGroup) error {
addIGs := getInstanceGroupsToAdd(be, igs)
if len(addIGs) == 0 {
return b.Update(be)
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, good point. Removing

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)
Copy link
Contributor

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

for _, bm := range []BalancingMode{Rate, Utilization} {
// Generate backends with given instance groups with a specific mode
if be.isAlpha {
originalIGBackends := []*computealpha.Backend{}
Copy link
Contributor

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 {
Copy link
Contributor

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

}

// Update calls either the GA or Alpha update path depending on Protocol
func (b *Backends) Update(be *BackendService) error {
Copy link
Contributor

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

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 function has to be public?

Copy link
Contributor Author

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

if newHC.ForNEG {
if newHC.isHttp2() {
glog.V(2).Infof("Updating health check with protocol %v", newHC.Type)
return h.cloud.UpdateAlphaHealthCheck(newHC.ToAlphaComputeHealthCheck())
Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor

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.

  1. Need to handle PortSpecification + HTTP2
  2. Need to preserve whatever user healthcheck configuration even if protocol or NEG setting changes.

@agau4779 agau4779 force-pushed the http2 branch 2 times, most recently from ea42fc7 to 6aa47f9 Compare March 15, 2018 20:12

// If the Protocol is empty, this means this is a alpha BackendService and
// Protocol is expected to be HTTP2
if beGa.Protocol == "" {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

if err := b.cloud.CreateGlobalBackendService(bs); err != nil {
return nil, err

bsAlpha := &computealpha.BackendService{}
Copy link
Contributor

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

be.HealthChecks = []string{hcLink}
be.Description = p.Description()
if err = b.cloud.UpdateGlobalBackendService(be); err != nil {
existingProtocol := be.Ga.Protocol
Copy link
Contributor

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()

}

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))
Copy link
Contributor

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.


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)
Copy link
Contributor

@freehan freehan Mar 16, 2018

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.

@@ -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 := ""
Copy link
Contributor

Choose a reason for hiding this comment

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

existingHCLink := be.GetHealthCheckLink()

}

// generateBackends generates backends with given instance groups with a specific mode
func (be *BackendService) generateBackends(bm BalancingMode, addIGs []*compute.InstanceGroup) {
Copy link
Contributor

Choose a reason for hiding this comment

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

rename to ensureBackends?

}

igLinks := sets.String{}
Copy link
Contributor

Choose a reason for hiding this comment

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

s/igLinks/expectedIGs

if newHC.ForNEG {
if newHC.isHttp2() {
glog.V(2).Infof("Updating health check with protocol %v", newHC.Type)
return h.cloud.UpdateAlphaHealthCheck(newHC.ToAlphaComputeHealthCheck())
Copy link
Contributor

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.

  1. Need to handle PortSpecification + HTTP2
  2. Need to preserve whatever user healthcheck configuration even if protocol or NEG setting changes.

@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Mar 19, 2018
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Mar 19, 2018
Alpha *computealpha.BackendService
Ga *compute.BackendService
// Flag to indicate we want to use the Alpha compute type
isAlpha bool
Copy link
Member

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?

Copy link
Contributor Author

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.

// If NEG is enabled, do not link backend service to instance groups.
if p.NEGEnabled {
var errs []string
for _, bm := range []BalancingMode{Rate, Utilization} {
Copy link
Contributor

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.

}

// ensureProtocol updates the BackendService Protocol with the expected value
func (be *BackendService) ensureProtocol(p ServicePort, hcLink string) (needsUpdate bool) {
Copy link
Contributor

Choose a reason for hiding this comment

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

hcLink not used right?

}

if be.Alpha != nil {
be.Alpha.Protocol = string(p.Protocol)
Copy link
Contributor

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.

Copy link
Contributor Author

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
	}

@@ -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() {
Copy link
Contributor

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

@freehan
Copy link
Contributor

freehan commented Mar 22, 2018

Please also unit test the ensurexxxxx methods.

@agau4779 agau4779 force-pushed the http2 branch 2 times, most recently from a2272b3 to 501b19b Compare March 23, 2018 00:31
return nil, err
}
} else {
bsAlpha := toAlphaBackendService(bs)
Copy link
Contributor

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?


// 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 {
Copy link
Contributor

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.

@agau4779 agau4779 force-pushed the http2 branch 3 times, most recently from 52b9572 to 76405ba Compare March 27, 2018 17:29
}

if be.Alpha != nil {
be.Alpha.HealthChecks = []string{hcLink}
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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 {
Copy link
Contributor

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 {
Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Contributor

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?

Copy link
Contributor Author

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.

@freehan
Copy link
Contributor

freehan commented Mar 30, 2018

LGTM

@bowei @nicksardo do you want another look?

@freehan
Copy link
Contributor

freehan commented Mar 30, 2018

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Mar 30, 2018
@freehan freehan merged commit 9857d92 into kubernetes:master Apr 2, 2018
k8s-github-robot pushed a commit to kubernetes/kubernetes that referenced this pull request Apr 7, 2018
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
```
@agau4779 agau4779 deleted the http2 branch April 9, 2018 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants