Run Kubernetes on Oracle Linux


Kubernetes is Greek for pilot or helmsman - in other words, the person who follows commands and steers a ship towards its ultimate goal (rather than being the Captain giving orders). To that end, Kubernetes is an open-source, extensible platform for deploying, managing, and scaling containerized applications. It achieves this by using several command-line tools. This lab uses one of those called kubectl together with YAML files to define the required attributes for the organization deploying the application and understand how to set up and maintain the application once done deploying it.

All deployments onto a Kubernetes cluster get represented as objects. These deployed objects use text-based YAML files to provide details of the required state of any application deployed onto the cluster. These YAML files may describe the following:

This third point, although important, is complicated without understanding the basics. Therefore, we’ll hold off for now and handle that topic in future tutorials.

This tutorial works with Kubernetes running within a compact Oracle Cloud Native Environment on Oracle Linux. The intent is not to be a ‘one stop shop’ for everything needed to administer a production deployment. Instead, it introduces the skills required to deploy a working sample application.



An Oracle Linux 8 or later system with the following configuration:

Set up Lab Environment

Note: When using the free lab environment, see Oracle Linux Lab Basics for connection and other usage instructions.

Information: The free lab environment deploys Oracle Cloud Native Environment on the provided node, ready for creating environments. This deployment takes approximately 8-10 minutes to finish after launch. Therefore, you might want to step away while this runs and then return to complete the lab.

  1. If not already connected, open a terminal and connect via ssh to the ocne-node01 system.

    ssh oracle@<ip_address_of_ol_node>
  2. Confirm the environment is ready.

    kubectl get pods -A

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get pods -A
    NAMESPACE                      NAME                                             READY   STATUS    RESTARTS   AGE
    externalip-validation-system   externalip-validation-webhook-7988bff847-8ws2v   1/1     Running   0          3m18s
    kube-system                    coredns-7cbc77dbc7-qxqth                         1/1     Running   0          3m18s
    kube-system                    coredns-7cbc77dbc7-r9bgj                         1/1     Running   0          3m18s
    kube-system                    etcd-ocne-node01                                 1/1     Running   0          3m37s
    kube-system                    kube-apiserver-ocne-node01                       1/1     Running   0          3m37s
    kube-system                    kube-controller-manager-ocne-node01              1/1     Running   0          3m37s
    kube-system                    kube-flannel-ds-vcwzn                            1/1     Running   0          3m18s
    kube-system                    kube-proxy-7lx59                                 1/1     Running   0          3m18s
    kube-system                    kube-scheduler-ocne-node01                       1/1     Running   0          3m37s
    kubernetes-dashboard           kubernetes-dashboard-5d5d4947b5-7pffh            1/1     Running   0          3m18s

Create a Deployment on a Pod and Request Details

In Kubernetes, deployment is a technical term referring to a file that governs a pod’s behavior and characteristics. Administrators use deployments to instruct the application on what to do, and Kubernetes performs the task to reach that state.

