Skip to content

Instantly share code, notes, and snippets.

@apinter
Created February 26, 2026 07:37
Show Gist options
  • Select an option

  • Save apinter/458ffe0b22184deb19a62f63c033f869 to your computer and use it in GitHub Desktop.

Select an option

Save apinter/458ffe0b22184deb19a62f63c033f869 to your computer and use it in GitHub Desktop.
k8s intro stuff
tags
k8s, k8sking

https://gist.github.com/apinter/f83ae95f8be378ece38e66bc37084bf4 https://developers.redhat.com/articles/2023/11/06/working-kubernetes-podman-desktop#

K8s is king!

kubectl

kubectl create secret docker-registry .dockerconfigjson --docker-server=registry.gitlab.com --docker-username=docker_pull --docker-password=glpat-somexyz --docker-email=pinter1@xund.ai --dry-run=client -o yaml

Manifests

  1. Basic Pod & Deployment
apiVersion: v1
kind: Namespace
metadata:
  name: suse 
apiVersion: v1
kind: Pod
metadata:
  name: tumbleweed-pod
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: main
    image: registry.opensuse.org/opensuse/tumbleweed:latest
    command: ["/usr/bin/sleep", "infinity"]
    securityContext:
      readOnlyRootFilesystem: true
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tumbleweed-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: tumbleweed
  template:
    metadata:
      labels:
        app: tumbleweed
    spec:
      containers:
      - name: main
        image: registry.opensuse.org/opensuse/tumbleweed:latest
        command: ["/usr/bin/sleep", "infinity"]
        securityContext:
          runAsUser: 1000
          runAsGroup: 3000
          readOnlyRootFilesystem: true
  1. DaemonSet & StatefulSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: tumbleweed-ds
spec:
  selector:
    matchLabels:
      app: ds-worker
  template:
    metadata:
      labels:
        app: ds-worker
    spec:
      containers:
      - name: main
        image: registry.opensuse.org/opensuse/tumbleweed:latest
        command: ["/usr/bin/sleep", "infinity"]
        securityContext:
          runAsUser: 1000
          runAsGroup: 3000
          readOnlyRootFilesystem: true
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: tumbleweed-sts
spec:
  serviceName: "tumbleweed-svc"
  replicas: 1
  selector:
    matchLabels:
      app: sts-worker
  template:
    metadata:
      labels:
        app: sts-worker
    spec:
      containers:
      - name: main
        image: registry.opensuse.org/opensuse/tumbleweed:latest
        command: ["/usr/bin/sleep", "infinity"]
        securityContext:
          runAsUser: 1000
          runAsGroup: 3000
          readOnlyRootFilesystem: true
  1. Jobs & CronJobs
apiVersion: batch/v1
kind: Job
metadata:
  name: tumbleweed-job
spec:
  template:
    spec:
      containers:
      - name: main
        image: registry.opensuse.org/opensuse/tumbleweed:latest
        command: ["/usr/bin/sleep", "infinity"]
        securityContext:
          runAsUser: 1000
          runAsGroup: 3000
          readOnlyRootFilesystem: true
      restartPolicy: Never
CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
  name: tumbleweed-cron
spec:
  schedule: "*/5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: main
            image: registry.opensuse.org/opensuse/tumbleweed:latest
            command: ["/usr/bin/sleep", "infinity"]
            securityContext:
              runAsUser: 1000
              runAsGroup: 3000
              readOnlyRootFilesystem: true
          restartPolicy: OnFailure
  1. Secrets & Services The "glue" and the sensitive data. Note that Secret data must be base64 encoded.
apiVersion: v1
kind: Secret
metadata:
  name: my-opaque-secret
type: Opaque
data:
  username: YWRtaW4= # "admin"
  password: MWYyZDM0NTY= # "1f2d3456"
Docker Registry Secret
apiVersion: v1
kind: Secret
metadata:
  name: regcred
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJwYXNzIiwiaW1haWwiOiJhZG1pbkBleC5jb20iLCJhdXRoIjoiWVdSdGFXNDZkR0Z6Y3c9PSJ9fX0=

Service (ClusterIP)

apiVersion: v1
kind: Service
metadata:
  name: tumbleweed-svc
spec:
  selector:
    app: tumbleweed
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

Quick Tip for your Lesson: Since you’ve enabled readOnlyRootFilesystem: true, remind your students that the container will fail if the application tries to write to any local directory (like /tmp or /var/log) unless you mount an emptyDir volume to those locations! Persistent Volume Claim (PVC)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: tumbleweed-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Updated Pod (Mounting the PVC) To make this useful for your class, here is how you attach that PVC to the Tumbleweed pod. This allows the container to write to /data even though the rest of the root filesystem is read-only.

