Rapidly spinning up a VM with Ubuntu, Docker and Minikube (using the –vm-driver=none option) on my Windows laptop using Vagrant and Oracle VirtualBox

2

For training and demo purposes, on my Windows laptop, I needed an environment with a guest Operating System, Docker and Minikube available within an Oracle VirtualBox appliance.

In this article, I will share with you the steps I took, to get Docker and Minikube (using the –vm-driver=none option) installed on top of an Ubuntu guest Operating System within an Oracle VirtualBox appliance, with the help of Vagrant.

The prerequisites for following along, are to have the following installed:

The Vagrant VirtualBox provider is compatible with VirtualBox versions 4.0.x, 4.1.x, 4.2.x, 4.3.x, 5.0.x, 5.1.x, 5.2.x, and 6.0.x. Other versions are unsupported and the provider will display an error message. Please note that beta and pre-release versions of VirtualBox are not supported and may not be well-behaved.

Minikube

Minikube is a tool that makes it easy to run Kubernetes locally. Minikube runs a single-node Kubernetes cluster for users looking to try out Kubernetes or develop with it day-to-day.
[https://kubernetes.io/docs/setup/minikube/]

Normally, you have to install some kind of hypervisor (for example VirtualBox or KVM) on your Operating System that the minikube start command will use to create and configure a Virtual Machine, that runs a single-node Kubernetes cluster. After that you have to install kubectl.

In my case however that Operating System will already be running in a VM. Creating a VM within a VM is not going to work, but there is a solution for this.

Minikube also supports a –vm-driver=none option that runs the Kubernetes components on the host and not in a VM. Using this driver requires Docker (docker-ce 18.06) and a Linux environment but not a hypervisor.

NOTE: Minikube also supports a –vm-driver=none option that runs the Kubernetes components on the host and not in a VM. Docker is required to use this driver but no hypervisor. If you use –vm-driver=none, be sure to specify a bridge network for docker. Otherwise it might change between network restarts, causing loss of connectivity to your cluster. None driver requires docker-ce 18.06 (18.09+ will be compatible from kubernetes version 1.13+, see #3323) .
[https://github.com/kubernetes/minikube#other-ways-to-install]

So, for my training environment I needed to install, within VirtualBox:

With this list in mind I started to look for the requirements needed by each of the products to be installed.

I knew I had to install Docker on a Linux environment within VirtualBox. Luckily my colleague Lucas Jellema wrote a blog in May 2018, that helped me on the way with creating the VirtualBox appliance with the help of Vagrant.
[Rapidly spinning up a VM with Ubuntu and Docker–on my Windows machine using Vagrant and VirtualBox]

Linux environment

Following my colleagues blog, I chose Ubuntu.

Docker

Docker Community Edition (CE) is ideal for individual developers and small teams looking to get started with Docker and experimenting with container-based apps.

Docker Enterprise Edition (EE) is designed for enterprise development and IT teams who build, ship, and run business critical applications in production at scale.
[https://docs.docker.com/install/overview/]

Also remember the Minikube –vm-driver=none option requires Docker CE, version 18.06.

So, I chose Docker Community Edition (CE).

To install Docker CE, you need the 64-bit version of one of these Ubuntu versions:

  • Cosmic 18.10
  • Bionic 18.04 (LTS)
  • Xenial 16.04 (LTS)

[https://docs.docker.com/install/linux/docker-ce/ubuntu/]

Knowing that I was going to use Vagrant, I had a look at the available Vagrant Boxes for VirtualBox (and Ubuntu).
[https://app.vagrantup.com/boxes/search]

Based on the available Vagrant Boxes and the Ubuntu short list (for Docker CE), I chose Ubuntu Xenial 16.04 as the Vagrant Box.

Then I started to put together the Vagrantfile.
Because I am new to Vagrant I also tried out some things, along the way.

Installing Ubuntu Xenial 16.04

First, I created a subdirectory named env on my Windows laptop.

From that directory I opened a Windows Command Prompt (cmd) and typed:

vagrant init ubuntu/xenial64

This initializes the current directory to be a Vagrant environment by creating an initial Vagrantfile if one does not already exist.
[https://www.vagrantup.com/docs/cli/init.html]

With the following output:

A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

This created an example Vagrantfile in the env directory, with the following content:

  
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://vagrantcloud.com/search.
  config.vm.box = "ubuntu/xenial64"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # NOTE: This will enable public access to the opened port
  # config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine and only allow access
  # via 127.0.0.1 to disable public access
  # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  # config.vm.provider "virtualbox" do |vb|
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
  #   vb.memory = "1024"
  # end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   apt-get update
  #   apt-get install -y apache2
  # SHELL
end

Please see https://www.vagrantup.com/docs/vagrantfile/machine_settings.html for information about Vagrantfile. Below I will explain the configuration options that I will be using in the Vagrantfile in this blog:

  • config.vm.box
      This configures what box the machine will be brought up against. The value here should be the name of an installed box or a shorthand name of a box in HashiCorp’s Vagrant Cloud.
  • config.vm.network
      Configures networks on the machine. Please see the networking page for more information.
  • config.vm.provider
      Configures provider-specific configuration, which is used to modify settings which are specific to a certain provider. If the provider you are configuring does not exist or is not setup on the system of the person who runs vagrant up, Vagrant will ignore this configuration block. This allows a Vagrantfile that is configured for many providers to be shared among a group of people who may not have all the same providers installed.
  • config.vm.provision
      Configures provisioners on the machine, so that software can be automatically installed and configured when the machine is created. Please see the page on provisioners for more information on how this setting works.

For simplicity I changed the content of the Vagrantfile to:

 
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
     
  config.vm.provider "virtualbox" do |vb|
      vb.memory = "4096"
      vb.cpus = "1"
  end
  
end

Windows Command Prompt: vagrant up

This command creates and configures guest machines according to your Vagrantfile.
[https://www.vagrantup.com/docs/cli/up.html]

With the following output:

Bringing machine ‘default’ up with ‘virtualbox’ provider…
==> default: Importing base box ‘ubuntu/xenial64’…
==> default: Matching MAC address for NAT networking…
==> default: Checking if box ‘ubuntu/xenial64’ is up to date…
==> default: Setting the name of the VM: env_default_1548960518002_8988

By default, Vagrant sets the Virtual Machine Name to the containing folder of the Vagrantfile plus a timestamp of when the machine was created. By setting another name, your VM can be more easily identified.
[https://www.vagrantup.com/docs/virtualbox/configuration.html]

The name defaults to the format: <directory>_default_<timestamp>

Next I changed the name of the VM.

Content of Vagrantfile:

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
     
  config.vm.provider "virtualbox" do |vb|
      vb.name = "Ubuntu Minikube"
      vb.memory = "4096"
      vb.cpus = "1"
  end
  
end

In order to stop the running machine and destroy its resources, use the following command on the Windows Command Prompt: vagrant destroy

With the following output:

    default: Are you sure you want to destroy the ‘default’ VM? [y/N] y
==> default: Forcing shutdown of VM…
==> default: Destroying VM and associated drives…


This command stops the running machine Vagrant is managing and destroys all resources that were created during the machine creation process. After running this command, your computer should be left at a clean state, as if you never created the guest machine in the first place.
[https://www.vagrantup.com/docs/cli/destroy.html]

Windows Command Prompt: vagrant up

With the following output:

Bringing machine ‘default’ up with ‘virtualbox’ provider…
==> default: Importing base box ‘ubuntu/xenial64’…
==> default: Matching MAC address for NAT networking…
==> default: Checking if box ‘ubuntu/xenial64’ is up to date…
==> default: Setting the name of the VM: Ubuntu Minikube

With regard to the machine ‘default’ I also noticed this name is used when you for example use the vagrant destroy command, as we have seen above:


default: Are you sure you want to destroy the ‘default‘ VM? [y/N] y
==> default: Forcing shutdown of VM…
==> default: Destroying VM and associated drives…

In line with the method call for creating multiple machines, you can also explicitly define a VM and that name then replaces the token ‘default’.

Multiple machines are defined within the same project Vagrantfile using the config.vm.define method call.
[https://www.vagrantup.com/docs/multi-machine/]

Content of Vagrantfile:

 
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  
  config.vm.define "ubuntu_minikube" do |ubuntu_minikube|
  
    config.vm.provider "virtualbox" do |vb|
        vb.name = "Ubuntu Minikube"
        vb.memory = "4096"
        vb.cpus = "1"    
    end
  
  end

end

Windows Command Prompt: vagrant up

With the following output:

Bringing machine ‘ubuntu_minikube‘ up with ‘virtualbox’ provider…
==> ubuntu_minikube: Importing base box ‘ubuntu/xenial64’…
==> ubuntu_minikube: Matching MAC address for NAT networking…
==> ubuntu_minikube: Checking if box ‘ubuntu/xenial64’ is up to date…
==> ubuntu_minikube: Setting the name of the VM: Ubuntu Minikube

So, when you use the vagrant destroy command, the following output is shown:

    ubuntu_minikube: Are you sure you want to destroy the ‘ubuntu_minikube‘ VM? [y/N] y
==> ubuntu_minikube: Forcing shutdown of VM…
==> ubuntu_minikube: Destroying VM and associated drives…

Installing Docker CE

To install the needed software, I added to the env directory a subdirectory called scripts.

In the scripts directory I created a file docker.sh with the following content:

 
#!/bin/bash
echo "**** Begin installing Docker CE"

#Uninstall old versions
sudo apt-get remove docker docker-engine docker.io containerd runc

#Set up the repository
##Update the apt package index
sudo apt-get update
##Install packages to allow apt to use a repository over HTTPS
sudo apt-get install apt-transport-https
sudo apt-get install ca-certificates
sudo apt-get install curl
sudo apt-get install gnupg-agent
sudo apt-get install software-properties-common
##Add Docker’s official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
##Set up the stable repository
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

#Install Docker CE
##Update the apt package index
sudo apt-get update
#Install a specific version of Docker CE 
sudo apt-get install -y docker-ce=18.06.1~ce~3-0~ubuntu
#Verify that Docker CE is installed correctly by running the hello-world image
sudo docker run hello-world
#use Docker as a non-root user
usermod -aG docker vagrant

echo "**** End installing Docker CE"

This content is based on the steps, described in the Docker CE for Ubuntu installation guide:

  • Uninstall old versions
  • Install using the repository
      Before you install Docker CE for the first time on a new host machine, you need to set up the Docker repository. Afterward, you can install and update Docker from the repository.
  • Install a specific version of Docker CE
  • [https://docs.docker.com/install/linux/docker-ce/ubuntu/]

Also remember the Minikube –vm-driver=none option requires Docker CE, version 18.06.

So, I chose to install a specific version of Docker CE.

Remark:

When I used the original example line (from the docker documentation) for installing a specific version of Docker CE, that part of the Vagrantfile looked like:

#Install a specific version of Docker CE
sudo apt-get install docker-ce=18.06.1~ce~3-0~ubuntu

The output from command vagrant up was:


ubuntu_minikube: The following NEW packages will be installed:
ubuntu_minikube: aufs-tools cgroupfs-mount docker-ce libltdl7 pigz
ubuntu_minikube: 0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.
ubuntu_minikube: Need to get 40.2 MB of archives.
ubuntu_minikube: After this operation, 198 MB of additional disk space will be used.
ubuntu_minikube: Do you want to continue?
ubuntu_minikube: [Y/n]
ubuntu_minikube: Abort.
ubuntu_minikube: sudo: docker: command not found

Apparently the command was aborted.
So, I changed that part of the Vagrantfile to:

 
#Install a specific version of Docker CE
sudo apt-get install -y docker-ce=18.06.1~ce~3-0~ubuntu

Here is why:
-y, –yes, –assume-yes
      Automatic yes to prompts; assume “yes” as answer to all prompts and run
      non-interactively. If an undesirable situation, such as changing a held package,
      trying to install a unauthenticated package or removing an essential package occurs
      then apt-get will abort. Configuration Item: APT::Get::Assume-Yes.
[http://manpages.ubuntu.com/manpages/bionic/man8/apt-get.8.html]

Be aware that in addition you could also use the -quiet flag with the apt-get command.

-q, –quiet
      Quiet; produces output suitable for logging, omitting progress indicators. More q’s
      will produce more quiet up to a maximum of 2. You can also use -q=# to set the quiet
      level, overriding the configuration file. Note that quiet level 2 implies -y; you
      should never use -qq without a no-action modifier such as -d, –print-uris or -s as
      APT may decide to do something you did not expect. Configuration Item: quiet.
[http://manpages.ubuntu.com/manpages/bionic/man8/apt-get.8.html]

Content of Vagrantfile:

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  
  config.vm.define "ubuntu_minikube" do |ubuntu_minikube|
  
    config.vm.provider "virtualbox" do |vb|
        vb.name = "Ubuntu Minikube"
        vb.memory = "4096"
        vb.cpus = "1"
        
    args = []
    config.vm.provision "shell",
        path: "scripts/docker.sh",
        args: args
    end
  
  end

end

From here on in this blog, for simplicity, I will no longer mention the vagrant destroy command preceding the vagrant up command.

Windows Command Prompt: vagrant up

With the following output:

Bringing machine ‘ubuntu_minikube’ up with ‘virtualbox’ provider…
==> ubuntu_minikube: Importing base box ‘ubuntu/xenial64’…
==> ubuntu_minikube: Matching MAC address for NAT networking…
==> ubuntu_minikube: Checking if box ‘ubuntu/xenial64’ is up to date…
==> ubuntu_minikube: Setting the name of the VM: Ubuntu Minikube

ubuntu_minikube: **** Begin installing Docker CE

ubuntu_minikube:
ubuntu_minikube: Hello from Docker!
ubuntu_minikube: This message shows that your installation appears to be working correctly.
ubuntu_minikube:
ubuntu_minikube: To generate this message, Docker took the following steps:
ubuntu_minikube: 1. The Docker client contacted the Docker daemon.
ubuntu_minikube: 2. The Docker daemon pulled the “hello-world” image from the Docker Hub.
ubuntu_minikube: (amd64)
ubuntu_minikube: 3. The Docker daemon created a new container from that image which runs the
ubuntu_minikube: executable that produces the output you are currently reading.
ubuntu_minikube: 4. The Docker daemon streamed that output to the Docker client, which sent it
ubuntu_minikube: to your terminal.
ubuntu_minikube:
ubuntu_minikube: To try something more ambitious, you can run an Ubuntu container with:
ubuntu_minikube: $ docker run -it ubuntu bash
ubuntu_minikube:
ubuntu_minikube: Share images, automate workflows, and more with a free Docker ID:
ubuntu_minikube: https://hub.docker.com/
ubuntu_minikube:
ubuntu_minikube: For more examples and ideas, visit:
ubuntu_minikube: https://docs.docker.com/get-started/
ubuntu_minikube: **** End installing Docker CE

Installing kubectl

In the scripts directory I created a file kubectl.sh with the following content:

 
#!/bin/bash
echo "**** Begin installing kubectl"

#Install kubectl binary
sudo apt-get update && sudo apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl

#Check the kubectl configuration
kubectl cluster-info

echo "**** End installing kubectl"

This content is based on the steps, described in the Kubernetes, Install and Set Up kubectl guide:

  • Install kubectl binary using native package management
  • Check the kubectl configuration

[https://kubernetes.io/docs/tasks/tools/install-kubectl/]

Content of Vagrantfile:

 Vagrant.configure("2") do |config|
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  
  config.vm.define "ubuntu_minikube" do |ubuntu_minikube|
  
    config.vm.provider "virtualbox" do |vb|
        vb.name = "Ubuntu Minikube"
        vb.memory = "4096"
        vb.cpus = "1"
        
    args = []
    config.vm.provision "shell",
        path: "scripts/docker.sh",
        args: args
	    
    args = []
    config.vm.provision "shell",
        path: "scripts/kubectl.sh",
        args: args
    end
  
  end

end

Windows Command Prompt: vagrant up

With the following output:

Bringing machine ‘ubuntu_minikube’ up with ‘virtualbox’ provider…
==> ubuntu_minikube: Importing base box ‘ubuntu/xenial64’…
==> ubuntu_minikube: Matching MAC address for NAT networking…
==> ubuntu_minikube: Checking if box ‘ubuntu/xenial64’ is up to date…
==> ubuntu_minikube: Setting the name of the VM: Ubuntu Minikube

ubuntu_minikube: **** Begin installing kubectl

ubuntu_minikube: Setting up kubectl (1.13.2-00) …
ubuntu_minikube:
ubuntu_minikube: To further debug and diagnose cluster problems, use ‘kubectl cluster-info dump’.
ubuntu_minikube: The connection to the server localhost:8080 was refused – did you specify the right host or port?
ubuntu_minikube: **** End installing kubectl

As you can see in the output, there is a problem. The following type of message is shown (in response to using the kubectl cluster-info command) :

If you see a message similar to the following, kubectl is not correctly configured or not able to connect to a Kubernetes cluster.

The connection to the server <server-name:port> was refused – did you specify the right host or port?

For example, if you are intending to run a Kubernetes cluster on your laptop (locally), you will need a tool like Minikube to be installed first and then re-run the commands stated above.
[https://kubernetes.io/docs/tasks/tools/install-kubectl/#check-the-kubectl-configuration]

According to the text above, Minikube needs to be installed first, so that we have a running Kubernetes cluster. Only then, kubectl can connect to that cluster.

So, let’s try this by changing the installation order in the Vagrantfile (you can see further below).

Installing Minikube

In the scripts directory I created a file minikube.sh with the following content:

 
#!/bin/bash
echo "**** Begin downloading minikube"

#Download a static binary
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.32.0/minikube-linux-amd64
chmod +x minikube

#Add the Minikube executable to your path
sudo cp minikube /usr/local/bin/
rm minikube

echo "**** End downloading minikube"

echo "**** Begin starting a Cluster"

#Start a Cluster
minikube start --vm-driver=none

echo "**** End starting a Cluster"

This content is based on the steps, described in the Kubernetes documentation:

In order for kubectl to connect to a Kuberneter cluster, Minikube must be started.

The minikube start command can be used to start your cluster. This command creates and configures a Virtual Machine that runs a single-node Kubernetes cluster. This command also configures your kubectl installation to communicate with this cluster.
[https://kubernetes.io/docs/setup/minikube/#starting-a-cluster]

Content of Vagrantfile:

 
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  
  config.vm.define "ubuntu_minikube" do |ubuntu_minikube|
  
    config.vm.provider "virtualbox" do |vb|
        vb.name = "Ubuntu Minikube"
        vb.memory = "4096"
        vb.cpus = "1"
        
    args = []
    config.vm.provision "shell",
        path: "scripts/docker.sh",
        args: args
        
    args = []
    config.vm.provision "shell",
        path: "scripts/minikube.sh",
        args: args

    args = []
    config.vm.provision "shell",
        path: "scripts/kubectl.sh",
        args: args
    end
  
  end

end

Windows Command Prompt: vagrant up

With the following output:

Bringing machine ‘ubuntu_minikube’ up with ‘virtualbox’ provider…
==> ubuntu_minikube: Importing base box ‘ubuntu/xenial64’…
==> ubuntu_minikube: Matching MAC address for NAT networking…
==> ubuntu_minikube: Checking if box ‘ubuntu/xenial64’ is up to date…
==> ubuntu_minikube: Setting the name of the VM: Ubuntu Minikube

ubuntu_minikube: **** Begin downloading minikube

ubuntu_minikube: **** End downloading minikube
ubuntu_minikube: **** Begin starting a Cluster

ubuntu_minikube: Kubectl is now configured to use the cluster.

ubuntu_minikube: Everything looks great. Please enjoy minikube!
ubuntu_minikube: **** End starting a Cluster

ubuntu_minikube: **** Begin installing kubectl

ubuntu_minikube: Setting up kubectl (1.13.2-00) …
ubuntu_minikube: Kubernetes master is running at https://10.0.2.15:8443
ubuntu_minikube: KubeDNS is running at https://10.0.2.15:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
ubuntu_minikube:
ubuntu_minikube: To further debug and diagnose cluster problems, use ‘kubectl cluster-info dump’.
ubuntu_minikube: **** End installing kubectl

Everything seems to be working fine.

So now let’s try to interact with the Cluster. This can be done in the following ways:

Interact with the Cluster via kubectl

Go to the env directory and open a Windows Command Prompt (cmd) to access linux (within the VirtualBox Appliance) via ssh: vagrant ssh

Linux Command Prompt: kubectl get nodes

With the following output:
The connection to the server localhost:8080 was refused – did you specify the right host or port?

Again, we get the message we have seen before.

The connection to the server <server-name:port> was refused – did you specify the right host or port?

If you see a message similar to the following, kubectl is not correctly configured or not able to connect to a Kubernetes cluster.
[https://kubernetes.io/docs/tasks/tools/install-kubectl/#check-the-kubectl-configuration]

Earlier we fixed the connection to the Kubernetes cluster, so now let’s focus on having the correct configuration.

By default, kubectl looks for a file named config in the $HOME/.kube directory. You can specify other kubeconfig files by setting the KUBECONFIG environment variable or by setting the –kubeconfig flag.
[https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/]

To make kubectl work for your non-root user, run these commands, which are also part of the kubeadm init output:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

export KUBECONFIG=/etc/kubernetes/admin.conf
[https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/]

Until now all the commands via the Vagrantfile (and provisioning scripts) are via Vagrant executed as user root with /root as $HOME.

However, via de Linux Command Prompt (ssh), the user named vagrant is executing the kubectl commands. This user does not have a file named config in the $HOME/.kube directory.

Linux Command Prompt: whoami
With the following output: vagrant

Linux Command Prompt: echo $HOME
With the following output: /home/vagrant

Linux Command Prompt: cd $HOME
Linux Command Prompt: ls -latr

With the following output:

total 32
-rw-r–r– 1 vagrant vagrant 655 Jan 23 08:29 .profile
-rw-r–r– 1 vagrant vagrant 3771 Jan 23 08:29 .bashrc
-rw-r–r– 1 vagrant vagrant 220 Jan 23 08:29 .bash_logout
drwxr-xr-x 4 root root 4096 Feb 3 12:29 ..
drwx—— 2 vagrant vagrant 4096 Feb 3 12:29 .cache
drwx—— 2 vagrant vagrant 4096 Feb 3 12:29 .ssh
-rw——- 1 vagrant vagrant 121 Feb 3 12:39 .bash_history
drwxr-xr-x 4 vagrant vagrant 4096 Feb 3 12:39 .

Linux Command Prompt: cd /etc/kubernetes
Linux Command Prompt: ls -latr

With the following output:

total 48
drwxr-xr-x 2 root root 4096 Feb 3 12:33 addons
drwxr-xr-x 93 root root 4096 Feb 3 12:33 ..
-rw——- 1 root root 5505 Feb 3 12:34 admin.conf
-rw——- 1 root root 5525 Feb 3 12:34 kubelet.conf
-rw——- 1 root root 5541 Feb 3 12:34 controller-manager.conf
-rw——- 1 root root 5489 Feb 3 12:34 scheduler.conf
drwxr-xr-x 4 root root 4096 Feb 3 12:34 .
drwxr-xr-x 2 root root 4096 Feb 3 12:34 manifests

Linux Command Prompt: exit

In the scripts directory I changed file kubectl.sh to the following content:

 
#!/bin/bash
echo "**** Begin installing kubectl"

#Install kubectl binary
sudo apt-get update && sudo apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl

#Check the kubectl configuration
kubectl cluster-info

#Make kubectl work for your non-root user named vagrant
mkdir -p /home/vagrant/.kube
sudo cp -i /etc/kubernetes/admin.conf /home/vagrant/.kube/config
sudo chown vagrant:vagrant /home/vagrant/.kube/config

echo "**** End installing kubectl"

With the chown command I changed the config file’s owner to vagrant and the group to vagrant with a single command.
[https://help.ubuntu.com/community/FilePermissions]

Windows Command Prompt: vagrant up

With the following output:

Bringing machine ‘ubuntu_minikube’ up with ‘virtualbox’ provider…
==> ubuntu_minikube: Importing base box ‘ubuntu/xenial64’…
==> ubuntu_minikube: Matching MAC address for NAT networking…
==> ubuntu_minikube: Checking if box ‘ubuntu/xenial64’ is up to date…
==> ubuntu_minikube: Setting the name of the VM: Ubuntu Minikube

ubuntu_minikube: **** Begin installing kubectl

ubuntu_minikube: Setting up kubectl (1.13.2-00) …
ubuntu_minikube: Kubernetes master is running at https://10.0.2.15:8443
ubuntu_minikube: KubeDNS is running at https://10.0.2.15:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
ubuntu_minikube:
ubuntu_minikube: To further debug and diagnose cluster problems, use ‘kubectl cluster-info dump’.
ubuntu_minikube: **** End installing kubectl

Linux Command Prompt: cd $HOME/.kube
Linux Command Prompt: ls -latr

With the following output:

total 16
drwxr-xr-x 5 vagrant vagrant 4096 Feb 3 14:10 ..
-rw——- 1 vagrant vagrant 5505 Feb 3 14:10 config
drwxr-xr-x 2 root root 4096 Feb 3 14:10 .

Now that the correct configuration is in place let’s try again to interact with the Cluster via kubectl.

Linux Command Prompt: kubectl get nodes

With the following output:

NAME STATUS ROLES AGE VERSION
minikube Ready master 2m41s v1.12.4

Interact with the Cluster via the Dashboard

Now let’s try to interact with the Cluster via the Dashboard.

You can access Dashboard using the kubectl command-line tool by running the following command:

kubectl proxy

Kubectl will make Dashboard available at:

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

The UI can only be accessed from the machine where the command is executed. See kubectl proxy –help for more options.
[https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/]

Linux Command Prompt: exit

In the scripts directory I changed file kubectl.sh to the following content:

 
#!/bin/bash
echo "**** Begin installing kubectl"

#Install kubectl binary
sudo apt-get update && sudo apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl

#Check the kubectl configuration
kubectl cluster-info

#Make kubectl work for your non-root user named vagrant
mkdir -p /home/vagrant/.kube
sudo cp -i /etc/kubernetes/admin.conf /home/vagrant/.kube/config
sudo chown vagrant:vagrant /home/vagrant/.kube/config

echo "**** End installing kubectl"

echo "**** Begin preparing dashboard"

kubectl proxy

echo "**** End preparing dashboard"

Because kubectl will make the Dashboard available at:

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

and I wanted to use a Web Browser on my Windows laptop, I used the forwarded_port configuration option, to forward port 8001 on my host (Windows) to port 8001 on my guest (Ubuntu).

Vagrant forwarded ports allow you to access a port on your host machine and have all data forwarded to a port on the guest machine, over either TCP or UDP.
[https://www.vagrantup.com/docs/networking/forwarded_ports.html]

Content of Vagrantfile:

 
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  
  config.vm.define "ubuntu_minikube" do |ubuntu_minikube|
  
    config.vm.network "forwarded_port",
      guest: 8001,
      host:  8001,
      auto_correct: true
  
    config.vm.provider "virtualbox" do |vb|
        vb.name = "Ubuntu Minikube"
        vb.memory = "4096"
        vb.cpus = "4"
        
    args = []
    config.vm.provision "shell",
        path: "scripts/docker.sh",
        args: args
        
    args = []
    config.vm.provision "shell",
        path: "scripts/minikube.sh",
        args: args

    args = []
    config.vm.provision "shell",
        path: "scripts/kubectl.sh",
        args: args    
    end
  
  end

end

Windows Command Prompt: vagrant up

With the following output:

Bringing machine ‘ubuntu_minikube’ up with ‘virtualbox’ provider…
==> ubuntu_minikube: Importing base box ‘ubuntu/xenial64’…
==> ubuntu_minikube: Matching MAC address for NAT networking…
==> ubuntu_minikube: Checking if box ‘ubuntu/xenial64’ is up to date…
==> ubuntu_minikube: Setting the name of the VM: Ubuntu Minikube
==> ubuntu_minikube: Clearing any previously set network interfaces…
==> ubuntu_minikube: Preparing network interfaces based on configuration…
ubuntu_minikube: Adapter 1: nat
==> ubuntu_minikube: Forwarding ports…
ubuntu_minikube: 8001 (guest) => 8001 (host) (adapter 1)
ubuntu_minikube: 22 (guest) => 2222 (host) (adapter 1)
==> ubuntu_minikube: Running ‘pre-boot’ VM customizations…
==> ubuntu_minikube: Booting VM…

ubuntu_minikube: Starting to serve on 127.0.0.1:8001

Remark:

When I used the following commands in the end part of the minikube.sh file in order to get the dashboard URL, in the output I got the error you see below.

echo "**** Begin starting a Cluster"

#Start a Cluster
minikube start --vm-driver=none
echo "Dashboard UI: "
minikube dashboard --url

echo "**** End starting a Cluster"

Error:

ubuntu_minikube: Dashboard UI:
ubuntu_minikube: ========================================
ubuntu_minikube: kubectl could not be found on your path. kubectl is a requirement for using minikube
ubuntu_minikube: To install kubectl, please run the following:
ubuntu_minikube:
ubuntu_minikube: curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v1.12.4/bin/linux/amd64/kubectl && chmod +x kubectl && sudo cp kubectl /usr/local/bin/ && rm kubectl
ubuntu_minikube:
ubuntu_minikube: To disable this message, run the following:
ubuntu_minikube:
ubuntu_minikube: minikube config set WantKubectlDownloadMsg false
ubuntu_minikube: ========================================
ubuntu_minikube: F0208 10:59:29.351260 5606 dashboard.go:77] kubectl proxy: kubectl not found in PATH: exec: “kubectl”: executable file not found in $PATH

The minikube dashboard command needs kubectl, and that wasn’t installed, because minikube needed to be installed first (see before).

When I used the following commands in the end part of the kubectl.sh file in order to get the dashboard URL, in the output I could see the script wasn’t executed till the last command.
The –url flag means: Display dashboard URL instead of opening a browser

 
echo "**** Begin preparing dashboard"

echo "Dashboard UI: "
minikube dashboard --url
kubectl proxy

echo "**** End preparing dashboard"

Output:

ubuntu_minikube: **** End installing kubectl
ubuntu_minikube: **** Begin preparing dashboard
ubuntu_minikube: Dashboard UI:
ubuntu_minikube:
http://127.0.0.1:42796/api/v1/namespaces/kube-system/services/http:kubernetes-dashboard:/proxy/
On the Internet I found others had the same problem, but it seems to be done intentionally as a security precaution.
[https://github.com/kubernetes/minikube/issues/3236]

So, I removed the minikube dashboard –url command, because from the documentation and the output we know by now the dashboard URL.

In a Web Browser I entered the URL:

http://127.0.0.1:8001/api/v1/namespaces/kube-system/services/http:kubernetes-dashboard:/proxy/

And got an error message: This site can’t be reached.

So again, a had to find a solution. I search on the Internet and found a solution that worked for me.
[https://stackoverflow.com/questions/47173463/how-to-access-local-kubernetes-minikube-dashboard-remotely/47585628]

In the scripts directory I changed file kubectl.sh to the following content:

 
#!/bin/bash
echo "**** Begin installing kubectl"

#Install kubectl binary
sudo apt-get update && sudo apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl

#Check the kubectl configuration
kubectl cluster-info

#Make kubectl work for your non-root user named vagrant
mkdir -p /home/vagrant/.kube
sudo cp -i /etc/kubernetes/admin.conf /home/vagrant/.kube/config
sudo chown vagrant:vagrant /home/vagrant/.kube/config

echo "**** End installing kubectl"

echo "**** Begin preparing dashboard"

kubectl proxy --address='0.0.0.0'

echo "**** End preparing dashboard"

Remark:

You can also use the following command:
kubectl proxy –address=’0.0.0.0′ –port=8001

Windows Command Prompt: vagrant up

With the following output:

Bringing machine ‘ubuntu_minikube’ up with ‘virtualbox’ provider…
==> ubuntu_minikube: Importing base box ‘ubuntu/xenial64’…
==> ubuntu_minikube: Matching MAC address for NAT networking…
==> ubuntu_minikube: Checking if box ‘ubuntu/xenial64’ is up to date…
==> ubuntu_minikube: Setting the name of the VM: Ubuntu Minikube
==> ubuntu_minikube: Clearing any previously set network interfaces…
==> ubuntu_minikube: Preparing network interfaces based on configuration…
ubuntu_minikube: Adapter 1: nat
==> ubuntu_minikube: Forwarding ports…
ubuntu_minikube: 8001 (guest) => 8001 (host) (adapter 1)
ubuntu_minikube: 22 (guest) => 2222 (host) (adapter 1)
==> ubuntu_minikube: Running ‘pre-boot’ VM customizations…
==> ubuntu_minikube: Booting VM…

ubuntu_minikube: Starting to serve on [::]:8001

In a Web Browser I entered the URL:

http://127.0.0.1:8001/api/v1/namespaces/kube-system/services/http:kubernetes-dashboard:/proxy/

And I finally got the result I was looking for:

Navigating to Cluster | Nodes gave me the following result:

So here you can see we have a Kubernetes cluster with one node, named “minikube”.

After that I closed the Windows Command Prompt.

I opened Oracle VM VirtualBox Manager and for the Running “Ubuntu Minikube” VM I selected with a right click, Close | Save State.

To conclude this blog, I will sum up the files I used to get it all working:

Of course, you can for example combine all the 3 script files in just one provisioning script file, or even put all the commands in the Vagrantfile. I leave that up to you.

About Author

Marc, active in IT (and with Oracle) since 1995, is a Principal Oracle SOA Consultant with focus on Oracle Cloud, Oracle Service Bus, Oracle SOA Suite, Oracle Database (SQL & PL/SQL) and Java, Docker, Kubernetes, Minikube and Helm. He's Oracle SOA Suite 12c Certified Implementation Specialist. Over the past 20 years he has worked for several customers in the Netherlands. Marc likes to share his knowledge through publications, blog’s and presentations.

2 Comments

  1. Joaz Soares on

    Really nice work you’ve done! Following the detailed steps I managed to reproduce the environment on Hyper-v Ubuntu VM hosted on Windows 10 as well

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.