Installing Minikube on AWS EC2 (Ubuntu 20.04)

Kubernetes earns its keep on large applications. Security, scalability, portability, all the things people put on slides. And if you’re running it on AWS, GCP or Azure, the managed offerings take the control plane off your hands entirely. You look after worker nodes, the provider looks after the master, and that split is most of the appeal.

But sometimes you just want somewhere to try something out. Learn the thing, test a manifest, run a pipeline end to end before it goes anywhere near production. For that you want a cluster you can throw away, and there are a few ways to get one.
The usual candidates:
  • minikube
  • Kind
  • K3s, compare K3s to K8s

An Introduction to minikube

This one is about Minikube, running on an AWS EC2 box with Ubuntu 20.04. Minikube spins up a single node Kubernetes cluster in Docker or VirtualBox, which is enough to develop a containerised application against. Single node means the master and the one worker are the same machine. You’ll want kubectl alongside it, and that part is the same whether you’re on Linux, a Mac, or a Windows laptop.

Install kubectl

First job is getting apt ready to talk to the Kubernetes repository:
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl

Download the Google Cloud public signing key:
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

Add the Kubernetes apt repository:
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

Update apt package index with the new repository and install kubectl:
sudo apt-get update
sudo apt-get install -y kubectl

kubectl version --client 
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.4", GitCommit:"e6c093d87ea4cbb530a7b2ae91e54c0842d8308a", GitTreeState:"clean", BuildDate:"2022-02-16T12:38:05Z", GoVersion:"go1.17.7", Compiler:"gc", Platform:"linux/amd64"}

Install Docker

sudo apt-get update
sudo apt-get install docker.io -y

sudo systemctl status docker.service
sudo systemctl enable docker.service

sudo systemctl restart docker.service
sudo systemctl start docker.service
sudo systemctl stop docker.service

sudo usermod -aG docker ${USER}

Install Minikube

Before you start, what Minikube wants from the box:
  • 2 GB RAM or more
  • 2 CPU / vCPU or more
  • 20 GB free hard disk space or more
  • Docker / Virtual Machine Manager – KVM & VirtualBox
I’m going with Docker as the base here rather than a VM.
sudo curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64

Once the binary is downloaded, copy it to the path /usr/local/bin and set the executable permissions on it.
sudo cp minikube /usr/local/bin/minikube
sudo chmod +x /usr/local/bin/minikube

Verify the minikube version

minikube version
minikube version: v1.25.2
commit: 362d5fdc0a3dbee389b3d3f1034e8023e72bd3a7

Start Minikube

sudo minikube status
* Profile "minikube" not found. Run "minikube profile list" to view all profiles.
  To start a cluster, run: "minikube start"

sudo minikube start --vm-driver=none
* minikube v1.25.2 on Ubuntu 20.04 (xen/amd64)
* Using the none driver based on user configuration
X Exiting due to GUEST_MISSING_CONNTRACK: Sorry, Kubernetes 1.23.3 requires conntrack to be installed in root's path
Solution:
sudo apt install conntrack

$ sudo minikube start --vm-driver=none
* minikube v1.25.2 on Ubuntu 20.04 (xen/amd64)
* Using the none driver based on user configuration
* Starting control plane node minikube in cluster minikube
* Running on localhost (CPUs=2, Memory=3924MB, Disk=15817MB) ...
* OS release is Ubuntu 20.04.4 LTS
* Preparing Kubernetes v1.23.3 on Docker 20.10.7 ...
  - kubelet.resolv-conf=/run/systemd/resolve/resolv.conf
  - kubelet.housekeeping-interval=5m
    > kubeadm.sha256: 64 B / 64 B [--------------------------] 100.00% ? p/s 0s
    > kubelet.sha256: 64 B / 64 B [--------------------------] 100.00% ? p/s 0s
    > kubectl.sha256: 64 B / 64 B [--------------------------] 100.00% ? p/s 0s
    > kubectl: 44.43 MiB / 44.43 MiB [-------------] 100.00% 46.10 MiB p/s 1.2s
    > kubeadm: 43.12 MiB / 43.12 MiB [-------------] 100.00% 30.69 MiB p/s 1.6s
    > kubelet: 118.75 MiB / 118.75 MiB [-----------] 100.00% 51.08 MiB p/s 2.5s
  - Generating certificates and keys ...
  - Booting up control plane ...
  - Configuring RBAC rules ...
