Merge pull request #113307 from andrewsykim/apiserver-identity-hostname

apiserver identity: use persistent names for lease objects
This commit is contained in:
Kubernetes Prow Robot 2022-11-04 07:28:25 -07:00 committed by GitHub
commit c8a3657bde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 9 deletions

View File

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
"os"
"reflect" "reflect"
"strconv" "strconv"
"time" "time"
@ -60,6 +61,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apiserver/pkg/endpoints/discovery" "k8s.io/apiserver/pkg/endpoints/discovery"
apiserverfeatures "k8s.io/apiserver/pkg/features" apiserverfeatures "k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
@ -470,13 +472,18 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
if err != nil { if err != nil {
return err return err
} }
leaseName := m.GenericAPIServer.APIServerID
holderIdentity := m.GenericAPIServer.APIServerID + "_" + string(uuid.NewUUID())
controller := lease.NewController( controller := lease.NewController(
clock.RealClock{}, clock.RealClock{},
kubeClient, kubeClient,
m.GenericAPIServer.APIServerID, holderIdentity,
int32(c.ExtraConfig.IdentityLeaseDurationSeconds), int32(c.ExtraConfig.IdentityLeaseDurationSeconds),
nil, nil,
time.Duration(c.ExtraConfig.IdentityLeaseRenewIntervalSeconds)*time.Second, time.Duration(c.ExtraConfig.IdentityLeaseRenewIntervalSeconds)*time.Second,
leaseName,
metav1.NamespaceSystem, metav1.NamespaceSystem,
labelAPIServerHeartbeat) labelAPIServerHeartbeat)
go controller.Run(hookContext.StopCh) go controller.Run(hookContext.StopCh)
@ -515,6 +522,14 @@ func labelAPIServerHeartbeat(lease *coordinationapiv1.Lease) error {
} }
// This label indicates that kube-apiserver owns this identity lease object // This label indicates that kube-apiserver owns this identity lease object
lease.Labels[IdentityLeaseComponentLabelKey] = KubeAPIServer lease.Labels[IdentityLeaseComponentLabelKey] = KubeAPIServer
hostname, err := os.Hostname()
if err != nil {
return err
}
// convenience label to easily map a lease object to a specific apiserver
lease.Labels[apiv1.LabelHostname] = hostname
return nil return nil
} }

View File

@ -835,6 +835,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
kubeCfg.NodeLeaseDurationSeconds, kubeCfg.NodeLeaseDurationSeconds,
klet.onRepeatedHeartbeatFailure, klet.onRepeatedHeartbeatFailure,
renewInterval, renewInterval,
string(klet.nodeName),
v1.NamespaceNodeLease, v1.NamespaceNodeLease,
util.SetNodeOwnerFunc(klet.heartbeatClient, string(klet.nodeName))) util.SetNodeOwnerFunc(klet.heartbeatClient, string(klet.nodeName)))

View File

