Kubernetes in local the easy way: Docker Desktop (on Mac)

Andrés Torres García
9 min readFeb 5, 2020

--

What this is

A step by step tutorial about one of the easiest and most straight forward ways to have a simple single-node Kubernetes cluster running in your local using Docker Desktop (on Mac).

Some basic knowledge of Docker and Kubernetes is expected.

What this is not

This is not a tutorial or an article about what is Docker? or what is Kubernetes?. There is plenty of literature on those topics on the internet e.g:

Docker Desktop download

No Docker Hub option

  • If you don’t have a Docker account already
  • If you are not planning to publish container images
  • If you prefer not to share your email address

Get the macOS Installer .dmg directly here
(Apple Chip) https://desktop.docker.com/mac/main/arm64/Docker.dmg
(Intel) https://desktop.docker.com/mac/main/amd64/Docker.dmg

Here for the Edge version -> https://download.docker.com/mac/static/edge/

Docker Hub option

If you use the standard Docker links, for example:

https://www.docker.com/products/docker-desktop

https://hub.docker.com/editions/community/docker-ce-desktop-mac

They will kindly invite you to Sign In to Docker Hub, which will enable the download link, allow you to publish containers to Docker Hub, etc.

You can also do this step after the installation:

Select Sign in /Create Docker ID from the Docker Desktop menu to access your Docker Hub account.

Once logged in, you can access your Docker Hub repositories directly from the Docker Desktop menu.

Docker Desktop installation

  1. Double-click Docker.dmg to open the installer, drag the Docker icon to the Applications folder.

2. Cmd + Space and type Docker. You should be able to see the Docker app there; just hit Enter to open it

3. Docker will start after a few seconds

You should see a pop up like this:

You can clone the pop-up and click on the Docker icon in the top status bar

Top Status Bar

and go to Preferences window as shown below.

Docker menu

Then click on the Kubernetes icon.

You will notice that Kubernetes is not enabled. Simply check on the Enable Kubernetes option and then hit the Apply & Restart button as shown below:

Create a Kubernetes cluster as easy as check one checkbox

This will display a loading spinner while the Kubernetes cluster gets installed.

The installation starts. Please be patient since this could take a while, depending on your network.

The Kubernetes icon on the bottom left will turn green when the installation is done

When the installation is done, you should see the same screen:

Note the two icons at the bottom of the window mentioning:

  • The Docker icon is green, meaning Docker is up and running
  • The Kubernetes icon is green, meaning Kubernetes cluster is up and running

Congratulations! You now have the following:

  • A standalone Kubernetes server and client, as well as Docker CLI integration.
  • The Kubernetes server is a single-node cluster and is not configurable.

If you check your About Docker, you should see something similar to:

Check our installation

Let us try out a few things to ensure that we can make sense of what has been installed. Execute the following commands in a terminal:

> kubectl versionClient Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.1", GitCommit:"b7394102d6ef778017f2ca4046abbaa23b88c290", GitTreeState:"clean", BuildDate:"2019-04-19T22:13:37Z", GoVersion:"go1.12.4", Compiler:"gc", Platform:"darwin/amd64"}Server Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7", GitCommit:"1dd5338295409edcfff11505e7bb246f0d325d15", GitTreeState:"clean", BuildDate:"2021-01-13T13:15:20Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}

You might have noticed that my server and client versions are different. I am using kubectl from my gCloud SDK tools and Docker for Mac, when it launched the Kubernetes cluster has been able to set the cluster context for the kubectl utility for you. So, if we fire the following command:

> kubectl config current-context
docker-desktop

You can see that the cluster is set to docker-for-desktop.

Tip: In case you switch between different clusters, you can always get back using the following:

$ kubectl config use-context docker-for-desktop
Switched to context “docker-for-desktop”

Let us get some information on the cluster.

> kubectl cluster-info
Kubernetes master is running at https://kubernetes.docker.internal:6443
KubeDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

Let us check out the nodes in the cluster:

