Skip to content

Commit

Permalink
refactor: incorporate second feedback
Browse files Browse the repository at this point in the history
Signed-off-by: Nico Kratky <[email protected]>
  • Loading branch information
nicokratky committed Jan 18, 2024
1 parent c97085d commit c7eddbe
Show file tree
Hide file tree
Showing 15 changed files with 1,034 additions and 1,125 deletions.
12 changes: 6 additions & 6 deletions chapters/background.tex
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ \chapter{Background}
\section{Elasticity in Cloud Computing}
\label{sec:elasticity}

Elasticity is one of the core concepts that solves a big problem of cloud computing: providing limited resources for potentially unlimited use. The solution is to scale workloads up and down as needed, to claim resources when bigger load is experienced and release resources when they are not needed, therefore making them available to other workloads.
Elasticity is one of the core concepts that solves a big problem of cloud computing: providing limited resources for potentially unlimited use. The solution is to scale workloads up and down as needed, to claim resources when bigger load is experienced and release resources when they are not needed, therefore making them available to other workloads and saving costs.

The term elasticity in computing is conceptually similiar to the term in physics. Dating back to \citeyear{hookLecturesPotentiaRestitutiva1678}, \citefirstlastauthor{hookLecturesPotentiaRestitutiva1678} formulated a first definition of elasticity: \textit{``Ut tensio sic vis''} which literally translates to ``As extension, so force'' \cite{hookLecturesPotentiaRestitutiva1678}. This law results in the fact that when a elastic material experiences an external force and is therefore deformed, it also experiences internal forces that restore the material to its original shape when the external force is no longer present.
The term elasticity in computing is conceptually similiar to the term in physics. Dating back to \citeyear{hookLecturesPotentiaRestitutiva1678}, \citefirstlastauthor{hookLecturesPotentiaRestitutiva1678} formulated a first definition of elasticity: \textit{``Ut tensio sic vis''} which literally translates to ``As extension, so force'' \cite{hookLecturesPotentiaRestitutiva1678}. This law results in the fact that when an elastic material experiences an external force and is therefore deformed, it also experiences internal forces that restore the material to its original shape when the external force is no longer present.

The formula - which takes a more mathematical approach - of elasticity can be defined as \[ e(Y, X) = \diff{Y}{X} \frac{X}{Y}, \] where \(e(Y, X)\) is the elasticity of \(Y\) with respect to \(X\) \cite{dustdarPrinciplesElasticProcesses2011}.

Expand All @@ -21,7 +21,7 @@ \section{Elasticity in Cloud Computing}
\label{fig:elasticity-application-no-scaling}
\end{figure}

If the concept of elasticity is applied to this example, resources can be released during the night (so called \textit{scale-in}) and more resources can be claimed as they are needed during the day (so called \textit{scale-out}). This is illustrated in \Cref{fig:elasticity-application-scaling}.
If the concept of elasticity is applied to this example, resources can be released during the night (so called \textit{scale-in} or \textit{scale-down}) and more resources can be claimed as they are needed during the day (so called \textit{scale-out} or \textit{scale-up}). This is illustrated in \Cref{fig:elasticity-application-scaling}.

\begin{figure}
\centering
Expand All @@ -38,7 +38,7 @@ \subsection{Resource Elasticity}

What makes this definition easily mistaken, is that it solely considers the aquired resources and not the consequently incurred costs or changing quality.

Prime examples for this elasticity dimension are horizontal and vertical scaling. Horizontal scaling adds and removes machines. Vertical scaling adapts resources of these machines, e.g. CPU and memory.
Prime examples for this elasticity dimension are horizontal and vertical scaling. Horizontal scaling adds and removes instances. Vertical scaling adapts resources of these instances, e.g. CPU and memory.

\subsection{Cost Elasticity}

Expand Down Expand Up @@ -67,9 +67,9 @@ \section{Polaris SLO Framework}

