Use Stateless Deployments with Oracle Cloud Native Environment
Introduction
There are two primary approaches to managing applications on Kubernetes - StatefulSets and Deployments. StatefulSets, which require persistent storage, manage stateful workloads such as a MySQL deployment on Kubernetes. Deployments provide a way to manage stateless applications such as websites, which often do not need to maintain their internal state.
A DaemonSets is another workload management type available to a Kubernetes controller. Daemonsets ensure a replica of a Pod will always run on all or some of the cluster's Nodes, and you'll typically use it for cluster storage daemons, logging agents, or monitoring systems.
Objectives
n this tutorial, you'll learn how to:
- Deploy and manage stateless applications
- Perform a rolling update and rollback
Prerequisites
- Installation of Oracle Cloud Native Environment
- a single control and worker node
Deploy Oracle Cloud Native Environment
Note: If running in your own tenancy, read the linux-virt-labs
GitHub project README.md and complete the prerequisites before deploying the lab environment.
Open a terminal on the Luna Desktop.
Clone the
linux-virt-labs
GitHub project.git clone https://github.com/oracle-devrel/linux-virt-labs.git
Change into the working directory.
cd linux-virt-labs/ocne2
Install the required collections.
ansible-galaxy collection install -r requirements.yml
Deploy the lab environment.
ansible-playbook create_instance.yml -e localhost_python_interpreter="/usr/bin/python3.6" -e install_ocne_rpm=true -e create_ocne_cluster=true -e "ocne_cluster_node_options='-n 1 -w 1'"
The free lab environment requires the extra variable
local_python_interpreter
, which setsansible_python_interpreter
for plays running on localhost. This variable is needed because the environment installs the RPM package for the Oracle Cloud Infrastructure SDK for Python, located under the python3.6 modules.The default deployment shape uses the AMD CPU and Oracle Linux 8. To use an Intel CPU or Oracle Linux 9, add
-e instance_shape="VM.Standard3.Flex"
or-e os_version="9"
to the deployment command.Important: Wait for the playbook to run successfully and reach the pause task. At this stage of the playbook, the installation of Oracle Cloud Native Environment is complete, and the instances are ready. Take note of the previous play, which prints the public and private IP addresses of the nodes it deploys and any other deployment information needed while running the lab.
Confirm the Number of Nodes
It helps to know the number and names of nodes in your Kubernetes cluster.
Open a terminal and connect via SSH to the ocne instance.
ssh oracle@<ip_address_of_node>
List the nodes in the cluster.
kubectl get nodes
The output shows the control plane and worker nodes in a Ready state along with their current Kubernetes version.
Create a Deployment
Kubernetes uses Deployment and ReplicaSets API objects to manage the Pods used as part of a stateless application. These API objects allow administrators to control your applications' configuration, rollout, and rollback. They also allow you to automate the rollout of Pod updates without incurring downtime. Using a Deployment provides more resilience for your application than scheduling individual Pods on a cluster because Kubernetes handles the orchestration task and will allocate each Pod automatically to an available Node. Additionally, Kubernetes ensures the 'desired' number of Pods are active by rescheduling them if a Node fails. Conversely, Pods can't be directly scaled or replicated automatically across your cluster's Nodes.
While it is possible to run individual Pods, this approach does not scale. For example, it does not scale if the workload increases. Neither does it provide failure tolerance if, for example, a Node fails (because individual Pods don't reschedule) if this occurs. Using a Kubernetes Deployment provides both application scalability and failure tolerance. A Deployment allows Kubernetes to perform actions such as rolling updates or scaling the number of Pods used (Replicas).
Create a Deployment.
kubectl create deployment mytest --image=ghcr.io/oracle/oraclelinux8-nginx:1.20 --replicas=6
Get a list of Deployments.
kubectl get deployments
Example Output:
NAME READY UP-TO-DATE AVAILABLE AGE mytest 6/6 6 6 9s
Note: If the output shows the Deployment partially complete, re-query a few more times. Eventually, the READY and AVAILABLE columns will confirm that the replicas are running.
Where the columns displayed represent the following information:
READY
: The number of replicas ready compared to the desired. This value corresponds to the spec.replicas value.UP-TO-DATE
: The number of replicas updated to meet the desired state.AVAILABLE
: The number of replicas available.
Show the Deployments, Pods, and ReplicaSets.
Deployments control the creation and deletion of the Pod objects they manage using an associated ReplicaSet object. The purpose of the ReplicaSet is to ensure the number of replica Pods requested is maintained. The ReplicaSet uses the Deployment name as its prefix and a hash value as the suffix to identify the Pods it manages. So, a Deployment uses both Pods and ReplicaSets to control a Deployment.
kubectl get deployments,pods,replicasets
Include the Namespace (
-n <your-namespace>
) if you want to return details of a specific namespace.Example Output:
NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/mytest 6/6 6 6 3m17s NAME READY STATUS RESTARTS AGE pod/mytest-76688bc4c4-2b5jd 1/1 Running 0 3m17s pod/mytest-76688bc4c4-68gtg 1/1 Running 0 3m17s pod/mytest-76688bc4c4-7w9jq 1/1 Running 0 3m17s pod/mytest-76688bc4c4-8stwt 1/1 Running 0 3m17s pod/mytest-76688bc4c4-cbfdz 1/1 Running 0 3m17s pod/mytest-76688bc4c4-v4nm6 1/1 Running 0 3m17s NAME DESIRED CURRENT READY AGE replicaset.apps/mytest-76688bc4c4 6 6 6 3m17s
List the Pods In a Deployment
Next, you will learn how to view the details of a specific Deployment. In a production environment, the kubectl get pods
command usually returns many other Pods. You could filter by namespace using the -n <namespace>
option, which will also likely return other Pods. Is there a way to list the Pods for a specific deployment? Yes, but you need to confirm the label created by Kubernetes to manage the Deployment.
Query the Deployment details.
This query helps by showing the configuration applied by Kubernetes.
kubectl describe deployment mytest
Example Output:
Name: mytest Namespace: default CreationTimestamp: Fri, 26 Apr 2024 16:06:13 +0000 Labels: app=mytest Annotations: deployment.kubernetes.io/revision: 1 Selector: app=mytest Replicas: 6 desired | 6 updated | 6 total | 6 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=mytest Containers: oraclelinux8-nginx: Image: ghcr.io/oracle/oraclelinux8-nginx:1.20 Port: <none> Host Port: <none> Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: <none> NewReplicaSet: mytest-76688bc4c4 (6/6 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 5m7s deployment-controller Scaled up replica set mytest-76688bc4c4 to 6
Take note of the Labels: and Selector: fields listed in the output. In this example, they are:
Labels: app=mytest
Selector: app=mytest
Create another deployment to prove this works.
kubectl create deployment apache --image docker.io/httpd --replicas=3
List the Pods deployed.
kubectl get pods
Example Output:
NAME READY STATUS RESTARTS AGE apache-f9489c7dc-7rplz 1/1 Running 0 9s apache-f9489c7dc-x6fr6 1/1 Running 0 9s apache-f9489c7dc-zxgm8 1/1 Running 0 9s mytest-76688bc4c4-2b5jd 1/1 Running 0 8m51s mytest-76688bc4c4-68gtg 1/1 Running 0 8m51s mytest-76688bc4c4-7w9jq 1/1 Running 0 8m51s mytest-76688bc4c4-8stwt 1/1 Running 0 8m51s mytest-76688bc4c4-cbfdz 1/1 Running 0 8m51s mytest-76688bc4c4-v4nm6 1/1 Running 0 8m51s
If the STATUS column shows the Deployment as ContainerCreating, don't worry; it is normal. Re-query a few times. The STATUS should change to Running.
Using the label, query the Pods for the mytest Deployment.
kubectl get pods -l=app=mytest
Example Output:
NAME READY STATUS RESTARTS AGE mytest-76688bc4c4-2b5jd 1/1 Running 0 11m mytest-76688bc4c4-68gtg 1/1 Running 0 11m mytest-76688bc4c4-7w9jq 1/1 Running 0 11m mytest-76688bc4c4-8stwt 1/1 Running 0 11m mytest-76688bc4c4-cbfdz 1/1 Running 0 11m mytest-76688bc4c4-v4nm6 1/1 Running 0 11m
Re-query the Pods using the selector value.
kubectl get pods --selector=app=mytest
Example Output:
NAME READY STATUS RESTARTS AGE mytest-76688bc4c4-2b5jd 1/1 Running 0 12m mytest-76688bc4c4-68gtg 1/1 Running 0 12m mytest-76688bc4c4-7w9jq 1/1 Running 0 12m mytest-76688bc4c4-8stwt 1/1 Running 0 12m mytest-76688bc4c4-cbfdz 1/1 Running 0 12m mytest-76688bc4c4-v4nm6 1/1 Running 0 12m
Delete the Apache deployment because it is no longer required.
kubectl delete deployment apache
Perform a Rolling Update and Rollback
Using Deployments allows you to utilize the rollout and rollback functionality built into the Kubernetes API. Because Kubernetes uses the Deployment object to manage an application, deployments can also manage applications. Next, you will deploy a new version of the nginx application and revert (rollback) to the original version.
Confirm the Rollout History
Review the rollout history for the mytest application.
kubectl rollout history deployment mytest
Example Output:
deployment.apps/mytest REVISION CHANGE-CAUSE 1 <none>
The application has no recorded change because only one revision is listed.
Add a note to the history.
If the CHANGE-CAUSE column is empty, you can add an annotation to this column.
kubectl annotate deployment mytest kubernetes.io/change-cause="Initial Deployment of Nginx 1.20"
Review the rollout history again.
kubectl rollout history deployment mytest
Example Output:
deployment.apps/mytest REVISION CHANGE-CAUSE 1 Initial deployment of Nginx 1.20
The CHANGE-CAUSE column has recorded the annotation you added.
Note: Annotating is not mandatory but will help you identify changes to the application deployments on your cluster.
Deploy a New Version of the Application
Changing the Pod image causes Kubernetes to replace the Pods in the Deployment's ReplicaSet. Kubernetes only terminates the existing Pods in a ReplicaSet once the new Pods are up and running. This process is known as a rollout strategy and ensures no downtime occurs or lost connections and that the application continues running during the change.
Kubernetes provides two rollout strategies:
- rolling update: A ReplicaSet only replaces Pods once the new Pods are ready and running. If you do not state a preference, Kubernetes applies this as the default strategy.
- recreate: A ReplicaSet terminates Pods before the new Pods are ready and running. This strategy delivers the fastest rollout strategy for changes but causes downtime for the application.
You can view the current rollout strategy by reviewing the deployment YAML file (if used) or using the kubectl describe
command. See this excerpt from the previously run command.
... Replicas: 6 desired | 6 updated | 6 total | 6 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: ...
Where:
- The
StrategyType
is RollingUpdate - The
RollingUpdateStrategy
defines its two parameters as:- maxSurge: 25% Restricts the creation of new Pods to 25% of the total
- maxUnavailable: 25% Defines that no more than 25% of existing Pods can be unavailable
Both maxSurge and maxUnavailable can be defined as either a percentage or a number.
Update the image version.
kubectl set image deployment mytest oraclelinux8-nginx=ghcr.io/oracle/oraclelinux8-nginx:1.22
Check the status of the changes.
kubectl rollout status deployment mytest
Depending on the cluster's workload, the output may have time to show the rollout progression as it completes.
Annotate the Deployment.
kubectl annotate deployment/mytest kubernetes.io/change-cause="Update Nginx image to 1.22"
Review more details of this revision.
kubectl rollout history deployment/mytest --revision=2
Example Output:
deployment.apps/mytest with revision #2 Pod Template: Labels: app=mytest pod-template-hash=6f9d84b8cb Annotations: kubernetes.io/change-cause: Update Nginx image to 1.22 Containers: oraclelinux8-nginx: Image: ghcr.io/oracle/oraclelinux8-nginx:1.22 Port: <none> Host Port: <none> Environment: <none> Mounts: <none> Volumes: <none>
Confirming that the Nginx image has been updated from 1.20 to 1.22
Roll Back to a Previous Version
Occasionally, users experience bugs when administrators move a new version to production. Kubernetes Deployments make the rollback process easy.
Roll back to the previous version.
kubectl rollout undo deploy mytest
Information: It is possible to roll back to a specific previous revision by appending a
--revision
flag to therollout undo
command. In this example, you could have used this flag to undo therollout
as in the following:kubectl rollout undo deploy mytest --revision=1
. Since therollout history
only contains two revisions in this example, adding the--revision
option makes no difference. However, if you need to revert several versions, this flag makes the process easier to achieve.Confirm tracking of the Deployment changes.
kubectl rollout history deployment mytest
Example Output:
deployment.apps/mytest REVISION CHANGE-CAUSE 2 Update Nginx image to 1.22 3 Initial Deployment of Nginx 1.20
Confirm the Nginx image version changed to 1.20
kubectl describe deployment mytest
Example Output (excerpt):
Name: mytest Namespace: default CreationTimestamp: Fri, 26 Apr 2024 19:03:10 +0000 Labels: app=mytest Annotations: deployment.kubernetes.io/revision: 3 .. .. Pod Template: Labels: app=mytest Containers: oraclelinux8-nginx: Image: ghcr.io/oracle/oraclelinux8-nginx:1.20 Port: <none> Host Port: <none> Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason .. .. Normal ScalingReplicaSet 2m34s deployment-controller Scaled up replica set mytest-76688bc4c4 to 6 from 5 Normal ScalingReplicaSet 2m33s (x5 over 103m) deployment-controller (combined from similar events): Scaled down replica set mytest-6f9d84b8cb to 0 from 1
The line showing Image: ghcr.io/oracle/oraclelinux8-nginx:1.20 confirms the Nginx Pod version is now 1.20.
Next Steps
Kubernetes allows you to orchestrate and manage application workloads to deliver optimum service. Hopefully, you now understand how Deployments work and can manage your application deployments, including their ability to be rolled out or rolled back.