Bare-Metal Provisioning fai da te, o Preparazione automatica dei server da zero

Ciao, sono Denis e uno dei miei ambiti di attività è lo sviluppo di soluzioni infrastrutturali in X5. Oggi vorrei condividere con voi come sia possibile creare un sistema automatico di preparazione dei server utilizzando strumenti accessibili a tutti. A mio avviso, è una soluzione interessante, semplice e flessibile.

Bare-Metal Provisioning fai da te, o Preparazione automatica dei server da zero

Per preparazione si intende: creare un server nuovo di fabbrica, un server completamente configurato con sistema operativo Linux o con hypervisor ESXi (il dispiegamento server di Windows non è trattato in questo articolo).

Termini:

  • server – server che devono essere configurati.
  • install-server – server principale che gestisce l'intero processo di preparazione attraverso la rete.

Perché è necessaria l'automazione?

Supponiamo di avere la necessità di preparare server in massa a partire da zero, al massimo – 30 al giorno. Server di diversi produttori e modelli, su cui possono essere installati diversi sistemi operativi, può esserci o non esserci un hypervisor.

Quali operazioni sono incluse nel processo di configurazione (senza automazione):

  • collegare tastiera, mouse, monitor al server;
  • configurare BIOS, RAID, IPMI;
  • aggiornare il firmware dei componenti;
  • distribuire un'immagine del filesystem (oppure installare un hypervisor e copiare le macchine virtuali);

Nota. Come opzione, il dispiegamento del sistema operativo è possibile tramite installazione con un file di risposte automatiche. Ma questo non sarà discusso nell'articolo. Anche se più avanti vedrete che aggiungere questa funzionalità non è complicato.

  • configurare i parametri del sistema operativo (hostname, IP, ecc.).

Con questo approccio, le stesse configurazioni vengono eseguite in sequenza su ogni server. L'efficacia di questo lavoro è molto bassa.

L'essenza dell'automazione è quella di escludere la partecipazione umana dal processo di preparazione del server. Il massimo, per quanto possibile.

Grazie all'automazione, si riduce il tempo di inattività tra le operazioni e si ha la possibilità di preparare più server contemporaneamente. Inoltre, si riduce notevolmente la probabilità di errori dovuti al fattore umano.

Bare-Metal Provisioning fai da te, o Preparazione automatica dei server da zero

Come avviene la configurazione automatica dei server?

Analizziamo ogni fase in dettaglio.

Hai un server Linux che utilizzi come PXE install-server. Su di esso sono installati e configurati i servizi: DHCP, TFTP.

Quindi, carichiamo il server (quello che deve essere configurato) tramite PXE. Ricordiamo come funziona:

  • Sul server è selezionato il caricamento dalla rete.
  • Il server carica il PXE-ROM della scheda di rete e si rivolge al server di installazione tramite DHCP per ottenere un indirizzo di rete.
  • Il server di installazione DHCP assegna un indirizzo e fornisce anche istruzioni per il caricamento successivo tramite PXE.
  • Il server carica il bootloader di rete dal server di installazione tramite PXE, il caricamento successivo avviene secondo il file di configurazione PXE.
  • Il caricamento avviene sulla base dei parametri ricevuti (kernel, initramfs, punti di montaggio, immagine squashfs e altro).

Nota. Nell'articolo viene fornita una descrizione del caricamento tramite PXE in modalità BIOS. Attualmente i produttori stanno implementando attivamente la modalità di avvio UEFI. Per PXE, la differenza sarà nella configurazione del server DHCP e nella presenza di un bootloader aggiuntivo.

Consideriamo un esempio della configurazione del server PXE (menu pxelinux).

File pxelinux.cfg/default:

default menu.c32
prompt 0
timeout 100
menu title X5 PXE Boot Menu
LABEL InstallServer Menu
	MENU LABEL InstallServer
	KERNEL menu.c32
	APPEND pxelinux.cfg/installserver
LABEL VMware Menu
	MENU LABEL VMware ESXi Install
	KERNEL menu.c32
	APPEND pxelinux.cfg/vmware
LABEL toolkit // menu predefinito
	MENU LABEL Linux Scripting Toolkits
	MENU default
	KERNEL menu.c32
	APPEND pxelinux.cfg/toolkit // passaggio al menu successivo

File pxelinux.cfg/toolkit:

prompt 0
timeout 100
menu title X5 PXE Boot Menu
label mainmenu
    menu label ^Torna al menu principale
    kernel menu.c32
    append pxelinux.cfg/default
label x5toolkit-auto // predefinito — modalità automatica
        menu label x5 toolkit autoinstall
        menu default
        kernel toolkit/tkcustom-kernel
        append initrd=toolkit/tk-initramfs.gz quiet net.ifnames=0 biosdevname=0 nfs_toolkit_ip=192.168.200.1 nfs_toolkit_path=tftpboot/toolkit nfs_toolkit_script=scripts/mount.sh script_cmd=master-install.sh CMDIS2="…"
label x5toolkit-shell // per debug - console
        menu label x5 toolkit shell
        kernel toolkit/tkcustom-kernel
        append initrd=toolkit/tkcustom-initramfs.gz quiet net.ifnames=0 biosdevname=0 nfs_toolkit_ip=192.168.200.1 nfs_toolkit_path=tftpboot/toolkit nfs_toolkit_script=scripts/mount.sh script_cmd=/bin/bash CMDIS2="…"

Il kernel e l'initramfs a questo punto sono un'immagine linux intermedia, grazie alla quale avverrà la preparazione e configurazione principale del server.

Come potete vedere, il bootloader passa molti parametri al kernel. Parte di questi parametri sono utilizzati dal kernel stesso. Alcuni possiamo utilizzarli per i nostri scopi. Questo verrà spiegato ulteriormente, ma per ora si può semplicemente ricordare che tutti i parametri passati saranno disponibili nell'immagine linux intermedia attraverso /proc/cmdline.

Da dove prendere il kernel e l'initramfs?
Come base, si può scegliere qualsiasi distribuzione linux. A cosa prestare attenzione nella scelta:

  • l'immagine di avvio deve essere universale (disponibilità di driver, possibilità di installare utilità aggiuntive);
  • probabilmente sarà necessario personalizzare initramfs.

Come è fatto nella nostra soluzione per X5? È stata scelta come base CentOS 7. Faremo il seguente trucco: prepareremo la futura struttura dell'immagine, la impacchetteremo in un archivio e creeremo initramfs, all'interno del quale ci sarà il nostro archivio del filesystem. All'avvio dell'immagine, l'archivio verrà estratto nella partizione tmpfs creata. In questo modo otterremo un'immagine live di linux minimale, ma completa, con tutte le utilità necessarie, composta da soli due file: vmkernel e initramfs.

#создаем директории: 

mkdir -p /tftpboot/toolkit/CustomTK/rootfs /tftpboot/toolkit/CustomTK/initramfs/bin

#подготавливаем структуру:

yum groups -y install "Minimal Install" --installroot=/tftpboot/toolkit/CustomTK/rootfs/
yum -y install nfs-utils mariadb ntpdate mtools syslinux mdadm tbb libgomp efibootmgr dosfstools net-tools pciutils openssl make ipmitool OpenIPMI-modalias rng-tools --installroot=/tftpboot/toolkit/CustomTK/rootfs/
yum -y remove biosdevname --installroot=/tftpboot/toolkit/CustomTK/rootfs/

# подготавливаем initramfs:

wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64 -O /tftpboot/toolkit/CustomTK/initramfs/bin/busybox
chmod a+x /tftpboot/toolkit/CustomTK/initramfs/bin/busybox
cp /tftpboot/toolkit/CustomTK/rootfs/boot/vmlinuz-3.10.0-957.el7.x86_64 /tftpboot/toolkit/tkcustom-kernel

# создаем /tftpboot/toolkit/CustomTK/initramfs/init (ниже содержание скрипта):

