Ansible playbooks and Packer templates for creating hybrid Windows/Linux Kubernetes 1.10+ cluster with experimental Flannel pod network (host-gw backend)
NOTE: work in progress - Kubernetes networking heavily relies on Windows HNS which is still unstable.
Ansible playbooks and Packer templates for provisioning of Hyper-V Vagrant boxes and configuration of hybrid Kubernetes 1.10+ cluster with Flannel network (host-gw backend). Currently supports:
Ansible playbooks provided in this repository have been initially based on the official Microsoft Getting Started guide for Kubernetes on Windows. Most of the original scripts in this guide have been replaced by Ansible tasks and NSSM windows services, and on top of that experimental Flannel support with host-gw backend has been added.
The original kubernetes-for-windows was modified by @pablodav trying to reuse more parts from external projects to deploy k8s on linux and focusing this project only on the required parts to add a windows node to the existing kubernetes cluster. For that reason @pablodav have removed most parts of k8s for linux and started to deploy k8s with kubespray then adding this project as integrated part of that to deploy win_node, but the modifications are done thinking in the possibility to integrate with any other k8s project that uses kubeadm (this project uses kubeadm to generate tokens to join the win_node).
ntlm: HTTPSConnectionPool(host='127.0.0.1', port=63008): Max retries exceeded with url: /wsman (Caused by SSLError(SSLError(1, u'[SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:590)'),))
Similar issues are present with Ubuntu templates:
SSH Error: data could not be sent to remote host \"127.0.0.1\". Make sure this host can be reached over ssh
Unfortunately I did not have time to investigate this issue yet, but the Packer provisioning process used to work on lower versions of Ansible and Packer.
For a quickstart you can see: https://github.com/pablodav/kubernetes-for-windows-quickstart
Ansible only:
+ Packer:
Windows has limited support for different pod networks, as mentioned in official Kubernetes Windows guide. Flannel with host-gw (and vxlan) backends and appropriate CNI plugins are currently in experimental stage and are available as the following pull requests by rakelkar: https://github.com/coreos/flannel/pull/921 https://github.com/containernetworking/plugins/pull/85 Unfortunately, these original pull requests had some minor issues which had to be fixed or worked around:
First, install all prerequisites from the previous paragraph. Ensure that a basic Ansible playbook is working properly when using Ubuntu for Windows (WSL) for both Windows and Linux nodes.
A sample inventory file has been provided in Ansible playbook directory. Assuming that you would like to create cluster having the following hosts:
your inventory should be defined as:
# ## Configure 'ip' variable to bind kubernetes services on a
# ## different ip than the default iface
# node1 ansible_host=95.54.0.12 # ip=10.3.0.1
# node2 ansible_host=95.54.0.13 # ip=10.3.0.2
# node3 ansible_host=95.54.0.14 # ip=10.3.0.3
# node4 ansible_host=95.54.0.15 # ip=10.3.0.4
# node5 ansible_host=95.54.0.16 # ip=10.3.0.5
# node6 ansible_host=95.54.0.17 # ip=10.3.0.6
# This inventory is based on an integrated inventory with kubespray
# the kube-master, kube-node, etcd, kube-ingres, k8s-cluster comes from kubespray
# https://github.com/kubernetes-incubator/kubespray/blob/master/docs/integration.md
# https://github.com/kubernetes-incubator/kubespray/blob/master/inventory/sample/hosts.ini
[Location1]
ubuntu01 ansible_host=10.3.0.1
ubuntu02 ansible_host=10.3.0.2
ubuntu03 ansible_host=10.3.0.3
# ## configure a bastion host if your nodes are not directly reachable
# bastion ansible_host=x.x.x.x ansible_user=some_user
[kube-master]
ubuntu01
ubuntu02
[etcd]
ubuntu01
ubuntu02
ubuntu03
[kube-node]
ubuntu02
ubuntu03
[kube-ingress]
ubuntu02
ubuntu03
[k8s-cluster:children]
kube-master
kube-node
kube-ingress
# Add group of master-ubuntu for kubernetes-for-windows project
[master-ubuntu:children]
kube-master
[node-windows]
windows01 kubernetes_node_hostname=windows01 ansible_host=10.3.0.4
windows02 kubernetes_node_hostname=windows02 ansible_host=10.3.0.5
[node:children]
node-windows
# This k8s-cluster-local is a group added to add all variables inside that group
# all variables for kubespray and for kubernetes-for-windows projects in your group_vars inventory
[k8s-cluster-local:children]
k8s-cluster
node-windows
[all-ubuntu:children]
master-ubuntu
The host variable kubernetes_node_hostname
will be used as Windows hostname and at the same time it will be used to identify node in Kubernetes.
Any other variables that you wish to configure are available in group_vars you must copy these vars to your own group_vars dir, for example cluster/service pod CIDRs.
You will find more vars for kubespray roles in k8s-cluster-local, the vars added here already have flannel and are integrated with vars for kubernetes-for-windows.
We are using steps from kubespray integration
First you must have an ansible git repo, if not init one:
git init
Then copy these files to your inventory:
├── ansible.cfg # It will have the roles_path and library path you can read and add to your own ansible.cfg file
├── inventory # For all files in inventory you have a sample in this project
│ ├── kubernetes.ini
│ ├── group_vars
│ │ ├── k8s-cluster-local # Directory with vars for group k8s-cluster-local
│ │ │ ├── all-k8s.yml
│ │ │ ├── k8s-cluster.yml
│ │ │ └── k8s-win.yml
├── roles.kubernetes.yml # It will have all kubespray and kubernetes-for-windows import playbooks
Also create directory:
mkdir -p roles/3d
Then add as submodules kubespray and kubernetes-for-windows to use these roles:
git submodule add https://github.com/kubernetes-incubator/kubespray.git roles/3d/kubespray
git submodule add https://github.com/pablodav/kubernetes-for-windows roles/3d/kubernetes-for-windows # hope in future we can agree with main author of it to use his repository https://github.com/ptylenda/kubernetes-for-windows
You must have now:
ls -1 roles/3d/
kubernetes-for-windows
kubespray
Now you have all the roles and variables ready, just install the requirements from kubespray:
sudo pip install -r roles/3d/kubespray/requirements.txt
In order to install basic Kubernetes packages on Windows and Linux nodes, run roles.kubernetes.yml
playbook:
ansible-playbook roles.kubernetes.yml -i inventory/kubernetes.ini -b -v
You can also install only linux with kubespray:
ansible-playbook roles.kubernetes.yml -i inventory/kubernetes.ini --tags role::kubespray -b -vvv
And then the windows nodes:
ansible-playbook roles.kubernetes.yml -i inventory/kubernetes.ini --tags role::kubernetes-for-windows -b -vvv
You will notice that the role with tag role::kubernetes-for-windows
will patch kube-proxy and kube-flannel:
kubectl get ds -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-flannel 3 3 3 3 3 beta.kubernetes.io/os=linux 34m
kube-proxy 3 3 3 3 3 beta.kubernetes.io/os=linux 8d
So the daemonset will not be deployed in windows.
The playbook consists of the following stages:
Filtering by init
tag omits installation of Kubernetes packages which have been already installed in Step 3.
Installation consists of the following stages:
beta.kubernetes.io/os: linux
has been added in order to prevent from deploying on Windows nodes, where Flannel is being handled independently.Right now the cluster is fully functional, you can proceed with deploying an example Windows service.
The playbook is idempotent, so you can add more nodes to an existing cluster just by extending inventory and rerunning the playbook.
win-webserver.yml contains an example Deployment and Service based on Microsoft guide. In order to deploy the web service execute:
kubectl apply -f win-webserver.yaml
This will also deploy a Kubernetes NodePort Service which can be accessed on every Kubernetes node, both Windows and Linux, for example:
If you wish to tear down the cluster, without destroying the master node, execute the following playbook:
ansible-playbook -i inventory reset-kubeadm-cluster.yml
This will reset only windows node, to reset linux cluster follow the kubespray steps. Remove nodes
Actually it needs review, to integrate kubespray so it is not working as it was working before.
For easier usage of Packer with Ansible Remote provisoner on Windows, an additional wrapper script has been provided: packer-ansible-windows.ps1. Basically it adds Ansible wrappers to PATH and ensures that proxy settings are configured properly.
ISO files are expected to be loaded from ./iso
subdirectory in packer build space. For Ubuntu it is also possible to download the image automatically from the official http server. For Windows you have to provide your own copy of Windows Server 1803 ISO.
To build Windows node on Hyper-V:
$ .\packer-ansible-windows.ps1 build --only=hyperv-iso .\kubernetes-node-windows1803-march2018.json
Default user: ubuntu
Default password: ubuntu
(configurable in template variables)
To build Ubuntu node/master on Hyper-V:
$ .\packer-ansible-windows.ps1 build --only=hyperv-iso .\kubernetes-node-ubuntu1604.json
Default user: Administrator
Default password: password
(configurable in template variables AND in http\Autounattend.xml file, which does not support templating)
This template has been created in order to resolve problems with provisioning Ubuntu Server 16.04 behind a proxy. Keep in mind that:
choose-mirror-bin mirror/http/proxy string addr
. It is not possible to customize addr
in this case.choose-mirror-bin mirror/http/proxy string addr
in preseed.cfg has different impact compared to using mirror/http/proxy=addr
from boot parameters. The latter also affects downloading preseed.cfg from http server (seems like a debconf bug).preseed/url
is sensitive to proxy settings inherited from mirror/http/proxy
(which seems contrary to description of this parameter). Fortunately I have discovered that setting no_proxy={{ .HTTPIP }}
environment variable from boot parameters is enough to force no proxy for wget in order to communicate with Packer http server.auto-install/enable=true
and feed them from preseed.cfgpreseed/url
method for providing preseed.cfg.d-i preseed/late_command string in-target apt-get install -y --install-recommends linux-virtual-lts-xenial linux-tools-virtual-lts-xenial linux-cloud-tools-virtual-lts-xenial;
, directly in preseed.cfg, BEFORE any provisioner runs. These packages are needed in order to discover IP address of VM properly so that Packer can connect via SSH. Otherwise it will be waiting for IP address forever, more details can be found in "Notes" in https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-ubuntu-virtual-machines-on-hyper-v
"environment_vars": [
"FTP_PROXY={{ user `ftp_proxy` }}",
"HTTPS_PROXY={{ user `https_proxy` }}",
"HTTP_PROXY={{ user `http_proxy` }}",
"NO_PROXY={{ user `no_proxy` }}",
"ftp_proxy={{ user `ftp_proxy` }}",
"http_proxy={{ user `http_proxy` }}",
"https_proxy={{ user `https_proxy` }}",
"no_proxy={{ user `no_proxy` }}"
]
"extra_arguments": [
"--extra-vars",
"{'\"http_proxy\":\"{{ user `http_proxy` }}\", \"https_proxy\":\"{{ user `https_proxy` }}\", \"no_proxy\":\"{{ user `no_proxy` }}\", \"ftp_proxy\":\"{{ user `ftp_proxy` }}\"}'"
]
Then handle these variables appropriately in playbook, set environment variables, etc.
[linux]
127.0.0.1 ansible_connection=local
Exclude windows defender on docker path and exe files:
Add-MpPreference -ExclusionPath C:\ProgramData\docker\
set-MpPreference -ExclusionProcess "dockerd.exe, flanneld.exe, kube-proxy.exe, kubelet.exe"
Or exclude the docker path in your antivirus.
In case of doubt with windows defender, disable it temporarly:
Set-MpPreference -DisableRealtimeMonitoring $true
Service start order matters, in some tests I (pablodav) have confirmed that this order is required to get all network devices and IP addresses created during start:
For that reason I have added serialized dependencies on nssm service config on tasks.
If docker Install step fails when using DockerMsftProvider, see which ones are the available versions for docker:
Find-Package –providerName DockerMsftProvider –AllVersions
Then change variable win_docker_version
with correct one, example:
win_docker_version: "17.06.2-ee-16"
Basic proxy usage was added for docker install, but doesn't use auth, only uses:
win_choco_proxy_url: "http://proxy:port"
If something fails when not using proxy, try to empty this var or add is as bool
Use this model for on-premises: