Skip to content

Instantly share code, notes, and snippets.

@onpaws
Last active July 14, 2023 04:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save onpaws/3379908bb236bebabeb7678600cab003 to your computer and use it in GitHub Desktop.
Save onpaws/3379908bb236bebabeb7678600cab003 to your computer and use it in GitHub Desktop.
Easily set up a single node Kubernetes+Rancher instance for fun

Kubernetes and Rancher Adventures...on the cheap

TL, DR: a guide to setup k8s clusters quickly and cheaply, optionally with Rancher for easy GUI-based admin and role management.

I wanted to learn more about k8s but didn't particularly want to commit to paying substantial $ for hosting a 'normal' Kubernetes cluster somewhere. I wanted to stick to the lowest possible budget, but still have real k8s running on a server somewhere.

This doc explains how I did that. It happens to go into Rancher but it could be any cloud native app. Intended for my own learning purposes and edification. If things go well, I might stand up some real apps with external users one day.

Cheap unmanaged k8s option

Hetzner offers 'just compute' at pretty competitive prices. While you don't get managed Kubernetes, if you're early in your k8s journey, it may be in your interest from a lowest possible cost perspective. Consider e.g. k3s_hetzner

Cheap managed k8s option

I learned Digital Ocean offers a single node Kubernetes clusters for $10/month, and this seems like it could meet my needs quite well. So, I created a single node cluster there, configured kubectl and got the public IP address of the node (DO "droplet") using

$ kubectl get nodes -o wide

Quick DNS change

I created a new A record on a domain (one I happen to already own) pointing to that public IP. Now I will be able to pull up whatever deployments using a human friendly domain name. Yay for convenience.

How to avoid a Load Balancer

Like other providers, DO provides a load balancer which is fine, however I don't think the $10/month extra is worth it for my case.

No problem, b/c accessing my apps by port number is good enough. So long as I e.g. expose my Deployments via Services of type NodePort we're gonna be fine from here on out.

Rancher setup

Now onto the Rancher piece. Following the official docs I first tried adding Rancher via Helm, but I saw lots of CrashLoopBackOffs and things were glacial. I also saw there's a default of 3 Rancher instances, even on a single node cluster, which seems not necessary for a tiny cluster like mine.

In the end I followed their Docker-oriented docs but wrote my own Kubernetes manifest implementing their tips. But before you copapasta rancher-single-node-manifest.yaml below, do check the next step first.

Configure ClusterRoleBinding

Since Rancher is an 'admin' category tool and requires higher privs, you may need to configure the Rancher user accordingly. sauce

kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --user=admin --user=kubelet --user=kube-system --user=default --user=rancher --group=system:serviceaccounts

I suggest you use a dedicated namespace, e.g.

kubectl create ns rancher

Finally, go ahead and startup Rancher.

kubectl apply -f rancher-single-node-manifest.yaml -n rancher

We're done

Rancher is now running at

https://example.com:30303

Now you're cooking with gas!

...or are we?

I noticed a "convenience issue" that bothered me enough to want to fix. Turns out, Google Chrome no longer offers to save passwords on sites you visit, unless the origin is served over TLS.

Since...

  1. it's more convenient when Chrome saves credentials for me
  2. I'm not interested in paying for a real certificate since the server is a throwaway (also DO might change the IP on me)
  3. normally I'd consider a Let's Encrypt cert for free, but this time I don't want to waste limited RAM/mental cycles having to deal with a webserver for the usual ACME automation dance

...I ended up creating my own Certificate Authority using macOS Keychain Access.

Your own Certificate Authority

It might sound like a huge job, but really it's just some applied crypto and once you grok the concepts it's not bad.

CAs are the root of trust for TLS. If you make one on your own, e.g. using macOS, the CA is automatically added to your local macOS trust store, which you can see using Keychain Access. You can optionally add the CA to other devices e.g. using AirDrop which I did. (Colleagues could also get your CA which might be useful e.g. if you wanted to prove the server's identity)

So after making the CA, I then generated a certificate for my node (aka Rancher) using my new CA. I next exported that certificate, the private key for the cert, and the CA authority certificate aka the "fullchain" cert. Because Rancher's Docker image expects .pem formatted files, I converted from .cer.

How to convert Keychain Access exports (.cer) to .pem files:

openssl x509 -inform der -in cert.cer -out cert.pem
openssl x509 -inform der -in fullchain.cer -out fullchain.pem
openssl pkcs12 -in key.p12 -nodes -out key.pem -nocerts

Paste these values into the relevant bits of the manifest, and you're off to the races. Convoluted? You could say that, yeah, but I do think having your own CA is a useful trick to have up your sleeve, and depending on your needs is likely to be useful in the future also. I've used it twice this year and counting :)

Finally, when you logon Chrome will happily save your creds as per usual. Yay for convenience!

# Intended for experiments/local dev only
apiVersion: v1
kind: Service
metadata:
name: rancher-service
spec:
selector:
app: rancher
type: NodePort
ports:
- name: https
nodePort: 30303
port: 9090
targetPort: 443
protocol: TCP
- name: http
port: 9191
targetPort: 80
protocol: TCP
---
apiVersion: v1
kind: ConfigMap
metadata:
name: certs-config-map
data:
cert.pem: |
-----BEGIN CERTIFICATE-----
MII...
-----END CERTIFICATE-----
key.pem: |
Bag Attributes
friendlyName: rancher.example.com
localKeyID: 01 E3 E0 FC 64 DB 38 1A EB 33 24 64 7B 22 F4 0B 5C 79 E1 05
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
MII...
-----END PRIVATE KEY-----
cacerts.pem: |
-----BEGIN CERTIFICATE-----
MII...
-----END CERTIFICATE-----
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rancher
labels:
app: rancher
spec:
replicas: 1
selector:
matchLabels:
app: rancher
template:
metadata:
labels:
app: rancher
name: rancher
spec:
terminationGracePeriodSeconds: 20
containers:
- name: rancher-container
image: rancher/rancher:v2.3.5
imagePullPolicy: Always
volumeMounts:
- name: certs-config-map-volume
mountPath: /etc/rancher/ssl
volumes:
- name: certs-config-map-volume
configMap:
name: certs-config-map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment