kubeadm: Add configurable control plane up timeout

Until now the control plane timeout was fixed to 4 minutes and users did not
have the ability to change it. This commit allows that timeout to be configured
via the new `timeoutForControlPlane` option in the API server config (itself a
member of the ClusterConfiguration).

The default timeout is still 4 minutes.

Signed-off-by: Rostislav M. Georgiev <rostislavg@vmware.com>
This commit is contained in:
Rostislav M. Georgiev 2018-10-29 18:50:44 +02:00
parent c0d248ad3a
commit eb6f7b1f17
16 changed files with 57 additions and 9 deletions

View File

@ -50,6 +50,11 @@ func fuzzInitConfiguration(obj *kubeadm.InitConfiguration, c fuzz.Continue) {
// More specifically: // More specifically:
// internal with manually applied defaults -> external object : loosing ClusterConfiguration) -> internal object with automatically applied defaults // internal with manually applied defaults -> external object : loosing ClusterConfiguration) -> internal object with automatically applied defaults
obj.ClusterConfiguration = kubeadm.ClusterConfiguration{ obj.ClusterConfiguration = kubeadm.ClusterConfiguration{
APIServer: kubeadm.APIServer{
TimeoutForControlPlane: &metav1.Duration{
Duration: constants.DefaultControlPlaneTimeout,
},
},
AuditPolicyConfiguration: kubeadm.AuditPolicyConfiguration{ AuditPolicyConfiguration: kubeadm.AuditPolicyConfiguration{
LogDir: constants.StaticPodAuditPolicyLogDir, LogDir: constants.StaticPodAuditPolicyLogDir,
LogMaxAge: &v1beta1.DefaultAuditPolicyLogMaxAge, LogMaxAge: &v1beta1.DefaultAuditPolicyLogMaxAge,
@ -97,6 +102,9 @@ func fuzzClusterConfiguration(obj *kubeadm.ClusterConfiguration, c fuzz.Continue
obj.ClusterName = "bar" obj.ClusterName = "bar"
obj.ImageRepository = "baz" obj.ImageRepository = "baz"
obj.KubernetesVersion = "qux" obj.KubernetesVersion = "qux"
obj.APIServer.TimeoutForControlPlane = &metav1.Duration{
Duration: constants.DefaultControlPlaneTimeout,
}
} }
func fuzzAuditPolicyConfiguration(obj *kubeadm.AuditPolicyConfiguration, c fuzz.Continue) { func fuzzAuditPolicyConfiguration(obj *kubeadm.AuditPolicyConfiguration, c fuzz.Continue) {

View File

@ -130,6 +130,9 @@ type APIServer struct {
// CertSANs sets extra Subject Alternative Names for the API Server signing cert. // CertSANs sets extra Subject Alternative Names for the API Server signing cert.
CertSANs []string CertSANs []string
// TimeoutForControlPlane controls the timeout that we use for API server to appear
TimeoutForControlPlane *metav1.Duration
} }
// ComponentConfigs holds known internal ComponentConfig types for other components // ComponentConfigs holds known internal ComponentConfig types for other components

View File

@ -17,8 +17,10 @@ limitations under the License.
package v1alpha3 package v1alpha3
import ( import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/conversion"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
) )
func Convert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in *JoinConfiguration, out *kubeadm.JoinConfiguration, s conversion.Scope) error { func Convert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in *JoinConfiguration, out *kubeadm.JoinConfiguration, s conversion.Scope) error {
@ -84,6 +86,9 @@ func Convert_v1alpha3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in *C
out.APIServer.ExtraArgs = in.APIServerExtraArgs out.APIServer.ExtraArgs = in.APIServerExtraArgs
out.APIServer.CertSANs = in.APIServerCertSANs out.APIServer.CertSANs = in.APIServerCertSANs
out.APIServer.TimeoutForControlPlane = &metav1.Duration{
Duration: constants.DefaultControlPlaneTimeout,
}
if err := convertSlice_v1alpha3_HostPathMount_To_kubeadm_HostPathMount(&in.APIServerExtraVolumes, &out.APIServer.ExtraVolumes, s); err != nil { if err := convertSlice_v1alpha3_HostPathMount_To_kubeadm_HostPathMount(&in.APIServerExtraVolumes, &out.APIServer.ExtraVolumes, s); err != nil {
return err return err
} }

View File

@ -101,6 +101,16 @@ func SetDefaults_ClusterConfiguration(obj *ClusterConfiguration) {
SetDefaults_Etcd(obj) SetDefaults_Etcd(obj)
SetDefaults_AuditPolicyConfiguration(obj) SetDefaults_AuditPolicyConfiguration(obj)
SetDefaults_APIServer(&obj.APIServer)
}
// SetDefaults_APIServer assigns default values for the API Server
func SetDefaults_APIServer(obj *APIServer) {
if obj.TimeoutForControlPlane == nil {
obj.TimeoutForControlPlane = &metav1.Duration{
Duration: constants.DefaultControlPlaneTimeout,
}
}
} }
// SetDefaults_Etcd assigns default values for the Proxy // SetDefaults_Etcd assigns default values for the Proxy

View File

@ -212,6 +212,7 @@ limitations under the License.
// certSANs: // certSANs:
// - "10.100.1.1" // - "10.100.1.1"
// - "ec2-10-100-0-1.compute-1.amazonaws.com" // - "ec2-10-100-0-1.compute-1.amazonaws.com"
// timeoutForControlPlane: 4m0s
// controllerManager: // controllerManager:
// extraArgs: // extraArgs:
// node-cidr-mask-size: 20 // node-cidr-mask-size: 20

View File

@ -120,6 +120,9 @@ type APIServer struct {
// CertSANs sets extra Subject Alternative Names for the API Server signing cert. // CertSANs sets extra Subject Alternative Names for the API Server signing cert.
CertSANs []string `json:"certSANs,omitempty"` CertSANs []string `json:"certSANs,omitempty"`
// TimeoutForControlPlane controls the timeout that we use for API server to appear
TimeoutForControlPlane *metav1.Duration `json:"timeoutForControlPlane,omitempty"`
} }
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@ -257,6 +257,7 @@ func autoConvert_v1beta1_APIServer_To_kubeadm_APIServer(in *APIServer, out *kube
return err return err
} }
out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs))
out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane))
return nil return nil
} }
@ -270,6 +271,7 @@ func autoConvert_kubeadm_APIServer_To_v1beta1_APIServer(in *kubeadm.APIServer, o
return err return err
} }
out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs))
out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane))
return nil return nil
} }

View File

@ -51,6 +51,11 @@ func (in *APIServer) DeepCopyInto(out *APIServer) {
*out = make([]string, len(*in)) *out = make([]string, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.TimeoutForControlPlane != nil {
in, out := &in.TimeoutForControlPlane, &out.TimeoutForControlPlane
*out = new(v1.Duration)
**out = **in
}
return return
} }

View File

@ -37,6 +37,7 @@ func RegisterDefaults(scheme *runtime.Scheme) error {
func SetObjectDefaults_ClusterConfiguration(in *ClusterConfiguration) { func SetObjectDefaults_ClusterConfiguration(in *ClusterConfiguration) {
SetDefaults_ClusterConfiguration(in) SetDefaults_ClusterConfiguration(in)
SetDefaults_APIServer(&in.APIServer)
} }
func SetObjectDefaults_ClusterStatus(in *ClusterStatus) { func SetObjectDefaults_ClusterStatus(in *ClusterStatus) {

View File

@ -53,6 +53,11 @@ func (in *APIServer) DeepCopyInto(out *APIServer) {
*out = make([]string, len(*in)) *out = make([]string, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.TimeoutForControlPlane != nil {
in, out := &in.TimeoutForControlPlane, &out.TimeoutForControlPlane
*out = new(v1.Duration)
**out = **in
}
return return
} }

View File

@ -469,8 +469,10 @@ func runInit(i *initData, out io.Writer) error {
if err != nil { if err != nil {
return errors.Wrap(err, "failed to create client") return errors.Wrap(err, "failed to create client")
} }
// TODO: NewControlPlaneWaiter should be converted to private after the self-hosting phase is removed. // TODO: NewControlPlaneWaiter should be converted to private after the self-hosting phase is removed.
waiter, err := phases.NewControlPlaneWaiter(i.dryRun, client, i.outputWriter) timeout := i.cfg.ClusterConfiguration.APIServer.TimeoutForControlPlane.Duration
waiter, err := phases.NewControlPlaneWaiter(i.dryRun, timeout, client, i.outputWriter)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to create waiter") return errors.Wrap(err, "failed to create waiter")
} }

View File

@ -93,12 +93,13 @@ func runWaitControlPlanePhase(c workflow.RunData) error {
return errors.Wrap(err, "cannot obtain client") return errors.Wrap(err, "cannot obtain client")
} }
waiter, err := NewControlPlaneWaiter(data.DryRun(), client, data.OutputWriter()) timeout := data.Cfg().ClusterConfiguration.APIServer.TimeoutForControlPlane.Duration
waiter, err := NewControlPlaneWaiter(data.DryRun(), timeout, client, data.OutputWriter())
if err != nil { if err != nil {
return errors.Wrap(err, "error creating waiter") return errors.Wrap(err, "error creating waiter")
} }
fmt.Printf("[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory %q\n", data.ManifestDir()) fmt.Printf("[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory %q. This can take up to %v\n", data.ManifestDir(), timeout)
if err := waiter.WaitForKubeletAndFunc(waiter.WaitForAPI); err != nil { if err := waiter.WaitForKubeletAndFunc(waiter.WaitForAPI); err != nil {
ctx := map[string]string{ ctx := map[string]string{
@ -143,14 +144,10 @@ func printFilesIfDryRunning(data waitControlPlaneData) error {
// NewControlPlaneWaiter returns a new waiter that is used to wait on the control plane to boot up. // NewControlPlaneWaiter returns a new waiter that is used to wait on the control plane to boot up.
// TODO: make private (lowercase) after self-hosting phase is removed. // TODO: make private (lowercase) after self-hosting phase is removed.
func NewControlPlaneWaiter(dryRun bool, client clientset.Interface, out io.Writer) (apiclient.Waiter, error) { func NewControlPlaneWaiter(dryRun bool, timeout time.Duration, client clientset.Interface, out io.Writer) (apiclient.Waiter, error) {
if dryRun { if dryRun {
return dryrunutil.NewWaiter(), nil return dryrunutil.NewWaiter(), nil
} }
// We know that the images should be cached locally already as we have pulled them using
// crictl in the preflight checks. Hence we can have a pretty short timeout for the kubelet
// to start creating Static Pods.
timeout := 4 * time.Minute
return apiclient.NewKubeWaiter(client, timeout, out), nil return apiclient.NewKubeWaiter(client, timeout, out), nil
} }

View File

@ -178,6 +178,9 @@ const (
// TLSBootstrapTimeout specifies how long kubeadm should wait for the kubelet to perform the TLS Bootstrap // TLSBootstrapTimeout specifies how long kubeadm should wait for the kubelet to perform the TLS Bootstrap
TLSBootstrapTimeout = 2 * time.Minute TLSBootstrapTimeout = 2 * time.Minute
// DefaultControlPlaneTimeout specifies the default control plane (actually API Server) timeout for use by kubeadm
DefaultControlPlaneTimeout = 4 * time.Minute
// MinimumAddressesInServiceSubnet defines minimum amount of nodes the Service subnet should allow. // MinimumAddressesInServiceSubnet defines minimum amount of nodes the Service subnet should allow.
// We need at least ten, because the DNS service is always at the tenth cluster clusterIP // We need at least ten, because the DNS service is always at the tenth cluster clusterIP
MinimumAddressesInServiceSubnet = 10 MinimumAddressesInServiceSubnet = 10

View File

@ -16,6 +16,7 @@ APIServer:
Name: WritableVolume Name: WritableVolume
PathType: "" PathType: ""
ReadOnly: false ReadOnly: false
TimeoutForControlPlane: 4m0s
AuditPolicyConfiguration: AuditPolicyConfiguration:
LogDir: /var/log/kubernetes/audit LogDir: /var/log/kubernetes/audit
LogMaxAge: 2 LogMaxAge: 2

View File

@ -29,6 +29,7 @@ apiServer:
- hostPath: /host/writable - hostPath: /host/writable
mountPath: /mount/writable mountPath: /mount/writable
name: WritableVolume name: WritableVolume
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta1 apiVersion: kubeadm.k8s.io/v1beta1
auditPolicy: auditPolicy:
logDir: /var/log/kubernetes/audit logDir: /var/log/kubernetes/audit

View File

@ -18,7 +18,8 @@ nodeRegistration:
- effect: NoSchedule - effect: NoSchedule
key: node-role.kubernetes.io/master key: node-role.kubernetes.io/master
--- ---
apiServer: {} apiServer:
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta1 apiVersion: kubeadm.k8s.io/v1beta1
auditPolicy: auditPolicy:
logDir: /var/log/kubernetes/audit logDir: /var/log/kubernetes/audit