Kubernetes

Deploy services

Use kubectl to deploy services to your cluster

The kubectl commands on this page can be executed on the k3s-master node installed in the previous chapters. Alternatively, you can install kubectl on your laptop and operator Kubernetes remotely (which is however out of the scope of this tutorial).

Deploy nginx

Let's deploy nginx to all nodes in the cluster. The easiest way to do is using a daemonset. Create a file nginx-daemon-set.yml and populate it as follows:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-daemonset
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80

After this use kubectl to apply the file:

kubectl apply -f nginx-daemon-set.yml

You should receive the output: daemonset.apps/nginx-daemonset created. You can check the deployment with the kubectl command:

[root@k3s-master k8s]# kubectl get pods -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP          NODE         NOMINATED NODE   READINESS GATES
nginx-daemonset-6k4t8   1/1     Running   0          10s   10.42.3.4   k3s-node-2   <none>           <none>
nginx-daemonset-n7nxg   1/1     Running   0          10s   10.42.1.3   k3s-node-1   <none>           <none>
nginx-daemonset-t2sn2   1/1     Running   0          10s   10.42.0.9   k3s-master   <none>           <none>

As you can see in this output, nginx go deployed to each individual DeTEE VM.

Expose nginx

In order to expose nginx, we can use a service of type NopePort, because we created the VMs using public IPv4 addresses. Alternative solutions are possible, but for demonstration purposes let's create a file called nginx-svc.yml with the following contents:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080

And apply it using kubectl apply -f nginx-svc.yml. We can now obtain all information about our service in YAML format by using the kubectl command:

[root@k3s-master ~]# kubectl get svc nginx-service -o yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"nginx-service","namespace":"default"},"spec":{"ports":[{"nodePort":30080,"port":80,"targetPort":80}],"selector":{"app":"nginx"},"type":"NodePort"}}
  creationTimestamp: "2025-04-01T00:26:38Z"
  name: nginx-service
  namespace: default
  resourceVersion: "4076"
  uid: aeac57fe-381d-486c-96a1-cc0f31f3db2f
spec:
  clusterIP: 10.43.250.23
  clusterIPs:
  - 10.43.250.23
  externalTrafficPolicy: Cluster
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - nodePort: 30080
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

Accessing nginx

We can get the individual IPs of each VM and use curl to call the service on port 30080 (exposed via NodePort in the service above). It would be however very cool if we did this using one single command to:

  1. get the list of VMs
  2. filter the VMs that are part of the k3s cluster
  3. get the UUID of each VM
  4. inspect each VM using its UUID to obtain the public address
  5. use the public address to access nginx on port 30080
  6. grab the title of the webpage from the nginx output

The oneliner (split on multiple lines) would look like this:

detee-cli --format yaml vm list |
  grep -B 1 k3s |
  grep uuid | awk '{ print $2 }' |
  xargs -i detee-cli --format yaml vm inspect {} | grep ipv4 | awk '{ print $2 }' |
  xargs -i curl {}:30080 |
  grep title

Congratulations!

If you got this far, congratulations on completing this Kubernetes tutorial!

We also prepared an automation combinding all these steps in our examples repo.