Skip to content

Instantly share code, notes, and snippets.

@luisg0nc
Last active November 8, 2024 17:31
Show Gist options
  • Save luisg0nc/5213d6c7ad548c61de9090c0931d8f74 to your computer and use it in GitHub Desktop.
Save luisg0nc/5213d6c7ad548c61de9090c0931d8f74 to your computer and use it in GitHub Desktop.
Hybrid Kubernetes with OVN Setup

Hybrid Kubernetes with OVN

This gist compiles all necessary information on how to setup a Kubernetes with both Linux and Windows Nodes, enabling both use of Windows and Linux docker containers, using ovn-kubernetes as the network plugin on bare metal servers.

Two machines are required to run this, one will be running Linux Ubuntu and will act as our Master in the cluster and the other machine will be running Windows which will be added to our cluster as a Work Node able to run Windows Containers.

This configuration is based on the available vagrant setup.

I would like to thank Alin Balutoiu and Alin Gabriel Serdean for their amazing help.

Overlay mode architecture diagram:

The following diagram represents the internal architecture details of the overlay mode.

alt text

Table of Contents

Versions

Compilation of all versions used and tested, feel free to implement this setup with more recent versions and leave feedback so that this gist can be updated.

Linux

Versions
Ubuntu 16.04.4 LTS
ovs-dkms 2.9.0-1
ovs-common-switch 2.9.0-2
ovn-central 2.9.0-2
ovn-common-host 2.9.0-2
kubernetes 1.9.3
docker 17.0-ce-rc3

Windows

Versions
Windows Server 1709
ovs-ovn-cloudbase 2.9.0 (BETA)
kubelet 1.9.3
docker 17.0-ce-rc3

Hostnames

To facilitate deployment, hostnames will be used on all machines and their respective scripts.

K8s Role OS Hostname
Master Linux Ubuntu IMPERIAL
Node Linux Ubuntu SAGRES
Node Windows Server 1709 DESPERADOS

This example setup will only add one linux node and one windows node, however adding more for both OS should be exactly the same process, since new registered nodes will receive new ips from the available cluster subnet space.

Variables

Variable Description Ex. Value
GW_IP TODO Master IP
OVERLAY_IP TODO TODO
PROTOCOL The protocol used, options are SSL and TCP, for sake of simplicity tcp will be used. Look here for more details. tcp
CLUSTER_SUBNET Cluster wide IP subnet to use (default "11.11.0.0/16") 192.168.0.0/16
SERVICE_CLUSTER_RANGE A CIDR notation IP range from which k8s assigns service cluster IPs. This should be the same as the one provided for kube-apiserver's. 172.16.1.0/24
MASTER The ip or hostname of clusters master IMPERIAL
LINUX_INTERFACE Name of interface with access to the cluster network on Linux enp0s9
WINDOWS_INTERFACE Name of interface with access to the cluster network on Windows "Ethernet"

Master Setup

Master Initial Setup

Add external repos to install docker and OVS from packages

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates
echo "deb https://packages.wand.net.nz $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/wand.list
sudo curl https://packages.wand.net.nz/keyring.gpg -o /etc/apt/trusted.gpg.d/wand.gpg
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
sudo su -c "echo \"deb https://apt.dockerproject.org/repo ubuntu-xenial main\" >> /etc/apt/sources.list.d/docker.list"
sudo apt-get update

Docker installation

sudo apt-get purge lxc-docker
sudo apt-get install -y linux-image-extra-$(uname -r) linux-image-extra-virtual
sudo apt-get install -y docker-engine
sudo service docker start

OVS installation and necessary dependencies

sudo apt-get build-dep dkms
sudo apt-get install python-six openssl python-pip -y
sudo -H pip install --upgrade pip

sudo apt-get install openvswitch-datapath-dkms=2.9.0-1 -y
sudo apt-get install openvswitch-switch=2.9.0-2 openvswitch-common=2.9.0-2 libopenvswitch=2.9.0-2 -y
sudo -H pip install ovs

sudo apt-get install ovn-central=2.9.0-2 ovn-common=2.9.0-2 ovn-host=2.9.0-2 -y

Golang installation

wget -nv https://dl.google.com/go/go1.9.2.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.9.2.linux-amd64.tar.gz
export PATH="/usr/local/go/bin:echo $PATH"
export GOPATH=$HOME/work

Setup CNI directory

sudo mkdir -p /opt/cni/bin/

Install OVN+K8S Integration

mkdir -p $HOME/work/src/github.com/openvswitch
pushd $HOME/work/src/github.com/openvswitch
git clone https://github.com/openvswitch/ovn-kubernetes
popd
pushd $HOME/work/src/github.com/openvswitch/ovn-kubernetes/go-controller
make 1>&2 2>/dev/null
sudo make install
popd

Download and install K8s

mkdir k8s
cd k8s
wget https://github.com/kubernetes/kubernetes/releases/download/v1.9.3/kubernetes.tar.gz
tar xvzf kubernetes.tar.gz
./kubernetes/cluster/get-kube-binaries.sh
mkdir server
cd server
tar xvzf ../kubernetes/server/kubernetes-server-linux-amd64.tar.gz

Kubernetes Setup

Install an etcd cluster

sudo docker run --net=host -v /var/etcd/data:/var/etcd/data -d \
        gcr.io/google_containers/etcd:3.0.17 /usr/local/bin/etcd \
        --listen-peer-urls http://127.0.0.1:2380 \
        --advertise-client-urls=http://127.0.0.1:4001 \
        --listen-client-urls=http://0.0.0.0:4001 \
        --data-dir=/var/etcd/data

Start k8s daemons

sudo sh -c 'echo "PATH=$PATH:$HOME/k8s/server/kubernetes/server/bin" >> /etc/profile'
cd k8s/server/kubernetes/server/bin
sudo ./kube-apiserver --service-cluster-ip-range=$SERVICE_CLUSTER_RANGE/24 \
                            --address=0.0.0.0 \
                            --etcd-servers=http://127.0.0.1:4001 \
                            --advertise-address=$GW_IP \
                            --v=2 2>&1 0<&- &>/dev/null &

Wait for kube-apiserver to start up, then launch both Controller and Scheduler

sudo ./kube-controller-manager --master=127.0.0.1:8080 --v=2 2>&1 0<&- &>/dev/null &
sudo ./kube-scheduler --master=127.0.0.1:8080 --v=2 2>&1 0<&- &>/dev/null &

Create a kubeconfig file.

apiVersion: v1
clusters:
- cluster:
    server: http://localhost:8080
  name: default-cluster
- cluster:
    server: http://localhost:8080
  name: local-server
- cluster:
    server: http://localhost:8080
  name: ubuntu
contexts:
- context:
    cluster: ubuntu
    user: ubuntu
  name: ubuntu
current-context: ubuntu
kind: Config
preferences: {}
users:
- name: ubuntu
  user:
    password: p1NVMZqhOOOqkWQq
    username: admin
KUBECONFIG

Start ovnkube

sudo ovnkube -k8s-kubeconfig $HOME/kubeconfig.yaml -net-controller -loglevel=4 \
 -k8s-apiserver="http://$OVERLAY_IP:8080" \
 -logfile="/var/log/openvswitch/ovnkube.log" \
 -init-master=$MASTER -cluster-subnet="$CLUSTER_SUBNET" \
 -service-cluster-ip-range="$SERVICE_CLUSTER_RANGE" \
 -nodeport \
 -nb-address="$PROTOCOL://$OVERLAY_IP:6631" \
 -sb-address="$PROTOCOL://$OVERLAY_IP:6632" 2>&1 &

Linux Node Setup

Linux Initial Setup

Repeat Initial setup from Master.

K8s Startup

Install CNI

pushd ~/
wget -nv https://github.com/containernetworking/cni/releases/download/v0.5.2/cni-amd64-v0.5.2.tgz
popd
sudo mkdir -p /opt/cni/bin
pushd /opt/cni/bin
sudo tar xvzf ~/cni-amd64-v0.5.2.tgz
popd

Add kubeconfig

apiVersion: v1
clusters:
- cluster:
    server: http://imperial:8080
  name: default-cluster
- cluster:
    server: http://imperial:8080
  name: local-server
- cluster:
    server: http://imperial:8080
  name: ubuntu
contexts:
- context:
    cluster: ubuntu
    user: ubuntu
  name: ubuntu
current-context: ubuntu
kind: Config
preferences: {}
users:
- name: ubuntu
  user:
    password: p1NVMZqhOOOqkWQq
    username: admin
KUBECONFIG

Start k8s daemons

pushd k8s/server/kubernetes/server/bin
nohup sudo ./kubelet --kubeconfig $HOME/kubeconfig.yaml \
                     --v=2 --address=0.0.0.0 \
                     --fail-swap-on=false \
                     --runtime-cgroups=/systemd/system.slice \
                     --kubelet-cgroups=/systemd/system.slice \
                     --enable-server=true --network-plugin=cni \
                     --cni-conf-dir=/etc/cni/net.d \
                     --cni-bin-dir="/opt/cni/bin/" 2>&1 0<&- &>/dev/null &
sleep 10
popd

Setup OVNKube

nohup sudo ovnkube -k8s-kubeconfig $HOME/kubeconfig.yaml -loglevel=4 \
    -logfile="/var/log/openvswitch/ovnkube.log" \
    -k8s-apiserver="http://$MASTER:8080" \
    -init-node="$MINION_NAME"  \
    -nodeport \
    -nb-address="$PROTOCOL://$MASTER:6631" \
    -sb-address="$PROTOCOL://$MASTER:6632" \
    -init-gateways -gateway-interface="$LINUX_INTERFACE" -gateway-nexthop="$GW_IP" \
    -service-cluster-ip-range=$SERVICE_CLUSTER_RANGE/24 \
    -cluster-subnet=$CLUSTER_SUBNET 2>&1 &

Windows Node Setup

Windows Initial Setup

Installing both Hyper-V and Container features

Enable-WindowsOptionalFeature -Online -FeatureName containers –All
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V –All

Force install Docker CE edition

Install docker for windows. CE edition was choosen and force installed since it seems to function better, however the Windows Server recommended EE edition should work too.

Invoke-WebRequest https://download.docker.com/win/static/test/x86_64/docker-17.11.0-ce-rc3.zip -UseBasicParsing -OutFile docker.zip
Expand-Archive docker.zip -DestinationPath $Env:ProgramFiles
Remove-Item -Force docker.zip
$env:Path = $env:Path + "C:\Program Files\docker\;"
cd "C:\Program Files\docker\"
dockerd --register-service
Start-Service docker
setx PATH "$env:Path;C:\Program Files\docker\;" -m

Download and Install OVS

Download OVS for Windows from Cloudbase 2.9.0_beta.

Since this version is beta, we will have to disable driver certification:

bcdedit /set testsigning yes

To run this command it may be required to have safe boot disabled.

Make sure to check OVN Host on installation.

Download and Install Kubernetes

You can find the client binaries here.

OVS Configuration

Setup a transparent docker network

docker network create -d transparent --gateway $GATEWAY_IP --subnet $SUBNET `
    -o com.docker.network.windowsshim.interface="$WINDOWS_INTERFACE" external

Configure VMSwitch and OVS

Get-VMSwitch -SwitchType External | Disable-VMSwitchExtension "Cloudbase Open vSwitch Extension"

ovs-vsctl --if-exists --no-wait del-br br-ex

Get-VMSwitch -SwitchType External | Set-VMSwitch -AllowManagementOS $false

Get-VMSwitch -SwitchType External | Set-VMSwitch -AllowManagementOS $false

ovs-vsctl --no-wait --may-exist add-br br-ex

ovs-vsctl --no-wait add-port br-ex '$WINDOWS_INTERFACE'

Stop-Service ovs-vswitchd

Get-VMSwitch -SwitchType External | Enable-VMSwitchExtension "Cloudbase Open vSwitch Extension"

Start-Service ovs-vswitchd

sleep 2

Restart-Service ovs-vswitchd

Enable-NetAdapter br-ex

Remove-NetIPAddress -ifAlias br-ex -Confirm:$false

New-NetIPAddress -ifAlias br-ex -IPAddress $INTERFACE_IP -DefaultGateway $DEFAULT_GW -PrefixLength 24

Set-DnsClientServerAddress -InterfaceAlias br-ex -ServerAddresses ("1.1.1.1")

$GUID = (New-Guid).Guid

ovs-vsctl set Open_vSwitch . external_ids:system-id="$($GUID)"

[Optiona] Fix DHCP

The last script will change from dhcp to static ip, to change back to dhcp it will be necessary to give the our external interface macaddress to our new virtual interface and generate a new macaddress to give to our external interface, this will make sure we receive from dhcp the same IP to our virtual interface.

Set-NetAdapter -Name <interface_name> -MacAddress <new_interface_mac> -Confirm:$false
Set-NetAdapter -Name br-ex -MacAddress <interface_mac> -Confirm:$false

After running both commands we change the network settings to dhcp.

Startup

We will first need to register the node in the cluster. For this we will launch kubelet and wait for node registration to be checked, we can then stop the kubelet process and run ovnkube once, after that we can rerun the kubelet.

Add kubeconfig file

apiVersion: v1
clusters:
- cluster:
    server: http://imperial:8080
  name: default-cluster
- cluster:
    server: http://imperial:8080
  name: local-server
- cluster:
    server: http://imperial:8080
  name: ubuntu
contexts:
- context:
    cluster: ubuntu
    user: ubuntu
  name: ubuntu
current-context: ubuntu
kind: Config
preferences: {}
users:
- name: ubuntu
  user:
    password: p1NVMZqhOOOqkWQq
    username: admin
KUBECONFIG

Launch Kubelet

.\kubelet.exe --hostname-override="desperados" `
        --pod-infra-container-image="cloudbase/pause" `
        --resolv-conf="" `
        --kubeconfig="C:\k\kubeconfig.yaml" `
        --network-plugin=cni `
        --cni-bin-dir="C:\k" `
        --cni-conf-dir="C:\etc\cni\net.d"

Run OVNkube

.\ovnkube.exe -k8s-kubeconfig kubeconfig.yaml -loglevel=4 `
    -k8s-apiserver="http://$MASTER:8080" `
    -init-node="desperados"  `
    -nodeport `
    -nb-address="$PROTOCOL://$MASTER:6631" `
    -sb-address="$PROTOCOL://$MASTER:6632" -k8s-token="test" `
    -service-cluster-ip-range="$SERVICE_CLUSTER_RANGE" `
    -cluster-subnet="$CLUSTER_SUBNET"

Launching Test Pod

To test our hybrid cluster, we will deploy a simple IIS pod. Since these pods will only run on windows, we can force node selection with the following:

nodeSelector:
    beta.kubernetes.io/os: windows

Test Pod

apiVersion: v1
kind: Pod
metadata:
  name: nano-1709
  labels:
    name: webserver
spec:
  containers:
  - name: nano
    image: cloudbase/pause
    imagePullPolicy: IfNotPresent
  - name: nano2
    image: microsoft/iis:windowsservercore-1709
    imagePullPolicy: IfNotPresent
  nodeSelector:
    beta.kubernetes.io/os: windows

Debugging

Check here for official information on debugging.

Sources and Further Reading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment