diff --git a/docs/proposals/20210722-yurtcluster-operator.md b/docs/proposals/20210722-yurtcluster-operator.md index 8b8c9d7b7a8..d3ac777f994 100644 --- a/docs/proposals/20210722-yurtcluster-operator.md +++ b/docs/proposals/20210722-yurtcluster-operator.md @@ -3,20 +3,21 @@ title: YurtCluster Operator authors: - "@gnunu" - "@lindayu17" + - "@SataQiu" reviewers: - "@rambohe-ch" - "@guofei" - "@kadisi" - "@wenjun93" creation-date: 2021-07-22 -last-updated: 2021-07-22 +last-updated: 2021-08-18 status: provisional --- # Convert Vanilla Kubernetes cluster to OpenYurt cluster through Operator ## Table of Contents -* [Convert Vanilla Kubernetes cluster to OpenYurt cluster through Opertaor](#convert-vanilla-kubernetes-cluster-to-openyurt-cluster-through-operator) +* [Convert Vanilla Kubernetes cluster to OpenYurt cluster through Operator](#convert-vanilla-kubernetes-cluster-to-openyurt-cluster-through-operator) * [Table of Contents](#table-of-contents) * [Glossary](#glossary) * [Summary](#summary) @@ -63,7 +64,8 @@ To make OpenYurt translation easy for user, i.e., as automative as possible. ### Non-Goals/Future Work -This is NOT a replacement of yurtctl command. +This is NOT a replacement of yurtctl command, but the convert and revert functions of yurtctl will be deprecated in the future. +And we recommend that you do the node conversion based on the declarative API of YurtCluster Operator. ## Proposal @@ -72,43 +74,180 @@ This is an operator, with CRD and Controller. CRD's definition: ```go +import ( + corev1 "k8s.io/api/core/v1" +) + +// ImageMeta allows to customize the image used for components that are not +// originated from the openyurtio/openyurt release process +type ImageMeta struct { + // Repository sets the container registry to pull images from. + // if not set, the ImageRepository defined in YurtClusterSpec will be used instead. + // +optional + Repository string `json:"repository,omitempty"` + // Tag allows to specify a tag for the image. + // if not set, the tag releated to the YurtVersion defined in YurtClusterSpec will be used instead. + // +optional + Tag string `json:"tag,omitempty"` +} + +// ComponentConfig defines the common config for the yurt components type ComponentConfig struct { - Enabled bool `json:"enebled,omitempty"` - Version string `json:"version,omitempty"` + // Enabled indicates whether the yurt component has been enabled + // +optional + Enabled *bool `json:"enabled,omitempty"` + // ImageMeta allows to customize the image used for the yurt component + // +optional + ImageMeta `json:",inline"` +} + +// YurtHubSpec defines the configuration for yurthub component +type YurtHubSpec struct { + // ComponentConfig defines the common config for the yurt components + // +optional + ComponentConfig `json:",inline"` + // EnableResourceFilter enables to filter response that comes back from reverse proxy + // +optional + EnableResourceFilter *bool `json:"enableResourceFilter,omitempty"` + // AccessServerThroughHub enables pods access kube-apiserver through yurthub or not + // +optional + AccessServerThroughHub *bool `json:"accessServerThroughHub,omitempty"` + // AutoRestartNodePod represents whether to automatically restart the pod on the node after it was converted + // This will trigger the re-injection of environment variables (e.g. KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT) + // This should only be set when EnableResourceFilter is true. + // +optional + AutoRestartNodePod *bool `json:"autoRestartNodePod,omitempty"` + // PodManifestPath defines the path to the directory on edge node containing static pod files. + // +optional + PodManifestPath string `json:"podManifestPath,omitempty"` + // KubeadmConfPath defines the path to kubelet service conf that is used by kubelet component to join the cluster on the edge node. + // +optional + KubeadmConfPath string `json:"kubeadmConfPath,omitempty"` } -type NodeSpec struct { - Names []string `json:"names,omitempty"` - Pattern string `json:"pattern,omitempty"` - ExcludedNames []string `json:"excludedNames,omitempty"` - ExcludedPattern string `json:"excludedPattern,omitempty"` +// YurtTunnelSpec defines the configuration for yurt tunnel component +type YurtTunnelSpec struct { + // ComponentConfig defines the common config for the yurt components + // +optional + ComponentConfig `json:",inline"` + // ServerCount defines the replicas for the tunnel server Pod. + // Its value should be greater than or equal to the number of API Server. + // Operator will automatically override this value if it is less than the number of API Server. + // +optional + ServerCount int `json:"serverCount,omitempty"` + // PublicIP defines the public IP for tunnel server listen on. + // If this field is empty, the tunnel agent will use NodePort Service to connect to the tunnel server. + // +optional + PublicIP string `json:"publicIP,omitempty"` + // PublicPort defines the public port for tunnel server listen on. + // +optional + PublicPort int `json:"publicPort,omitempty"` } -type FailedNode struct { - Name string `json:"name,omitempty"` - Info string `json:"info,omitempty"` +// NodeSet defines a set of Kubernetes nodes. +// It will merge the nodes that selected by Names, NamePattern, and Selector, +// and then remove the nodes that match ExcludedNames and ExcludedNamePattern as the final set of nodes. +type NodeSet struct { + // Names defines the node names to be selected + // +optional + Names []string `json:"names,omitempty"` + // NamePattern defines the regular expression to select nodes based on node name + // +optional + NamePattern string `json:"namePattern,omitempty"` + // Selector defines the label selector to select nodes + // +optional + Selector *corev1.NodeSelector `json:"selector,omitempty"` + // ExcludedNames defines the node names to be excluded + // +optional + ExcludedNames []string `json:"excludedNames,omitempty"` + // ExcludedNamePattern defines the regular expression to exclude nodes based on node name + // +optional + ExcludedNamePattern string `json:"excludedNamePattern,omitempty"` } // YurtClusterSpec defines the desired state of YurtCluster type YurtClusterSpec struct { - CloudNodes NodeSpec `json:"cloudNodes,omitempty"` - EdgeNodes NodeSpec `json:"edgeNodes,omitempty"` - YurtHub ComponentConfig `json:"yurtHub,omitempty"` - YurtControllerManager ComponentConfig `json:"yurtControllerManager,omitempty"` - TunnelService ComponentConfig `json:"yurtTunnel,omitempty"` - AppManager ComponentConfig `json:"appManger,omitempty"` + // ImageRepository sets the container registry to pull images from. + // If empty, `docker.io/openyurt` will be used by default + // +optional + ImageRepository string `json:"imageRepository,omitempty"` + // YurtVersion is the target version of OpneYurt. + // +optional + YurtVersion string `json:"yurtVersion,omitempty"` + // CloudNodes defines the node set with cloud role. + // +optional + CloudNodes NodeSet `json:"cloudNodes,omitempty"` + // EdgeNodes defines the node set with edge role. + // +optional + EdgeNodes NodeSet `json:"edgeNodes,omitempty"` + // YurtHubSpec defines the configuration for yurthub component. + // +optional + YurtHubSpec YurtHubSpec `json:"yurtHubSpec,omitempty"` + // YurtTunnelSpec defines the configuration for yurt tunnel component. + // +optional + YurtTunnelSpec YurtTunnelSpec `json:"yurtTunnelSpec,omitempty"` +} + +// Phase is a string representation of a YurtCluster Phase. +type Phase string + +const ( + // PhaseInvalid is the state when the YurtCluster is invalid + PhaseInvalid = Phase("Invalid") + // PhaseConverting is the state when the YurtCluster is converting + PhaseConverting = Phase("Converting") + // PhaseDeleting is the state when the YurtCluster is deleting + PhaseDeleting = Phase("Deleting") + // PhaseSucceed is the state when the YurtCluster is ready + PhaseSucceed = Phase("Succeed") +) + +// YurtClusterStatusFailure defines errors states for YurtCluster objects. +type YurtClusterStatusFailure string + +const ( + // InvalidConfigurationYurtClusterError indicates a state that must be fixed before progress can be made. + InvalidConfigurationYurtClusterError YurtClusterStatusFailure = "InvalidConfiguration" +) + +// NodeCondition describes the state of a node at a certain point +type NodeCondition struct { + // The status for the condition's last transition. + // +optional + Status string `json:"status,omitempty"` + // The reason for the condition's last transition. + // +optional + Reason string `json:"reason,omitempty"` + // A human readable message indicating details about the transition. + // +optional + Message string `json:"message,omitempty"` + // The last time this condition was updated. + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + // The generation observed by the node agent controller. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` } // YurtClusterStatus defines the observed state of YurtCluster type YurtClusterStatus struct { - ConvertedCloudNodes []string `json:"convertedCloudNodes,omitempty"` - ConvertedEdgeNodes []string `json:"convertedEdgeNodes,omitempty"` - YurtHub ComponentConfig `json:"yurtHub,omitempty"` - YurtControllerManager ComponentConfig `json:"yurtControllerManager,omitempty"` - YurtTunnel ComponentConfig `json:"yurtTunnel,omitempty"` - AppManager ComponentConfig `json:"appManger,omitempty"` - ExcludedNodes []string `json:"excludedNodes,omitempty"` - FailedNodes []FailedNode `json:"failedNodes,omitempty"` + // Phase represents the current phase of the yurt cluster + // +optional + Phase Phase `json:"phase,omitempty"` + // FailureReason indicates that there is a problem reconciling the state, and + // will be set to a token value suitable for programmatic interpretation. + // +optional + FailureReason *YurtClusterStatusFailure `json:"failureReason,omitempty"` + // FailureMessage indicates that there is a fatal problem reconciling the + // state, and will be set to a descriptive error message. + // +optional + FailureMessage *string `json:"failureMessage,omitempty"` + // NodeConditions holds the info about node conditions + // +optional + NodeConditions map[string]NodeCondition `json:"nodeConditions,omitempty"` + // The generation observed by the operator controller. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` } ``` @@ -143,7 +282,7 @@ The Operator would do these things: ##### FR1 Analyze requests: -Through the YurtCluster CR, the controller would figure out what nodes to convert, what nodes to revert, and if enabling addons like YurtTunel and AppManger. +Through the YurtCluster CR, the controller would figure out what nodes to convert, what nodes to revert, and if enabling addons like YurtHub. Th nodes to be converted or reverted can be specified using Regular expressions, the controller would process the nodes set, including those coming in the future. ##### FR2 @@ -169,7 +308,7 @@ This operator should keep cluster serects safe. The procedure of coverting/reverting a node will refer to yurtctl's implementation. -Wheteher or not use helm is to be discovered. +Whether or not use helm is to be discovered. ### Risks and Mitigations @@ -183,7 +322,7 @@ manual: user should convert/revert nodes manually. ## Upgrade Strategy -The Operator supports OpenYurt components upgrade, such as YuntTunnel and AppManager. +The Operator supports OpenYurt components upgrade, such as YuntHub. For Operator itself, upgrade would have no side effects, idempotent semantics would be kept. @@ -199,3 +338,4 @@ test Operator upgrade ## Implementation History [x] 03/27/2021: Proposed idea in an issue (https://github.com/openyurtio/openyurt/issues/328). +[x] 08/18/2021: Refine and update the YurtCluster Spec definition.