{"id":16898594,"url":"https://github.com/afbjorklund/vanilla-kubernetes","last_synced_at":"2026-02-27T07:42:45.200Z","repository":{"id":92836432,"uuid":"194709625","full_name":"afbjorklund/vanilla-kubernetes","owner":"afbjorklund","description":"Standard installation of Kubernetes","archived":false,"fork":false,"pushed_at":"2023-05-09T20:00:08.000Z","size":273,"stargazers_count":7,"open_issues_count":1,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-27T18:44:01.595Z","etag":null,"topics":["ansible","docker","k8s","kubeadm","kubernetes","vagrant","virtualbox"],"latest_commit_sha":null,"homepage":"","language":"Jinja","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/afbjorklund.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-01T16:40:48.000Z","updated_at":"2025-02-15T13:04:04.000Z","dependencies_parsed_at":"2024-10-13T17:44:33.206Z","dependency_job_id":null,"html_url":"https://github.com/afbjorklund/vanilla-kubernetes","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/afbjorklund/vanilla-kubernetes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afbjorklund%2Fvanilla-kubernetes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afbjorklund%2Fvanilla-kubernetes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afbjorklund%2Fvanilla-kubernetes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afbjorklund%2Fvanilla-kubernetes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/afbjorklund","download_url":"https://codeload.github.com/afbjorklund/vanilla-kubernetes/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afbjorklund%2Fvanilla-kubernetes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29887786,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T05:38:26.446Z","status":"ssl_error","status_checked_at":"2026-02-27T05:38:25.235Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","docker","k8s","kubeadm","kubernetes","vagrant","virtualbox"],"created_at":"2024-10-13T17:44:30.584Z","updated_at":"2026-02-27T07:42:45.182Z","avatar_url":"https://github.com/afbjorklund.png","language":"Jinja","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Vanilla Kubernetes\n\nInstalling a multi-node Kubernetes cluster, by using:\n\n- Vagrant\n  - Ubuntu (operating system)\n  - VirtualBox (hypervisor)\n\n- Ansible\n  - Docker (container runtime)\n  - Kubeadm (installer)\n\nThere are alternatives, for each of the four components...\n\nBut this setup is a very common and tested combination.\n\nAlternative setups:\n\n- Minikube (uses machine)\n- Microk8s (uses snap)\n\nThose are more all-in-one solutions,and slightly opaque...\n\nThis method is transparent, although resource-intensive:\n\n```console\nvagrant up\n```\n\nWritten by Anders Björklund @afbjorklund\n\nMay 08, 2023 (Kubernetes version v1.27)\n\nOfficial method:\n\n- [Kubespray](https://kubernetes.io/docs/setup/production-environment/tools/kubespray/) (HA cluster)\n\n## Vagrant\n\nYou will need to install the `vagrant` tool, in order to run the `Vagrantfile`:\n\n```ruby\nVagrant.configure(\"2\") do |config|\n  ...\nend\n```\n\n\u003chttps://vagrantup.com\u003e\n\n### Ubuntu\n\nThe virtual machine is packed into a .box, for each provider (such as VirtualBox):\n\n```ruby\n  config.vm.box = \"ubuntu/focal64\"\n  config.vm.box_check_update = true\n```\n\n### VirtualBox\n\nKubernetes wants more resources from VirtualBox, than the default VM allocation:\n\n\u003e - 2 GB or more of RAM per machine (any less will leave little room for your apps)\n\u003e - 2 CPUs or more\n\n```ruby\n  config.vm.provider \"virtualbox\" do |vb|\n    vb.memory = \"2048\"\n    vb.cpus = 2\n  end\n```\n\nAnsible provisioning requires ssh access with sudo, and that python is installed:\n\n```ruby\n  config.vm.provision \"shell\", inline: \u003c\u003c-SHELL\n    apt-get update\n    apt-get install -y python python-apt\n  SHELL\n  config.vm.provision \"ansible\" do |ansible|\n    ansible.playbook = \"ansible/playbook.yml\"\n    ansible.verbose = false\n  end\n```\n\n## Ansible\n\nYou will need to install `ansible-playbook`, in order to run the `playbook.yaml`:\n\n```yaml\n- hosts: all\n  become: yes\n  roles:\n    - docker\n    - kubeadm\n```\n\n\u003chttps://ansible.com\u003e\n\n### Docker\n\nSee: \u003chttps://docs.docker.com/install/linux/docker-ce/ubuntu/\u003e\n\n[ansible/roles/docker/tasks/main.yml](ansible/roles/docker/tasks/main.yml)\n\nPackages:\n\n- docker-ce\n- docker-ce-cli\n- containerd\\.io\n\n### Requirements\n\nSince Kubernetes version 1.24, it is now **required** to install and configure CRI and CNI:\n\n- cri-tools (\u003chttps://github.com/kubernetes-sigs/cri-tools\u003e)\n  `/etc/crictl.yaml`\n- kubernetes-cni (\u003chttps://github.com/containernetworking/plugins\u003e)\n  `/etc/cni/net.d`\n\nThe default container runtime is containerd, to use docker additional setup is needed:\n\n- cri-dockerd (\u003chttps://github.com/Mirantis/cri-dockerd\u003e)\n  `cri-docker.service`\n- `docker-net` (simple `cni0` bridge single-node network)\n  [10-docker-net.conflist](ansible/roles/kubeadm/files/10-docker-net.conflist)\n\n### Kubeadm\n\nSee: \u003chttps://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/\u003e\n\n[ansible/roles/kubeadm/tasks/main.yml](ansible/roles/kubeadm/tasks/main.yml)\n\nPackages:\n\n- kubeadm\n- kubelet\n- kubectl\n\n### Config\n\nThe default kubeadm config doesn't work with docker, only containerd:\n\n```\nFound multiple CRI endpoints on the host. Please define which one do you wish\nto use by setting the 'criSocket' field in the kubeadm configuration file:\nunix:///var/run/containerd/containerd.sock, unix:///var/run/cri-dockerd.sock\n```\n\nUsing kubeadm now requires a YAML file, instead of the previous flags:\n\n```console\n$ vagrant ssh\n...\nvagrant@kubernetes:~$ sudo kubeadm init --config kubeadm-config.yaml\n```\n\n```yaml\napiVersion: kubeadm.k8s.io/v1beta3\nkind: InitConfiguration\nnodeRegistration:\n  criSocket: unix:///var/run/cri-dockerd.sock\n```\n`kubeadm config print init-defaults`\n\n```console\n$ vagrant ssh\n...\nvagrant@kubernetes:~$ sudo kubeadm join --config kubeadm-config.yaml ...\n```\n\n```yaml\napiVersion: kubeadm.k8s.io/v1beta3\nkind: JoinConfiguration\nnodeRegistration:\n  criSocket: unix:///var/run/cri-dockerd.sock\n```\n\n`kubeadm config print join-defaults`\n\n## Access\n\nTo set up access with the regular `ssh` client, use `vagrant ssh-config`:\n\n```console\n$ vagrant ssh-config \u003e\u003e~/.ssh/config\n```\n\nThen you can just use the name of the node, for _any_ command using ssh:\n\n```console\n$ ssh default\n```\n\nOne ssh example would be to tunnel the port of the dashboard (see below):\n\n```console\n$ ssh default -L 8001:127.0.0.1:8001 -N \u0026\n```\n\nTo tunnel the apiserver, it needs `--apiserver-cert-extra-sans=127.0.0.1`:\n\n```console\n$ ssh default -L 6443:127.0.0.1:6443 -N \u0026\n```\n\n`Unable to connect to the server: x509: certificate is valid for 10.96.0.1, 10.0.2.15, not 127.0.0.1`\n\n```yaml\napiVersion: kubeadm.k8s.io/v1beta3\nkind: ClusterConfiguration\napiServer:\n  certSANs:\n    - \"127.0.0.1\"\n```\n\n### Docker\n\nLocally on node:\n\n`ssh default docker version`\n\nRemotely from host:\n\n`DOCKER_HOST=ssh://default docker version`\n\n### Rootless\n\n`sudo apt install -y uidmap`\n\n`echo \"$(whoami):100000:65536\" | sudo tee -a /etc/subuid`\n\n`dockerd-rootless-setuptool.sh install --force`\n\n`docker context use default`\n\nSee: \u003chttps://docs.docker.com/engine/security/rootless/\u003e\n\nNow there are _two_ different daemons:\n\n#### System\n\n`docker --context=default info`\n\n`DOCKER_HOST=unix://var/run/docker.sock`\n\n```shell\nvagrant@kubernetes:~$ systemctl status --system docker\n● docker.service - Docker Application Container Engine\n...\n```\n\n#### User\n\n`docker --context=rootless info`\n\n`DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock`\n\n```shell\nvagrant@kubernetes:~$ systemctl status --user docker\n● docker.service - Docker Application Container Engine (Rootless)\n...\n```\n\nTo access rootless docker over ssh, edit `~/.bashrc`:\n\n```diff\n@@ -2,6 +2,9 @@\n # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)\n # for examples\n \n+DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock\n+\n # If not running interactively, don't do anything\n case $- in\n     *i*) ;;\n```\n\nTo build images rootless, and load them in cluster:\n\n`docker --context=rootless build -t myimage`\n\n`docker --context=rootless save myimage | docker --context=default load`\n\n## Cluster\n\nNow that machines have been prepared, it is time to install Kubernetes.\n\nYou need one master, and one or more nodes (formerly known as minions).\n\nMaster:\n\n```shell\nkubeadm init [--pod-network-cidr=10.244.0.0/16]\n```\n\n\u003chttps://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/\u003e\n\nNote: the change of subnet CIDR is only needed for \"flannel\" (see below)\n\n```yaml\napiVersion: kubeadm.k8s.io/v1beta3\nkind: ClusterConfiguration\nnetworking:\n  podSubnet: \"10.244.0.0/16\"\n```\n\nTo see the token again: `kubeadm token create --print-join-command`\n\nMinion:\n\n```shell\nkubeadm join master:6443 --token ... --discovery-token-ca-cert-hash ...\n```\n\n\u003chttps://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-join/\u003e\n\n### Single-node\n\nIf you only have room for a single machine, you run pods on the master:\n\n```shell\nkubectl taint nodes --all node-role.kubernetes.io/control-plane:NoSchedule-\n```\n\nIn a real production environment, you would have multiple masters (HA).\n\nSee: \u003chttps://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/\u003e\n\n### Network\n\nA cluster network needs to be deployed as well, two popular choices are:\n\n- Flannel\n\n- Calico\n\nThese are normally deployed as resources, by using a `kubectl` command.\n\nSee: \u003chttps://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#pod-network\u003e\n\n### Images\n\nAs per Kubernetes v1.27.0, here is the list of container images used:\n\n```shell\nkubeadm config images list\n```\n\n```text\nregistry.k8s.io/kube-apiserver:v1.27.0\nregistry.k8s.io/kube-controller-manager:v1.27.0\nregistry.k8s.io/kube-scheduler:v1.27.0\nregistry.k8s.io/kube-proxy:v1.27.0\nregistry.k8s.io/pause:3.9\nregistry.k8s.io/etcd:3.5.7-0\nregistry.k8s.io/coredns/coredns:v1.10.1\n```\n\nThey total a download size of 200 MiB, in their default compressed form.\n\nTo download all the kubernetes images to the current container runtime:\n\n```shell\nkubeadm config images pull\n```\n\nUse `docker save` to save all images in a tar archive, for `docker load`.\n\nOptionally use `pigz` or `pixz`, to compress the contents of the .tar file.\n\n```text\nLoaded image: registry.k8s.io/kube-apiserver:v1.27.0\nLoaded image: registry.k8s.io/kube-controller-manager:v1.27.0\nLoaded image: registry.k8s.io/kube-scheduler:v1.27.0\nLoaded image: registry.k8s.io/kube-proxy:v1.27.0\nLoaded image: registry.k8s.io/pause:3.9\nLoaded image: registry.k8s.io/etcd:3.5.7-0\nLoaded image: registry.k8s.io/coredns/coredns:v1.10.1\n```\n\n### Flannel\n\n\u003e Flannel is a way to configure a layer 3 network fabric designed for Kubernetes.\n\n\u003chttps://github.com/flannel-io/flannel\u003e\n\n```shell\nrm -f /etc/cni/net.d/*.conf*  # remove any existing CNI configuration, \"there can be only one\" \u003c/highlander\u003e\nkubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/v0.21.2/Documentation/kube-flannel.yml\n```\n\n```text\ndocker.io/flannelcni/flannel:v0.21.2\ndocker.io/flannelcni/flannel-cni-plugin:v1.1.0\n```\n\n### Storage\n\n\u003e nfs-server-provisioner is an out-of-tree dynamic provisioner for Kubernetes.\n\n\u003chttps://github.com/kubernetes-sigs/nfs-ganesha-server-and-external-provisioner\u003e\n\n```shell\nkubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/nfs-ganesha-server-and-external-provisioner/nfs-server-provisioner-1.4.0/deploy/kubernetes/rbac.yaml\nkubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/nfs-ganesha-server-and-external-provisioner/nfs-server-provisioner-1.4.0/deploy/kubernetes/deployment.yaml\nkubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/nfs-ganesha-server-and-external-provisioner/nfs-server-provisioner-1.4.0/deploy/kubernetes/class.yaml\n```\n\n```text\nk8s.gcr.io/sig-storage/nfs-provisioner:v3.0.0\n```\n\nYou can set it (nfs-server-provisioner) to be the cluster default storage class:\n\n```shell\nkubectl patch storageclass example-nfs -p '{\"metadata\": {\"annotations\":{\"storageclass.kubernetes.io/is-default-class\":\"true\"}}}'\n```\n\nNote: the **node** needs to have support for the `nfsv4` kernel module and nfs-client:\n\n```\nMountVolume.SetUp failed for volume \"pvc-...\" : mount failed: exit status 32 [...]\nOutput: mount: /var/lib/kubelet/pods/.../volumes/kubernetes.io~nfs/pvc-...: bad option;\nfor several filesystems (e.g. nfs, cifs) you might need a /sbin/mount.\u003ctype\u003e helper program.\n```\n\n`sudo apt install nfs-common`\n\n### Dashboard\n\n\u003e Kubernetes Dashboard is a general purpose, web-based UI for Kubernetes clusters.\n\n\u003chttps://github.com/kubernetes/dashboard\u003e\n\n```shell\nkubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml\n```\n\n```text\ndocker.io/kubernetesui/dashboard:v2.7.0\ndocker.io/kubernetesui/metrics-scraper:v1.0.8\n```\n\nNote: to be able to _access_ the dashboard you need to create a user, token and proxy:\n\n`kubectl proxy`\n\n\u003chttp://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/\u003e\n\n\u003chttps://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.md\u003e\n\n### Metrics Server\n\n\u003e Metrics Server collects resource metrics and exposes them in Kubernetes apiserver.\n\n\u003chttps://github.com/kubernetes-sigs/metrics-server\u003e\n\n```shell\nkubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.2/components.yaml\n```\n\n```text\nk8s.gcr.io/metrics-server/metrics-server:v0.6.3\n```\n\nNote: there is a bug in that the server name is not included in the https certificate.\n\nThe easiest workaround is editing the metrics-server Deployment, to allow the access:\n\n`kubectl edit deployment -n kube-system metrics-server`\n\n```diff\n@@ -129,6 +129,7 @@\n     spec:\n       containers:\n       - args:\n+        - --kubelet-insecure-tls\n         - --cert-dir=/tmp\n         - --secure-port=443\n         - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname\n```\n\n`kubectl top node`\n\n`kubectl top pod -A`\n\n## Screenshots\n\n![virtualbox manager](assets/virtualbox-manager.png)\n\n![kubernetes dashboard](assets/kubernetes-dashboard.png)\n\n## References\n\nSep 14, 2020 (Kubernetes version v1.19)\n\u003chttps://itnext.io/kubernetes-production-cluster-with-vagrant-and-virtualbox-on-mac-8718586f179f\u003e\n\nMarch 15, 2019 (Kubernetes version v1.13)\n\u003chttps://kubernetes.io/blog/2019/03/15/kubernetes-setup-using-ansible-and-vagrant/\u003e\n\nApr 2, 2018 (Kubernetes version v1.9)\n\u003chttps://medium.com/@lizrice/kubernetes-in-vagrant-with-kubeadm-21979ded6c63\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafbjorklund%2Fvanilla-kubernetes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fafbjorklund%2Fvanilla-kubernetes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafbjorklund%2Fvanilla-kubernetes/lists"}