apiVersion: v1
kind: Pod
metadata:
  name: tumbleweed-pvc-pod
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: main
    image: registry.opensuse.org/opensuse/tumbleweed:latest
    command: ["/usr/bin/sleep", "infinity"]
    securityContext:
      readOnlyRootFilesystem: true
    volumeMounts:
    - name: storage-volume
      mountPath: /data
  volumes:
  - name: storage-volume
    persistentVolumeClaim:
      claimName: tumbleweed-pvc

Pro-tip for your demo: If you want to prove the security context is working during your live demo, you can have your students exec into the pod and try to create a file:

  • kubectl exec -it tumbleweed-pvc-pod -- touch /test.txt → Should fail (Read-only file system).
  • kubectl exec -it tumbleweed-pvc-pod -- touch /data/test.txt → Should succeed (Writing to the mounted volume).

kubectl cheatsheet

  1. Deployment & Lifecycle Get your resources up and running, and check their status.
  • Apply everything in a folder: kubectl apply -f .
  • Check Pod status (with labels): kubectl get pods --show-labels
  • Watch scaling in real-time: kubectl get deployment tumbleweed-deploy -w
  • Scale the deployment on the fly: kubectl scale deployment tumbleweed-deploy --replicas=5
  1. Deep-Dive Inspection When a student asks, "Why isn't it working?", these are your go-to tools.
  • The "Everything" view: kubectl describe pod (Check the Events section at the bottom!)
  • Check container logs: kubectl logs
  • Stream logs (Follow): kubectl logs -f
  • View raw YAML of a running pod: kubectl get pod -o yaml
  1. Verification (Security & Storage) Use these to prove the SecurityContext and PVC are working as intended. | Goal | Command | |---|---| | Check Current User | kubectl exec -- whoami | | Check User ID | kubectl exec -- id | | Test Read-Only FS | kubectl exec -- touch /root/test.txt (Should fail) | | Test PVC Write | kubectl exec -- touch /data/success.txt (Should work) | | Check Mounts | `kubectl exec -- mount |

  2. Secrets & Troubleshooting How to see what's actually inside those encoded strings.

  • List all secrets: kubectl get secrets
  • Decode an Opaque Secret: kubectl get secret my-opaque-secret -o jsonpath='{.data.password}' | base64 --decode
  • Restart a Deployment (Rollout): kubectl rollout restart deployment tumbleweed-deploy
  1. Cleanup Don't leave the cluster messy!
  • Delete by file: kubectl delete -f manifest.yaml
  • Delete all pods in namespace: kubectl delete pods --all
  • Delete the PVC (Warning: wipes data): kubectl delete pvc tumbleweed-pvc 💡 A "Quick Win" Demo Idea If you want to show off the Self-Healing nature of Kubernetes:
  • Run kubectl get pods -l app=tumbleweed -w in one terminal window.
  • In another, "kill" one of the pods: kubectl delete pod .
  • Watch as Kubernetes immediately spins up a replacement to maintain your 2-replica count. Would you like me to put these commands into a copy-pasteable bash script so you can clear the environment quickly between classes?
  1. kubectl run with overrides

    kubectl run -i --tty --rm debug2 -n argocd --image=docker.io/alpine:latest --restart=Never --overrides='{ "spec": { "serviceAccount": "gcs-k8sdiff-sa" }  }'  -- sh
    
    kubectl run -i --tty --rm debug2 -n argocd --image=docker.io/alpine:latest --restart=Never --overrides='{ "spec": { "serviceAccount": "gcs-k8sdiff-sa" }  }' --overrides='{"spec": {"imagePullSecrets": [{"name": "dockerconfigjson"}]}}' -- bash
    

Rbac

apiVersion: v1
kind: ServiceAccount
metadata:
  name: gitlab-admin
  namespace: gitlab-runner
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: gitlab-admin-role
  namespace: gitlab-runner
rules:
  - apiGroups: [""]
    resources: ["*"]
    verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: gitlab-admin-rb
  namespace: gitlab-runner
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: gitlab-admin-role
subjects:
  - kind: ServiceAccount
    name: gitlab-admin
    namespace: gitlab-runner
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: gitlab-mongo-access
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["services", "pods", "endpoints"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: gitlab-mongo-access-rb
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: gitlab-mongo-access
subjects:
  - kind: ServiceAccount
    name: gitlab-admin
    namespace: gitlab-runner
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment