Backup work.
This commit is contained in:
parent
d3aabf04a8
commit
61a1d86049
673
README.md
673
README.md
|
@ -1 +1,672 @@
|
|||
# SIDN LXD / Ansible demo
|
||||
# Onderwerpen van de demo
|
||||
* Mini-omschrijving XS4ALL voice platform
|
||||
* Waarom LXD?
|
||||
* Doel van de demo: LXD + Ansible + Galera cluster
|
||||
* Installatie VM's voor de demo
|
||||
* Netwerktopologie
|
||||
* LXD clustering
|
||||
* De Ansible container
|
||||
* Ansible-vault voor opslag secrets
|
||||
* Dynamische inventory
|
||||
* Playbook roles structuur and handige aliases
|
||||
* Container bootstrapping vanuit Ansible
|
||||
* Installatie van het Galera cluster
|
||||
* Rolling upgrade Galera cluster
|
||||
|
||||
# Mini-omschrijving XS4ALL voice platform
|
||||
|
||||
* een aantal hardware LXD hosts, met tig geheugen en tig CPU
|
||||
* verspreid over meerdere lokaties
|
||||
* alle applicaties draaien in LXD containers op deze hosts
|
||||
* naasts deze hosts zijn er 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)
|
||||
|
||||
Configuration management is een hybride setup:
|
||||
|
||||
* 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)
|
||||
|
||||
De op te lossen vraagstukken voor Ansible waren vooral:
|
||||
|
||||
* Hoe kunnen we geautomatiseerd LXD containers optrekken?
|
||||
* Hoe beheren we het OS op al die LXD containers vanuit 1 Ansible omgeving?
|
||||
* Hoe kunnen we development en acceptatie handig inrichten?
|
||||
|
||||
Verschillende soorten inzet van Ansible:
|
||||
|
||||
* Nieuwe LXD containers optrekken indien nodig (one-shot runs)
|
||||
* Configuratiemanagement LXD containers en LXD hosts (one-shot runs)
|
||||
* Onderhoudstaken op regelmatige basis (periodieke runs in cron)
|
||||
|
||||
# Waarom LXD?
|
||||
* Het platform bestaat uit heel veel verschillende applicaties
|
||||
* Allemaal in het verleden gebouwd als Ubuntu hosts (hardware en VM's)
|
||||
* Te veel niet gebruikte resources, containerization was een logische keuze
|
||||
* Veel van de applicaties hebben ook een veelheid aan scheduled jobs en support scripts
|
||||
* Statisch netwerk, omdat SBC's geen dynamische config kennen
|
||||
* Daardoor ook geen hippe autoscaling behoefte
|
||||
* Migratie naar LXD binnen XS4ALL was strak tijdgebonden (gekoppeld aan einde Edutel)
|
||||
|
||||
Doordat de beschikbare tijd gelimiteerd was en LXD de mogelijkheid bood om de
|
||||
bestaande omgeving vrijwel 1-op-1 na te bouwen op basis van LXD systeemcontainers,
|
||||
was de keuze voor virtualisatie snel gemaakt.
|
||||
|
||||
Het platform omzetten naar bijvoorbeeld Docker was ongetwijfeld mogelijk geweest,
|
||||
maar dat zou heel veel extra werk hebben opgeleverd en voor extra complexiteit
|
||||
op de netwerklaag hebben gezorgd.
|
||||
|
||||
Jammer? Nee! Met LXD en Ansible hebben we een mooi evenwicht bereikt tussen de
|
||||
bekende cattle en pets analogie. Het voice platform heeft diverse pets in zich,
|
||||
maar die kunnen zonder blikken of blozen naar de slachtbank.
|
||||
|
||||
# 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
|
||||
|
||||
#### Upgrade na installatie
|
||||
|
||||
```text
|
||||
sidn-demo-0X# apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
#### LXD snap-versie installeren ipv apt-versie
|
||||
|
||||
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)
|
||||
```
|
||||
Om te zorgen dat alle tooling lxc goed kan vinden:
|
||||
```
|
||||
sidn-demo-0X# ln -s /snap/bin/lxc /usr/bin/lxc
|
||||
```
|
||||
|
||||
#### LXD initialiseren
|
||||
|
||||
Voor deze demo:
|
||||
|
||||
```text
|
||||
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 het management VLAN
|
||||
|
||||
|
||||
# Netwerktoplogie
|
||||
Belangrijke keuze qua netwerk: elk netwerk interface in de LXD host
|
||||
is ondergebracht in een bridge interface.
|
||||
|
||||
* Elke container kan simpel op elk benodigd netwerk "ingeplugd" worden
|
||||
door een interface van de container aan de bridge toe te voegen.
|
||||
* Elke container is daarmee ook direct exposed op dat netwerk.
|
||||
* Containers kunnen ook containers op een andere LXD host bereiken,
|
||||
mits het layer 2 netwerk doorgetrokken is uiteraard.
|
||||
* Containers kunnen eenvoudig naar een andere LXD host worden verplaatst,
|
||||
mits de nieuwe host dezelfde netwerkbridges heeft geconfigureerd.
|
||||
|
||||
__Praktisch gezien lijkt dit op hoe je het netwerk met hardware hosts en
|
||||
switches zou regelen__
|
||||
|
||||
#### Netwerk bridges op de LXD hosts
|
||||
|
||||
Tijdens lxd init maken we niet de standaard lxdbr0 bridge aan.
|
||||
Die bridge is alleen voor intern gebruik binnen de host. Wanneer
|
||||
je services beschikbaar wilt stellen aan andere systemen, dan moeten
|
||||
daar trucs voor uitgehaald worden (vgl. met Docker/Traefik).
|
||||
|
||||
In plaats daarvan worden de bridges door onszelf geconfigureerd in Netplan.
|
||||
Netplan-config op productie ziet er ongeveer zo uit:
|
||||
|
||||
```yaml
|
||||
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):
|
||||
|
||||
```text
|
||||
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 ... n
|
||||
```
|
||||
|
||||
Hier is bijvoorbeeld CONTAINER1 aangesloten op het public en het SIP netwerk, terwijl
|
||||
CONTAINER2 aangesloten is op SIP en mgmt. CONTAINER4 is alleen aangesloten op het
|
||||
SIP netwerk, en kan via dat netwerk op layer 2 CONTAINER1 en CONTAINER2 ook bereiken.
|
||||
|
||||
|
||||
__Voor de demo gebruiken we een iets simpeler opzet:__
|
||||
|
||||
```yaml
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
enp1s0: {}
|
||||
enp6s0: {}
|
||||
bridges:
|
||||
br-public:
|
||||
dhcp4: no
|
||||
dhcp6: no
|
||||
interfaces: ["enp1s0"]
|
||||
addresses: ["192.168.56.150/24"] # en 151 op de andere host
|
||||
gateway4: "192.168.56.1"
|
||||
nameservers:
|
||||
addresses: ["8.8.8.8", "9.9.9.9"]
|
||||
br-demo:
|
||||
dhcp4: no
|
||||
dhcp6: no
|
||||
interfaces: ["enp6s0"]
|
||||
addresses: ["10.0.0.150/24"] # en 151 op de andere host
|
||||
```
|
||||
|
||||
Bridge br-public is aangesloten op een bridge die NAT netwerk verzorgt op
|
||||
de VM host en daarmee gebruikt kan worden om via NAT naar het internet te
|
||||
komen. Bridge br-demo zit aangesloten op een host-only bridge op de VM host
|
||||
en heeft dus de rol van een losse, niet-connected netwerkswitch.
|
||||
|
||||
In een plaatje:
|
||||
|
||||
```text
|
||||
KVM host
|
||||
192.168.56.1 (gateway + DHCP + DNS)
|
||||
|
|
||||
|
|
||||
+------192.168.56.0/24-----------+
|
||||
| |
|
||||
| private |
|
||||
| +----bridge----+ |
|
||||
| | 10.0.0.0/24 | |
|
||||
| | | |
|
||||
+------O------O--------+ +--O----------O--------+
|
||||
| enp1s0 enp6s0 | | enp6s0 enp1s0 |
|
||||
| | | | | | | |
|
||||
| br-public br-demo | | br-demo br-public |
|
||||
| | \ / | | | | \ / | |
|
||||
| | \/ | | | | \/ | |
|
||||
| | /\ | | | | /\ | |
|
||||
| ansible-01 \ | | | galera-02 \ | |
|
||||
| | | | | \ | |
|
||||
| galera-01 | | galera-03 |
|
||||
+----------------------+ +----------------------+
|
||||
KVM guest: sidn-demo-01 KVM guest: sidn-demo-02
|
||||
```
|
||||
|
||||
|
||||
#### Netwerk interfaces op de 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-\* bridges.
|
||||
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 binnen een container het herkennen van de netwerk interfaces zo simpel
|
||||
mogelijk te houden, wordt de naamgeving van de bridges gevolg. Bijvoorbeeld
|
||||
een interface dat in bridge "br-aap" wordt geplugd, heet dan "if-aap".
|
||||
Veel handiger dan "eth0" o.i.d.
|
||||
|
||||
Op de SIPproxy zul je dan ook de interfaces if-mgmt, if-public en if-sipcore
|
||||
terug kunnen vinden.
|
||||
|
||||
__Interfaces vanuit LXD profielen__
|
||||
|
||||
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:
|
||||
|
||||
```text
|
||||
# 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 netwerk van de hierboven beschreven
|
||||
SIPproxy container wordt gebruikt:
|
||||
|
||||
```text
|
||||
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 duidelijk naam als "br-local" of "br-private".
|
||||
|
||||
|
||||
#### Aanmaken LXD profile voor de demo
|
||||
|
||||
Op basis van de gevolgde lxd init methode, is er al een default profiel aangemaakt
|
||||
voor LXD. Deze is heel erg basaal, omdat we geen netwerk bridge hebben geconfigureerd:
|
||||
|
||||
```text
|
||||
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.
|
||||
|
||||
```text
|
||||
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-public:
|
||||
name: if-public
|
||||
nictype: bridged
|
||||
parent: br-public
|
||||
type: nic
|
||||
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 demodeze met de cloud-init van Ubuntu 14.04 nog niet mogelijk was.
|
||||
|
||||
|
||||
|
||||
# LXD clustering
|
||||
Of beter gezegd: __geen__ LXD clustering.
|
||||
|
||||
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" niet alleen inzicht in de containers
|
||||
die op de lokale host draaien, maar ook in alle containers die op alle cluster
|
||||
hosts draaien.
|
||||
demo
|
||||
Het klinkt heel goed: LXD clustering. Functioneel heb ik er echter nog
|
||||
weinig heil in gevonden voor onze setup. Bovendien heb ik op clustersystemen
|
||||
regelmatig problemen gezien met het goed werkend houden van het cluster,
|
||||
door bijvoorbeeld het uit sync raken van de quorum database (gebaseerd op dqlite,
|
||||
wat een distributed sqlite database is.)
|
||||
|
||||
Voordat clustering bestond in LXD liepen wel al wel tegen een probleem aan
|
||||
dat clustering probeert op te lossen: het werken met containers, verspreid
|
||||
over meerdere LXD hosts.
|
||||
|
||||
Onze aanpak:
|
||||
|
||||
* De LXD daemon laten luisteren op het netwerk (zoals bij lxd init al 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.
|
||||
|
||||
Om dit op te zetten, zorg dan 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, omdat we dan
|
||||
vanaf elke willekeurige host bijv `lxc list sidn-demo-01:` kunnen doen en niet
|
||||
op die bestreffende host `lxc list local:` hoeven te gebruiken
|
||||
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
|
||||
```
|
||||
|
||||
__Ja, maar, hoe wordt dit gebruikt dan?__
|
||||
|
||||
Het belangrijkste gebruik hiervan, is dat er vanuit cron op elke LXD host een job draait
|
||||
die een `lxc list` uitvoert op alle remotes. Daarmee wordt een lijst gemaakt waarin
|
||||
de containers en hun LXD hosts staan. Vanuit die lijst wordt een bestand gemaakt, met
|
||||
daarin een serie bash aliases voor `lxc exec <remote>:<containernaam> bash`.
|
||||
|
||||
Als ik wil inloggen op bijvoorbeeld de container `tel-prd-sipproxy-02`, dan kan ik na
|
||||
het sourcen van het gegenereerde aliassen bestand simpelweg `tel-prd-sipproxy-02` als
|
||||
commando uitvoeren op een willekeurige LXD host, waarna ik binnenval op een shell op
|
||||
die container.
|
||||
|
||||
|
||||
# De Ansible container
|
||||
De Ansible management host is een container op het voice platform zelf. Vanwege de
|
||||
kip/ei problematiek en het feit dat deze container toch niet steeds opnieuw gebouwd
|
||||
gaat worden, bootstrappen we deze container met de hand. Hier kan natuurlijk ook een
|
||||
playbook van worden gemaakt, maar vooralsnog is het minder werk om het zo te doen.
|
||||
|
||||
De handigste manier voor het leveren van de netwerkconfiguratie van deze container,
|
||||
is gebruikmaken van een `user.network-config` in de container configuratie.
|
||||
|
||||
```text
|
||||
sidn-demo-01# vi /tmp/config
|
||||
|
||||
version: 1
|
||||
config:
|
||||
- type: physical
|
||||
name: if-public
|
||||
subnets:
|
||||
- type: static
|
||||
address: 192.168.56.160
|
||||
netmask: 255.255.255.0
|
||||
gateway: 192.168.56.1
|
||||
dns_nameservers: [192.168.56.1]
|
||||
- type: physical
|
||||
name: if-demo
|
||||
subnets:
|
||||
- type: static
|
||||
address: 10.0.0.160
|
||||
netmask: 255.255.255.0
|
||||
```
|
||||
|
||||
Bouw de container op en start hem:
|
||||
|
||||
```text
|
||||
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
|
||||
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
|
||||
```
|
||||
|
||||
Opmerkingen:
|
||||
|
||||
* 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.
|
||||
|
||||
* Er wordt een hard MAC-adres gezet. Dat is vooral nodig als je gebruik maakt
|
||||
van VirtualBox voor het maken van de VM's. De bridge in VirtualBox kan
|
||||
problemen geven wanneer je eenzelfde host met hetzelfde IP-adres maar een
|
||||
nieuw MAC-adres opnieuw optrekt. In andere omgeving zou dat MAC-adres
|
||||
ook weggelaten kunnen worden.
|
||||
|
||||
|
||||
Op de container is nu te zien dat het layer 2 netwerk via de netwerk bridges
|
||||
ontsloten 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)
|
||||
ansible# ping 10.0.0.150 (een ping naar de eigen LXD host)
|
||||
ansible# ping 10.0.0.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.
|
||||
|
||||
__Cool, lxc support binnen Ansible!__
|
||||
|
||||
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, door op de Ansible host alle LXD hosts als remotes
|
||||
toe te voegen en Ansible voor elk uit te voeren commando o een container
|
||||
lxc te laten gebruken.
|
||||
|
||||
__meh__
|
||||
|
||||
Helaas bleek dat dit een behoorlijke impact op de snelheid had. 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 daarom inmiddels een hybride setup:
|
||||
|
||||
* Management van de LXD hosts wordt volledig met SSH gedaan vanuit
|
||||
de Ansible container (dit is de standaard methode voor Ansible).
|
||||
|
||||
* Het bootstrappen van nieuwe containers en het configureren van het
|
||||
netwerk van containers worden gedaan door met SSH naar een LXD host
|
||||
te gaan (middels Ansible delegate). Vervolgens worden van daaruit met
|
||||
lxc commando's (met name `lxc exec` en `lxc file`) 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 de standaard
|
||||
SSH methode gedaan, direct vanuit de Ansible container.
|
||||
|
||||
Het mooie aan deze opzet, is dat we:
|
||||
|
||||
* Containers uit het niet automatisch kunnen bootstrappen.
|
||||
* Kapotte netwerkconfiguratie ook altijd weer kunnen repareren, omdat
|
||||
de `lxc` commando's geen containernetwerk nodig hebben.
|
||||
* Missende SSH keys voor Ansible toegang kunnen plaatsen.
|
||||
* Voor de bulk van de configuratie commando's snel via SSH kunnen versturen.
|
||||
|
||||
|
||||
#### 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 LXD hosts wordt gekopieerd.
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```bash
|
||||
ansible# ssh 10.0.0.150 touch .hushlogin
|
||||
ansible# ssh 10.0.0.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.
|
||||
|
||||
```text
|
||||
ansible# apt install -y python3 python3-pip
|
||||
ansible# pip3 install ansible
|
||||
```
|
||||
|
||||
De Ansible configuratie staat in een git repository. Go get!
|
||||
|
||||
```text
|
||||
ansible# apt install -y git
|
||||
ansible# REPO=https://git.makaay.nl/mauricem/sidn-lxd-ansible-demo.git
|
||||
ansible# git clone $REPO /etc/ansible
|
||||
```
|
||||
|
||||
De configuratie van Ansible zelf is redelijk eenvoudig:
|
||||
|
||||
```text
|
||||
ansible# vi /root/.ansible.cfg
|
||||
|
||||
[defaults]
|
||||
inventory = /etc/ansible/environments/demo/hosts
|
||||
vault_password_file = /root/.ansible-vault-password
|
||||
jinja2_extensions = jinja2.ext.do
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
```
|
||||
|
||||
De vault_password_file wordt gebruikt als password file voor Ansible vault.
|
||||
Met vault is het mogelijk om geheime informatie (normaliter: wachtwoorden)
|
||||
op te slaan in gecrypte vorm. Het te gebruiken symmetrische wachtwoord is
|
||||
opgeslagen in de password file. Bijvoorbeeld:
|
||||
|
||||
```text
|
||||
ansible# echo "My very secret 1337 super passw0rd##" \
|
||||
> /root/.ansible-vault-password
|
||||
ansible# chmod 600 /root/.ansible-vault-password
|
||||
```
|
||||
|
||||
|
|
671
demo.md
671
demo.md
|
@ -1,671 +0,0 @@
|
|||
# Onderwerpen van de demo
|
||||
* Mini-omschrijving XS4ALL voice platform
|
||||
* Waarom LXD?
|
||||
* Doel van de demo: LXD + Ansible + Galera cluster
|
||||
* Installatie VM's voor de demo
|
||||
* Netwerktopologie
|
||||
* LXD clustering
|
||||
* De Ansible container
|
||||
* Ansible-vault voor opslag secrets
|
||||
* Dynamische inventory
|
||||
* Container bootstrapping vanuit Ansible
|
||||
* Installatie van het Galera cluster
|
||||
* Rolling upgrade Galera cluster
|
||||
|
||||
# Mini-omschrijving XS4ALL voice platform
|
||||
|
||||
* een aantal hardware LXD hosts, met tig geheugen en tig CPU
|
||||
* verspreid over meerdere lokaties
|
||||
* alle applicaties draaien in LXD containers op deze hosts
|
||||
* naasts deze hosts zijn er 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)
|
||||
|
||||
Configuration management is een hybride setup:
|
||||
|
||||
* 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)
|
||||
|
||||
De op te lossen vraagstukken voor Ansible waren vooral:
|
||||
|
||||
* Hoe kunnen we geautomatiseerd LXD containers optrekken?
|
||||
* Hoe beheren we het OS op al die LXD containers vanuit 1 Ansible omgeving?
|
||||
* Hoe kunnen we development en acceptatie handig inrichten?
|
||||
|
||||
Verschillende soorten inzet van Ansible:
|
||||
|
||||
* Nieuwe LXD containers optrekken indien nodig (one-shot runs)
|
||||
* Configuratiemanagement LXD containers en LXD hosts (one-shot runs)
|
||||
* Onderhoudstaken op regelmatige basis (periodieke runs in cron)
|
||||
|
||||
# Waarom LXD?
|
||||
* Het platform bestaat uit heel veel verschillende applicaties
|
||||
* Allemaal in het verleden gebouwd als Ubuntu hosts (hardware en VM's)
|
||||
* Te veel niet gebruikte resources, containerization was een logische keuze
|
||||
* Veel van de applicaties hebben ook een veelheid aan scheduled jobs en support scripts
|
||||
* Statisch netwerk, omdat SBC's geen dynamische config kennen
|
||||
* Daardoor ook geen hippe autoscaling behoefte
|
||||
* Migratie naar LXD binnen XS4ALL was strak tijdgebonden (gekoppeld aan einde Edutel)
|
||||
|
||||
Doordat de beschikbare tijd gelimiteerd was en LXD de mogelijkheid bood om de
|
||||
bestaande omgeving vrijwel 1-op-1 na te bouwen op basis van LXD systeemcontainers,
|
||||
was de keuze voor virtualisatie snel gemaakt.
|
||||
|
||||
Het platform omzetten naar bijvoorbeeld Docker was ongetwijfeld mogelijk geweest,
|
||||
maar dat zou heel veel extra werk hebben opgeleverd en voor extra complexiteit
|
||||
op de netwerklaag hebben gezorgd.
|
||||
|
||||
Jammer? Nee! Met LXD en Ansible hebben we een mooi evenwicht bereikt tussen de
|
||||
bekende cattle en pets analogie. Het voice platform heeft diverse pets in zich,
|
||||
maar die kunnen zonder blikken of blozen naar de slachtbank.
|
||||
|
||||
# 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
|
||||
|
||||
#### Upgrade na installatie
|
||||
|
||||
```text
|
||||
sidn-demo-0X# apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
#### LXD snap-versie installeren ipv apt-versie
|
||||
|
||||
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)
|
||||
```
|
||||
Om te zorgen dat alle tooling lxc goed kan vinden:
|
||||
```
|
||||
sidn-demo-0X# ln -s /snap/bin/lxc /usr/bin/lxc
|
||||
```
|
||||
|
||||
#### LXD initialiseren
|
||||
|
||||
Voor deze demo:
|
||||
|
||||
```text
|
||||
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 het management VLAN
|
||||
|
||||
|
||||
# Netwerktoplogie
|
||||
Belangrijke keuze qua netwerk: elk netwerk interface in de LXD host
|
||||
is ondergebracht in een bridge interface.
|
||||
|
||||
* Elke container kan simpel op elk benodigd netwerk "ingeplugd" worden
|
||||
door een interface van de container aan de bridge toe te voegen.
|
||||
* Elke container is daarmee ook direct exposed op dat netwerk.
|
||||
* Containers kunnen ook containers op een andere LXD host bereiken,
|
||||
mits het layer 2 netwerk doorgetrokken is uiteraard.
|
||||
* Containers kunnen eenvoudig naar een andere LXD host worden verplaatst,
|
||||
mits de nieuwe host dezelfde netwerkbridges heeft geconfigureerd.
|
||||
|
||||
__Praktisch gezien lijkt dit op hoe je het netwerk met hardware hosts en
|
||||
switches zou regelen__
|
||||
|
||||
#### Netwerk bridges op de LXD hosts
|
||||
|
||||
Tijdens lxd init maken we niet de standaard lxdbr0 bridge aan.
|
||||
Die bridge is alleen voor intern gebruik binnen de host. Wanneer
|
||||
je services beschikbaar wilt stellen aan andere systemen, dan moeten
|
||||
daar trucs voor uitgehaald worden (vgl. met Docker/Traefik).
|
||||
|
||||
In plaats daarvan worden de bridges door onszelf geconfigureerd in Netplan.
|
||||
Netplan-config op productie ziet er ongeveer zo uit:
|
||||
|
||||
```yaml
|
||||
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):
|
||||
|
||||
```text
|
||||
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 ... n
|
||||
```
|
||||
|
||||
Hier is bijvoorbeeld CONTAINER1 aangesloten op het public en het SIP netwerk, terwijl
|
||||
CONTAINER2 aangesloten is op SIP en mgmt. CONTAINER4 is alleen aangesloten op het
|
||||
SIP netwerk, en kan via dat netwerk op layer 2 CONTAINER1 en CONTAINER2 ook bereiken.
|
||||
|
||||
|
||||
__Voor de demo gebruiken we een iets simpeler opzet:__
|
||||
|
||||
```yaml
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
enp1s0: {}
|
||||
enp6s0: {}
|
||||
bridges:
|
||||
br-public:
|
||||
dhcp4: no
|
||||
dhcp6: no
|
||||
interfaces: ["enp1s0"]
|
||||
addresses: ["192.168.56.150/24"] # en 151 op de andere host
|
||||
gateway4: "192.168.56.1"
|
||||
nameservers:
|
||||
addresses: ["8.8.8.8", "9.9.9.9"]
|
||||
br-demo:
|
||||
dhcp4: no
|
||||
dhcp6: no
|
||||
interfaces: ["enp6s0"]
|
||||
addresses: ["10.0.0.150/24"] # en 151 op de andere host
|
||||
```
|
||||
|
||||
Bridge br-public is aangesloten op een bridge die NAT netwerk verzorgt op
|
||||
de VM host en daarmee gebruikt kan worden om via NAT naar het internet te
|
||||
komen. Bridge br-demo zit aangesloten op een host-only bridge op de VM host
|
||||
en heeft dus de rol van een losse, niet-connected netwerkswitch.
|
||||
|
||||
In een plaatje:
|
||||
|
||||
```text
|
||||
KVM host
|
||||
192.168.56.1 (gateway + DHCP + DNS)
|
||||
|
|
||||
|
|
||||
+------192.168.56.0/24-----------+
|
||||
| |
|
||||
| private |
|
||||
| +----bridge----+ |
|
||||
| | 10.0.0.0/24 | |
|
||||
| | | |
|
||||
+------O------O--------+ +--O----------O--------+
|
||||
| enp1s0 enp6s0 | | enp6s0 enp1s0 |
|
||||
| | | | | | | |
|
||||
| br-public br-demo | | br-demo br-public |
|
||||
| | \ / | | | | \ / | |
|
||||
| | \/ | | | | \/ | |
|
||||
| | /\ | | | | /\ | |
|
||||
| ansible-01 \ | | | galera-02 \ | |
|
||||
| | | | | \ | |
|
||||
| galera-01 | | galera-03 |
|
||||
+----------------------+ +----------------------+
|
||||
KVM guest: sidn-demo-01 KVM guest: sidn-demo-02
|
||||
```
|
||||
|
||||
|
||||
#### Netwerk interfaces op de 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-\* bridges.
|
||||
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 binnen een container het herkennen van de netwerk interfaces zo simpel
|
||||
mogelijk te houden, wordt de naamgeving van de bridges gevolg. Bijvoorbeeld
|
||||
een interface dat in bridge "br-aap" wordt geplugd, heet dan "if-aap".
|
||||
Veel handiger dan "eth0" o.i.d.
|
||||
|
||||
Op de SIPproxy zul je dan ook de interfaces if-mgmt, if-public en if-sipcore
|
||||
terug kunnen vinden.
|
||||
|
||||
__Interfaces vanuit LXD profielen__
|
||||
|
||||
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:
|
||||
|
||||
```text
|
||||
# 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 netwerk van de hierboven beschreven
|
||||
SIPproxy container wordt gebruikt:
|
||||
|
||||
```text
|
||||
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 duidelijk naam als "br-local" of "br-private".
|
||||
|
||||
|
||||
#### Aanmaken LXD profile voor de demo
|
||||
|
||||
Op basis van de gevolgde lxd init methode, is er al een default profiel aangemaakt
|
||||
voor LXD. Deze is heel erg basaal, omdat we geen netwerk bridge hebben geconfigureerd:
|
||||
|
||||
```text
|
||||
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.
|
||||
|
||||
```text
|
||||
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-public:
|
||||
name: if-public
|
||||
nictype: bridged
|
||||
parent: br-public
|
||||
type: nic
|
||||
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 demodeze met de cloud-init van Ubuntu 14.04 nog niet mogelijk was.
|
||||
|
||||
|
||||
|
||||
# LXD clustering
|
||||
Of beter gezegd: __geen__ LXD clustering.
|
||||
|
||||
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" niet alleen inzicht in de containers
|
||||
die op de lokale host draaien, maar ook in alle containers die op alle cluster
|
||||
hosts draaien.
|
||||
demo
|
||||
Het klinkt heel goed: LXD clustering. Functioneel heb ik er echter nog
|
||||
weinig heil in gevonden voor onze setup. Bovendien heb ik op clustersystemen
|
||||
regelmatig problemen gezien met het goed werkend houden van het cluster,
|
||||
door bijvoorbeeld het uit sync raken van de quorum database (gebaseerd op dqlite,
|
||||
wat een distributed sqlite database is.)
|
||||
|
||||
Voordat clustering bestond in LXD liepen wel al wel tegen een probleem aan
|
||||
dat clustering probeert op te lossen: het werken met containers, verspreid
|
||||
over meerdere LXD hosts.
|
||||
|
||||
Onze aanpak:
|
||||
|
||||
* De LXD daemon laten luisteren op het netwerk (zoals bij lxd init al 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.
|
||||
|
||||
Om dit op te zetten, zorg dan 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, omdat we dan
|
||||
vanaf elke willekeurige host bijv `lxc list sidn-demo-01:` kunnen doen en niet
|
||||
op die bestreffende host `lxc list local:` hoeven te gebruiken
|
||||
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
|
||||
```
|
||||
|
||||
__Ja, maar, hoe wordt dit gebruikt dan?__
|
||||
|
||||
Het belangrijkste gebruik hiervan, is dat er vanuit cron op elke LXD host een job draait
|
||||
die een `lxc list` uitvoert op alle remotes. Daarmee wordt een lijst gemaakt waarin
|
||||
de containers en hun LXD hosts staan. Vanuit die lijst wordt een bestand gemaakt, met
|
||||
daarin een serie bash aliases voor `lxc exec <remote>:<containernaam> bash`.
|
||||
|
||||
Als ik wil inloggen op bijvoorbeeld de container `tel-prd-sipproxy-02`, dan kan ik na
|
||||
het sourcen van het gegenereerde aliassen bestand simpelweg `tel-prd-sipproxy-02` als
|
||||
commando uitvoeren op een willekeurige LXD host, waarna ik binnenval op een shell op
|
||||
die container.
|
||||
|
||||
|
||||
# De Ansible container
|
||||
De Ansible management host is een container op het voice platform zelf. Vanwege de
|
||||
kip/ei problematiek en het feit dat deze container toch niet steeds opnieuw gebouwd
|
||||
gaat worden, bootstrappen we deze container met de hand. Hier kan natuurlijk ook een
|
||||
playbook van worden gemaakt, maar vooralsnog is het minder werk om het zo te doen.
|
||||
|
||||
De handigste manier voor het leveren van de netwerkconfiguratie van deze container,
|
||||
is gebruikmaken van een `user.network-config` in de container configuratie.
|
||||
|
||||
```text
|
||||
sidn-demo-01# vi /tmp/config
|
||||
|
||||
version: 1
|
||||
config:
|
||||
- type: physical
|
||||
name: if-public
|
||||
subnets:
|
||||
- type: static
|
||||
address: 192.168.56.160
|
||||
netmask: 255.255.255.0
|
||||
gateway: 192.168.56.1
|
||||
dns_nameservers: [192.168.56.1]
|
||||
- type: physical
|
||||
name: if-demo
|
||||
subnets:
|
||||
- type: static
|
||||
address: 10.0.0.160
|
||||
netmask: 255.255.255.0
|
||||
```
|
||||
|
||||
Bouw de container op en start hem:
|
||||
|
||||
```text
|
||||
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
|
||||
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
|
||||
```
|
||||
|
||||
Opmerkingen:
|
||||
|
||||
* 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.
|
||||
|
||||
* Er wordt een hard MAC-adres gezet. Dat is vooral nodig als je gebruik maakt
|
||||
van VirtualBox voor het maken van de VM's. De bridge in VirtualBox kan
|
||||
problemen geven wanneer je eenzelfde host met hetzelfde IP-adres maar een
|
||||
nieuw MAC-adres opnieuw optrekt. In andere omgeving zou dat MAC-adres
|
||||
ook weggelaten kunnen worden.
|
||||
|
||||
|
||||
Op de container is nu te zien dat het layer 2 netwerk via de netwerk bridges
|
||||
ontsloten 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)
|
||||
ansible# ping 10.0.0.150 (een ping naar de eigen LXD host)
|
||||
ansible# ping 10.0.0.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.
|
||||
|
||||
__Cool, lxc support binnen Ansible!__
|
||||
|
||||
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, door op de Ansible host alle LXD hosts als remotes
|
||||
toe te voegen en Ansible voor elk uit te voeren commando o een container
|
||||
lxc te laten gebruken.
|
||||
|
||||
__meh__
|
||||
|
||||
Helaas bleek dat dit een behoorlijke impact op de snelheid had. 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 daarom inmiddels een hybride setup:
|
||||
|
||||
* Management van de LXD hosts wordt volledig met SSH gedaan vanuit
|
||||
de Ansible container (dit is de standaard methode voor Ansible).
|
||||
|
||||
* Het bootstrappen van nieuwe containers en het configureren van het
|
||||
netwerk van containers worden gedaan door met SSH naar een LXD host
|
||||
te gaan (middels Ansible delegate). Vervolgens worden van daaruit met
|
||||
lxc commando's (met name `lxc exec` en `lxc file`) 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 de standaard
|
||||
SSH methode gedaan, direct vanuit de Ansible container.
|
||||
|
||||
Het mooie aan deze opzet, is dat we:
|
||||
|
||||
* Containers uit het niet automatisch kunnen bootstrappen.
|
||||
* Kapotte netwerkconfiguratie ook altijd weer kunnen repareren, omdat
|
||||
de `lxc` commando's geen containernetwerk nodig hebben.
|
||||
* Missende SSH keys voor Ansible toegang kunnen plaatsen.
|
||||
* Voor de bulk van de configuratie commando's snel via SSH kunnen versturen.
|
||||
|
||||
|
||||
#### 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 LXD hosts wordt gekopieerd.
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```bash
|
||||
ansible# ssh 10.0.0.150 touch .hushlogin
|
||||
ansible# ssh 10.0.0.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.
|
||||
|
||||
```text
|
||||
ansible# apt install -y python3 python3-pip
|
||||
ansible# pip3 install ansible
|
||||
```
|
||||
|
||||
De Ansible configuratie staat in een git repository. Go get!
|
||||
|
||||
```text
|
||||
ansible# apt install -y git
|
||||
ansible# REPO=https://git.makaay.nl/mauricem/sidn-lxd-ansible-demo.git
|
||||
ansible# git clone $REPO /etc/ansible
|
||||
```
|
||||
|
||||
De configuratie van Ansible zelf is redelijk eenvoudig:
|
||||
|
||||
```text
|
||||
ansible# vi /root/.ansible.cfg
|
||||
|
||||
[defaults]
|
||||
inventory = /etc/ansible/environments/demo/hosts
|
||||
vault_password_file = /root/.ansible-vault-password
|
||||
jinja2_extensions = jinja2.ext.do
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
```
|
||||
|
||||
De vault_password_file wordt gebruikt als password file voor Ansible vault.
|
||||
Met vault is het mogelijk om geheime informatie (normaliter: wachtwoorden)
|
||||
op te slaan in gecrypte vorm. Het te gebruiken symmetrische wachtwoord is
|
||||
opgeslagen in de password file. Bijvoorbeeld:
|
||||
|
||||
```text
|
||||
ansible# echo "My very secret 1337 super passw0rd##" \
|
||||
> /root/.ansible-vault-password
|
||||
ansible# chmod 600 /root/.ansible-vault-password
|
||||
```
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
# In the Ubuntu 14.04 cloud image, cloud-init is supported, but it is an old
|
||||
# version and network configuration does not seem to work. Therefore, the
|
||||
# network configuration is handled in a clunky way.
|
||||
|
||||
- name: "Create Ubuntu 14.04 LXD container"
|
||||
delegate_to: "{{ hostvars[inventory_hostname].lxd_host }}"
|
||||
lxd_container:
|
||||
name: "{{ inventory_hostname }}"
|
||||
state: started
|
||||
source:
|
||||
type: image
|
||||
mode: pull
|
||||
server: "{{ vars.lxd_image_server }}"
|
||||
protocol: simplestreams
|
||||
alias: "{{ vars.lxd_image_name }}"
|
||||
profiles:
|
||||
- "{{ vars.lxd_profile }}"
|
||||
wait_for_ipv4_addresses: True
|
||||
|
||||
- name: "Generate network config for Ubuntu 14.04 LXD container"
|
||||
delegate_to: "{{ hostvars[inventory_hostname].lxd_host }}"
|
||||
template:
|
||||
src: network-interfaces.j2
|
||||
dest: /tmp/network-interfaces-{{ inventory_hostname }}
|
||||
|
||||
- name: "Install network config for Ubuntu 14.04 LXD container"
|
||||
delegate_to: "{{ hostvars[inventory_hostname].lxd_host }}"
|
||||
shell: >-
|
||||
lxc file push
|
||||
/tmp/network-interfaces-{{ inventory_hostname }}
|
||||
{{ inventory_hostname }}/etc/network/interfaces
|
|
@ -1,7 +1,13 @@
|
|||
---
|
||||
- include: bootstrap-ubuntu14.04.yml
|
||||
when: >
|
||||
inventory_hostname not in lxd_status and
|
||||
vars.distribution_codename == 'trusty'
|
||||
|
||||
- include: bootstrap-other.yml
|
||||
when: >
|
||||
inventory_hostname not in lxd_status
|
||||
inventory_hostname not in lxd_status and
|
||||
vars.distribution_codename != 'trusty'
|
||||
|
||||
- name: "Set interface MAC addresses"
|
||||
delegate_to: "{{ hostvars[inventory_hostname].lxd_host }}"
|
||||
|
@ -12,7 +18,7 @@
|
|||
with_items: "{{ hostvars[inventory_hostname].network.values() | list }}"
|
||||
when: '"mac_address" in item'
|
||||
|
||||
- name: "Set LXD custom configuration parameters"
|
||||
- name: "Set LXD configuration parameters"
|
||||
delegate_to: "{{ hostvars[inventory_hostname].lxd_host }}"
|
||||
shell: >-
|
||||
lxc config set
|
||||
|
@ -26,6 +32,21 @@
|
|||
- name: boot.autostart.priority
|
||||
value: "{{ hostvars[inventory_hostname].lxd_boot_priority | default(0) }}"
|
||||
|
||||
- name: "Make NFS client work in the container"
|
||||
delegate_to: "{{ hostvars[inventory_hostname].lxd_host }}"
|
||||
shell: >-
|
||||
lxc config set
|
||||
{{ hostvars[inventory_hostname].lxd_host | quote }}:{{ inventory_hostname | quote }}
|
||||
{{ item.name | quote }} {{ item.value | quote }}
|
||||
with_items:
|
||||
- name: "raw.apparmor"
|
||||
value: "mount fstype=nfs,"
|
||||
- name: "security.privileged"
|
||||
value: "true"
|
||||
when: '"nfs_mount" in hostvars[inventory_hostname]'
|
||||
|
||||
- include: hostname.yml
|
||||
|
||||
- name: "Stop created LXD container"
|
||||
delegate_to: "{{ hostvars[inventory_hostname].lxd_host }}"
|
||||
lxd_container:
|
||||
|
|
Loading…
Reference in New Issue