785 lines
28 KiB
Markdown
785 lines
28 KiB
Markdown
# Onderwerpen van de super 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 vs LXD remotes
|
|
* De Ansible container
|
|
* Ansible-vault voor opslag secrets
|
|
* Dynamische inventory
|
|
* Het gebruik van facts
|
|
* 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 2 KVM netwerken gekoppeld:
|
|
* NAT netwerk: 192.168.56.0/24, gateway/DNS 192.168.56.1
|
|
* Host-only netwerk: 10.0.0.0/24
|
|
* Ubuntu 18.04 LTS
|
|
|
|
#### Host-only netwerk
|
|
|
|
Het NAT netwerk is standaard beschikbaar, maar het host-only netwerk moest ik
|
|
nog met de hand aanmaken.
|
|
|
|
```text
|
|
# sudo apt-get install bridge-utils
|
|
# vi /etc/network/interfaces (onderstaande daaraan toegevoegd)
|
|
|
|
auto br-host
|
|
iface br-host inet static
|
|
address 10.0.0.1
|
|
netmask 255.255.255.0
|
|
pre-up brctl addbr br-host
|
|
post-down brctl delbr br-host
|
|
|
|
# echo '<network><name>host-only</name><bridge name="bg-host" /></network>' > /tmp/net.xml
|
|
# virsh net-define /tmp/net.xml
|
|
# virsh net-start host-only
|
|
# virsh net-autostart host-only
|
|
|
|
# virsh net-list --all (om te controleren of het er goed uitziet)
|
|
Name State Autostart Persistent
|
|
------------------------------------------------
|
|
nat active yes yes
|
|
host-only active yes yes
|
|
```
|
|
|
|
#### 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".
|
|
|
|
|
|
#### Gebruik van LXD profiles voor de netwerk device configuratie
|
|
|
|
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, om te laten zien wat
|
|
Ansible hier onder water doet.
|
|
|
|
```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 vs LXD remotes
|
|
Wij gebruiken __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
|
|
```
|
|
|
|
# Ansible vault voor opslag secrets
|
|
|
|
De bovengenoemde `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 -n "My very secret 1337 super passw0rd##" > /root/.ansible-vault-password
|
|
ansible# chmod 600 /root/.ansible-vault-password
|
|
```
|
|
Hierna is het mogelijk om met het `ansible-vault` commando bestanden of losse strings
|
|
te encrypten. Ansible herkent automatisch dit soort crypted informatie bij het lezen
|
|
van de recipe yaml bestanden en decrypt ze automatisch voor gebruik in een run.
|
|
|
|
Wij maken alleen gebruik van de losse string encryptie op de volgende manier:
|
|
|
|
```text
|
|
# ansible-vault encrypt_string 'SECRET!'
|
|
Reading plaintext input from stdin. (ctrl-d to end input)
|
|
!vault |
|
|
$ANSIBLE_VAULT;1.1;AES256
|
|
64663430316333633466353834343736333634666137653034323538316536376435616339313539
|
|
3536373866656238393031663665353364346530313933610a616233386234323363346266643262
|
|
37613963346134313461396130643939353037323835383132663864636266623736393361393636
|
|
3533613531353763610a303633323961366361333165393339343464336335653162663963393837
|
|
646
|
|
```
|
|
|
|
De complete string vanaf `!vault ...` kun je in een yaml value plakken, bijv:
|
|
|
|
```yaml
|
|
---
|
|
user:
|
|
username: john
|
|
password: !vault |
|
|
$ANSIBLE_VAULT;1.1;AES256
|
|
64663430316333633466353834343736333634666137653034323538316536376435616339313539
|
|
3536373866656238393031663665353364346530313933610a616233386234323363346266643262
|
|
37613963346134313461396130643939353037323835383132663864636266623736393361393636
|
|
3533613531353763610a303633323961366361333165393339343464336335653162663963393837
|
|
646
|
|
```
|
|
|
|
Om wachtwoorden te genereren voor onze systemen, gebruiken we pwgen op deze manier:
|
|
|
|
```text
|
|
# ansible-vault encrypt_string $(pwgen 32 -c -n -1)
|
|
```
|
|
|
|
Hiermee genereren we direct een crypted string en het plain text wachtwoord is
|
|
zelfs onbekend, omdat het nergens op het scherm verschijnt. Als je wilt weten
|
|
welk wachtwoord het is geworden, dan is dat uiteindelijk natuurlijk wel binnen
|
|
Ansible recipes opvraagbaar.
|
|
|
|
|
|
# Dynamische inventory
|
|
Ansible maakt gebruik van een inventory, waarin alle te beheren hosts, een
|
|
groepen-indeling en extra attributen voor hosts / groepen zijn oipgenomen.
|
|
|
|
Waar we al heel snel tegenaan liepen bij het inrichten van de Ansible omgeving
|
|
voor het voice platform, was dat de standaard manieren om een inventory in
|
|
te richten vrij beperkt waren. Bovendien kriebelde steeds het gevoel dat we
|
|
een laag van abstractie misten, omdat alle gegevens door elkaar in bestanden
|
|
terecht kwamen, wat het overzicht om zeep hielp.
|
|
|
|
De oplossing hiervoor was het implementeren van een dynamische inventory.
|
|
Het principe is heel simpel: op de plek waar je normaal gesproken het `hosts`
|
|
bestand voor Ansible neerzet, zet je nu een script neer dat een inventory
|
|
bestand op STDOUT uitpoept. Hiermee ben je in het script volledig in control
|
|
voor de manier waarop de inventory wordt gebouwd.
|
|
|
|
In het voice platform gebruiken we een aantal losse yaml bestanden (node_types,
|
|
nodes, networks, lxd, credentials), die elk hun eigen type informatie bevatten
|
|
en die door het `hosts` script worden gecombineerd tot een inventory met
|
|
zeer verrijkte host entries.
|
|
|
|
Dezelfde strategie kan natuurlijk ook worden gebruikt om de Ansible inventory
|
|
op te bouwen vanuit een database, een webservice die de data aan kan leveren,
|
|
etc.
|
|
|
|
|
|
# Het gebruik van facts
|
|
Simpel: wij gebruiken geen facts in het voice platform.
|
|
|
|
Facts zijn leuk, maar het verzamelen van facts voor een Ansible run kost
|
|
aardig wat tijd (en ik hou niet van wachten). Bovendien suggereert het
|
|
dat ik de nodes in het platform niet volledig zelf onder controle zou
|
|
hebben (maar ik ben een control freak).
|
|
|