Another unique feature of Polaris is its object model, which allows for orchestrator independence. This is achieved by encapsulating all data that is transmitted to the orchestrator into a \texttt{ApiObject} type. This type acts as an entrypoint for the transformation service that the framework provides. Instances of classes of the Polaris SLO framework are transformed into plain JavaScript objects, which are then serialized into the required data format by an orchestrator-specific connector library. Every orchestrator uses different abstractions, therefore transforming objects into these abstractions is a necessary step to reach orchestrator independence. Currently, a transformation service for Kubernetes is provided by the authors of the Polaris SLO framework. This services handles the transformation to Kubernetes CRDs. Kubernetes Custom Resource Definitions (CRDs) are definitions of Custom Resources. A Custom Resource is some kind of data that does not match any preexisting object kinds within Kubernetes. These Custom Resources are necessary to allow Kubernetes to be as flexible as it is. Typically, they are used to encapsulate data in order to store and retrieve it through the Kubernetes API.

Decoupling SLOs from elasticity strategies is also a feature that Polaris provides. Tight coupling is a charactaristic that is even observed in industry standard scaling mechanisms such as Kubernetes' Horizontal Pod Autoscaler\footnote{\raggedright\url{https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/}}. This autoscaler provides a CPU usage SLO which can only trigger horizontal elasticity, thus adding or removing machines. To achieve this decoupling, Polaris utilizes an architecture that is depicted in \Cref{fig:polaris-architecture}. The metrics controller provides raw and, as it is the case in this thesis, composed metrics, which are defined by the user. These metrics are then used by the SLO controller to compare the current state of the target against the desired state. If the SLO is violated, the specified elasticity strategy tries to bring the target back to SLO adherence. These single components are tied together using a SLO mapping, which specifies which SLO is used for which target component and which elasticity strategy shall be executed in the case of violation.
Decoupling SLOs from elasticity strategies is also a feature that Polaris provides. Tight coupling is a charactaristic that is even observed in industry standard scaling mechanisms such as Kubernetes' Horizontal Pod Autoscaler\footnote{\raggedright\url{https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/}}. This autoscaler provides a CPU usage SLO which can only trigger horizontal elasticity, thus adding or removing machines. To achieve this decoupling, Polaris utilizes an architecture that is depicted in \Cref{fig:polaris-architecture}. The metrics controller provides raw and, as it is the case in this thesis, composed metrics, which are defined by the user. These metrics are then used by the SLO controller to compare the current state of the target against the desired state. If the SLO is violated, the specified elasticity strategy tries to bring the target back to SLO adherence. These single components are tied together using an SLO mapping, which specifies which SLO is used for which target component and which elasticity strategy shall be executed in the case of violation.

To ensure compability between a SLO and an elasticity strategy they must both accept the same output and input type, respectively. This type is called \texttt{SloOutput}. The framework provides a generic class \texttt{SloCompliance} which simply expresses the current percentage of conformance of the SLO. This allows for compatibility of as many SLOs and elasticity strategies as possible.
To ensure compability between an SLO and an elasticity strategy they must both accept the same output and input type, respectively. This type is called \texttt{SloOutput}. The framework provides a generic class \texttt{SloCompliance} which simply expresses the current percentage of conformance of the SLO. This allows for compatibility of as many SLOs and elasticity strategies as possible.

\begin{figure}
\centering
Expand Down
22 changes: 11 additions & 11 deletions chapters/components.tex
Original file line number Diff line number Diff line change
Expand Up @@ -165,38 +165,38 @@ \subsection{Compliance Types}

\subsection{API Object}

To enable the Polaris SLO framework to interact with the K8ssandra CRD subtype of \texttt{ApiObject} was used. \texttt{ApiObject} is used for any object that should be added, read, changed or deleted by Polaris using the orchestrator's API.
To enable the Polaris SLO framework to interact with the K8ssandra CRD, a subtype of \texttt{ApiObject} was used. \texttt{ApiObject} is used for any object that should be added, read, changed or deleted by Polaris using the orchestrator's API.

Because of this use of a subtype the framework is also able to automatically transform fields. Kubernetes for example uses two separate fields for resources, requests and limits. Polaris on the other hand simply uses ``resources'' as orchestrator details are abstracted. This conversion from requests and limits to resources is handled by the Polaris SLO framework through annotating the respective fields with \texttt{PolarisType}, which is a TypeScript decorator. This annotation is necessary as the type of any given class is not available during runtime when using TypeScript.