> kubectl get nodes
NAME STATUS ROLES AGE VERSION
docker-desktop Ready master 21h v1.19.7

Installing the Kubernetes Dashboard

The next step we need to do here is to install the Kubernetes Dashboard. We can use the Kubernetes Dashboard YAML that is available and submit the same to the Kubernetes Master as follows:

> kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc3/aio/deploy/recommended.yamlnamespace/kubernetes-dashboard created
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created

The Dashboard application will get deployed as a Pod in the Kubernetes-dashboard namespace. We can get a list of all our Pods in all namespaces via the following command:

> kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
docker compose-7b7c5cbbcc-ft6zt 1/1 Running 0 2d
docker compose-api-dbbf7c5db-8w9qw 1/1 Running 0 2d
kube-system coredns-5c98db65d4-sj2kp 1/1 Running 0 2d
kube-system coredns-5c98db65d4-sq2xc 1/1 Running 0 2d
kube-system etcd-docker-desktop 1/1 Running 0 2d
kube-system kube-apiserver-docker-desktop 1/1 Running 0 2d
kube-system kube-controller-manager-docker-desktop 1/1 Running 0 2d
kube-system kube-proxy-mph42 1/1 Running 0 2d
kube-system kube-scheduler-docker-desktop 1/1 Running 0 2d
kubernetes-dashboard dashboard-metrics-scraper-7f5767668b-w5tl4 1/1 Running 0 41h
kubernetes-dashboard kubernetes-dashboard-58df6bddcb-2xbkg 1/1 Running 0 41h

Ensure that the Pod “kubernetes-dashboard-*“ is in Running state. It could take some time to change from ContainerCreating to Running, so be patient.

Once it is in running state, you must create a secure channel to your Kubernetes cluster to access Dashboard from your local workstation.

Kubectl Proxy

kubectl proxy creates a proxy server between your machine and the Kubernetes API server. By default, it is only accessible locally (from the machine that started it).

Start a local proxy server.

> kubectl proxy
Starting to serve on 127.0.0.1:8001

Once the proxy server is started, you should be able to access Dashboard from your browser.

Now you can access Dashboard at:

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy

You will see the following screen:

Login page for Kubernetes Dashboard

To find a valid token here you have a useful one-liner

> kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | awk '/^deployment-controller-token-/{print $1}') | awk '$1=="token:"{print $2}'eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZXBsb3ltZW50LWNvbnRyb2xsZXItdG9rZW4tZnp3NzgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVwbG95bWVudC1jb250cm9sbGVyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiOWJmOGE4ZDAtZDg1Ny00YzBlLThmNzktZDk3MDEyZDBjMjU5Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRlcGxveW1lbnQtY29udHJvbGxlciJ9.H_6nQYQ1ltSHUTddNgUjpRHKe82hC-ipL0S_08lK90rWE0hZir9fq1H9LhEyjSgt3QVIYCEiG8qiIqkQO0I2dvQN_CbQHnLl6dmhlrn7astFdu2F621plNQaC6MHfL5YTsTfJEER2sx5qDuEgtZXobWQ8-64w2d6RfHEIFZIxmno8Oj7XccjfBv12p74qyleJYIOBSuglaHyOx8RaDROCkW0OR-9DHa4zlcU28YqW83J3ynxjwqPvu_QzUc5xcycUk1nEfjWZORjH9A1D_Qdj5DNud7g1Pa_HHQZ50LBjIWKmwNLF7CSA-__goCPfWFfcC_gHANhFAfGYeU5sKWz9g

Copy & paste that token into the field and click Sign In to login, the dashboard will show as below:

Let’s Play!

Let us proceed now to run a simple Nginx container to see the whole thing in action:

First things first, set your command line for productivity:

  1. Kubectl autocomplete

Bashsource <(kubectl completion bash)

Zshsource <(kubectl completion zsh)

2. Alias for kubectl

Alias to type less (lazy is good), and it will also work with completion:

alias k=kubectl
complete -F __start_kubectl k

Now, let’s create our first namespace:

> k create namespace my-demo
namespace/my-demo created

To easily work within our new context, let’s make it the default:

kubectl config set-context — current — namespace=my-demo

TIP: create an alias with this to be able to check contexts easily:

alias kn=’kubectl config set-context — current — namespace

so now you can do

kn my-demo or kn default

We are going to use the run command as shown below:

k run nginx --image=nginx --port=80 --restart=Never

This creates a Pod (https://kubernetes.io/docs/reference/kubectl/conventions/#generators), and we can investigate the Pod that gets created, which will run the container:

> k get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 ContainerCreating 0 4s

You can see that the STATUS column value is ContainerCreating.

If we wait for a while, the Pod will eventually get created and it will ready as the command below shows:

> k get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 14s

Now, let us go back to the Dashboard and get to the Pods via the Pods link in the Workloads, as shown below:

Click on the Pod, and you can get various details on it as given below:

You can see that it has been given some default labels. You can see its IP address. It is part of the node named docker-for-desktop.

You will find some interesting links on this page, as shown below, via which you can directly EXEC into the pods or see the logs.

“View Logs” and “EXEC into pod” buttons

We could have got the Node and Pod details via a variety of kubectl describe node/pod commands and we can still do that. An example of that is shown below:

k describe pod nginx
Name: nginx
Namespace: my-demo
Priority: 0
PriorityClassName: <none>
Node: docker-desktop/192.168.65.3
Start Time: Sun, 02 Feb 2020 09:34:07 +0100
Labels: run=nginx
Annotations: <none>
Status: Running
IP: 10.1.0.9
Containers:
nginx:
Container ID: docker://10f7fa716e60e7c010adb2e67d1488d4dca11cd1601793ced8847cc35f6f413f
Image: nginx
Image ID: docker-pullable://nginx@sha256:4da336e712e183bff64a09a54a727c14eb9c9d7610ce79c6fe30738b5a82cc10
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Sun, 02 Feb 2020 09:34:10 +0100
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-9v699 (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-9v699:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-9v699
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 25h default-scheduler Successfully assigned my-demo/nginx to docker-desktop
Normal Pulling 25h kubelet, docker-desktop Pulling image "nginx"
Normal Pulled 25h kubelet, docker-desktop Successfully pulled image "nginx"
Normal Created 25h kubelet, docker-desktop Created container nginx
Normal Started 25h kubelet, docker-desktop Started container nginx

Expose a Service

It is time now to expose our basic Nginx deployment as a service. We can use the command shown below:

> k expose pod nginx --type=NodePort
service/nginx exposed

If we visit the Dashboard at this point and go to the Services section, we can see our nginx service entry.

Alternatively, we can use the terminal too, to check it out:

> k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx NodePort 10.104.91.251 <none> 80:31338/TCP 4s

and

> k describe service nginx
Name: nginx
Namespace: my-demo
Labels: run=nginx
Annotations: <none>
Selector: run=nginx
Type: NodePort
IP: 10.104.91.251
LoadBalancer Ingress: localhost
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 31338/TCP
Endpoints: 10.1.0.9:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>

You can now visit: localhost:31338 and you will see nginx welcome page:

Nginx welcome page

Conclusion

If you want to have a Kubernetes cluster running locally in a quick and easy way, I hope this blog post helps you with Kubernetes and Docker Desktop for Mac.

Of course, there are other alternatives for running Kubernetes on your local machine, just to mention some of them:

Minikube: https://github.com/kubernetes/minikube

Minishift (from Openshift): https://github.com/MiniShift/minishift

MicroK8s: https://microk8s.io/

k3s: https://k3s.io/

k3sup: https://github.com/alexellis/k3sup

k3d: https://github.com/rancher/k3d

kind (Kubernetes in Docker): https://kind.sigs.k8s.io/

I also used a lot of online resources at the beginning to play with it and practice some basic concepts. I like especially:

--

--

Andrés Torres García

ni tan arrepentido ni encantado, de haberme conocido, lo confieso