* Configuring local host environment ...
*
! The 'none' driver is designed for experts who need to integrate with an existing VM
* Most users should use the newer 'docker' driver instead, which does not require root!
* For more information, see: https://minikube.sigs.k8s.io/docs/reference/drivers/none/
*
! kubectl and minikube configuration will be stored in /root
! To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:
*
  - sudo mv /root/.kube /root/.minikube $HOME
  - sudo chown -R $USER $HOME/.kube $HOME/.minikube
*
* This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true
* Verifying Kubernetes components...
  - Using image gcr.io/k8s-minikube/storage-provisioner:v5
* Enabled addons: default-storageclass, storage-provisioner
* Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

Check Minikube Status:

sudo minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

Deployment vs. Service

Two words that get mixed up constantly, so, briefly. A deployment is what keeps a set of pods running.
A service is what gives that set of pods network access. One handles staying alive, the other handles being reachable.

Running First Container

sudo kubectl run hello-minikube --image=gcr.io/google_containers/echoserver:1.10 --port=8080
pod/hello-minikube created

sudo kubectl get pods
NAME             READY   STATUS    RESTARTS   AGE
hello-minikube   1/1     Running   0          57s

Kubectl run no longer creates a deployment, it creates a pod as the instructions are slightly different now so I will change the command to create a deployment, check the below example to know the updated instructions:

sudo kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.10
deployment.apps/hello-minikube created

sudo kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
hello-minikube-7bfc84c94b-wsk7w   1/1     Running   0          33s

sudo kubectl get deployment
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
hello-minikube   1/1     1            1           51s

sudo kubectl expose deployment hello-minikube --type=NodePort --port=8080
service/hello-minikube exposed

sudo kubectl get services
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-minikube   NodePort    10.101.77.125   <none>        8080:31602/TCP   18s
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          20m

In this project the deployment and the service are separate yaml files, though nothing stops you putting both in one file if you’d rather.

  • apiVersion – Which version of the Kubernetes API you’re using to create this object
  • kind – What kind of object you want to create (service, application, etc.)
  • metadata – Data that helps uniquely identify the object (like a name, a label, etc.)
  • spec – What state you desire for the object (container info, exposed ports, etc.)

Worth being clear about which port is which here. 31602 is the port on the EC2 instance, and 8080 is the container port sitting behind it. Swap in the public IP and you can check the service actually answers.

sudo minikube service hello-minikube --url
http://172.31.42.79:31602
http://3.20.233.25:31602
And what comes back:
Hostname: hello-minikube-7bfc84c94b-wsk7w
Pod Information:
-no pod information available-
Server values:
server_version=nginx: 1.13.3 - lua: 10008
Request Information:
client_address=172.17.0.1
method=GET
real path=/
query=
request_version=1.1
request_scheme=http
request_uri=http://3.20.233.25:8080/
Request Headers:
accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
accept-encoding=gzip, deflate
accept-language=en-US,en;q=0.9
connection=keep-alive
host=3.20.233.25:31602
upgrade-insecure-requests=1
user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36
Request Body:
-no body in request-
My instance has a public IPv4 of 3.20.233.25. Port 8080 on hello-minikube is exposed as 31602, so what went in the browser was 3.20.233.25:31602.
Have a look at request_uri in that output. It reports port 8080, which is the container’s view of things rather than the one you typed.
Tidying up afterwards. Service first, then the deployment, then the cluster itself:
Delete the exposed service (port)

sudo kubectl delete services hello-minikube
service "hello-minikube" deleted

Delete the deployed container (hello-minikube)

sudo kubectl delete deployment hello-minikube
deployment.apps "hello-minikube" deleted

Stopping Minikube/Shutting Down the Cluster

sudo minikube stop
* Stopping node "minikube"  ...
* 1 node stopped.

Conclusion

That’s Minikube up and running on an EC2 Ubuntu box, with something deployed, exposed and then cleaned up again. Not a deep dive, and it isn’t meant to be. The point was to get you past the install and into a cluster you can actually poke at. Once you’re there the rest of Kubernetes is a lot easier to learn, because you’ve got somewhere to break things.

References:

Avatar photo

Asif Khan

I have spent over 10 years working across IT systems, open source software, DevOps, Linux administration and cloud operations. Three things drive most of what I do: automation, security and resilience. Much of that work involves planning and building the platforms that sit behind services people rely on daily, which means designing for failure just as carefully as for load. Cloud computing held my attention early on, largely for its flexibility. Being able to scale up and then back down again means far less guessing about how much capacity you will need. Across projects I work with the full DevOps toolchain, from provisioning, orchestration and configuration management through to release management and microservices architecture.