The examples use an image that contains a small nginx webserver that echos back the source IP of requests it receives through an HTTP header.

  1. Create a deployment of echoserver.

    kubectl create deployment test
  2. List all Pods in the cluster.

    kubectl get pods

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get pods
    NAME                    READY   STATUS    RESTARTS   AGE
    test-6c486b6d76-467p7   1/1     Running   0          53s

    Note: The Pod name contains a suffix value that varies each time deploying the Pod.

  3. Use JSONPath to assign the Pod name to a variable.

    TESTPOD=$(kubectl get pods -o jsonpath='{ $.items[*] }')
  4. Test the variable assignment.

    The kubectl get pods command also allows passing a pod name as a parameter to display only the information for that Pod.

    kubectl get pods $TESTPOD
  5. Request selected information about the Pod.

    kubectl get pod $TESTPOD --output,NODE_IP:status.hostIP,POD_IP:status.podIP

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get pod $TESTPOD --output,NODE_IP:status.hostIP,POD_IP:status.podIP
    NAME                    NODE_IP      POD_IP
  6. Get the Pod details.

    kubectl describe pod $TESTPOD

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl describe pod test-6c486b6d76-467p7
    Name:         test-6c486b6d76-467p7
    Namespace:    default
    Priority:     0
    Node:         ocne-node01/
    Start Time:   Tue, 28 Jun 2022 19:21:27 +0000
    Labels:       app=test
    Annotations:  <none>
    Status:       Running
    Controlled By:  ReplicaSet/test-6c486b6d76
        Container ID:   cri-o://5b7866a27722ec0998cd9fe74945fb82b4dd9ed4c5c80671d9e8aa239c7008a4
        Image ID:
        Port:           <none>
        Host Port:      <none>
        State:          Running
          Started:      Tue, 28 Jun 2022 19:21:30 +0000
        Ready:          True
        Restart Count:  0
        Environment:    <none>
          /var/run/secrets/ from kube-api-access-d67ph (ro)
      Type              Status
      Initialized       True 
      Ready             True 
      ContainersReady   True 
      PodScheduled      True 
        Type:                    Projected (a volume that contains injected data from multiple sources)
        TokenExpirationSeconds:  3607
        ConfigMapName:           kube-root-ca.crt
        ConfigMapOptional:       <nil>
        DownwardAPI:             true
    QoS Class:                   BestEffort
    Node-Selectors:              <none>
    Tolerations:        op=Exists for 300s
                        op=Exists for 300s
      Type    Reason     Age   From               Message
      ----    ------     ----  ----               -------
      Normal  Scheduled  21m   default-scheduler  Successfully assigned default/test-6c486b6d76-467p7 to ocne-node01
      Normal  Pulling    21m   kubelet            Pulling image ""
      Normal  Pulled     21m   kubelet            Successfully pulled image "" in 3.102843235s
      Normal  Created    21m   kubelet            Created container echoserver
      Normal  Started    21m   kubelet            Started container echoserver

Create a Deployment with a YAML File

Using Kubernetes Deployment manifests define how to deploy an application to the Kubernetes cluster and provide access to other Kubernetes functionality such as self-healing, scalability, versioning, and rolling updates. This lab will not address this more complex functionality provided within Kubernetes. Instead, it illustrates how to use a very basic manifest file to deploy an application.

A Deployment manifest file gets written in either JSON or YAML. Although possible to use JSON, YAML is far more popular due to its flexibility, readability, and the ability to include descriptive comments to clarify aspects of the final deployment.

When running a Deployment, a Pod is updated through a series of declarative updates to reach the desired state for the running application.

While all details in the deployment.yaml are essential for Kubernetes to be able to enact the deployment request, the following highlights some of the more vital parts:

See the upstream Deployments documentation for more information on these other fields.

  1. Create the Deployment file.

    cat << 'EOF' | tee mydeployment.yaml > /dev/null
    apiVersion: apps/v1
    kind: Deployment
     name: echo1
         app: echo1
           app: echo1
         - name: echoserver
  2. Deploy the application on a Pod using the Deployment manifest file.

    kubectl apply -f mydeployment.yaml

    Example Output:

    [[oracle@ocne-node01 ~]$ kubectl apply -f mydeployment.yaml
    deployment.apps/echo1 created
  3. List the Pod managed by the deployment.

    kubectl get pods -l app=echo1

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get pods -l app=echo1
    NAME                     READY   STATUS    RESTARTS   AGE
    echo1-7cbf6dfb96-4cgq7   1/1     Running   0          24s
    • The -l or --selector= option provides a selector (label query) to filter on. This option supports ‘=’, ‘==’, and ‘!=’.(e.g. -l key1=value1,key2=value2)

    Note: Reminder that the Pod name contains a suffix value that varies each time deploying the Pod.

  4. Verify the Deployment succeeded.

    kubectl get deploy echo1

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get deploy echo1
    echo1   1/1     1            1           16m
    • The deploy option is short-hand for deployments. The kubectl command allows using an abbreviated syntax for many of its options. More details are available by running kubectl --help.
  5. Return more detailed information for a deployment.

    kubectl describe deploy echo1

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl describe deploy echo1
    Name:                   echo1
    Namespace:              default
    CreationTimestamp:      Tue, 28 Jun 2022 20:20:40 +0000
    Labels:                 <none>
    Annotations:   1
    Selector:               app=echo1
    Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=echo1
        Port:         <none>
        Host Port:    <none>
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   echo1-7cbf6dfb96 (1/1 replicas created)
      Type    Reason             Age   From                   Message
      ----    ------             ----  ----                   -------
      Normal  ScalingReplicaSet  23m   deployment-controller  Scaled up replica set echo1-7cbf6dfb96 to 1

Use ClusterIP Service

Despite successfully deploying the echo1 Deployment to a Pod, it’s not much use if the end-users cannot access it internally or on the network. That access is where a Service comes in handy as it exposes a Deployment to the network.

The default Kubernetes service type is ClusterIP. However, you cannot access a ClusterIP service from the internet, but you can use the Kubernetes proxy. See the upstream documentation for more on Proxies.

This section exposes echo1 and creates inter-service communication within the cluster using an Oracle Linux Pod, demonstrating communication between your app’s front-end and back-end components.

  1. Get a list of nodes.

    kubectl get nodes

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get nodes
    NAME          STATUS   ROLES    AGE     VERSION
    ocne-node01   Ready    <none>   4h27m   v1.22.8+1.el8

    Nodes are the physical systems or virtual machines for deploying Pods.

  2. Query kube-proxy mode.

    Running kube-proxy in iptables mode causes packets sent to a ClusterIP Service never to be source NAT’d.

    curl -w "\n" http://localhost:10249/proxyMode
    • kube-proxy listens on port 10249 on the node where it’s running.
  3. Create the ClusterIP Service.

    kubectl expose deployment echo1 --name=clusterip-service --port=80 --target-port=8080
  4. Get the IP address assigned to the cluster.

    kubectl get svc clusterip-service

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get svc clusterip-service
    NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
    clusterip-service   ClusterIP   <none>        80/TCP    13s

    Take note of the CLUSTER-IP address in the output.

  5. Create a pod in the same cluster for accessing the ClusterIP Service.

    kubectl run ol -it --image=oraclelinux:8 --restart=Never --rm

    This command will create a Pod running an Oracle Linux 8 container in interactive mode and present a command prompt.

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl run ol -it --image=oraclelinux:8 --restart=Never --rm
    If you don't see a command prompt, try pressing enter.
    [root@ol /]#
  6. Get the IP address of the Oracle Linux container.

    ip -br a

    Example Output:

    [root@ol /]# ip -br a
    lo               UNKNOWN ::1/128 
    eth0@if12        UP    fe80::146f:2cff:fe73:b528/64
  7. Test the nginx webserver within echo1.

    curl -w "\n" <CLUSTER-IP_ADDRESS>

    Use the CLUSTER-IP address from the previous output.

    Example Output:

    [root@ol /]# curl -w "\n"
    real path=/
    server_version=nginx: 1.10.0 - lua: 10001
    -no body in request-

    The output shows the request from Oracle Linux and handled by the ClusterIP Service using the echo1 Deployment.

  8. Exit the container.


    Example Output:

    [root@ol /]# exit
    pod "ol" deleted

Use NodePort Service with a YAML File

Previously the echo1 Deployment was exposed using the kubectl expose command and accessed internally using the ClusterIP. Now, we’ll use a NodePort Service which is a developer’s approach to having echo1 accessible externally over the network.

The NodePort Service opens a specific port on all the nodes and forwards any traffic to that port to the Service.

Note Standard practice does not recommend using NodePort for Production systems for several reasons, principally the following:

  1. Define a Service file.

    cat << 'EOF' | tee myservice.yaml > /dev/null
    apiVersion: v1
    kind: Service
      name: echo1-nodeport
      namespace: default
      - IPv4
      ipFamilyPolicy: SingleStack
      - nodePort: 32387
        port: 80
        protocol: TCP
        targetPort: 8080
        app: echo1
      sessionAffinity: None
      type: NodePort
      loadBalancer: {}
    • type: - Makes the Service available to network requests from external clients. Valid values include: nodePort, LooadBalancer.
    • nodePort: - The external port used to access the Service.
    • port: - The port number exposed within the cluster.
    • targetPort: - The Port that the container is listening on.
  2. Create the Service.

    kubectl apply -f myservice.yaml

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl apply -f myservice.yaml 
    service/echo1 created

    Note: It is common to have the Deployment and Service definitions within the same YAML file to simplify the management of an application. Using separate files in these steps is for training purposes only. When combining them into a single file, use the --- YAML syntax to separate them.

  3. Display how Kubernetes stores the newly created Service.

    kubectl get service echo1-nodeport -o yaml

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get service echo1-nodeport -o yaml
    apiVersion: v1
    kind: Service
      annotations: |
      creationTimestamp: "2022-06-29T00:14:30Z"
      name: echo1-nodeport
      namespace: default
      resourceVersion: "6242"
      uid: 3171dda6-05b8-45b8-a0ba-457eab6e4f71
      externalTrafficPolicy: Cluster
      internalTrafficPolicy: Cluster
      - IPv4
      ipFamilyPolicy: SingleStack
      - nodePort: 32387
        port: 80
        protocol: TCP
        targetPort: 8080
        app: echo1
      sessionAffinity: None
      type: NodePort
      loadBalancer: {}
  4. Describe the Pods service.

    kubectl describe svc echo1-nodeport

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl describe svc echo1-nodeport
    Name:                     echo1-nodeport
    Namespace:                default
    Labels:                   <none>
    Annotations:              <none>
    Selector:                 app=echo1
    Type:                     NodePort
    IP Family Policy:         SingleStack
    IP Families:              IPv4
    Port:                     <unset>  80/TCP
    TargetPort:               8080/TCP
    NodePort:                 <unset>  32387/TCP
    Session Affinity:         None
    External Traffic Policy:  Cluster
    Events:                   <none>
  5. Get the object Endpoints.

    The Endpoints track the IP addresses of the Pods to which the service sends traffic.

    kubectl get endpoints echo1-nodeport

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get endpoints echo1-nodeport
    NAME             ENDPOINTS         AGE
    echo1-nodeport   8m39s
  6. List the Pods running the application.

    kubectl get pods --output=wide

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get pods -o wide
    NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE          NOMINATED NODE   READINESS GATES
    echo1-7cbf6dfb96-mlds4   1/1     Running   0          80m   ocne-node01   <none>           <none>
    test-6c486b6d76-v4htj    1/1     Running   0          83m   ocne-node01   <none>           <none>

    The IP address in this listing for echo1 should match the value shown in the previous step for Endpoints, which is the Pod’s IP address running on the specified node.

  7. List the Services.

    kubectl get svc -o wide

    Example Output:

    [oracle@ocne-node01 ~]$ kubectl get svc -o wide
    NAME                TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE   SELECTOR
    clusterip-service   ClusterIP   <none>        80/TCP         78m   app=echo1
    echo1-nodeport      NodePort   <none>        80:32387/TCP   10m   app=echo1
    kubernetes          ClusterIP      <none>        443/TCP        88m   <none>

    This command used the alternate option of -o wide, rather than --output=wide.

    Take note of the NodePort, which is set to 32387 for the echo1-nodeport Service.

  8. Get the IP address of the node.

    The free lab environment runs on a single node ocne-node01.

    ip -br a

    In the free lab environment, the IP address should return the instance’s private IP address of assigned to interface ens3.

  9. Use JSONPath to assign the NodePort a variable.

    NODEPORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services echo1-nodeport)
  10. Use JSONPath to assign the Node IP to a variable.

    NODES=$(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }')
  11. Create a firewall rule.

    This rule allows traffic on the node:nodeport, where node is the host IP address of the system or virtual machine where the Pod is running.

    sudo firewall-cmd --permanent --add-port=$NODEPORT/tcp
    sudo firewall-cmd --reload

    After the --reload, the firewalld daemon reloads it’s configuration, which includes iptables. As kube-proxy depends on iptables, there will be a delay in the response from Services.

  12. Use node address and node port to verify application.

    curl -s $NODES:$NODEPORT

    Example Output:

    [oracle@ocne-node01 ~]$ curl -s $NODES:NODEPORT
    real path=/
    server_version=nginx: 1.10.0 - lua: 10001

    The output shows the request from the local node routing through the NodePort Service, through kube-proxy, and to the Pod running the echo1 Deployment.

    Note: If the output appears to hang, this is due to the previous reload of the firewall. Type Ctrl-C and try again.

Remove Deployments and Services

Once done with a Service or Deployment, remove them from Kubernetes.

  1. Remove Services.

    kubectl delete svc clusterip-service echo1-nodeport
  2. Remove Deployments.

    kubectl delete deployments echo1
    kubectl delete deploy test

Removing objects can be done individually or in groups. Check the Kubernetes Reference Manual for more information.


This lab provides only the briefest of introductions to the functionality that using a Cloud-Native Orchestrator like Kubernetes delivers to any organization using Kubernetes to manage their Container deployments. These exercises provide the first step on what will most likely be a long journey into the flexibility that using Kubernetes can deliver.

For More Information

More Learning Resources

Explore other labs on or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit to become an Oracle Learning Explorer.

For product documentation, visit Oracle Help Center.