Note:
- This tutorial requires access to Oracle Cloud. To sign up for a free account, see Get started with Oracle Cloud Infrastructure Free Tier.
- It uses example values for Oracle Cloud Infrastructure credentials, tenancy, and compartments. When completing your lab, substitute these values with ones specific to your cloud environment.
Deploy Longhorn for Kubernetes on OKE
Introduction
Oracle Cloud Infrastructure Container Engine for Kubernetes (OKE) is a fully-managed, scalable, and highly available service that you can use to deploy your containerized applications to the cloud. You specify the compute resources that your applications require, and OKE provisions them on Oracle Cloud Infrastructure (OCI) in an existing OCI tenancy. Container Engine for Kubernetes uses Kubernetes - the open-source system for automating deployment, scaling, and management of containerized applications across clusters of hosts.
The OCI Block Volume service provides persistent, durable, and high-performance block storage for your data and OKE can utilize the block volumes as persistent disks for your Kubernetes environment and is fully managed by OKE. In case you want to be in complete control of the persistent storage solution you can deploy Longhorn on OKE and use it as your storage class. It gives you complete control over persistent volumes, scaling, backups and scheduling.
Objective
- Deploy and use Longhorn as the storage solution on OKE.
Prerequisites
-
Access to an Oracle Cloud tenancy.
-
A Virtual Cloud Network set up in your tenancy.
-
All the required policy set up for OKE.
Task 1: Create an OKE cluster
-
Log in to the OCI Console, navigate to Oracle Container Engine for Kubernetes and click Create.
-
In the Create cluster wizard, click Custom Create.
-
Specify the Cluster name, select the Kubernetes version you want to use and click Next at the bottom of the wizard.
Note: You must set up the VCN, subnets and routes before starting with the cluster creation
-
You can choose either of the networking types. The VCN native networking provides better performance. If you choose VCN native networking make sure your subnet has enough IP addresses available.
-
Configure the node pool, choose your desired compute resources. In Advanced options, paste the below custom
cloud-init
script. -
OKE does not provide an option to modify the node template, so in order to attach a block volume, you must run a
cloud-init
script at node initialization. The script will create a block volume of specified size and performance and attach it to the node at initialization. -
Make sure to modify the
size_in_gbs
(size of block storage to attach at init),vpus_per_gb
(vpu per gb for the block storage attachment), andmode
(attachment mode, PARA or ISCSI) variables in the script as per your need.
You need to create a dynamic group and provide this group manage access to block storage, this allows us to use instance principal authentication in our script.
cloud-init
script:#!/bin/bash curl --fail -H "Authorization: Bearer Oracle" -L0 http://169.254.169.254/opc/v2/instance/metadata/oke_init_script | base64 --decode >/var/run/oke-init.sh bash /var/run/oke-init.sh echo "installing python3-pip , oci sdk\n" sudo yum install python3 -y sudo yum install python3-pip -y pip3 install oci pip3 install requests cat << EOF > pyscript.py #!/usr/bin/python import oci import requests size_in_gbs = 200 vpus_per_gb = 10 mode = 'PARA' device_path = "/dev/oracleoci/oraclevdb" signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner() compute_client = oci.core.ComputeClient({}, signer = signer) block_storage_client = oci.core.BlockstorageClient({}, signer = signer) def get_current_instance_details(): r = requests.get(url= 'http://169.254.169.254/opc/v1/instance') return r.json() def create_volume(block_storage, compartment_id, availability_domain, display_name: str): print("--- creating block volume ---") result = block_storage.create_volume( oci.core.models.CreateVolumeDetails( compartment_id=compartment_id, availability_domain=availability_domain, display_name=display_name, size_in_gbs = size_in_gbs, vpus_per_gb = vpus_per_gb ) ) volume = oci.wait_until( block_storage, block_storage.get_volume(result.data.id), 'lifecycle_state', 'AVAILABLE' ).data print('--- Created Volume ocid: {} ---'.format(result.data.id)) return volume def attach_volume(instance_id, volume_id,device_path): volume_attachment_response = "" if mode == 'ISCSI': print("--- Attaching block volume {} to instance {}---".format(volume_id,instance_id)) volume_attachment_response = compute_client.attach_volume( oci.core.models.AttachIScsiVolumeDetails( display_name='IscsiVolAttachment', instance_id=instance_id, volume_id=volume_id, device= device_path ) ) elif mode == 'PARA': volume_attachment_response = compute_client.attach_volume( oci.core.models.AttachParavirtualizedVolumeDetails( display_name='ParavirtualizedVolAttachment', instance_id=instance_id, volume_id=volume_id, device= device_path ) ) oci.wait_until( compute_client, compute_client.get_volume_attachment(volume_attachment_response.data.id), 'lifecycle_state', 'ATTACHED' ) print("--- Attaching complete block volume {} to instance {}---".format(volume_id,instance_id)) print(volume_attachment_response.data) # Call instance metadata uri to get current instace details instanceDetails = get_current_instance_details() print(instanceDetails) volume = create_volume(block_storage= block_storage_client, compartment_id= instanceDetails['compartmentId'], availability_domain=instanceDetails['availabilityDomain'], display_name= instanceDetails['displayName']) attach_volume(instance_id=instanceDetails['id'], volume_id=volume.id, device_path= device_path) EOF echo "running python script\n" chmod 755 pyscript.py ./pyscript.py echo "creating file system on volume\n" sudo /sbin/mkfs.ext4 /dev/oracleoci/oraclevdb echo "mounting volume\n" sudo mkdir /mnt/volume sudo mount /dev/oracleoci/oraclevdb /mnt/volume echo "adding entry to fstab\n" echo "/dev/oracleoci/oraclevdb /mnt/volume ext4 defaults,_netdev,nofail 0 2" | sudo tee -a /etc/fstab
-
-
Review and click Create Cluster and wait for the cluster to become available.
-
Once the cluster is available, go to Node pools and then Nodes. You can see the nodes are in ready state, click on any node, it will open the instance details page, go to attached Block Volumes and you can verify that a block volume (of size and vpu as mentioned in the
cloud-init
script) is attached to the instance.
Task 2: Set up Longhorn
-
Once the cluster is available, you can access the cluster using cloud shell, click Access Cluster and copy and run the command in cloud shell.
-
Run
kubectl get node
to get the list of schedulable nodes. -
In order for Longhorn to recognize the attached disk we need to add labels and annotations to the Nodes as mentioned in Longhorn default disk setup. To do this we are going to create a
patch.yaml
file to patch the nodes.metadata: labels: node.longhorn.io/create-default-disk: "config" annotations: node.longhorn.io/default-disks-config: '[ { "path":"/var/lib/longhorn", "allowScheduling":true }, { "path":"/mnt/volume", "allowScheduling":true } ]'
-
Run the following command for each node.
kubectl patch node <node name> --patch-file patch.yaml
-
Run describe node command to verify that the nodes are patched.
kubectl describe node <node name>
Task 3: Install Longhorn
-
In this tutorial, we use Helm to deploy Longhorn on the OKE cluster. Follow the instructions mentioned in this document: Install with Helm.
-
Run the following command to deploy Longhorn.
helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace --version 1.3.2 --set defaultSettings.createDefaultDiskLabeledNodes=true
Note: Set the
defaultSettings.createDefaultDiskLabeledNodes
property to true while deploying. This will tell longhorn to use the attach block volume config we provided earlier. -
Verify that the pods are created and running.
-
To enable the UI for a better view of Longhorn and its administration, install the Ingress Controller as mentioned in this document: Example: Setting Up an Ingress Controller on a Cluster.
-
Create an
ingress.yaml
file to expose the Longhorn UI.apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: longhorn-ingress namespace: longhorn-system annotations: kubernetes.io/ingress.class: "nginx" spec: rules: - http: paths: - path: / pathType: Prefix backend: service: name: longhorn-frontend port: number: 80
-
Run
kubectl apply -f ingress.yaml
to expose the Longhorn UI using Ingress gateway. -
Get the gateway url by running the following command.
kubectl get ingress -n longhorn-system
. -
Open the IP address from a browser to access the Longhorn Console and confirm it is live.
You can see that we have the storage available and ready to use.
Task 4: Scale Longhorn
Longhorn gives you complete control of the storage solution for your kubernetes deployments, but scaling longhorn is still a manual process, there are 3 ways you can scale the Longhorn setup.
-
Increase the size or the performance units of the attached block volumes: This is a manual process, you have to scale each block volume individually and run some scripts on the nodes to extend the storage. For more information, see Resizing a Volume.
-
Attach more volumes: You have to create and attach volumes to the nodes manually.
-
Scale the cluster by increasing the nodes: Since we have already provided a
cloud-init
script, it will create the block volume and attach it to the node. We must patch the node after it is ready as mentioned in Task 2.3 for Longhorn to recognize the attached storage.
Note: You can modify the script to attach the desired number of block volumes to a single node.
Related Links
Acknowledgments
- Author - Mayank Kakani (OCI Cloud Architect)
More Learning Resources
Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.
For product documentation, visit Oracle Help Center.
Deploy Longhorn for Kubernetes on OKE
F76576-02
December 2023
Copyright © 2023, Oracle and/or its affiliates.