Skip to content

Commit b1d4d40

Browse files
authored
Merge pull request kubernetes#74668 from sttts/sttts-kube-apiserver-endpoints-when-ready
kube-apiserver: don't create endpoints before being ready
2 parents 3b4a9e4 + 2a9a9fa commit b1d4d40

File tree

9 files changed

+63
-25
lines changed

9 files changed

+63
-25
lines changed

pkg/master/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ go_library(
115115
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
116116
"//staging/src/k8s.io/client-go/informers:go_default_library",
117117
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
118+
"//staging/src/k8s.io/client-go/rest:go_default_library",
118119
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
119120
"//vendor/k8s.io/klog:go_default_library",
120121
],

pkg/master/controller.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package master
1919
import (
2020
"fmt"
2121
"net"
22+
"net/http"
2223
"time"
2324

2425
corev1 "k8s.io/api/core/v1"
@@ -31,6 +32,7 @@ import (
3132
genericapiserver "k8s.io/apiserver/pkg/server"
3233
utilfeature "k8s.io/apiserver/pkg/util/feature"
3334
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
35+
"k8s.io/client-go/rest"
3436
"k8s.io/klog"
3537
"k8s.io/kubernetes/pkg/features"
3638
"k8s.io/kubernetes/pkg/master/reconcilers"
@@ -51,6 +53,7 @@ type Controller struct {
5153
ServiceClient corev1client.ServicesGetter
5254
NamespaceClient corev1client.NamespacesGetter
5355
EventClient corev1client.EventsGetter
56+
healthClient rest.Interface
5457

5558
ServiceClusterIPRegistry rangeallocation.RangeRegistry
5659
ServiceClusterIPInterval time.Duration
@@ -80,7 +83,7 @@ type Controller struct {
8083
}
8184

8285
// NewBootstrapController returns a controller for watching the core capabilities of the master
83-
func (c *completedConfig) NewBootstrapController(legacyRESTStorage corerest.LegacyRESTStorage, serviceClient corev1client.ServicesGetter, nsClient corev1client.NamespacesGetter, eventClient corev1client.EventsGetter) *Controller {
86+
func (c *completedConfig) NewBootstrapController(legacyRESTStorage corerest.LegacyRESTStorage, serviceClient corev1client.ServicesGetter, nsClient corev1client.NamespacesGetter, eventClient corev1client.EventsGetter, healthClient rest.Interface) *Controller {
8487
_, publicServicePort, err := c.GenericConfig.SecureServing.HostPort()
8588
if err != nil {
8689
klog.Fatalf("failed to get listener address: %v", err)
@@ -95,6 +98,7 @@ func (c *completedConfig) NewBootstrapController(legacyRESTStorage corerest.Lega
9598
ServiceClient: serviceClient,
9699
NamespaceClient: nsClient,
97100
EventClient: eventClient,
101+
healthClient: healthClient,
98102

99103
EndpointReconciler: c.ExtraConfig.EndpointReconcilerConfig.Reconciler,
100104
EndpointInterval: c.ExtraConfig.EndpointReconcilerConfig.Interval,
@@ -138,6 +142,12 @@ func (c *Controller) Start() {
138142
return
139143
}
140144

145+
// Reconcile during first run removing itself until server is ready.
146+
endpointPorts := createEndpointPortSpec(c.PublicServicePort, "https", c.ExtraEndpointPorts)
147+
if err := c.EndpointReconciler.RemoveEndpoints(kubernetesServiceName, c.PublicIP, endpointPorts); err != nil {
148+
klog.Errorf("Unable to remove old endpoints from kubernetes service: %v", err)
149+
}
150+
141151
repairClusterIPs := servicecontroller.NewRepair(c.ServiceClusterIPInterval, c.ServiceClient, c.EventClient, &c.ServiceClusterIPRange, c.ServiceClusterIPRegistry)
142152
repairNodePorts := portallocatorcontroller.NewRepair(c.ServiceNodePortInterval, c.ServiceClient, c.EventClient, c.ServiceNodePortRange, c.ServiceNodePortRegistry)
143153

@@ -150,10 +160,6 @@ func (c *Controller) Start() {
150160
// If we fail to repair node ports apiserver is useless. We should restart and retry.
151161
klog.Fatalf("Unable to perform initial service nodePort check: %v", err)
152162
}
153-
// Service definition is reconciled during first run to correct port and type per expectations.
154-
if err := c.UpdateKubernetesService(true); err != nil {
155-
klog.Errorf("Unable to perform initial Kubernetes service initialization: %v", err)
156-
}
157163

158164
c.runner = async.NewRunner(c.RunKubernetesNamespaces, c.RunKubernetesService, repairClusterIPs.RunUntil, repairNodePorts.RunUntil)
159165
c.runner.Start()
@@ -168,7 +174,8 @@ func (c *Controller) Stop() {
168174
go func() {
169175
defer close(finishedReconciling)
170176
klog.Infof("Shutting down kubernetes service endpoint reconciler")
171-
if err := c.EndpointReconciler.StopReconciling(kubernetesServiceName, c.PublicIP, endpointPorts); err != nil {
177+
c.EndpointReconciler.StopReconciling()
178+
if err := c.EndpointReconciler.RemoveEndpoints(kubernetesServiceName, c.PublicIP, endpointPorts); err != nil {
172179
klog.Error(err)
173180
}
174181
}()
@@ -178,7 +185,7 @@ func (c *Controller) Stop() {
178185
// done
179186
case <-time.After(2 * c.EndpointInterval):
180187
// don't block server shutdown forever if we can't reach etcd to remove ourselves
181-
klog.Warning("StopReconciling() timed out")
188+
klog.Warning("RemoveEndpoints() timed out")
182189
}
183190
}
184191

@@ -196,7 +203,14 @@ func (c *Controller) RunKubernetesNamespaces(ch chan struct{}) {
196203

197204
// RunKubernetesService periodically updates the kubernetes service
198205
func (c *Controller) RunKubernetesService(ch chan struct{}) {
199-
wait.Until(func() {
206+
// wait until process is ready
207+
wait.PollImmediateUntil(100*time.Millisecond, func() (bool, error) {
208+
var code int
209+
c.healthClient.Get().AbsPath("/healthz").Do().StatusCode(&code)
210+
return code == http.StatusOK, nil
211+
}, ch)
212+
213+
wait.NonSlidingUntil(func() {
200214
// Service definition is not reconciled after first
201215
// run, ports and type will be corrected only during
202216
// start.

pkg/master/master.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ func (m *Master) InstallLegacyAPI(c *completedConfig, restOptionsGetter generic.
375375

376376
controllerName := "bootstrap-controller"
377377
coreClient := corev1client.NewForConfigOrDie(c.GenericConfig.LoopbackClientConfig)
378-
bootstrapController := c.NewBootstrapController(legacyRESTStorage, coreClient, coreClient, coreClient)
378+
bootstrapController := c.NewBootstrapController(legacyRESTStorage, coreClient, coreClient, coreClient, coreClient.RESTClient())
379379
m.GenericAPIServer.AddPostStartHookOrDie(controllerName, bootstrapController.PostStartHook)
380380
m.GenericAPIServer.AddPreShutdownHookOrDie(controllerName, bootstrapController.PreShutdownHook)
381381

pkg/master/reconcilers/lease.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -283,14 +283,16 @@ func checkEndpointSubsetFormatWithLease(e *corev1.Endpoints, expectedIPs []strin
283283
return true, ipsCorrect, portsCorrect
284284
}
285285

286-
func (r *leaseEndpointReconciler) StopReconciling(serviceName string, ip net.IP, endpointPorts []corev1.EndpointPort) error {
287-
r.reconcilingLock.Lock()
288-
defer r.reconcilingLock.Unlock()
289-
r.stopReconcilingCalled = true
290-
286+
func (r *leaseEndpointReconciler) RemoveEndpoints(serviceName string, ip net.IP, endpointPorts []corev1.EndpointPort) error {
291287
if err := r.masterLeases.RemoveLease(ip.String()); err != nil {
292288
return err
293289
}
294290

295291
return r.doReconcile(serviceName, endpointPorts, true)
296292
}
293+
294+
func (r *leaseEndpointReconciler) StopReconciling() {
295+
r.reconcilingLock.Lock()
296+
defer r.reconcilingLock.Unlock()
297+
r.stopReconcilingCalled = true
298+
}

pkg/master/reconcilers/lease_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ func TestLeaseEndpointReconciler(t *testing.T) {
547547
}
548548
}
549549

550-
func TestLeaseStopReconciling(t *testing.T) {
550+
func TestLeaseRemoveEndpoints(t *testing.T) {
551551
ns := corev1.NamespaceDefault
552552
om := func(name string) metav1.ObjectMeta {
553553
return metav1.ObjectMeta{Namespace: ns, Name: name}
@@ -627,7 +627,7 @@ func TestLeaseStopReconciling(t *testing.T) {
627627
}
628628
}
629629
r := NewLeaseEndpointReconciler(clientset.CoreV1(), fakeLeases)
630-
err := r.StopReconciling(test.serviceName, net.ParseIP(test.ip), test.endpointPorts)
630+
err := r.RemoveEndpoints(test.serviceName, net.ParseIP(test.ip), test.endpointPorts)
631631
if err != nil {
632632
t.Errorf("case %q: unexpected error: %v", test.testName, err)
633633
}

pkg/master/reconcilers/mastercount.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,9 @@ func (r *masterCountEndpointReconciler) ReconcileEndpoints(serviceName string, i
137137
return err
138138
}
139139

140-
func (r *masterCountEndpointReconciler) StopReconciling(serviceName string, ip net.IP, endpointPorts []corev1.EndpointPort) error {
140+
func (r *masterCountEndpointReconciler) RemoveEndpoints(serviceName string, ip net.IP, endpointPorts []corev1.EndpointPort) error {
141141
r.reconcilingLock.Lock()
142142
defer r.reconcilingLock.Unlock()
143-
r.stopReconcilingCalled = true
144143

145144
e, err := r.endpointClient.Endpoints(metav1.NamespaceDefault).Get(serviceName, metav1.GetOptions{})
146145
if err != nil {
@@ -167,6 +166,12 @@ func (r *masterCountEndpointReconciler) StopReconciling(serviceName string, ip n
167166
return err
168167
}
169168

169+
func (r *masterCountEndpointReconciler) StopReconciling() {
170+
r.reconcilingLock.Lock()
171+
defer r.reconcilingLock.Unlock()
172+
r.stopReconcilingCalled = true
173+
}
174+
170175
// Determine if the endpoint is in the format ReconcileEndpoints expects.
171176
//
172177
// Return values:

pkg/master/reconcilers/none.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ limitations under the License.
1818
package reconcilers
1919

2020
import (
21-
corev1 "k8s.io/api/core/v1"
2221
"net"
22+
23+
corev1 "k8s.io/api/core/v1"
2324
)
2425

2526
// NoneEndpointReconciler allows for the endpoint reconciler to be disabled
@@ -36,7 +37,10 @@ func (r *noneEndpointReconciler) ReconcileEndpoints(serviceName string, ip net.I
3637
return nil
3738
}
3839

39-
// StopReconciling noop reconcile
40-
func (r *noneEndpointReconciler) StopReconciling(serviceName string, ip net.IP, endpointPorts []corev1.EndpointPort) error {
40+
// RemoveEndpoints noop reconcile
41+
func (r *noneEndpointReconciler) RemoveEndpoints(serviceName string, ip net.IP, endpointPorts []corev1.EndpointPort) error {
4142
return nil
4243
}
44+
45+
func (r *noneEndpointReconciler) StopReconciling() {
46+
}

pkg/master/reconcilers/reconcilers.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ limitations under the License.
1818
package reconcilers
1919

2020
import (
21-
corev1 "k8s.io/api/core/v1"
2221
"net"
22+
23+
corev1 "k8s.io/api/core/v1"
2324
)
2425

2526
// EndpointReconciler knows how to reconcile the endpoints for the apiserver service.
@@ -35,7 +36,10 @@ type EndpointReconciler interface {
3536
// endpoints for their {rw, ro} services.
3637
// * ReconcileEndpoints is called periodically from all apiservers.
3738
ReconcileEndpoints(serviceName string, ip net.IP, endpointPorts []corev1.EndpointPort, reconcilePorts bool) error
38-
StopReconciling(serviceName string, ip net.IP, endpointPorts []corev1.EndpointPort) error
39+
// RemoveEndpoints removes this apiserver's lease.
40+
RemoveEndpoints(serviceName string, ip net.IP, endpointPorts []corev1.EndpointPort) error
41+
// StopReconciling turns any later ReconcileEndpoints call into a noop.
42+
StopReconciling()
3943
}
4044

4145
// Type the reconciler type

test/integration/master/synthetic_master_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,16 @@ func TestKubernetesService(t *testing.T) {
103103
_, _, closeFn := framework.RunAMaster(config)
104104
defer closeFn()
105105
coreClient := clientset.NewForConfigOrDie(config.GenericConfig.LoopbackClientConfig)
106-
if _, err := coreClient.Core().Services(metav1.NamespaceDefault).Get("kubernetes", metav1.GetOptions{}); err != nil {
107-
t.Fatalf("Expected kubernetes service to exists, got: %v", err)
106+
err := wait.PollImmediate(time.Millisecond*100, wait.ForeverTestTimeout, func() (bool, error) {
107+
if _, err := coreClient.Core().Services(metav1.NamespaceDefault).Get("kubernetes", metav1.GetOptions{}); err != nil && errors.IsNotFound(err) {
108+
return false, nil
109+
} else if err != nil {
110+
return false, err
111+
}
112+
return true, nil
113+
})
114+
if err != nil {
115+
t.Fatalf("Expected kubernetes service to exist, got: %v", err)
108116
}
109117
}
110118

0 commit comments

Comments
 (0)