vSphere - Install Kubernetes using Ansible
Introduction
In this post, we created a couple of virtual instances running on vSphere using Terraform. We will use that blog post as a reference to install 3 virtual servers on our vSphere environment.
Executing this code, will result in three instances on vSphere. Obviously you will need to modify the variables file for your environment. Use it as a guideline.
Preparing the servers
As I’m using Ubuntu 16.04 as the underlying template, I found that the installation I did as part of the Ansible playbook did not work. So I decided for now to use Terraform to provision the three servers (see previous step) and then to manually install Docker. To do so, follow the next steps. Note that this should not be necessary on Ubuntu 18.04.
cisco@wauterw-k8s-master:~$ sudo add-get update
cisco@wauterw-k8s-master:~$ sudo add-get upgrade
cisco@wauterw-k8s-master:~$ sudo swapoff -a
cisco@wauterw-k8s-master:~$ sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
cisco@wauterw-k8s-master:~$ sudo su
root@wauterw-k8s-master:~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
root@wauterw-k8s-master:~$ apt-get update && apt-get install apt-transport-https ca-certificates curl software-properties-common
root@wauterw-k8s-master:~$ add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
root@wauterw-k8s-master:~$ apt update
root@wauterw-k8s-master:~$ apt install docker-ce
root@wauterw-k8s-master:~$ su cisco
cisco@wauterw-k8s-master:~$ usermod -aG docker ${USER}
cisco@wauterw-k8s-master:~$ cat > /etc/docker/daemon.json < {
> "exec-opts": ["native.cgroupdriver=systemd"],
> "log-driver": "json-file",
> "log-opts": {
> "max-size": "100m"
> },
> "storage-driver": "overlay2"
> }
> EOF
In case the above does not work, you can also simply create a file called daemon.json
in the /etc/docker
folder and cut and paste the following content.
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
cisco@wauterw-k8s-master:~$ sudo su
root@wauterw-k8s:/home/cisco# mkdir -p /etc/systemd/system/docker.service.d
root@wauterw-k8s:/home/cisco# systemctl daemon-reload
root@wauterw-k8s:/home/cisco# systemctl restart docker
Using Ansible to install Kubernetes
Next, let’s install Kubernetes using Ansible. We did this already on AWS instances and DigitalOcean instances, so it should be pretty familiar in the meantime.
Let’s begin with creating a hosts file for our Ansible scripts. We will define 1 master and 2 workers. The IP addresses are the same as the ones in the screenshot above. Obviously, adapt the hosts file to your own environment.
#hosts
[masters]
master ansible_host=10.16.2.236 ansible_user=cisco ansible_password=****
[workers]
worker1 ansible_host=10.16.2.237 ansible_user=cisco ansible_password=****
worker2 ansible_host=10.16.2.238 ansible_user=cisco ansible_password=****
[all:vars]
ansible_python_interpreter=/usr/bin/python3
Next, we will take care of some initial configuration steps. We call this file the initial.yml
file and it essentially will create the ubuntu user, ensure the ubuntu user has sudo rights and setup the keys for us to be able to login via SSH.
#initial.yml
- hosts: all
become: yes
tasks:
- name: create the 'ubuntu' user
user: name=ubuntu append=yes state=present createhome=yes shell=/bin/bash
- name: allow 'ubuntu' to have passwordless sudo
lineinfile:
dest: /etc/sudoers
line: 'ubuntu ALL=(ALL) NOPASSWD: ALL'
validate: 'visudo -cf %s'
- name: set up authorized keys for the ubuntu user
authorized_key: user=ubuntu key="{{item}}"
with_file:
- /home/cisco/software/vSphere_Kubernetes_Ansible/lab_cisco_vsphere.pub
Next up, we will create a different file that installs the Kubernetes specific dependencies on the 3 nodes. In the other posts (installing Kubernetes on AWS and DigitalOcean) we would have first a task to install Docker. As mentioned above, we are running Ubuntu 16.04 and this requires some other steps, hence we installed Docker manually. This results in not having to define a ‘install docker’ tasks in the below kube-dependencies.yml
file. All the other steps remain the same: add Kubernetes repository, install kubelet and kubeadm toolset…
#kube-dependencies.yml
- hosts: all
become: yes
tasks:
- name: install APT Transport HTTPS
apt:
name: apt-transport-https
state: present
- name: add Kubernetes apt-key
apt_key:
url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
state: present
- name: add Kubernetes' APT repository
apt_repository:
repo: deb http://apt.kubernetes.io/ kubernetes-xenial main
state: present
filename: 'kubernetes'
- name: install kubelet
apt:
name: kubelet
state: present
update_cache: true
- name: install kubeadm
apt:
name: kubeadm
state: present
- hosts: master
become: yes
tasks:
- name: install kubectl
apt:
name: kubectl
state: present
When the above is finished, we will create a specific file for the master node. This file will take care of the initialization of the Kubernetes cluster, will create the .kube directory and will copy the admin.conf file to the user’s kube.config file and also install the Calico network.
#master.yml
- hosts: master
become: yes
tasks:
- name: initialize the cluster
shell: kubeadm init --pod-network-cidr=10.244.0.0/16 >> cluster_initialized.txt
args:
chdir: $HOME
creates: cluster_initialized.txt
- name: create .kube directory
become: yes
become_user: ubuntu
file:
path: $HOME/.kube
state: directory
mode: 0755
- name: copy admin.conf to user's kube config
copy:
src: /etc/kubernetes/admin.conf
dest: /home/ubuntu/.kube/config
remote_src: yes
owner: ubuntu
- name: install Pod network
become: yes
become_user: ubuntu
shell: kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml >> pod_network_setup.txt
args:
chdir: $HOME
creates: pod_network_setup.txt
Last step is to create the workers.yml file. In that file, we will first retrieve the join command from the master node and then we will join worker nodes to the cluster.
#workers.yml
- hosts: master
become: yes
gather_facts: false
tasks:
- name: get join command
shell: kubeadm token create --print-join-command
register: join_command_raw
- name: set join command
set_fact:
join_command: "{{ join_command_raw.stdout_lines[0] }}"
- hosts: workers
become: yes
tasks:
- name: join cluster
shell: "{{ hostvars['master'].join_command }} >> node_joined.txt"
args:
chdir: $HOME
creates: node_joined.txt
That’s it in terms of the ansible related files. Let’s know continue with executing these playbooks.
Executing Ansible playbooks
First, we will perform the initial configuration as described above. Simply run the Ansible playbook called initial.yml
.
cisco@wauterw-ubuntu-desktop:~/software/vSphere_Kubernetes_Ansible/Ansible$ ansible-playbook -i hosts initial.yml -K
BECOME password:
PLAY [all] ***********************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************
ok: [worker1]
ok: [master]
ok: [worker2]
TASK [create the 'ubuntu' user] **************************************************************************************
changed: [master]
changed: [worker2]
changed: [worker1]
TASK [allow 'ubuntu' to have passwordless sudo] **********************************************************************
changed: [worker1]
changed: [master]
changed: [worker2]
TASK [set up authorized keys for the ubuntu user] ********************************************************************
changed: [worker2] => (item=ssh-rsa AAAAB3NzaC1yBdB1WW0n73NlxKvIHE/pZk+oKk825QmRCTZy+yez5h30U= wauterw@WAUTERW-M-65P7)
changed: [worker1] => (item=ssh-rsa AAAAB3NzaC1yanyYlPB1WW0n73NlxKvIHE/pZk+oKk825QmRCyez5h30U= wauterw@WAUTERW-M-65P7)
changed: [master] => (item=ssh-rsa AAAAB3NzaC1yc2EAA8N0n73NlxKvIHE/pZk+oKk825QmRCTZy+yez5h30U= wauterw@WAUTERW-M-65P7)
PLAY RECAP ***********************************************************************************************************
master : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
worker1 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
worker2 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Next, we will install the Kubernetes dependencies:
cisco@wauterw-ubuntu-desktop:~/software/vSphere_Kubernetes_Ansible/Ansible$ ansible-playbook -i hosts kube-dependencies.yml -K
BECOME password:
PLAY [all] ***********************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************
ok: [worker2]
ok: [worker1]
ok: [master]
TASK [install APT Transport HTTPS] ***********************************************************************************
ok: [worker2]
ok: [master]
ok: [worker1]
TASK [add Kubernetes apt-key] ****************************************************************************************
changed: [worker2]
changed: [worker1]
changed: [master]
TASK [add Kubernetes' APT repository] ********************************************************************************
changed: [worker2]
changed: [worker1]
changed: [master]
TASK [install kubelet] ***********************************************************************************************
changed: [worker1]
changed: [worker2]
changed: [master]
TASK [install kubeadm] ***********************************************************************************************
changed: [worker2]
changed: [master]
changed: [worker1]
PLAY [master] ********************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************
ok: [master]
TASK [install kubectl] ***********************************************************************************************
ok: [master]
PLAY RECAP ***********************************************************************************************************
master : ok=8 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
worker1 : ok=6 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
worker2 : ok=6 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When the dependencies are installed, it’s time to configure the master.
cisco@wauterw-ubuntu-desktop:~/software/vSphere_Kubernetes_Ansible/Ansible$ ansible-playbook -i hosts master.yml -K
BECOME password:
PLAY [master] ********************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************
ok: [master]
TASK [initialize the cluster] ****************************************************************************************
changed: [master]
TASK [create .kube directory] ****************************************************************************************
changed: [master]
TASK [copy admin.conf to user's kube config] *************************************************************************
changed: [master]
TASK [install Pod network] *******************************************************************************************
changed: [master]
PLAY RECAP ***********************************************************************************************************
master : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And finally, we will have the workers join our Kubernetes cluster by executing the workers playbook.
cisco@wauterw-ubuntu-desktop:~/software/vSphere_Kubernetes_Ansible/Ansible$ ansible-playbook -i hosts workers.yml -K
BECOME password:
PLAY [master] ********************************************************************************************************
TASK [get join command] **********************************************************************************************
changed: [master]
TASK [set join command] **********************************************************************************************
ok: [master]
PLAY [workers] *******************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************
ok: [worker2]
ok: [worker1]
TASK [join cluster] **************************************************************************************************
changed: [worker2]
changed: [worker1]
PLAY RECAP ***********************************************************************************************************
master : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
worker1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
worker2 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Result
The verify that everything went fine and according to plan, we can do a quick check by logging into the master node and check if the workers have joined successfully.
ubuntu@wauterw-k8s-master:/etc/docker$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
wauterw-k8s-master Ready master 10m v1.18.0 10.16.2.236 <none> Ubuntu 16.04.6 LTS 4.4.0-154-generic docker://19.3.8
wauterw-k8s-worker01 Ready <none> 8m2s v1.18.0 10.16.2.237 <none> Ubuntu 16.04.6 LTS 4.4.0-154-generic docker://19.3.8
wauterw-k8s-worker02 Ready <none> 8m2s v1.18.0 10.16.2.238 <none> Ubuntu 16.04.6 LTS 4.4.0-154-generic docker://19.3.8
Take a look at the files on my Github [repo](https://github.com/wiwa1978/blog-hugo-netlify-code/tree/master/vSphere_Kubernetes_Ansible).