Started documentation.

This commit is contained in:
Maurice Makaay 2020-02-06 20:18:17 +00:00
parent 0b0a485f19
commit a799148f63
2 changed files with 495 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
**/*.sw*

494
demo.md Normal file
View File

@ -0,0 +1,494 @@
# Mini-omschrijving voice platform
* 3 hardware LXD hosts, met tig geheugen en tig CPU
* verspreid over 3 lokaties
* alle applicaties draaien in LXD containers op deze hosts
* naasts deze hosts nog een paar SBC clusters, die zorgen
voor goede en veilige interconnectie met externe partijen
(onze uplink naar het KPN netwerk en alle connecties met
de klantapparatuur / softphones)
* puppet beheert een stuk van de LXD host configuratie
* Ansible beheert andere stukken van de LXD host configuratie,
plus alle LXD containers in volledigheid (dus geen 2 kapiteins
op 1 schip, maar een strakke verdeling in taken)
Het op te lossen vraagstuk voor Ansible was vooral: hoe beheren
we de LXD containers die op de 3 hardware nodes staan eenvoudig
vanuit 1 Ansible omgeving?
Taken van Ansible:
* Nieuwe LXD containers optrekken indien nodig (one-shot runs)
* Alle LXD containers en LXD hosts configureren (one-shot runs)
* Onderhoudstaken op regelmatige basis (periodieke runs in cron)
Installatie VM's voor de demo
=============================
- 2 VM's: sidn-demo-01, sidn-demo-02
- virtual machine met NAT netwerk: 192.168.56.0/24, gateway/DNS 192.168.56.1
- Ubuntu 18.04 LTS
Na installatie:
sidn-demo-0X# apt update && apt upgrade -y
LXD snap versie installeren
===========================
Standaard gebruikt 18.04 een apt-package voor LXD.
Deze is prima bruikbaar, maar voor een meer up-to-date feature set
is het beter om de snap installatie te gebruiken. Snap is op dit moment
de aanbevolen method voor het draaien van LXD.
sidn-demo-0X# snap refresh
sidn-demo-0X# snap install lxd
sidn-demo-0X# lxd.migrate ("no" voor automatisch verwijderen LXD)
sidn-demo-0X# apt remove --purge lxd lxd-client (want zo zie ik wel hoe ver deze is)
LXD initialiseren
=================
Voor deze demo:
sidn-demo-0X# lxd init
> clustering: no
> storage pool: yes, default, dir
> connect to MAAS: no
> network bridge: no
> available over the network: yes, all interfaces, port 8443, password
Op productie doen we het (uiteraard) anders:
- ZFS als filesystem
- "Available over network" alleen op een private space mgmt VLAN
Netwerk volledig op basis van LXD host bridges
==============================================
Belangrijkste keuze qua netwerk: elk netwerk interface in de LXD host
is ondergebracht in een bridge interface. Voordeel is dat elke LXD
container daarmee "ingeplugd" kan worden op alle ontsloten netwerken.
Twee LXD containers op twee verschillende hosts die in eenzelfde
bridge netwerk zitten, kunnen simpel op layer 2 bij elkaar.
** Dit lijkt dus op hoe je het met hardware switches zou regelen**
Daarom wordt tijdens lxd init niet automatisch een bridge aangemaakt.
Je kunt natuurlijk de standaard lxdbr0 bridge maken en gebruiken,
maar dat netwerk is alleen voor intern gebruik binnen de host.
In een multi-host setup zou je netwerk-trucs moeten uithalen om
te zorgen dat hosts bij elkaar kunnen komen.
Netplan-config op productie ziet er ongeveer zo uit:
network:
version: 2
ethernets:
eno1: {} # management network (untagged)
eno2: {} # SIP network (SIP signalling & services, tagged)
eno3: {} # public interface (untagged)
vlans:
# SIP core: proxy & SBC signalling
vlan.6
id: 6
link: eno2
# SIP applications (voicemail, mediaserver, class5 services)
vlan.9
id: 9
link: eno2
bridges:
br-mgmt:
dhcp4: no
dhcp6: no
interfaces: ["eno1"]
addresses: ["172.17.4.10/24"]
br-public:
dhcp4: no
dhcp6: no
interfaces: ["eno3"]
gateway4: 194.109.16.129
addresses: ["194.109.16.132/26"]
nameservers:
addresses: ["194.109.6.66", "194.109.9.99"]
br-sipcore:
dhcp4: no
dhcp6: no
interfaces: ["vlan.6"]
addresses: ["10.160.64.132/24"]
br-sipapp:
dhcp4: no
dhcp6: no
interfaces: ["vlan.9"]
addresses: ["10.116.1.132/24"]
Met een dergelijke setup is het heel eenvoudig om de LXD containers
beschikbaar te maken binnen bepaalde netwerksegmenten. De containers kunnen
een of meer netwerk interfaces krijgen, die verbonden kunnen worden met de
benodigde netwerken. Dat zien er dan bijvoorbeeld zo uit (afwijkende
bridge-namen ivm de ruimte):
GATEWAY
|
|
+-------------------------+------+----------public--+
| | |
| +-------------------------+----------SIP------------+
| | | | | |
| | +-------------------------+---mgmt------------------+
| | | | | | | | |
| | | | | | | | |
+---O------O------O----+ +---O------O------O----+ +---O------O------O----+
| eno3 eno2 eno1 | | eno3 eno2 eno1 | | eno3 eno2 eno1 |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
| br3 br2 br1 | | br3 br2 br1 | | br3 br2 br1 |
| | | \ | | | | | | | | | |
| | | | | | | \ | | | | | |
| CONTAINER1 | | | | `--CONTAINER3 | | | CONTAINER4 |
| | | | | | | | |
| CONTAINER2 | | | | CONTAINER5 |
| | | | | |
+----------------------+ +----------------------+ +----------------------+
LXD Host 1 LXD Host 2 LXD Host 3
Voor de demo gebruiken we een simpeler opzet:
network:
version: 2
ethernets:
enp1s0: {}
bridges:
br-demo:
dhcp4: no
dhcp6: no
interfaces: ["enp1s0"]
addresses: ["192.168.56.150/24"] # of 151 voor sidn-demo-02
gateway4: "192.168.56.1"
nameservers:
addresses: ["192.168.56.1"]
Dus maar 1 bridge en het "fysieke" interface enp1s0 van de VM zit
"ingeplugd" in deze bridge. In een plaatje:
KVM host
192.168.56.1 (gateway + DHCP + DNS)
|
|
+------192.168.56.0/24----+
| |
+------O---------------+ +------O---------------+
| enp1s0 | | enp1s0 |
| | | | | |
| +---O------------+ | | +---O------------+ |
| | br-demo | | | | br-demo | |
| | 192.168.56.150 | | | | 192.168.56.151 | |
| +--O---O---------+ | | +--O--O----------+ |
| | | | | | | |
| | CONTAINER2 | | | CONTAINER4 |
| | | | | |
| CONTAINER1 | | CONTAINER3 |
| | | |
+----------------------+ +----------------------+
KVM guest: sidn-demo-01 KVM guest: sidn-demo-02
Netwerkinterfaces op LXD containers
===================================
Met bovenstaande setup is het dus mogelijk om een LXD container netwerk
interfaces te geven die "ingeplugd" worden in een of meer van de br-* bridge
interfaces. Een SIPproxy server bijvoorbeeld, krijgt drie netwerk interfaces
in drie verschillende bridges:
- br-mgmt: voor host management, o.a. Ansible aansturing
- br-public: op een SIPproxy voor SSH toegang en OS updates
- br-sipcore: voor SIP signalling verkeer richting SBC's
Om op een container eenvoudig het doel voor de verschillende interfaces te
herkennen, wordt in de naamgeving de bridge-naamgeving gevolgd. Een interface
dat in "br-public" wordt geplugd heet bijvoorbeeld "if-public". Veel handiger
dan "eth0" o.i.d.
Om bij het optrekken van een LXD container de benodigde netwerk interfaces
aan te maken, wordt gebruik gemaakt van profielen. Er zijn verschillende
profielen gemaakt voor verschillende netwerkbehoeftes:
# lxc profile list
+---------------+---------+
| NAME | USED BY |
+---------------+---------+
| ansible-acc | 1 |
+---------------+---------+
| default | 11 |
+---------------+---------+
| sipproxy | 2 |
+---------------+---------+
| voicemail | 2 |
+---------------+---------+
| voiceservices | 2 |
+---------------+---------+
Hier het stuk van het profiel wat voor het bovenstaande SIPproxy netwerk wordt gebruikt.
productie# lxc profile show sipproxy
config:
---8<-------
devices:
if-mgmt:
name: if-mgmt
nictype: bridged
parent: br-mgmt
type: nic
if-public:
name: if-public
nictype: bridged
parent: br-public
type: nic
if-sipcore:
name: if-sipcore
nictype: bridged
parent: br-sipcore
type: nic
---8<-------
Noot:
Voor het voice platform is dit een handige setup. Uiteraard kan elke mogelijk
setup worden gemaakt op basis van de behoefte. Als de behoefte alleen maar
een lokaal werkende bridge met private IP space was, dan zou ik het geheel nog
steeds wel handmatig inrichten zoals hierboven, alleen dan met een loopback
bridge met een naam als "br-local".
Voorbeeldprofiel voor de demo
=============================
Op basis van de gevolgde lxd init methode, is er een default profiel aangemaakt
voor LXD. Deze is heel erg basaal, omdat we geen netwerk hebben geconfigureerd:
sidn-demo-0X# lxc profile show default
config: {}
description: Default LXD profile
devices:
root:
path: /
pool: default
type: disk
name: default
used_by: []
Het beheer van profielen regelen we primair vanuit Ansible, maar op dit punt
van de demo maak ik met de hand een profiel aan.
sidn-demo-0X# lxc profile create demo
sidn-demo-0X# lxc profile edit demo
config:
user.user-data: |
# cloud-config
package_upgrade: true
packages:
- python3
timezone: Europe/Amsterdam
description: SIDN demo profile
devices:
if-demo:
name: if-demo
nictype: bridged
parent: br-demo
type: nic
root:
path: /
pool: default
type: disk
sidn-demo-0X# lxc profile show demo
In deze config zijn wat cloud-init statements toegevoegd om te laten zien dat
cloud-init te gebruiken is in een profiel. De installatie van Python is handig,
omdat daarmee de container klaar is voor Ansible. Echter, in het voice platform
wordt dit niet zo gedaan. Daar worden wat bootstrapping Ansible recipes gebruikt
om Python op een vers systeem te krijgen. Voordeel is dat de Ansible-methode
ook voor oudere versies van Ubuntu en voor niet cloud-init images te gebruiken is.
Noot:
In de praktijk hebben wij tot op heden alleen maar timezone en netwerkconfiguratie
gepusht met de cloud-init configuratie. En zelfs de netwerkconfguratie slechts
deels, omdat deze met de cloud-init van Ubuntu 14.04 nog niet mogelijk was.
Geen LXD clustering
===================
Het klinkt heel goed: LXD clustering. Functioneel heb ik er echter nog
weinig nuttigs in gevonden voor onze setup. Bovendien heb ik op clustersystemen
regelmatig problemen gezien met het goed werkend houden van het cluster,
door het uit sync raken van de quorum database bijvoorbeeld.
Een cluster zorgt er voornamelijk voor dat je een aantal LXD hosts aan elkaar
koppelt, die onderling gaan uitwisselen welke containers er op de hosts draaien.
Daarna krijg je met bijvoorbeeld "lxc list" geen inzicht in de containers die
op de lokale host draaien, maar inzicht in alle containers die op alle hosts
draaien.
Voordat clustering bestond in LXD liepen wel al wel tegen het probleem aan
wat clustering probeert op te lossen: containers verspreid over meerdere hosts.
Onze aanpak:
- De LXD daemon kan luisteren op het netwerk (zoals hierboven aangezet)
- Je kunt vanuit de ene LXD host een andere LXD host als "remote" toevoegen
onder een bepaalde naam.
- Vanaf dan kunnen lxc commando's de naam van de remote gebruiken om
operaties op de andere host te doen.
- We hebben alle LXD hosts op deze manier kruislings gekoppeld, zodat vanuit
elke host elke andere host aangesproken kan worden.
Zorg eerst dat alle LXD hosts remote access actief hebben, mocht dat nog
niet geconfigureerd zijn vanuit lxd init. Doe het volgende op elke host:
sidn-demo-0X# lxc config set core.https_address [::]:8443
sidn-demo-0X# lxc config set core.trust_password SuperGeheimGoedWachtwoordHorseStaple
Nu kunnen de remotes op elke LXD host worden aangemaakt.
Merk op dat we ook voor de lokale LXD daemon een remote aanmaken.
Dat maakt een aantal zaken vanuit Ansible een stuk simpeler.
Als wachtwoord gebruiken we uiteraard het bovenstaande wachtwoord.
sidn-demo-0X# lxc remote add --accept-certificate sidn-demo-01 192.168.56.150
sidn-demo-0X# lxc remote add --accept-certificate sidn-demo-02 192.168.56.151
Vanaf nu kun je vanaf elk van de twee LXD hosts commando's uitvoeren
op alle LXD hosts, bijv:
sidn-demo-0X# lxc list -cns sidn-demo-01:
sidn-demo-0X# lxc list -cns sidn-demo-02:
sidn-demo-0X# lxc launch ubuntu:18.04 sidn-demo-02:test
Een eerste container: Ansible management host
=============================================
De Ansible management host is een container op het voice platform zelf. Vanwege de
kip/ei problematiek en het feit dat deze container niet steeds opnieuw gebouwd
gaat worden, bootstrappen we deze container met de hand.
De handigste manier voor de netwerkconfiguratie van deze container is gebruik
maken van een user.network-config.
sidn-demo-01# vi /tmp/config
version: 1
config:
- type: physical
name: if-demo
subnets:
- type: static
address: 192.168.56.160
netmask: 255.255.255.0
gateway: 192.168.56.1
dns_nameservers: [192.168.56.1] # zodat dezelfde resolving als de KVM host wordt gebruikt
Bouw de container op en start hem:
sidn-demo-01# H=ansible
sidn-demo-01# lxc init --storage default --no-profiles ubuntu:18.04 $H
sidn-demo-01# lxc config device add $H if-demo nic name=if-demo nictype=bridged parent=br-demo
sidn-demo-01# lxc config set $H volatile.if-demo.hwaddr 00:16:3e:00:00:a0 # mainly for VirtualBox
sidn-demo-01# lxc config set $H user.network-config - < /tmp/config
sidn-demo-01# lxc config show $H
sidn-demo-01# lxc start $H
sidn-demo-01# lxc exec $H bash
ansible# apt update && apt upgrade -y && apt autoclean -y && apt autoremove -y
Noot:
Voor deze setup zou ook het eerder aangemaakte "demo" profiel gebruikt kunnen
worden. Echter, op het voice platform doen we het op deze manier, omdat we
de profielen vanuit Ansible opzetten en het benodigde profiel daarom niet
noodzakelijkerwijs al bestaat op de LXD host.
Op de container is nu te zien dat het layer 2 netwerk bereikbaar is
vanuit de container:
ansible# ip a (laat zien dat er nu een if-demo interface bestaat)
ansible# ping 192.168.56.150 (een ping naar de eigen LXD host)
ansible# ping 192.168.56.151 (een ping naar de andere LXD host)
Connectie Ansible -> managed hosts
==================================
Even een uitstapje naar de manier waarop we vanuit Ansible verbinding
gaan maken naar de LXD hosts en de LXD containers, voor het uitvoeren
van de nodige commando's.
In Ansible zit support voor het gebruik van lxc in plaats van ssh voor
het verbinden met de te configureren hosts. Aanvankelijk zijn we daar
100% voor gegaan, maar helaas bleek dat dit een behoorlijke impact op
de snelheid heeft. Met een SSH koppeling, kan de SSH verbinding open
worden gehouden en kunnen commando's in rap tempo's naar een host worden
gestuurd. De lxc connector _doet dit helemaal niet_. Voor elk commando
wordt een nieuwe connectie opgezet.
Om de zaken te versnellen, gebruiken we nu een hybride setup:
- Management van de LXD hosts wordt volledig met SSH gedaan vanuit
de Ansible container.
- Het bootstrappen van nieuwe containers wordt gedaan door met SSH
naar een LXD host te gaan (delegate), en vervolgens worden van
daaruit (met de hierboven geconfigureerde remotes) met lxc
connecties de containers opgetrokken en geconfigureerd tot het
niveau dat de Ansible container er met SSH bij kan.
- Het verder configureren van containers wordt volledig met SSH
gedaan, direct vanuit de Ansible container.
Ansible SSH toegang Ansible -> LXD hosts
============================================
Om de LXD hosts te kunnen beheren met SSH, is het nodig dat er een SSH key
wordt gegenereerd en dat deze naar de hosts wordt gekopieerd.
ansible# ssh-keygen -b 2048 -N '' -f /root/.ssh/id_rsa
sidn-demo-0X# mkdir -p /root/.ssh
sidn-demo-0X# chmod 700 /root/.ssh
sidn-demo-0X# touch /root/.ssh/authorized_keys
sidn-demo-0X# chmod 600 /root/.ssh/authorized_keys
sidn-demo-0X# lxc exec sidn-demo-01:ansible cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys
Hierna moet het volgende werken:
ansible# ssh 192.168.56.150 touch .hushlogin
ansible# ssh 192.168.56.151 touch .hushlogin
Installatie van de Ansible software
===================================
De basis infrastructuur staat. Nu kan de benodigde software op de
Ansible host worden geïnstalleerd.
Op het voiceplatform wordt overal gebruik gemaakt van Python3 voor Ansible.
ansible# apt install -y python3 python3-pip
ansible# pip3 install ansible
The Ansible configuration is managed in a git repository. Go get!
ansible# apt install -y git
ansible# REPO=https://git.makaay.nl/mauricem/sidn-lxd-ansible-demo.git
ansible# git clone $REPO /etc/ansible