\section{Elasticity Strategies}

The elasticity strategy controllers perform the actions that are required to scale the workload. All elasticity strategy controllers must implement the interface \texttt{Elasticity\-Strategy\-Controller} which requires the implementation of four methods: \texttt{check\-If\-Action\-Needed}, \texttt{execute}, \texttt{on\-Elasticity\-Strategy\-Deleted} and \texttt{on\-Destroy}, with the latter two being optional.

These elasticity strategy controllers are called with the appropriate \texttt{SloOutput} during the SLO control loop \cite{pusztaiNovelMiddlewareEfficiently2021a}.
These elasticity strategy controllers are called with the appropriate \texttt{SloOutput} by the Polaris framework \cite{pusztaiNovelMiddlewareEfficiently2021a}.

As part of this thesis, three elasticity strategies for K8ssandra have been implemented. One each for vertical and horizontal elasticity and one that combines these two into a diagonal elasticity strategy.

\subsection{Vertical Elasticity Strategy}
\label{sec:vertical-elasticity}

The vertical elasticity strategy controller is a subtype of \texttt{Elasticity\-Strategy\-Controller}, which is the interface that all controllers responsible for a elasticity strategy must implement. It expects \texttt{K8ssandra\-Vertical\-Slo\-Compliance}, as described in \Cref{sec:compliance-types}, as input. The controller uses the CPU and memory compliance value to scale the current resources accordingly. If the current CPU and memory compliance is the given tolerance range, no scaling is performed by the elasticity strategy controller.
The vertical elasticity strategy controller is a subtype of \texttt{Elasticity\-Strategy\-Controller}, which is the interface that all controllers responsible for a elasticity strategy must implement. It expects \texttt{K8ssandra\-Vertical\-Slo\-Compliance}, as described in \Cref{sec:compliance-types}, as input. The controller uses the CPU and memory compliance value to scale the current resources accordingly. The method of updating these values is the same that the diagonal elasticity strategy uses, which is listed in \Cref{lst:diagonal-elasticity:updateResources}. If the current CPU and memory compliance is the given tolerance range, no scaling is performed by the elasticity strategy controller.

\subsection{Horizontal Elasticity Strategy}
\label{sec:horizontal-elasticity}

The horizontal elasticity strategy controller is able to use the \texttt{Slo\-Compliance\-E\-las\-tic\-i\-ty\-Strategy\-Controller\-Base} as its supertype as it expects \texttt{Slo\-Compliance} as input. This controller base is one of the provided common superclasses. Different superclasses exist for different use cases. The superclass targeted at horizontal elasticity strategies could not be used, as it is already too specific as it expects the target to have a \texttt{scale} subresource, which a \texttt{K8ssandraCluster} CRD does not have. This also reduces the amount of boilerplate code and therefore also complies with the ``Don't repeat yourself'' (DRY) principle. Again, the elasticity strategy controller performs a scaling action if the compliance is out of range of the set tolerance.
The horizontal elasticity strategy controller is able to use the \texttt{Slo\-Compliance\-E\-las\-tic\-i\-ty\-Strategy\-Controller\-Base} as its supertype as it expects \texttt{Slo\-Compliance} as input. This controller base is one of the provided common superclasses. Different superclasses exist for different use cases. Deriving from one of these superclasses reduces the amount of boilerplate code and therefore also complies with the ``Don't repeat yourself'' (DRY) principle. The superclass targeted at horizontal elasticity strategies could not be used, as it is already too specific as it expects the target to have a \texttt{scale} subresource, which a \texttt{K8ssandraCluster} CRD does not have. Again, the elasticity strategy controller performs a scaling action if the compliance is out of range of the set tolerance.

The here implemented version of horizontal scaling \textit{only} performs scale-out. The reason for this is that for scaling-in databases, special considerations have to be made. This is especially true for storage. When, for example, reducing the node count in a Cassandra cluster from 3 to 2, the amount of stored data stays the same, therefore it is possible that the stored data per node increases. This, however, was considered out of scope of this thesis.
The here implemented version of horizontal scaling \textit{only} performs scale-out. The reason for this is that for scaling-in databases, special considerations have to be made. This is especially true for storage. When, for example, reducing the node count in a Cassandra cluster from three to two, the amount of stored data stays the same, therefore it is possible that the stored data per node increases. This, however, was considered out of scope of this thesis.

\subsection{Diagonal Elasticity Strategy}
\label{sec:diagonal-elasticity}

The third and last elasticity strategy controller combines the controller described in \Cref{sec:vertical-elasticity,sec:horizontal-elasticity}.
The third and last elasticity strategy controller combines the controllers described in \Cref{sec:vertical-elasticity,sec:horizontal-elasticity}.

Again, because this controller expects a different input than \texttt{SloCompliance}, \texttt{K8\-ssan\-dra\-Slo\-Compliance} it is not possible to use any of the provided controller bases. Therefore a custom controller base that expects this input has been implemented. The diagonal elasticity controller is a subtype of this newly created controller base.

Two different elasticity dimensions are combined. The elasticity strategy controller is able to autonomously decide which scaling action is best to take. This is possible because of the data that is encapsulated in the \texttt{K8\-ssan\-dra\-Slo\-Compliance}. If the SLO controller determined a violation regarding resource utilization, the elasticity strategy controller will adapt these resources to comply with the target values. On the other hand, if the SLO controller discovers that the K8ssandra cluster experiences significant write load and the SLO is therefore violated the elasticity strategy controller will perform a horizontal scale-out. This will result in a new node to be added to the K8ssandra cluster which subsequently increases the possible throughput. These two processes are listed in \Cref{lst:diagonal-elasticity:updateResources,lst:diagonal-elasticity:updateSize}.
Two different elasticity dimensions are combined. The elasticity strategy controller is able to autonomously decide which scaling action is best to take. This is possible because of the data that is encapsulated in the \texttt{K8\-ssan\-dra\-Slo\-Compliance}. If the SLO controller determined a violation regarding resource utilization, the elasticity strategy controller will adapt these resources to comply with the target values. The calculation of the adaption is shown between \cref{lst:line:beginScalePercentCalculation} and \cref{lst:line:endScalePercentCalculation} in \Cref{lst:diagonal-elasticity:updateResources}. On the other hand, if the SLO controller discovers that the K8ssandra cluster experiences significant write load and the SLO is therefore violated the elasticity strategy controller will perform a horizontal scale-out, which can be seen between \cref{lst:line:beginUpdateSize} and \cref{lst:line:endUpdateSize} in \Cref{lst:diagonal-elasticity:updateSize}. This will result in a new node to be added to the K8ssandra cluster which subsequently increases the possible throughput. These two processes are listed in \Cref{lst:diagonal-elasticity:updateResources,lst:diagonal-elasticity:updateSize}.


\begin{lstlisting}[caption={Method of diagonal elasticity strategy controller which manages vertical scaling},
Expand All @@ -223,13 +223,13 @@ \subsection{Diagonal Elasticity Strategy}
let memoryScalePercent = 1;
let cpuScalePercent = 1;

if (Math.abs(memoryComplianceDiff) > tolerance) {
(*\label{lst:line:beginScalePercentCalculation}*)if (Math.abs(memoryComplianceDiff) > tolerance) {
memoryScalePercent = (100 - memoryComplianceDiff) / 100;
}

if (Math.abs(cpuComplianceDiff) > tolerance) {
cpuScalePercent = (100 - cpuComplianceDiff) / 100;
}
}(*\label{lst:line:endScalePercentCalculation}*)

const resources = k8c.spec.cassandra.resources;

Expand Down Expand Up @@ -267,12 +267,12 @@ \subsection{Diagonal Elasticity Strategy}

const tolerance = this.getTolerance(sloOutputParams);

if (horizontalComplianceDiff > tolerance) {
(*\label{lst:line:beginUpdateSize}*)if (horizontalComplianceDiff > tolerance) {
Logger.log('Triggering horizontal scale up');
newSize = newSize + 1;
} else {
Logger.log('Not triggering horizontal scale up');
}
}(*\label{lst:line:endUpdateSize}*)

Logger.log('Setting size', newSize);
k8c.spec.cassandra.datacenters[0].size = newSize;
Expand Down
Loading

0 comments on commit c7eddbe

Please sign in to comment.