@ -19,8 +19,10 @@ package server
import ( import (
"context" "context"
"fmt" "fmt"
"hash/fnv"
"net" "net"
"net/http" "net/http"
"os"
goruntime "runtime" goruntime "runtime"
"runtime/debug" "runtime/debug"
"sort" "sort"
@ -328,7 +330,14 @@ func NewConfig(codecs serializer.CodecFactory) *Config {
defaultHealthChecks := []healthz.HealthChecker{healthz.PingHealthz, healthz.LogHealthz} defaultHealthChecks := []healthz.HealthChecker{healthz.PingHealthz, healthz.LogHealthz}
var id string var id string
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerIdentity) { if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerIdentity) {
id = "kube-apiserver-" + uuid.New().String() hostname, err := os.Hostname()
if err != nil {
klog.Fatalf("error getting hostname for apiserver identity: %v", err)
}
h := fnv.New32a()
h.Write([]byte(hostname))
id = "kube-apiserver-" + fmt.Sprint(h.Sum32())
} }
lifecycleSignals := newLifecycleSignals() lifecycleSignals := newLifecycleSignals()

View File

@ -54,6 +54,7 @@ type controller struct {
client clientset.Interface client clientset.Interface
leaseClient coordclientset.LeaseInterface leaseClient coordclientset.LeaseInterface
holderIdentity string holderIdentity string
leaseName string
leaseNamespace string leaseNamespace string
leaseDurationSeconds int32 leaseDurationSeconds int32
renewInterval time.Duration renewInterval time.Duration
@ -71,7 +72,7 @@ type controller struct {
} }
// NewController constructs and returns a controller // NewController constructs and returns a controller
func NewController(clock clock.Clock, client clientset.Interface, holderIdentity string, leaseDurationSeconds int32, onRepeatedHeartbeatFailure func(), renewInterval time.Duration, leaseNamespace string, newLeasePostProcessFunc ProcessLeaseFunc) Controller { func NewController(clock clock.Clock, client clientset.Interface, holderIdentity string, leaseDurationSeconds int32, onRepeatedHeartbeatFailure func(), renewInterval time.Duration, leaseName, leaseNamespace string, newLeasePostProcessFunc ProcessLeaseFunc) Controller {
var leaseClient coordclientset.LeaseInterface var leaseClient coordclientset.LeaseInterface
if client != nil { if client != nil {
leaseClient = client.CoordinationV1().Leases(leaseNamespace) leaseClient = client.CoordinationV1().Leases(leaseNamespace)
@ -80,6 +81,7 @@ func NewController(clock clock.Clock, client clientset.Interface, holderIdentity
client: client, client: client,
leaseClient: leaseClient, leaseClient: leaseClient,
holderIdentity: holderIdentity, holderIdentity: holderIdentity,
leaseName: leaseName,
leaseNamespace: leaseNamespace, leaseNamespace: leaseNamespace,
leaseDurationSeconds: leaseDurationSeconds, leaseDurationSeconds: leaseDurationSeconds,
renewInterval: renewInterval, renewInterval: renewInterval,
@ -151,7 +153,7 @@ func (c *controller) backoffEnsureLease() (*coordinationv1.Lease, bool) {
// ensureLease creates the lease if it does not exist. Returns the lease and // ensureLease creates the lease if it does not exist. Returns the lease and
// a bool (true if this call created the lease), or any error that occurs. // a bool (true if this call created the lease), or any error that occurs.
func (c *controller) ensureLease() (*coordinationv1.Lease, bool, error) { func (c *controller) ensureLease() (*coordinationv1.Lease, bool, error) {
lease, err := c.leaseClient.Get(context.TODO(), c.holderIdentity, metav1.GetOptions{}) lease, err := c.leaseClient.Get(context.TODO(), c.leaseName, metav1.GetOptions{})
if apierrors.IsNotFound(err) { if apierrors.IsNotFound(err) {
// lease does not exist, create it. // lease does not exist, create it.
leaseToCreate, err := c.newLease(nil) leaseToCreate, err := c.newLease(nil)
@ -208,7 +210,7 @@ func (c *controller) newLease(base *coordinationv1.Lease) (*coordinationv1.Lease
if base == nil { if base == nil {
lease = &coordinationv1.Lease{ lease = &coordinationv1.Lease{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: c.holderIdentity, Name: c.leaseName,
Namespace: c.leaseNamespace, Namespace: c.leaseNamespace,
}, },
Spec: coordinationv1.LeaseSpec{ Spec: coordinationv1.LeaseSpec{

View File

@ -59,6 +59,7 @@ func TestNewNodeLease(t *testing.T) {
desc: "nil base without node", desc: "nil base without node",
controller: &controller{ controller: &controller{
client: fake.NewSimpleClientset(), client: fake.NewSimpleClientset(),
leaseName: node.Name,
holderIdentity: node.Name, holderIdentity: node.Name,
leaseDurationSeconds: 10, leaseDurationSeconds: 10,
clock: fakeClock, clock: fakeClock,
@ -80,6 +81,7 @@ func TestNewNodeLease(t *testing.T) {
desc: "nil base with node", desc: "nil base with node",
controller: &controller{ controller: &controller{
client: fake.NewSimpleClientset(node), client: fake.NewSimpleClientset(node),
leaseName: node.Name,
holderIdentity: node.Name, holderIdentity: node.Name,
leaseDurationSeconds: 10, leaseDurationSeconds: 10,
clock: fakeClock, clock: fakeClock,

View File

@ -18,11 +18,14 @@ package controlplane
import ( import (
"context" "context"
"strings" "fmt"
"hash/fnv"
"os"
"testing" "testing"
"time" "time"
coordinationv1 "k8s.io/api/coordination/v1" coordinationv1 "k8s.io/api/coordination/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
@ -40,6 +43,12 @@ const (
testLeaseName = "apiserver-lease-test" testLeaseName = "apiserver-lease-test"
) )
func expectedAPIServerIdentity(hostname string) string {
h := fnv.New32a()
h.Write([]byte(hostname))
return "kube-apiserver-" + fmt.Sprint(h.Sum32())
}
func TestCreateLeaseOnStart(t *testing.T) { func TestCreateLeaseOnStart(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.APIServerIdentity, true)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.APIServerIdentity, true)()
result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
@ -50,6 +59,11 @@ func TestCreateLeaseOnStart(t *testing.T) {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
hostname, err := os.Hostname()
if err != nil {
t.Fatalf("Unexpected error getting apiserver hostname: %v", err)
}
t.Logf(`Waiting the kube-apiserver Lease to be created`) t.Logf(`Waiting the kube-apiserver Lease to be created`)
if err := wait.PollImmediate(500*time.Millisecond, 10*time.Second, func() (bool, error) { if err := wait.PollImmediate(500*time.Millisecond, 10*time.Second, func() (bool, error) {
leases, err := kubeclient. leases, err := kubeclient.
@ -59,10 +73,25 @@ func TestCreateLeaseOnStart(t *testing.T) {
if err != nil { if err != nil {
return false, err return false, err
} }
if leases != nil && len(leases.Items) == 1 && strings.HasPrefix(leases.Items[0].Name, "kube-apiserver-") {
return true, nil if leases == nil {
return false, nil
} }
return false, nil
if len(leases.Items) != 1 {
return false, nil
}
lease := leases.Items[0]
if lease.Name != expectedAPIServerIdentity(hostname) {
return false, fmt.Errorf("unexpected apiserver identity, got: %v, expected: %v", lease.Name, expectedAPIServerIdentity(hostname))
}
if lease.Labels[corev1.LabelHostname] != hostname {
return false, fmt.Errorf("unexpected hostname label, got: %v, expected: %v", lease.Labels[corev1.LabelHostname], hostname)
}
return true, nil
}); err != nil { }); err != nil {
t.Fatalf("Failed to see the kube-apiserver lease: %v", err) t.Fatalf("Failed to see the kube-apiserver lease: %v", err)
} }