Jeśli Twoja infrastruktura IT rozwija się zbyt szybko, prędzej czy później napotkasz decyzję – liniowo zwiększać zasoby ludzkie do jej utrzymania czy rozpocząć automatyzację. Do pewnego momentu żyliśmy w pierwszej paradygmacie, a potem rozpoczął się długi proces przejścia do Infrastructure-as-Code.

Oczywiście, NSPK to nie startup, ale taka atmosfera panowała w firmie w pierwszych latach jej istnienia, a były to bardzo interesujące lata. Nazywam się , od ponad 10 lat zajmuję się utrzymywaniem infrastruktury Linux o wysokich wymaganiach dostępności. Do zespołu NSPK dołączyłem w styczniu 2016 roku i, niestety, nie doświadczyłem samego początku istnienia firmy, ale przyszedłem na etapie dużych zmian.
Ogólnie można powiedzieć, że nasz zespół dostarcza dla firmy 2 produkty. Pierwszy to infrastruktura. Poczta musi działać, DNS musi funkcjonować, a kontrolery domeny muszą umożliwiać dostęp do serwerów, które nie mogą zawodzić. Krajobraz IT firmy jest ogromny! To systemy krytyczne dla biznesu i misji, a wymagania dotyczące dostępności niektórych sięgają 99,999. Drugi produkt to same serwery, fizyczne i wirtualne. Należy dbać o istniejące, a nowe regularnie dostarczać klientom z wielu działów. W tym artykule chciałbym skupić się na tym, jak rozwijaliśmy infrastrukturę, która odpowiada za cykl życia serwerów.
Początek drogi
Na początku naszej drogi nasz stos technologii wyglądał tak:
OS CentOS 7
Kontrolery domeny FreeIPA
Automatyzacja — Ansible(+Tower), Cobbler
Wszystko to było rozmieszczone w 3 domenach, rozłożonych na kilku DC. W jednym DC – systemy biurowe i testowe, w pozostałych PRODUCTION.
Tworzenie serwerów w pewnym momencie wyglądało tak:

W szablonie VM CentOS minimal i niezbędny minimum, jak poprawny /etc/resolv.conf, reszta przychodzi przez Ansible.
CMDB – Excel.
Jeśli serwer jest fizyczny, to zamiast kopiować wirtualną maszynę na niego instalowany był system operacyjny za pomocą Cobbler – do konfiguracji Cobbler dodawane są adresy MAC docelowego serwera, serwer przez DHCP uzyskuje adres IP, a następnie instalowany jest system operacyjny.
Na początku staraliśmy się nawet stosować pewne zarządzanie konfiguracją w Cobbler. Jednak z czasem przyniosło to problemy z przenośnością konfiguracji zarówno do innych DC, jak i do kodu Ansible do przygotowania VM.
Ansible był wówczas postrzegany przez wielu z nas jako wygodne rozszerzenie Bash i nie oszczędzaliśmy na konstrukcjach z użyciem shell, sed. Generalnie Bashsible. Ostatecznie prowadziło to do sytuacji, że jeżeli playbook z jakiegoś powodu nie działał na serwerze, łatwiej było usunąć serwer, poprawić playbook i uruchomić go ponownie. W zasadzie nie było żadnego wersjonowania skryptów, ani przenośności konfiguracji.
Na przykład, chcieliśmy zmienić jakiś konfigurację na wszystkich serwerach:
- Zmiana konfiguracji na istniejących serwerach w logicznym segmencie / centrum danych. Czasami nie jest to kwestia jednego dnia – wymagania dotyczące dostępności i prawo wielkich liczb uniemożliwiają wprowadzanie wszystkich zmian jednocześnie. A niektóre zmiany potencjalnie mogą być destrukcyjne i wymagają restartu czegoś – od usług do samego systemu operacyjnego.
- Poprawiamy w Ansible
- Poprawiamy w Cobbler
- Powtarzamy N razy dla każdego logicznego segmentu / centrum danych
Aby wszystkie zmiany przebiegały gładko, należało uwzględnić wiele czynników, a zmiany zachodzą nieustannie.
- Refaktoryzacja kodu ansible, plików konfiguracyjnych
- Zmiana wewnętrznych best practices
- Zmiany w wyniku analizy incydentów / awarii
- Zmiana standardów bezpieczeństwa, zarówno wewnętrznych, jak i zewnętrznych. Na przykład, PCI DSS co roku uzupełniane jest nowymi wymaganiami
Rozwój infrastruktury i początek drogi
Liczba serwerów / logicznych domen / centrum danych rosła, a wraz z nimi liczba błędów w konfiguracjach. W pewnym momencie doszliśmy do trzech kierunków, w które trzeba rozwijać zarządzanie konfiguracją:
- Automatyzacja. W miarę możliwości należy unikać czynnika ludzkiego w powtarzalnych operacjach.
- Powtarzalność. Zarządzanie infrastrukturą jest znacznie łatwiejsze, gdy jest ona przewidywalna. Konfiguracja serwerów i narzędzi do ich przygotowania powinna być wszędzie identyczna. Jest to również ważne dla zespołów produktowych – aplikacja powinna pewnie trafić do środowiska produkcyjnego po testach, skonfigurowana analogicznie do środowiska testowego.
- Prostota i przejrzystość wprowadzania zmian w zarządzaniu konfiguracją.
Pozostało dodać kilka narzędzi.
Jako magazyn kodu wybraliśmy GitLab CE, nie bez znaczenia za sprawą wbudowanych modułów CI / CD.
Magazyn sekretów — Hashicorp Vault, również za wspaniałe API.
Testowanie konfiguracji i ról Ansible – Molecule+Testinfra. Testy przebiegają znacznie szybciej, jeśli używasz mitogen w Ansible. Równolegle zaczęliśmy pisać własną CMDB oraz orkiestrator do automatycznego wdrażania (na zdjęciu nad Cobblerem), ale to już zupełnie inna historia, o której w przyszłości opowie mój kolega i główny deweloper tych systemów.
Nasz wybór:
Molecule + Testinfra
Ansible + Tower + AWX
Świat Serwerów + DITNET (Własny rozwój)
Cobbler
Gitlab + GitLab runner
Hashicorp Vault

Swoją drogą, jeśli chodzi o role Ansible. Z początku była jedna, po kilku refaktoryzacjach powstało ich 17. Kategorycznie polecam dzielić monolit na idempotentne role, które można później uruchamiać oddzielnie, dodatkowo można dodać tagi. My podzieliliśmy role według funkcjonalności – sieć, logowanie, pakiety, sprzęt, molekuły itd. Ogólnie trzymaliśmy się strategii poniżej. Nie twierdzę, że to jedyna prawda, ale u nas zadziałało.
- Kopiowanie serwerów z 'złotego obrazu' – zło!Z głównych wad – nie wiesz dokładnie, w jakim stanie są obrazy, a wszystkie zmiany dotrą do wszystkich obrazów we wszystkich farmach wirtualizacji.
- Używaj domyślnych plików konfiguracyjnych w minimum i uzgodnij z innymi działami, że za główne pliki systemowe odpowiadasz Ty., na przykład:
- Pozostaw /etc/sysctl.conf pustym, ustawienia powinny być tylko w /etc/sysctl.d/. Twój domyślny plik w jednym miejscu, a niestandardowy dla aplikacji w innym.
- Używaj plików override do edytowania jednostek systemd.
- Szablonuj wszystkie konfiguracje i dodawaj je w całości, jeśli to możliwe, unikaj sed i jego odpowiedników w playbookach.
- Refaktoryzując kod systemu zarządzania konfiguracją:
- Podziel zadania na logiczne jednostki i przekształć monolit w role.
- Używaj linterów! Ansible-lint, yaml-lint itd.
- Zmień podejście! Żadnego bashsible. Należy opisać stan systemu.
- Do wszystkich ról Ansible należy napisać testy w molekule i generować raporty raz dziennie.
- W naszym przypadku, po przygotowaniu testów (których jest ponad 100) znaleziono około 70000 błędów. Poprawialiśmy je przez kilka miesięcy.

Nasza realizacja
I tak, role Ansible zostały przygotowane, zszablonowane i sprawdzone przez lintery. I nawet gity wszędzie zostały uruchomione. Ale kwestia niezawodnego dostarczania kodu do różnych segmentów pozostała otwarta. Postanowiliśmy zsynchronizować skrypty. Wygląda to tak:

Po tym, jak wprowadzone zostaną zmiany, uruchamiany jest CI, tworzony jest serwer testowy, wdrażane są role, a testy są wykonywane przy użyciu Molecule. Jeśli wszystko jest w porządku, kod trafia do gałęzi produkcyjnej. Nie stosujemy jednak nowego kodu automatycznie na istniejące serwery. To swoisty hamulec, który jest niezbędny dla zapewnienia wysokiej dostępności naszych systemów. A gdy infrastruktura staje się ogromna, wchodzi również prawo wielkich liczb – nawet jeśli jesteś pewny, że zmiana jest nieszkodliwa, może ona prowadzić do smutnych konsekwencji.
Istnieje wiele opcji tworzenia serwerów. Ostatecznie zdecydowaliśmy się na spersonalizowane skrypty w Pythonie. A do CI używamy Ansible:
- name: create1.yml - Utwórz VM z szablonu
vmware_guest:
hostname: "{{datacenter}}".domain.pl
username: "{{ username_vc }}"
password: "{{ password_vc }}"
validate_certs: no
cluster: "{{cluster}}"
datacenter: "{{datacenter}}"
name: "{{ name }}"
state: poweredon
folder: "\/{{folder}}"
template: "{{template}}"
customization:
hostname: "{{ name }}"
domain: domain.pl
dns_servers:
- "{{ ipa1_dns }}"
- "{{ ipa2_dns }}"
networks:
- name: "{{ network }}"
type: static
ip: "{{ip}}"
netmask: "{{netmask}}"
gateway: "{{gateway}}"
wake_on_lan: True
start_connected: True
allow_guest_control: True
wait_for_ip_address: yes
disk:
- size_gb: 1
type: thin
datastore: "{{datastore}}"
- size_gb: 20
type: thin
datastore: "{{datastore}}"Oto do czego doszliśmy, system wciąż żyje i rozwija się.
- 17 ról Ansible do konfiguracji serwera. Każda z ról przeznaczona jest do rozwiązania odrębnego zadania logicznego (logowanie, audyt, autoryzacja użytkowników, monitorowanie itp.).
- Testowanie ról. Molecule + TestInfra.
- Własny rozwój: CMDB + Orkiestrator.
- Czas tworzenia serwera to ~30 minut, zautomatyzowane i praktycznie niezależne od kolejki zadań.
- Jednolity stan/nazewnictwo infrastruktury we wszystkich segmentach – playbooki, repozytoria, elementy wirtualizacji.
- Codzienna kontrola stanu serwerów z generowaniem raportów o rozbieżnościach z wzorcem.
Mam nadzieję, że moja historia będzie pomocna dla tych, którzy są na początku drogi. A jaki stos automatyzacji stosujecie wy?
Źródło: habr.com

