Skip to content

Commit f958272

Browse files
authored
fix the default healthCheck setting for GRPC (#1698)
1 parent 9aec5a0 commit f958272

File tree

4 files changed

+268
-14
lines changed

4 files changed

+268
-14
lines changed

docs/guide/ingress/annotations.md

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ You can add annotations to kubernetes Ingress and Service objects to customize t
3838
|[alb.ingress.kubernetes.io/target-group-attributes](#target-group-attributes)|stringMap|N/A|Ingress,Service|N/A|
3939
|[alb.ingress.kubernetes.io/healthcheck-port](#healthcheck-port)|integer \| traffic-port|traffic-port|Ingress,Service|N/A|
4040
|[alb.ingress.kubernetes.io/healthcheck-protocol](#healthcheck-protocol)|HTTP \| HTTPS|HTTP|Ingress,Service|N/A|
41-
|[alb.ingress.kubernetes.io/healthcheck-path](#healthcheck-path)|string|/|Ingress,Service|N/A|
41+
|[alb.ingress.kubernetes.io/healthcheck-path](#healthcheck-path)|string|/ \| /AWS.ALB/healthcheck |Ingress,Service|N/A|
4242
|[alb.ingress.kubernetes.io/healthcheck-interval-seconds](#healthcheck-interval-seconds)|integer|'15'|Ingress,Service|N/A|
4343
|[alb.ingress.kubernetes.io/healthcheck-timeout-seconds](#healthcheck-timeout-seconds)|integer|'5'|Ingress,Service|N/A|
4444
|[alb.ingress.kubernetes.io/healthy-threshold-count](#healthy-threshold-count)|integer|'2'|Ingress,Service|N/A|
4545
|[alb.ingress.kubernetes.io/unhealthy-threshold-count](#unhealthy-threshold-count)|integer|'2'|Ingress,Service|N/A|
46-
|[alb.ingress.kubernetes.io/success-codes](#success-codes)|string|'200'|Ingress,Service|N/A|
46+
|[alb.ingress.kubernetes.io/success-codes](#success-codes)|string|'200' \| '12' |Ingress,Service|N/A|
4747
|[alb.ingress.kubernetes.io/auth-type](#auth-type)|none\|oidc\|cognito|none|Ingress,Service|N/A|
4848
|[alb.ingress.kubernetes.io/auth-idp-cognito](#auth-idp-cognito)|json|N/A|Ingress,Service|N/A|
4949
|[alb.ingress.kubernetes.io/auth-idp-oidc](#auth-idp-oidc)|json|N/A|Ingress,Service|N/A|
@@ -170,9 +170,14 @@ Traffic Routing can be controlled with following annotations:
170170
- <a name="backend-protocol-version">`alb.ingress.kubernetes.io/backend-protocol-version`</a> specifies the application protocol used to route traffic to pods. Only valid when HTTP or HTTPS is used as the backend protocol.
171171

172172
!!!example
173-
```
174-
alb.ingress.kubernetes.io/backend-protocol-version: HTTP2
175-
```
173+
- HTTP2
174+
```
175+
alb.ingress.kubernetes.io/backend-protocol-version: HTTP2
176+
```
177+
- GRPC
178+
```
179+
alb.ingress.kubernetes.io/backend-protocol-version: GRPC
180+
```
176181

177182
- <a name="subnets">`alb.ingress.kubernetes.io/subnets`</a> specifies the [Availability Zone](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html) that ALB will route traffic to. See [Load Balancer subnets](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-subnets.html) for more details.
178183

@@ -521,9 +526,14 @@ Health check on target groups can be controlled with following annotations:
521526
- <a name="healthcheck-path">`alb.ingress.kubernetes.io/healthcheck-path`</a> specifies the HTTP path when performing health check on targets.
522527

523528
!!!example
524-
```
525-
alb.ingress.kubernetes.io/healthcheck-path: /ping
526-
```
529+
- HTTP
530+
```
531+
alb.ingress.kubernetes.io/healthcheck-path: /ping
532+
```
533+
- GRPC
534+
```
535+
alb.ingress.kubernetes.io/healthcheck-path: /package.service/method
536+
```
527537

528538
- <a name="healthcheck-interval-seconds">`alb.ingress.kubernetes.io/healthcheck-interval-seconds`</a> specifies the interval(in seconds) between health check of an individual target.
529539

pkg/ingress/model_build_target_group.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ func (t *defaultModelBuildTask) buildTargetGroupHealthCheckConfig(ctx context.Co
230230
if err != nil {
231231
return elbv2model.TargetGroupHealthCheckConfig{}, err
232232
}
233-
healthCheckPath := t.buildTargetGroupHealthCheckPath(ctx, svcAndIngAnnotations)
233+
healthCheckPath := t.buildTargetGroupHealthCheckPath(ctx, svcAndIngAnnotations, tgProtocolVersion)
234234
healthCheckMatcher := t.buildTargetGroupHealthCheckMatcher(ctx, svcAndIngAnnotations, tgProtocolVersion)
235235
healthCheckIntervalSeconds, err := t.buildTargetGroupHealthCheckIntervalSeconds(ctx, svcAndIngAnnotations)
236236
if err != nil {
@@ -299,14 +299,27 @@ func (t *defaultModelBuildTask) buildTargetGroupHealthCheckProtocol(_ context.Co
299299
}
300300
}
301301

302-
func (t *defaultModelBuildTask) buildTargetGroupHealthCheckPath(_ context.Context, svcAndIngAnnotations map[string]string) string {
303-
rawHealthCheckPath := t.defaultHealthCheckPath
302+
func (t *defaultModelBuildTask) buildTargetGroupHealthCheckPath(_ context.Context, svcAndIngAnnotations map[string]string, tgProtocolVersion elbv2model.ProtocolVersion) string {
303+
var rawHealthCheckPath string
304+
switch tgProtocolVersion {
305+
case elbv2model.ProtocolVersionHTTP1, elbv2model.ProtocolVersionHTTP2:
306+
rawHealthCheckPath = t.defaultHealthCheckPathHTTP
307+
case elbv2model.ProtocolVersionGRPC:
308+
rawHealthCheckPath = t.defaultHealthCheckPathGRPC
309+
}
304310
_ = t.annotationParser.ParseStringAnnotation(annotations.IngressSuffixHealthCheckPath, &rawHealthCheckPath, svcAndIngAnnotations)
305311
return rawHealthCheckPath
306312
}
307313

308314
func (t *defaultModelBuildTask) buildTargetGroupHealthCheckMatcher(_ context.Context, svcAndIngAnnotations map[string]string, tgProtocolVersion elbv2model.ProtocolVersion) elbv2model.HealthCheckMatcher {
309-
rawHealthCheckMatcherHTTPCode := t.defaultHealthCheckMatcherHTTPCode
315+
var rawHealthCheckMatcherHTTPCode string
316+
switch tgProtocolVersion {
317+
case elbv2model.ProtocolVersionHTTP1, elbv2model.ProtocolVersionHTTP2:
318+
rawHealthCheckMatcherHTTPCode = t.defaultHealthCheckMatcherHTTPCode
319+
case elbv2model.ProtocolVersionGRPC:
320+
rawHealthCheckMatcherHTTPCode = t.defaultHealthCheckMatcherGRPCCode
321+
}
322+
310323
_ = t.annotationParser.ParseStringAnnotation(annotations.IngressSuffixSuccessCodes, &rawHealthCheckMatcherHTTPCode, svcAndIngAnnotations)
311324
if tgProtocolVersion == elbv2model.ProtocolVersionGRPC {
312325
return elbv2model.HealthCheckMatcher{

pkg/ingress/model_build_target_group_test.go

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ingress
22

33
import (
44
"context"
5+
awssdk "github.com/aws/aws-sdk-go/aws"
56
"github.com/stretchr/testify/assert"
67
corev1 "k8s.io/api/core/v1"
78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -283,3 +284,229 @@ func Test_defaultModelBuildTask_buildTargetGroupTags(t *testing.T) {
283284
})
284285
}
285286
}
287+
288+
func Test_defaultModelBuildTask_buildTargetGroupHealthCheckPath(t *testing.T) {
289+
type fields struct {
290+
defaultHealthCheckPathHTTP string
291+
defaultHealthCheckPathGRPC string
292+
}
293+
type args struct {
294+
svcAndIngAnnotations map[string]string
295+
tgProtocolVersion elbv2model.ProtocolVersion
296+
}
297+
tests := []struct {
298+
name string
299+
fields fields
300+
args args
301+
want string
302+
}{
303+
{
304+
name: "HTTP1, without annotation configured",
305+
fields: fields{
306+
defaultHealthCheckPathHTTP: "/",
307+
defaultHealthCheckPathGRPC: "/AWS.ALB/healthcheck",
308+
},
309+
args: args{
310+
svcAndIngAnnotations: nil,
311+
tgProtocolVersion: elbv2model.ProtocolVersionHTTP1,
312+
},
313+
want: "/",
314+
},
315+
{
316+
name: "HTTP2, without annotation configured",
317+
fields: fields{
318+
defaultHealthCheckPathHTTP: "/",
319+
defaultHealthCheckPathGRPC: "/AWS.ALB/healthcheck",
320+
},
321+
args: args{
322+
svcAndIngAnnotations: nil,
323+
tgProtocolVersion: elbv2model.ProtocolVersionHTTP2,
324+
},
325+
want: "/",
326+
},
327+
{
328+
name: "GRPC, without annotation configured",
329+
fields: fields{
330+
defaultHealthCheckPathHTTP: "/",
331+
defaultHealthCheckPathGRPC: "/AWS.ALB/healthcheck",
332+
},
333+
args: args{
334+
svcAndIngAnnotations: nil,
335+
tgProtocolVersion: elbv2model.ProtocolVersionGRPC,
336+
},
337+
want: "/AWS.ALB/healthcheck",
338+
},
339+
{
340+
name: "HTTP1, with annotation configured",
341+
fields: fields{
342+
defaultHealthCheckPathHTTP: "/",
343+
defaultHealthCheckPathGRPC: "/AWS.ALB/healthcheck",
344+
},
345+
args: args{
346+
svcAndIngAnnotations: map[string]string{
347+
"alb.ingress.kubernetes.io/healthcheck-path": "/ping",
348+
},
349+
tgProtocolVersion: elbv2model.ProtocolVersionHTTP1,
350+
},
351+
want: "/ping",
352+
},
353+
{
354+
name: "HTTP2, with annotation configured",
355+
fields: fields{
356+
defaultHealthCheckPathHTTP: "/",
357+
defaultHealthCheckPathGRPC: "/AWS.ALB/healthcheck",
358+
},
359+
args: args{
360+
svcAndIngAnnotations: map[string]string{
361+
"alb.ingress.kubernetes.io/healthcheck-path": "/ping",
362+
},
363+
tgProtocolVersion: elbv2model.ProtocolVersionHTTP2,
364+
},
365+
want: "/ping",
366+
},
367+
{
368+
name: "GRPC, with annotation configured",
369+
fields: fields{
370+
defaultHealthCheckPathHTTP: "/",
371+
defaultHealthCheckPathGRPC: "/AWS.ALB/healthcheck",
372+
},
373+
args: args{
374+
svcAndIngAnnotations: map[string]string{
375+
"alb.ingress.kubernetes.io/healthcheck-path": "/package.service/method",
376+
},
377+
tgProtocolVersion: elbv2model.ProtocolVersionGRPC,
378+
},
379+
want: "/package.service/method",
380+
},
381+
}
382+
for _, tt := range tests {
383+
t.Run(tt.name, func(t *testing.T) {
384+
task := &defaultModelBuildTask{
385+
annotationParser: annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io"),
386+
defaultHealthCheckPathHTTP: tt.fields.defaultHealthCheckPathHTTP,
387+
defaultHealthCheckPathGRPC: tt.fields.defaultHealthCheckPathGRPC,
388+
}
389+
got := task.buildTargetGroupHealthCheckPath(context.Background(), tt.args.svcAndIngAnnotations, tt.args.tgProtocolVersion)
390+
assert.Equal(t, tt.want, got)
391+
})
392+
}
393+
}
394+
395+
func Test_defaultModelBuildTask_buildTargetGroupHealthCheckMatcher(t *testing.T) {
396+
type fields struct {
397+
defaultHealthCheckMatcherHTTPCode string
398+
defaultHealthCheckMatcherGRPCCode string
399+
}
400+
type args struct {
401+
svcAndIngAnnotations map[string]string
402+
tgProtocolVersion elbv2model.ProtocolVersion
403+
}
404+
tests := []struct {
405+
name string
406+
fields fields
407+
args args
408+
want elbv2model.HealthCheckMatcher
409+
}{
410+
{
411+
name: "HTTP1, without annotation configured",
412+
fields: fields{
413+
defaultHealthCheckMatcherHTTPCode: "200",
414+
defaultHealthCheckMatcherGRPCCode: "12",
415+
},
416+
args: args{
417+
svcAndIngAnnotations: nil,
418+
tgProtocolVersion: elbv2model.ProtocolVersionHTTP1,
419+
},
420+
want: elbv2model.HealthCheckMatcher{
421+
HTTPCode: awssdk.String("200"),
422+
},
423+
},
424+
{
425+
name: "HTTP2, without annotation configured",
426+
fields: fields{
427+
defaultHealthCheckMatcherHTTPCode: "200",
428+
defaultHealthCheckMatcherGRPCCode: "12",
429+
},
430+
args: args{
431+
svcAndIngAnnotations: nil,
432+
tgProtocolVersion: elbv2model.ProtocolVersionHTTP2,
433+
},
434+
want: elbv2model.HealthCheckMatcher{
435+
HTTPCode: awssdk.String("200"),
436+
},
437+
},
438+
{
439+
name: "GRPC, without annotation configured",
440+
fields: fields{
441+
defaultHealthCheckMatcherHTTPCode: "200",
442+
defaultHealthCheckMatcherGRPCCode: "12",
443+
},
444+
args: args{
445+
svcAndIngAnnotations: nil,
446+
tgProtocolVersion: elbv2model.ProtocolVersionGRPC,
447+
},
448+
want: elbv2model.HealthCheckMatcher{
449+
GRPCCode: awssdk.String("12"),
450+
},
451+
},
452+
{
453+
name: "HTTP1, with annotation configured",
454+
fields: fields{
455+
defaultHealthCheckMatcherHTTPCode: "200",
456+
defaultHealthCheckMatcherGRPCCode: "12",
457+
},
458+
args: args{
459+
svcAndIngAnnotations: map[string]string{
460+
"alb.ingress.kubernetes.io/success-codes": "200-300",
461+
},
462+
tgProtocolVersion: elbv2model.ProtocolVersionHTTP1,
463+
},
464+
want: elbv2model.HealthCheckMatcher{
465+
HTTPCode: awssdk.String("200-300"),
466+
},
467+
},
468+
{
469+
name: "HTTP2, with annotation configured",
470+
fields: fields{
471+
defaultHealthCheckMatcherHTTPCode: "200",
472+
defaultHealthCheckMatcherGRPCCode: "12",
473+
},
474+
args: args{
475+
svcAndIngAnnotations: map[string]string{
476+
"alb.ingress.kubernetes.io/success-codes": "200-300",
477+
},
478+
tgProtocolVersion: elbv2model.ProtocolVersionHTTP2,
479+
},
480+
want: elbv2model.HealthCheckMatcher{
481+
HTTPCode: awssdk.String("200-300"),
482+
},
483+
},
484+
{
485+
name: "GRPC, with annotation configured",
486+
fields: fields{
487+
defaultHealthCheckMatcherHTTPCode: "200",
488+
defaultHealthCheckMatcherGRPCCode: "12",
489+
},
490+
args: args{
491+
svcAndIngAnnotations: map[string]string{
492+
"alb.ingress.kubernetes.io/success-codes": "0",
493+
},
494+
tgProtocolVersion: elbv2model.ProtocolVersionGRPC,
495+
},
496+
want: elbv2model.HealthCheckMatcher{
497+
GRPCCode: awssdk.String("0"),
498+
},
499+
},
500+
}
501+
for _, tt := range tests {
502+
t.Run(tt.name, func(t *testing.T) {
503+
task := &defaultModelBuildTask{
504+
annotationParser: annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io"),
505+
defaultHealthCheckMatcherHTTPCode: tt.fields.defaultHealthCheckMatcherHTTPCode,
506+
defaultHealthCheckMatcherGRPCCode: tt.fields.defaultHealthCheckMatcherGRPCCode,
507+
}
508+
got := task.buildTargetGroupHealthCheckMatcher(context.Background(), tt.args.svcAndIngAnnotations, tt.args.tgProtocolVersion)
509+
assert.Equal(t, tt.want, got)
510+
})
511+
}
512+
}

pkg/ingress/model_builder.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,14 @@ func (b *defaultModelBuilder) Build(ctx context.Context, ingGroup Group) (core.S
103103
defaultTargetType: elbv2model.TargetTypeInstance,
104104
defaultBackendProtocol: elbv2model.ProtocolHTTP,
105105
defaultBackendProtocolVersion: elbv2model.ProtocolVersionHTTP1,
106-
defaultHealthCheckPath: "/",
106+
defaultHealthCheckPathHTTP: "/",
107+
defaultHealthCheckPathGRPC: "/AWS.ALB/healthcheck",
107108
defaultHealthCheckIntervalSeconds: 15,
108109
defaultHealthCheckTimeoutSeconds: 5,
109110
defaultHealthCheckHealthyThresholdCount: 2,
110111
defaultHealthCheckUnhealthyThresholdCount: 2,
111112
defaultHealthCheckMatcherHTTPCode: "200",
113+
defaultHealthCheckMatcherGRPCCode: "12",
112114

113115
loadBalancer: nil,
114116
tgByResID: make(map[string]*elbv2model.TargetGroup),
@@ -144,12 +146,14 @@ type defaultModelBuildTask struct {
144146
defaultTargetType elbv2model.TargetType
145147
defaultBackendProtocol elbv2model.Protocol
146148
defaultBackendProtocolVersion elbv2model.ProtocolVersion
147-
defaultHealthCheckPath string
149+
defaultHealthCheckPathHTTP string
150+
defaultHealthCheckPathGRPC string
148151
defaultHealthCheckTimeoutSeconds int64
149152
defaultHealthCheckIntervalSeconds int64
150153
defaultHealthCheckHealthyThresholdCount int64
151154
defaultHealthCheckUnhealthyThresholdCount int64
152155
defaultHealthCheckMatcherHTTPCode string
156+
defaultHealthCheckMatcherGRPCCode string
153157

154158
loadBalancer *elbv2model.LoadBalancer
155159
managedSG *ec2model.SecurityGroup

0 commit comments

Comments
 (0)