#!/bin/busybox sh
/bin/busybox --install /bin
mkdir -p /dev /proc /sys /var/run /newroot
mount -t proc proc /proc
mount -o mode=0755 -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts /dev/shm /dev/mapper /dev/vc
mount -t devpts -o gid=5,mode=620 devpts /dev/pts
mount -t sysfs sysfs /sys
mount -t tmpfs -o size=4000m tmpfs /newroot
echo -n "Extracting rootfs... "
xz -d -c -f rootfs.tar.xz | tar -x -f - -C /newroot
echo "done"
mkdir -p /newroot/dev /newroot/proc /newroot/sys
mount --move /sys  /newroot/sys
mount --move /proc /newroot/proc
mount --move /dev  /newroot/dev
exec switch_root /newroot /sbin/init

# упаковываем rootfs и initramfs:

cd /tftpboot/toolkit/CustomTK/rootfs
tar cJf /tftpboot/toolkit/CustomTK/initramfs/rootfs.tar.xz --exclude ./proc --exclude ./sys --exclude ./dev .
cd /tftpboot/toolkit/CustomTK/initramfs
find . -print0 | cpio --null -ov --format=newc | gzip -9 > /tftpboot/toolkit/tkcustom-initramfs-new.gz

Quindi, abbiamo specificato il kernel e initramfs che devono essere caricati. Di conseguenza, in questa fase, avviando l'immagine intermedia linux tramite PXE, otterremo la console del SO.

Ottimo, ma ora dobbiamo trasferire il controllo alla nostra 'automazione'.

Questo può essere fatto in questo modo.

Supponiamo che, dopo il caricamento dell'immagine, intendiamo trasferire il controllo allo script mount.sh.
Inseriremo lo script mount.sh nel caricamento automatico. Per questo sarà necessario modificare initramfs:

  • estrarre initramfs (se utilizziamo la variante initramfs sopra menzionata, questo non è necessario)
  • includere nel caricamento automatico il codice che analizzerà i parametri trasmessi tramite /proc/cmdline e passerà il controllo successivo;
  • impacchettare initramfs.

Nota. Nel caso di X5 toolkit, il controllo del caricamento viene trasferito allo script /opt/x5/toolkit/bin/hook.sh с помощью override.conf в getty tty1 (ExecStart=…)

Quindi, viene caricata un'immagine in cui allo startup automatico viene avviato lo script mount.sh. Successivamente, lo script mount.sh analizza durante l'esecuzione i parametri trasmessi (script_cmd=) e avvia il programma/script necessario.

etichetta toolkit-auto
kernel …
aggiungi … nfs_toolkit_script=scripts/mount.sh script_cmd=master-install.sh

etichetta toolkit-shell
kernel …
aggiungi … nfs_toolkit_script=scripts/mount.sh script_cmd=/bin/bash

Bare-Metal Provisioning fai da te, o Preparazione automatica dei server da zero

Qui a sinistra c'è il menu PXE, a destra – lo schema di trasferimento del controllo.

Abbiamo chiarito il trasferimento del controllo. A seconda della scelta nel menu PXE, viene avviato o lo script di auto-configurazione o la console di debug.

Nel caso di configurazione automatica, vengono montate le directory necessarie dal server di installazione, che contengono:

  • script;
  • modelli BIOS/UEFI salvati di vari server;
  • firmware;
  • utilità per server;
  • log.

Successivamente, lo script mount.sh trasferisce il controllo allo script master-install.sh dalla directory degli script.

L'albero degli script (l'ordine della loro esecuzione) appare più o meno così:

  • master-install
  • sharefunctions (funzioni comuni)
  • info (output delle informazioni)
  • modelli (installazione dei parametri di installazione in base al modello del server)
  • prepare_utils (installazione delle utilità necessarie)
  • fwupdate (aggiornamento del firmware)
  • diag (diagnostica elementare)
  • biosconf (configurazione del BIOS/UEFI)
  • clockfix (impostazione dell'ora sulla scheda madre)
  • srmconf (configurazione dell'interfaccia remota)
  • raidconf (configurazione dei volumi logici)

uno dei seguenti:

  • preinstall (passaggio di controllo all'installatore del sistema operativo o del hypervisor, ad esempio ESXi)
  • merged-install (avvio diretto dell'estrazione dell'immagine)

Ora sappiamo:

  • come avviare il server tramite PXE;
  • come passare il controllo a uno script personalizzato.


Continuiamo. Le seguenti domande sono ora pertinenti:

  • Come identificare il server che stiamo preparando?
  • Quali utilità e come configurare il server?
  • Come ottenere le impostazioni per un server specifico?

Come identificare il server che stiamo preparando?

È semplice – DMI:

dmidecode –s system-product-name
dmidecode –s system-manufacturer
dmidecode –s system-serial-number

Qui ci sono tutte le informazioni necessarie: venditore, modello, numero di serie. Se non sei sicuro che queste informazioni siano disponibili su tutti i server, puoi identificarli tramite l'indirizzo MAC. Oppure in entrambi i modi contemporaneamente, se i venditori dei server sono diversi e su alcuni modelli le informazioni sul numero di serie sono semplicemente assenti.

Sulla base delle informazioni ricevute, vengono montate le cartelle di rete dal server di installazione e vengono caricati tutti i necessari (utilità, firmware e altro).

Quali utilità e come configurare il server?

Fornerò le utilità per linux di alcuni produttori. Tutte le utilità sono disponibili sui siti ufficiali dei venditori.

Bare-Metal Provisioning fai da te, o Preparazione automatica dei server da zero

Per quanto riguarda i firmware, penso che sia tutto chiaro. Di solito vengono forniti in forma di file eseguibili compressi. Il file eseguibile controlla il processo di aggiornamento del firmware e restituisce un codice di uscita.

Il BIOS e l'IPMI di solito vengono configurati tramite modelli. Se necessario, il modello può essere modificato prima del caricamento.

Le utilità RAID di alcuni venditori possono anche configurare tramite un modello. Se non è così, sarà necessario scrivere uno script di configurazione.

L'ordine di configurazione del RAID è solitamente il seguente:

  • Richiediamo la configurazione attuale.
  • Se ci sono già array logici – li cancelliamo.
  • Controlliamo quali dischi fisici sono presenti e quanti sono.
  • Creiamo un nuovo array logico. Interrompiamo il processo in caso di errore.

Come ottenere le impostazioni per un server specifico?

Supponiamo che le impostazioni di tutti i server siano archiviate sul server di installazione. In tal caso, per rispondere alla nostra domanda, dobbiamo prima decidere: come trasferire le impostazioni al server di installazione.

Inizialmente, si può tranquillamente fare a meno di file di testo. (In futuro si può utilizzare un file di testo come metodo di backup per il trasferimento delle impostazioni).

Si può 'condividere' un file di testo sul server di installazione. E aggiungere il suo montaggio nello script mount.sh.

Le righe saranno, ad esempio, di questo tipo:

Queste righe saranno trasmesse in un file dall'ingegnere dalla sua macchina di lavoro. E successivamente, durante la configurazione del server, i parametri per il server specifico saranno letti dal file.

Ma, a lungo termine, è meglio utilizzare un database per memorizzare le impostazioni, gli stati e i log delle installazioni dei server.

Certo, non è sufficiente un solo database, e sarà necessario creare una parte client, tramite la quale le impostazioni saranno trasmesse al database. Realizzarlo è più complicato rispetto a un file di testo, ma in realtà non è così difficile come sembra. È assolutamente possibile scrivere da soli una versione minima del client, che trasmetterà semplicemente i dati al database. E in seguito, si potrà migliorare il programma client in modalità libera (report, stampa etichette, invio di notifiche e qualsiasi altra cosa che venga in mente).

Facendo una certa richiesta al database e specificando il numero di serie del server, otterremo i parametri necessari per la configurazione del server.

Inoltre, non sarà necessario pensare a blocchi per l'accesso simultaneo, come nel caso di un file di testo.

Possiamo scrivere il registro delle configurazioni nel database in ogni fase e controllare il processo di installazione tramite eventi e flag delle fasi di preparazione.

Ora sappiamo come:

  • avviare il server tramite PXE;
  • passare il controllo al nostro script;
  • identificare il server da preparare in base al numero di serie;
  • configurare il server con gli strumenti appropriati;
  • trasmettere le impostazioni al database del server di installazione tramite la parte client.

Abbiamo scoperto come:

  • il server da installare riceve le impostazioni necessarie dal database;
  • tutti i progressi della preparazione vengono registrati nel database (log, eventi, flag delle fasi).

E per quanto riguarda i vari tipi di software da installare? Come installare un hypervisor, copiare una VM e configurare tutto questo?

Nel caso di distribuzione di un'immagine di filesystem (linux) sull'hardware, è tutto piuttosto semplice:

  • Dopo aver configurato tutti i componenti del server, distribuiamo l'immagine.
  • Installiamo il bootloader grub.
  • Eseguiamo chroot e configuriamo tutto ciò che serve.

Come passare il controllo all'installatore del sistema operativo (con ESXi come esempio).

  • Organizziamo il passaggio di controllo dal nostro script all'installatore del hypervisor tramite un file di risposte automatiche (kickstart):
  • Rimuoviamo le partizioni attuali sul disco.
  • Creiamo una partizione di 500MB.
  • La segnaliamo come avviabile.
  • Formattiamo in FAT32.
  • Copiamo i file di installazione di ESXi nella radice.
  • Installiamo syslinux.
  • Copiamo syslinux.cfg in /syslinux/

default esxi
prompt 1
timeout 50
label esxi
kernel mboot.c32
append -c boot.cfg

  • Copiamo mboot.c32 in /syslinux.
  • In boot.cfg deve essere kernelopt=ks=ftp:///ks_esxi.cfg
  • Riavviamo il server.

Dopo il riavvio del server, l'installatore di ESXi si avvierà dal disco rigido. Tutti i file necessari dell'installatore verranno caricati in memoria e quindi inizierà l'installazione di ESXi, secondo il file di risposte automatiche indicato.

Qui riporterò alcune righe dal file di risposte automatiche ks_esxi.cfg:

%firstboot --interpreter=busybox
…
# otteniamo il numero di serie

SYSSN=$(esxcli hardware platform get | grep Serial | awk -F " " '{print $3}')

# otteniamo l'IP

IPADDRT=$(esxcli network ip interface ipv4 get | grep vmk0 | awk -F " " '{print $2}')
LAST_OCTET=$(echo $IPADDRT | awk -F'.' '{print $4}')

# colleghiamo il server di installazione NFS

esxcli storage nfs add -H is -s \/srv\/nfs_share -v nfsshare1

# copiamo le impostazioni temporanee di ssh, per utilizzare il client ssh

mv \/etc\/ssh \/etc\/ssh.tmp
cp -R \/vmfs\/volumes\/nfsshare1\/ssh \/etc\/ 
chmod go-r \/etc\/ssh\/ssh_host_rsa_key

# copiamo ovftool, per distribuire le VM adesso, più potrebbe tornare utile dopo

cp -R \/vmfs\/volumes\/nfsshare1\/ovftool \/vmfs\/volumes\/datastore1\/

# distribuiamo le VM

\/vmfs\/volumes\/datastore1\/ovftool\/tools\/ovftool --acceptAllEulas --noSSLVerify --datastore=datastore1 --name=VM1 \/vmfs\/volumes\/nfsshare1\/VM_T\/VM1.ova vi:\/\/root:esxi_password@127.0.0.1
\/vmfs\/volumes\/datastore1\/ovftool\/tools\/ovftool --acceptAllEulas --noSSLVerify --datastore=datastore1 --name=VM2 \/vmfs\/volumes\/nfsshare1\/VM_T\/VM2.ova vi:\/\/root:esxi_password@127.0.0.1

# otteniamo la riga con le impostazioni del nostro server

ssh root@is "mysql -h'192.168.0.1' -D'servers' -u'user' -p'secretpassword' -e "SELECT ... WHERE servers.serial='$SYSSN'"" | grep -v ^$ | sed 's\/NULL\/\/g' > \/tmp\/servers
...
# generiamo lo script di configurazione della rete

echo '#!\/bin\/sh' > \/vmfs\/volumes\/datastore1\/netconf.sh
echo "esxcli network ip interface ipv4 set -i=vmk0 -t=static --ipv4=$IPADDR --netmask=$S_SUB || exit 1" >> \/vmfs\/volumes\/datastore1\/netconf.sh
echo "esxcli network ip route ipv4 add -g=$S_GW -n=default || exit 1" >> \/vmfs\/volumes\/datastore1\/netconf.sh
chmod a+x \/vmfs\/volumes\/datastore1\/netconf.sh

# impostiamo il parametro guestinfo.esxihost.id, specificando il numero di serie

echo "guestinfo.esxihost.id = "$SYSSN"" >> \/vmfs\/volumes\/datastore1\/VM1\/VM1.vmx
echo "guestinfo.esxihost.id = "$SYSSN"" >> \/vmfs\/volumes\/datastore1\/VM2\/VM2.vmx
...
# aggiorniamo le informazioni nel database

SYSNAME=$(esxcli hardware platform get | grep Product | sed 's\/Product Name:\/\/\' | sed 's\/^ *\/\/')
UUID=$(vim-cmd hostsvc\/hostsummary | grep uuid | sed 's\/ \/\/g;s\/,$\/\' | sed 's\/^uuid="\/\/;s\/"$\/\')
ssh root@is "mysql -D'servers' -u'user' -p'secretpassword' -e "UPDATE servers ... SET ... WHERE servers.serial='$SYSSN'""
ssh root@is "mysql -D'servers' -u'user' -p'secretpassword' -e "INSERT INTO events ...""

# ripristiniamo le impostazioni di SSH

rm -rf \/etc\/ssh
mv \/etc\/ssh.tmp \/etc\/ssh

# configuriamo la rete e riavviamo

esxcli system hostname set --fqdn=esx-${G_NICK}.x5.ru
\/vmfs\/volumes\/datastore1\/netconf.sh
reboot

In questa fase, l'hypervisor è stato installato e configurato, e le macchine virtuali sono state copiati.

Come posso ora configurare le macchine virtuali?

Abbiamo fatto un piccolo trucco: durante l'installazione abbiamo impostato il parametro guestinfo.esxihost.id = "$SYSSN" nel file VM1.vmx, specificando il numero di serie del server fisico.

Ora, dopo l'avvio, la macchina virtuale (con il pacchetto vmware-tools installato) può accedere a questo parametro:

ESXI_SN=$(vmtoolsd --cmd "info-get guestinfo.esxihost.id")

Cioè, la VM sarà in grado di identificarsi (sa il numero di serie dell'host fisico), fare una richiesta al database del server di installazione e ottenere i parametri da configurare. Tutto questo viene formalizzato in uno script che deve essere eseguito automaticamente all'avvio della VM guestos (ma solo una volta: RunOnce).

Ora sappiamo come:

  • avviare il server tramite PXE;
  • passare il controllo al nostro script;
  • identificare il server da preparare in base al numero di serie;
  • configurare il server con gli strumenti appropriati;
  • trasmettere le impostazioni nel database del server di installazione tramite la parte client;
  • configurare diversi tipi di software, incluso il deployment dell'hypervisor esxi e la configurazione delle macchine virtuali (e tutto automaticamente).

Abbiamo scoperto come:

  • il server da installare riceve le impostazioni necessarie dal database;
  • tutti i progressi della preparazione vengono registrati nel database (log, eventi, flag delle fasi).


Risultato:

Ritengo che l'unicità di questa soluzione risieda nella flessibilità, nella semplicità, nelle sue capacità e nella sua versatilità.

Per favore, scrivi nei commenti cosa ne pensi.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster