@@ -187,9 +187,25 @@ const ServiceAnnotationLoadBalancerBEProtocol = "service.beta.kubernetes.io/aws-
187
187
// For example: "Key1=Val1,Key2=Val2,KeyNoVal1=,KeyNoVal2"
188
188
const ServiceAnnotationLoadBalancerAdditionalTags = "service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags"
189
189
190
+ // ServiceAnnotationLoadBalancerHealthCheckProtocol is the annotation used on the service to
191
+ // specify the protocol used for the ELB health check. Supported values are TCP, HTTP, HTTPS
192
+ // Default is TCP if externalTrafficPolicy is Cluster, HTTP if externalTrafficPolicy is Local
193
+ const ServiceAnnotationLoadBalancerHealthCheckProtocol = "service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol"
194
+
195
+ // ServiceAnnotationLoadBalancerHealthCheckPort is the annotation used on the service to
196
+ // specify the port used for ELB health check.
197
+ // Default is traffic-port if externalTrafficPolicy is Cluster, healthCheckNodePort if externalTrafficPolicy is Local
198
+ const ServiceAnnotationLoadBalancerHealthCheckPort = "service.beta.kubernetes.io/aws-load-balancer-healthcheck-port"
199
+
200
+ // ServiceAnnotationLoadBalancerHealthCheckPath is the annotation used on the service to
201
+ // specify the path for the ELB health check when the health check protocol is HTTP/HTTPS
202
+ // Defaults to /healthz if externalTrafficPolicy is Local, / otherwise
203
+ const ServiceAnnotationLoadBalancerHealthCheckPath = "service.beta.kubernetes.io/aws-load-balancer-healthcheck-path"
204
+
190
205
// ServiceAnnotationLoadBalancerHCHealthyThreshold is the annotation used on
191
206
// the service to specify the number of successive successful health checks
192
- // required for a backend to be considered healthy for traffic.
207
+ // required for a backend to be considered healthy for traffic. For NLB, healthy-threshold
208
+ // and unhealthy-threshold must be equal.
193
209
const ServiceAnnotationLoadBalancerHCHealthyThreshold = "service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold"
194
210
195
211
// ServiceAnnotationLoadBalancerHCUnhealthyThreshold is the annotation used
@@ -3686,6 +3702,91 @@ func (c *Cloud) getSubnetCidrs(subnetIDs []string) ([]string, error) {
3686
3702
return cidrs , nil
3687
3703
}
3688
3704
3705
+ func parseStringAnnotation (annotations map [string ]string , annotation string , value * string ) bool {
3706
+ if v , ok := annotations [annotation ]; ok {
3707
+ * value = v
3708
+ return true
3709
+ }
3710
+ return false
3711
+ }
3712
+
3713
+ func parseInt64Annotation (annotations map [string ]string , annotation string , value * int64 ) (bool , error ) {
3714
+ if v , ok := annotations [annotation ]; ok {
3715
+ parsed , err := strconv .ParseInt (v , 10 , 0 )
3716
+ if err != nil {
3717
+ return true , fmt .Errorf ("failed to parse annotation %v=%v" , annotation , v )
3718
+ }
3719
+ * value = parsed
3720
+ return true , nil
3721
+ }
3722
+ return false , nil
3723
+ }
3724
+
3725
+ func (c * Cloud ) buildNLBHealthCheckConfiguration (svc * v1.Service ) (healthCheckConfig , error ) {
3726
+ hc := healthCheckConfig {
3727
+ Port : defaultHealthCheckPort ,
3728
+ Path : defaultHealthCheckPath ,
3729
+ Protocol : elbv2 .ProtocolEnumTcp ,
3730
+ Interval : defaultNlbHealthCheckInterval ,
3731
+ Timeout : defaultNlbHealthCheckTimeout ,
3732
+ HealthyThreshold : defaultNlbHealthCheckThreshold ,
3733
+ UnhealthyThreshold : defaultNlbHealthCheckThreshold ,
3734
+ }
3735
+ if svc .Spec .ExternalTrafficPolicy == v1 .ServiceExternalTrafficPolicyTypeLocal {
3736
+ path , port := servicehelpers .GetServiceHealthCheckPathPort (svc )
3737
+ hc = healthCheckConfig {
3738
+ Port : strconv .Itoa (int (port )),
3739
+ Path : path ,
3740
+ Protocol : elbv2 .ProtocolEnumHttp ,
3741
+ Interval : 10 ,
3742
+ Timeout : 10 ,
3743
+ HealthyThreshold : 2 ,
3744
+ UnhealthyThreshold : 2 ,
3745
+ }
3746
+ }
3747
+ if parseStringAnnotation (svc .Annotations , ServiceAnnotationLoadBalancerHealthCheckProtocol , & hc .Protocol ) {
3748
+ hc .Protocol = strings .ToUpper (hc .Protocol )
3749
+ }
3750
+ switch hc .Protocol {
3751
+ case elbv2 .ProtocolEnumHttp , elbv2 .ProtocolEnumHttps :
3752
+ parseStringAnnotation (svc .Annotations , ServiceAnnotationLoadBalancerHealthCheckPath , & hc .Path )
3753
+ case elbv2 .ProtocolEnumTcp :
3754
+ hc .Path = ""
3755
+ default :
3756
+ return healthCheckConfig {}, fmt .Errorf ("Unsupported health check protocol %v" , hc .Protocol )
3757
+ }
3758
+
3759
+ parseStringAnnotation (svc .Annotations , ServiceAnnotationLoadBalancerHealthCheckPort , & hc .Port )
3760
+
3761
+ if _ , err := parseInt64Annotation (svc .Annotations , ServiceAnnotationLoadBalancerHCInterval , & hc .Interval ); err != nil {
3762
+ return healthCheckConfig {}, err
3763
+ }
3764
+ if _ , err := parseInt64Annotation (svc .Annotations , ServiceAnnotationLoadBalancerHCTimeout , & hc .Timeout ); err != nil {
3765
+ return healthCheckConfig {}, err
3766
+ }
3767
+ if _ , err := parseInt64Annotation (svc .Annotations , ServiceAnnotationLoadBalancerHCHealthyThreshold , & hc .HealthyThreshold ); err != nil {
3768
+ return healthCheckConfig {}, err
3769
+ }
3770
+ if _ , err := parseInt64Annotation (svc .Annotations , ServiceAnnotationLoadBalancerHCUnhealthyThreshold , & hc .UnhealthyThreshold ); err != nil {
3771
+ return healthCheckConfig {}, err
3772
+ }
3773
+
3774
+ if hc .HealthyThreshold != hc .UnhealthyThreshold {
3775
+ return healthCheckConfig {}, fmt .Errorf ("Health check healthy threshold and unhealthy threshold must be equal" )
3776
+ }
3777
+
3778
+ if hc .Interval != 10 && hc .Interval != 30 {
3779
+ return healthCheckConfig {}, fmt .Errorf ("Invalid health check interval '%v', must be either 10 or 30" , hc .Interval )
3780
+ }
3781
+
3782
+ if hc .Port != defaultHealthCheckPort {
3783
+ if _ , err := strconv .ParseInt (hc .Port , 10 , 0 ); err != nil {
3784
+ return healthCheckConfig {}, fmt .Errorf ("Invalid health check port '%v'" , hc .Port )
3785
+ }
3786
+ }
3787
+ return hc , nil
3788
+ }
3789
+
3689
3790
// EnsureLoadBalancer implements LoadBalancer.EnsureLoadBalancer
3690
3791
func (c * Cloud ) EnsureLoadBalancer (ctx context.Context , clusterName string , apiService * v1.Service , nodes []* v1.Node ) (* v1.LoadBalancerStatus , error ) {
3691
3792
annotations := apiService .Annotations
@@ -3724,11 +3825,10 @@ func (c *Cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, apiS
3724
3825
FrontendProtocol : string (port .Protocol ),
3725
3826
TrafficPort : int64 (port .NodePort ),
3726
3827
TrafficProtocol : string (port .Protocol ),
3727
-
3728
- // if externalTrafficPolicy == "Local", we'll override the
3729
- // health check later
3730
- HealthCheckPort : int64 (port .NodePort ),
3731
- HealthCheckProtocol : elbv2 .ProtocolEnumTcp ,
3828
+ }
3829
+ var err error
3830
+ if portMapping .HealthCheckConfig , err = c .buildNLBHealthCheckConfiguration (apiService ); err != nil {
3831
+ return nil , err
3732
3832
}
3733
3833
3734
3834
certificateARN := annotations [ServiceAnnotationLoadBalancerCertificate ]
@@ -3776,15 +3876,6 @@ func (c *Cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, apiS
3776
3876
}
3777
3877
3778
3878
if isNLB (annotations ) {
3779
-
3780
- if path , healthCheckNodePort := servicehelpers .GetServiceHealthCheckPathPort (apiService ); path != "" {
3781
- for i := range v2Mappings {
3782
- v2Mappings [i ].HealthCheckPort = int64 (healthCheckNodePort )
3783
- v2Mappings [i ].HealthCheckPath = path
3784
- v2Mappings [i ].HealthCheckProtocol = elbv2 .ProtocolEnumHttp
3785
- }
3786
- }
3787
-
3788
3879
// Find the subnets that the ELB will live in
3789
3880
subnetIDs , err := c .findELBSubnets (internalELB )
3790
3881
if err != nil {
@@ -4041,23 +4132,26 @@ func (c *Cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, apiS
4041
4132
}
4042
4133
}
4043
4134
4135
+ // We only configure a TCP health-check on the first port
4136
+ var tcpHealthCheckPort int32
4137
+ for _ , listener := range listeners {
4138
+ if listener .InstancePort == nil {
4139
+ continue
4140
+ }
4141
+ tcpHealthCheckPort = int32 (* listener .InstancePort )
4142
+ break
4143
+ }
4044
4144
if path , healthCheckNodePort := servicehelpers .GetServiceHealthCheckPathPort (apiService ); path != "" {
4045
4145
klog .V (4 ).Infof ("service %v (%v) needs health checks on :%d%s)" , apiService .Name , loadBalancerName , healthCheckNodePort , path )
4146
+ if annotations [ServiceAnnotationLoadBalancerHealthCheckPort ] == defaultHealthCheckPort {
4147
+ healthCheckNodePort = tcpHealthCheckPort
4148
+ }
4046
4149
err = c .ensureLoadBalancerHealthCheck (loadBalancer , "HTTP" , healthCheckNodePort , path , annotations )
4047
4150
if err != nil {
4048
4151
return nil , fmt .Errorf ("Failed to ensure health check for localized service %v on node port %v: %q" , loadBalancerName , healthCheckNodePort , err )
4049
4152
}
4050
4153
} else {
4051
4154
klog .V (4 ).Infof ("service %v does not need custom health checks" , apiService .Name )
4052
- // We only configure a TCP health-check on the first port
4053
- var tcpHealthCheckPort int32
4054
- for _ , listener := range listeners {
4055
- if listener .InstancePort == nil {
4056
- continue
4057
- }
4058
- tcpHealthCheckPort = int32 (* listener .InstancePort )
4059
- break
4060
- }
4061
4155
annotationProtocol := strings .ToLower (annotations [ServiceAnnotationLoadBalancerBEProtocol ])
4062
4156
var hcProtocol string
4063
4157
if annotationProtocol == "https" || annotationProtocol == "ssl" {
0 commit comments