Devops z Ansible - Squid proxy na Ubuntu 18.04

Poradniki społeczności
SystemZ
SystemZ Admin lvlup.pro

Serwer Squid proxy zarządzany przez Ansible

Ten poradnik NIE jest przeznaczony dla początkujących.

Jeśli jeszcze nie czujesz się pewnie z administrowaniem systemów, zawsze możesz zerknąć na to jako ciekawostkę. Jest to dość krótki przykład jak wygląda administracja w większej skali gdzie problemem nie jest już samo postawienie serwera aby to działało przez kilka dni lecz:

  • zrobienie tego na X hostów w kilka minut uwzględniając dłuższe wstępne przygotowania
  • przy minimalnej interwencji admina
  • utrzymanie tej usługi bezpiecznie przez kilka lat.

Chcę zaznaczyć że nie jest to poradnik "Jak używać Ansible krok po kroku". Bardziej jest to "Zobacz jak się z tego korzysta", czyli bardziej prezentacja tego narzędzia i być może motywacja do nauki niż sam materiał do nauki. Jest tu (nie)stety sporo mojej gadki-szmatki i pomijam sporo szczegółów ale powinno wam to ogólnie rozjaśnić popularną ideę "devops" a przynajmniej część z narzędzi które się stosuje w takiej pracy. Piszę to jako osoba która uczestniczyła w projekcie dla dużej bardzo znanej firmy i dotyczył on użycia Ansible do stawiania OpenStack'a w skali nie per serwer tylko już per rzędy szaf przy użyciu narzędzi dostępnych w OpenStack'u.

Piszę tu o skali gdzie X przy hostach to powiedzmy 1-100. Jeśli Twoje X to bardziej 100-10 000, warto wtedy rozejrzeć się za innymi narzędziami. Sam w lvlup.pro korzystałem na początku z innego narzędzia - SaltStack: https://docs.saltstack.com/en/latest/ Jednak sądzę że lvlup.pro to zbyt mała skala aby w pełni rozwinąć skrzydła tego narzędzia które już wtedy w powiedzmy 2014 było kilkukrotnie szybsze niż Ansible. Salt jednak z mojego doświadczenia wymagał znacznie więcej czasu i dyscypliny przy tworzeniu konfiguracji. Ansible jest prostszy i bardziej elastyczny pod tym względem. Jest trochę takim złotym środkiem, nie jest zbyt skomplikowany i nie jest zbyt wolny a ma dużo możliwości

Uprzedzam pytania, nie planuję nigdzie wrzucać przykładowego kodu na git, gist czy pastebin. Założenie jest takie aby

  • najpierw dokładnie przeczytać całość i stwierdzić czy to dla Ciebie
  • przy drugim czytaniu w międzyczasie wklejać niewielkie kawałki kodu i je wykonywać ze zrozumieniem aby się czegoś nauczyć

Motywacja utworzenia poradnika

Jedna z funkcji panelu v4 jest bardzo specyficzna i wymaga osobnego odizolowanego hosta do jej realizacji aby uniknąć ewentualnych problemów - webhooki.

Nie mogę postawić tej usługi proxy korzystając ze swojej standardowej infrastruktury ale za to mogę pokazać wam jak sam zazwyczaj sam się tym zajmuje korzystając z Ansible :)

Dodatkowo mój ostatni projekt który realizowałem zanim wyszedłem ze szkoły średniej to szkolny serwer proxy który przyspieszał ładowanie się stron. Miało to wtedy sens biorąc pod uwagę że

  • łącze z prędkością tylko kilka megabitów
  • komputerów w sieci lokalnej było przynajmniej z 80
  • w 2011/2012 HTTPS nie był jeszcze aż tak popularny co umożliwiało łatwe cache'owanie zasobów

Więc konfiguruję to też trochę z nostalgią :slight_smile:

Cele użycia Ansible w tym przypadku

  • najmniejszy czas poświęcony na stawianie wielu instancji (kopii) usługi jeśli będzie taka potrzeba
  • największe podobieństwo postawionych w ten sposób hostów
    • jeśli coś pójdzie nie tak to naprawa będzie wyglądać identycznie (kolejna oszczędność czasu)
  • szybkie odtworzenie (~10 min) całej usługi w razie całkowitej awarii dostawcy (znów oszczędność czasu)

Pisząc o oszczędności czasu nie mam na myśli krótkiej perspektywy tylko tą dłuższą. Tak, ten sposób wymaga znacznie więcej czasu na początku (przygotowania), ale jeśli coś miałoby się zepsuć i przerwać Ci grę czy inne ulubione zajęcie to szansa na to się znacznie zmniejsza. Warto też brać pod uwagę, że życie nie zawsze układa się kolorowo i możesz mieć inne problemy - serwer, który wymaga uwagi to może być ostatnia rzecz której wtedy potrzebujesz.

Zależności

Host

Tak nazywamy serwer VPS / instancję cloud na którym stawiamy naszą usługę proxy. W tym wypadku będzie to najtańsza możliwa instancja instancja cloud oparta o OpenStack z OVH Public Cloud (zawrotne 12 PLN netto / msc)

Instalacja systemu na hoście

Skorzystajmy z automatycznej instalacji systemu u swojego dostawcy serwerów. W tym przypadku będzie to Ubuntu 18.04. Z małymi modyfikacjami typu nazwa użytkownika SSH ten poradnik zadziała też na Debianie.

To nie 2000 rok żebyśmy tracili czas na instalację ręczną. Zwłaszcza że w większości przypadków nie jest to w ogóle potrzebne. Czas który poświęcilibyśmy na ręczną instalację (która jest odtwórczym i nie wnoszącym zbyt wiele zajęciem w takich zastosowaniach) przeznaczymy na postawienie usługi której faktycznie potrzebujemy :thinksmart:

Szybkość reinstalacji jest o tyle istotna że będziemy ją na początku wykonywać dużo razy aby mieć pewność że nasz Ansible Playbook czyli opis jak co ma wyglądać na hoście działa tak jak chcemy zarówno na już postawionym jak i świeżym hoście Słowo które to opisuje i na które możesz często napotkać przy angielskich instrukcjach do Ansible to Idempotence

https://en.wikipedia.org/wiki/Idempotence

Ważność idempotentności

W przypadku gdy zarządzamy wieloma hostami, nie chcemy przypadkiem odinstalować czegoś co już było zainstalowane. Jeśli było zainstalowane to nie trzeba nic robić od strony skryptu.

Jest to mega przydatne jeśli mamy już gotowe X hostów i nagle chcemy dodać do nich jedną drobnostkę. W takiej sytuacji dopisujemy instalację jednej paczki, odpalamy nasze skrypty tak jak zazwyczaj a Ansible doinstaluje tylko tą jedną paczkę. Serwer sprzedany zaktualizowany i dzień udany :merchant:

Jeśli się do tego przyłożymy to nasze skrypty możemy odpalać do woli bez ryzyka uszkodzenia, nawet w cronie, wtedy będą w stanie naprawić coś jeśli się zepsuło bez Twojego udziału.

Następnym tematem będzie stan.

Ważność stanu

Jeśli chcemy ocenić dany Ansible Playbook jako dobry to warto go odpalić przynajmniej kilka razy zarówno na czystym hoście, częściowo postawionym oraz całkowicie postawionym aby mieć pewność że zawsze uzyskamy taki sam wynik końcowy.

Naszym wrogiem którego będziemy zwalczać to stan (prawie całkowita eliminacja tego wroga jest możliwa przy pomocy Dockera ale o tym innym razem) Idealistycznie rzecz ujmując chcemy aby stan był na wszystkich hostach które postawimy taki sam. Jeśli coś będzie się różnić to nasz Playbook ma to zauważyć i ogarnąć. Musimy też patrzeć zdrowym rozsądkiem, nie będziemy w stanie kontrolować każdego możliwego pliku w systemie, to wymagałoby przygotowania masy playbooków które szybko traciłyby na ważności i wymagałyby aktualizacji. Jeśli chcemy mieć całkowitą kontrolę to lepiej użyć innego podejścia i tworzyć obraz systemu z tego co potrzebujemy i wymuszać aby był zawsze taki sam przy każdym starcie (Docker).

Najważniejszą różnicą względem ręcznego odpalania komend to fakt, że w Ansible deklarujemy co chcemy osiągnąć a on zajmie się resztą.

Instalacja Ansible

Chcemy użyć Ansible więc potrzebujemy go na komputerze z którego będziemy zlecać konfigurację. Ansible połączy się do wskazanych hostów przez SSH i zrobi co trzeba. Nie trzeba specjalnego przygotowania na hostach z którymi trzeba się połączyć oprócz kluczy SSH (o tym niżej), chyba że chcemy ciągle klepać hasło przy odpalaniu Playbooka, to jak wolisz ¯_(ツ)_/¯

Oficjalna dokumentacja świetnie opisuje proces instalacji: https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html

Dostęp do hosta

Zasadniczo po przygotowaniu Playbook'a nie będziemy wykonywać absolutnie żadnej komendy ręcznie przez SSH bo to za nas zrobią skrypty. Taka jest idea automatyzacji. Zadbajmy o automatyczne logowanie przez SSH aby cała praca była dla nas wygodna i bezpieczna. Polecam ten poradnik:

https://lvlup.rok.ovh/t/klucze-ssh-podstawowy-poradnik/6745

Jeśli Twój dostawca obsługuje dodawanie klucza SSH na etapie instalacji systemu to nie musisz w ogóle ręcznie korzystać ze standardowego klienta SSH, całkowicie i dosłownie cały proces zrobią skrypty.

Konfiguracja

Ansible - pliki bazowe

Tworzymy sobie folder ~/Projects/infra. Będziemy mogli potem bez przeszkód przekształcić go potem w git repo. Trzymamy wszystkie aspekty infrastruktury w jednym katalogu, łatwiej będzie nam zarządzać różnymi usługami.

Następny etap to ~/Projects/infra/inv. Skracam tu sobie inventory. Tu będziemy wpisywać adresy do hostów którymi będzie sterować nasz Playbook. Od razu uzupełnimy nasz inwentarz:

[squid]
192.0.2.1
192.0.2.2 ansible_user=root ansible_port=22

[squid]

Tworzy grupę squid. Będziemy mogli wskazać na grupę hostów i bez zbędnego klepania osobno z nich za jednym zamachem zastosować na nich zmiany.

192.0.2.1

Jest to host o adresie IP 192.0.2.1 w grupie squid z domyślną konfiguracją połączenia przez SSH.

192.0.2.2 ansible_user=root ansible_port=22

Jest to host o adresie IP 192.0.2.2 w grupie squid z nadpisanym portem i użytkownikiem SSH. Zamiast używać samych adresów IP możemy bez przeszkód użyć nazw hostów.

example.com ansible_user=root ansible_port=22

W tym wypadku do SSH podłączymy się rozwiązując DNSy.

Jeśli mamy sytuację w której adres nie rozwiąże się zanim nie odpalimy właśnie tego Playbooka czyli sytuacja jajko czy kura, możemy nadpisać adres IP

example.com ansible_ssh_host=192.0.2.3 ansible_user=ubuntu ansible_port=22

W tym wypadku nazwa hosta pełni funkcje przejrzystości dla nas, nie musi być poprawna i nie musi się rozwiązywać, prezydent [.] gov [.] pl należy do Ciebie, jednak z dużą gwiazdką że wcale tak nie jest. Z niewiadomego powodu mamy też tu nadpisany port który i tak domyślnie jest ustawiony na 22. Większym sensem za to jest nadpisanie usera, domyślnie w ubuntu cloud image jest to user ubuntu z dostępem do sudo.

Wybieramy taką wersję zapisu jaka jest nam najbardziej po drodze, będę tu pokazywać na przykładzie jednego hosta

Tworzymy teraz Ansible Playbook w ~/Projects/infra/squid.yml :

- hosts: squid
  tasks:
    - name: Install basic tools
      apt:
        name: "{{ packages }}"
      vars:
        packages:
          - htop

Naiwnie odpalamy myśląc że zadziała.

cd ~/Projects/infra
ansible-playbook -i inv squid.yml

No niestety nie udało się, wyskakuje nam jakiś błąd

PLAY [squid] *******************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************
ok: [192.0.2.1]

TASK [Install basic tools] ****************************************************************************************************************
fatal: [192.0.2.1]: FAILED! => {"changed": false, "msg": "'/usr/bin/apt-mark manual htop' failed: E: Could not create temporary file for /var/lib/apt/extended_states - mkstemp (13: Permission denied)\nE: Failed to write temporary StateFile /var/lib/apt/extended_states\n", "rc": 100, "stderr": "E: Could not create temporary file for /var/lib/apt/extended_states - mkstemp (13: Permission denied)\nE: Failed to write temporary StateFile /var/lib/apt/extended_states\n", "stderr_lines": ["E: Could not create temporary file for /var/lib/apt/extended_states - mkstemp (13: Permission denied)", "E: Failed to write temporary StateFile /var/lib/apt/extended_states"], "stdout": "htop set to manually installed.\n", "stdout_lines": ["htop set to manually installed."]}

PLAY RECAP ************************************************************************************************************
192.0.2.1               : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

Coś o braku dostępu. No tak, to nie te czasy gdy mamy nieposkromiony i natychmiastowy dostęp root do własnego serwera. Dodajmy jedną linię która powie Ansible że trzeba użyć sudo.

- hosts: squid
  become: yes
  tasks:
    - name: Install basic tools
      apt:
        name: "{{ packages }}"
      vars:
        packages:
          - htop

Od razu lepiej

PLAY [squid] ************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [192.0.2.1]

TASK [Install basic tools] **********************************************************************************
ok: [192.0.2.1]

PLAY RECAP ***************************************************************************************************************
192.0.2.1               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Co tak naprawdę mówi nam wynik? Stały się tu dwie rzeczy:

  • Ansible zebrał bardzo dużo informacji o hoście z których możemy potem korzystać w zmiennych (Facts)
    • jest to domyślnie włączone dla naszej wygody, da się jednak to wyłączyć
  • Ansible wykonał nasz Playbook który składał się z jednego Play (zbiór zadań), zaś nasz Play to jedno zadanie (Task)
    • ten podział umożliwia potem wielokrotne użycie jednej małej roli np. aktualizacja systemu czy zmiana MOTD na wszystkich hostach jakie zarządzamy niezależnie od tego jaką pełnią funkcję
      • mniejsza duplikacja kodu i łatwiejsze zmiany w wielu miejscach za jednym zamachem
        • niestety błędy też się skalują i stosują na wszystkim na raz, sugeruję pod żadnym pozorem nie korzystać z "rm -rf"

Dla obu kroków jest OK czyli błędów nie było. Warto zauważyć że jest changed=0 czyli w sumie nic nie zostało zmodyfikowane na hoście. Htop już był zainstalowany więc Ansible nic nie ruszał.

Dodajmy kolejne zadanie do Playbooka, zainstalujmy w końcu tego Squid'a

- hosts: squid
  become: yes
  tasks:
    - name: Install basic tools
      apt:
        name: "{{ packages }}"
      vars:
        packages:
          - htop
    - name: Install Squid
      apt:
        name: squid
        state: present

Zmodyfikowałem trochę składnie ale generalnie efekt jest taki sam czyli instalacja paczki przez apt. Poprzednia metoda jest trochę dłuższa ale umożliwia nam łatwe rozbudowanie paczek w systemie przez proste dodawanie jedna paczka - jednia linia. Osobiście używam podejścia pierwszego, zwłaszcza do pierwszych etapów konfiguracji gdzie jest potrzebnych sporo paczek a drugą metodą zajęłoby to za dużo linii config'a.

Niestety takie podejście jest czasami skazane na niepowodzenie. Przy świeżym systemie cache apt jest przestarzały. Jest spora szansa że apt nie zainstaluje paczki ze względu na błędy HTTP 404. Jeśli to ma dobrze działać w skali, musimy uwzględnić to zjawisko i mu zapobiec.

- hosts: squid
  become: yes
  tasks:
    - name: Update apt cache if needed and install htop
      apt: pkg=htop state=present update_cache=yes cache_valid_time=3600
    - name: Install basic tools
      apt:
        name: "{{ packages }}"
      vars:
        packages:
          - htop
          - iotop
          - nload
          - ncdu
    - name: Install Squid
      apt:
        name: squid
        state: present

W zaktualizowanej wersji wyżej wprowadzamy pewną sztuczkę. Pierwszym zadaniem jest instalacja htop, jednak jest podane więcej parametrów. Na dodatek te parametry są podane w inny sposób niż w obu pozostałych przykładach użycia apt.

Nasz pierwszy task przed instalacją paczki sprawdza cache apta czyli lokalną listę paczek które są dostępne na serwerach. Jeśli ta lista jest starsza niż 1h to wtedy robi on pod maską odpowiednik apt-get update. Dzięki temu mamy

  • dużą pewność że nie napotkamy na 404
  • na tyle długi cache że włączanie Playbooka kilkukrotnie nie będzie trwało długo ani nie będzie obciążać zbędnie mirrorów dystrybucji
  • mniej kodu w pozostałych taskach, odświeżenie cache jest potrzebne tylko na starcie wykonywania wszystkich tasków, jest bardzo mała szansa że w ciągu godziny akurat się coś zmieni w kwestii paczek
    • nawet jeśli to nastąpi, ustawiamy na chwilę cache na 1 sekundę i odpalamy ponownie, problem solved

Ansible - większy kaliber

Jak można założyć, przy bardziej złożonych wdrożeniach projektowych tych tasków może być dużo. Trzymanie wszystkiego w jednym pliku nie jest wygodne ani czytelne. Naprawimy to tworząc role, które będą mieścić wiele zadań. Utworzymy teraz kilka folderów:

  • ~/Projects/infra/roles/squid/templates
    • tu będziemy trzymać szablony plików, będą w locie dostosowane do tego co jest aktualnie potrzebne
  • ~/Projects/infra/roles/squid/files
    • miejsce plików które będą skopiowane na hosta ale bez ich modyfikacji
  • ~/Projects/infra/roles/squid/tasks
    • należyty folder do tasków w .yml które już tworzyliśmy chwilę wcześniej

Protip: Całą ściężkę katalogów można utworzyć przy pomocy mkdir -p . Nie musimy posiadać np. roles/squid/templates. Zostaną utworzone wszystkie trzy jednocześnie

Wrócmy do naszego Playbook'a. Wygląda on tak:

- hosts: squid
  become: yes
  tasks:
    - name: Update apt cache if needed and install htop
      apt: pkg=htop state=present update_cache=yes cache_valid_time=3600
    - name: Install basic tools
      apt:
        name: "{{ packages }}"
      vars:
        packages:
          - htop
          - iotop
          - nload
          - ncdu
    - name: Install Squid
      apt:
        name: squid
        state: present

Wymieńmy go na taki model:

~/Projects/infra/squid.yml

- hosts: squid
  become: yes
  roles:
    - squid

Dużo mniejszy prawda? Zgodnie z dobrymi praktykami będziemy tu umieszczać tylko gotowe do użycia role. Same role będziemy pisać w osobnych katalogach, które utworzyliśmy trochę wcześniej.

~/Projects/infra/roles/squid/tasks/main.yml

- name: Update apt cache if needed and install htop
  apt: pkg=htop state=present update_cache=yes cache_valid_time=3600
- name: Install basic tools
  apt:
    name: "{{ packages }}"
  vars:
    packages:
      - htop
      - iotop
      - nload
      - ncdu
- name: Install Squid
  apt:
    name: squid
    state: present

[details="Dlaczego main.yml?"] main.yml to główny domyślnie wczytywany task w konwencji Ansible.

Jeśli nasza rola będzie naprawdę duża lub chcemy mieć bardzo konkretny porządek, możemy użyć taska który zaimportuje inne taski z tego samego katalogu ale innego pliku.

Przykład którego póki co nie użyjemy, chcę jednak pokazać jakby to działało.

- import_tasks: ssh.yml
  tags: [ssh]

Dałoby nam to możliwość separacji i późniejszego odpalenia tylko rzeczy związanych z ssh gdzie rola byłaby dostosowana tylko do pewnego rodzaju zadania Nie zawsze jednak chcemy odpalać ponownie wszystko, przy setkach tasków to już kilka minut a może chcemy tylko zmodyfikować dozwolone klucze SSH? [/details]

Ansible - szablony

Po automatycznej instalacji systemu bez problemu działa nam sieć. Chcemy jednak bardziej rozbudowaną konfigurację bo potrzebujemy wielu adresów IP. W Ubuntu 18.04 mamy netplan który zajmuje się wygodną konfiguracją sieci. W chwili gdy to piszę Ansible jeszcze natywnie nie obsługuje netplan a nie chcę tu komplikować sprawy więc napiszemy prosty szablon który poradzi sobie bez tego. Przy okazji zawsze można nauczyć się czegoś więcej gdy rozwiązanie nie jest wspierane i chcemy poradzić sobie sami :)

Utworzymy sobie osobną rolę która zajmie się tylko siecią. Tu akurat korzystam z instancji w OVH więc nazwę tą rolę ovh-ipfo-netplan od IP Failover gdyż tak oficjalnie ten dostawca nazywa dodatkowe adresy IP Utworzymy sobie standardowy plik z taskami ~/Projects/infra/roles/ovh-ipfo-netplan/tasks/main.yml

- name: Configure netplan with IP FO
  template: src=51-ipfo.yaml.j2 dest=/etc/netplan/51-ipfo.yaml owner=root group=root mode=0644 backup=no

Tu jest póki co jeden task ale dopiszemy jeszcze kilka aby to całkowicie działało tak jak chcemy. Bierze on szablon który zaraz zdefiniujemy niżej, uzpełnia danymi uwzględniając zmienne i wrzuca na serwer tam gdzie sprecyzujemy. Przy okazji ustawia właściciela oraz grupę dla pliku a także uprawnienia. Ansible domyślnie przy każdym nadpisaniu pliku szablonu przygotuje kopię ale tu chcemy tego uniknąć więc dajemy no przy opcji backup

Sam szablon konfiguracji sieci wygląda w ten sposób ~/Projects/infra/roles/ovh-ipfo-netplan/templates/51-ipfo.yaml.j2

network:
    version: 2
    vlans:
{% for ip in ipfo_list %}
        veth{{ loop.index }}:
            id: {{ loop.index }}
            link: ens3
            dhcp4: no
            addresses: [{{ ip }}/32]
{% endfor %}

Jest to dość łopatologiczny przykład, zapewne można się posłużyć wbudowną w Ansible obsługą YAML, jednak ten przykład pokazuje że format w jakim mamy konfigurację systemu czy aplikacji nie ma żadnego znaczenia. Można użyć zmiennych, pętli czy warunków i automatycznie w locie tworzyć potrzebną konfigurację.

Zapytasz, a skąd ma brać adresy IP żeby podstawić do szablonu? Właśnie to ustawimy w playbooku

~/Projects/infra/squid.yml

- hosts: squid
 become: yes
 vars:
   ipfo_list:
     - "192.0.2.2"
     - "192.0.2.3"
 roles:
   - ovh-ipfo-netplan
   - squid

Dzięki takiemu podejściu tylko raz konkretnie piszemy role. Jeśli coś się zmieni w środowisku np. chcemy dodać nowe adresy IP do proxy to wystarczy dodać linie z tymi adresami i jeszcze raz odpalić Playbooka. I to tyle, magia Ansible.

Chcę tylko zaznaczyć że dla uproszczenia jest tylko ta jedna zmienna i jest to dostosowane dla jednego hosta. Jeśli chcielibyśmy więcej hostów jednocześnie, należy zadeklarować adresy IP w zmiennych per dany serwer. Po szczegóły sugeruję zapoznać się z dokumentacją Ansible, niestety nie udało mi się zagospodarować więcej czasu aby to rozpisać. Już obecnie ten tekst zajął mi w zasadzie cały dzień

Jak widzimy obecnie w naszym Playbooku są użyte dwie role. Nic nie stoi na przeszkodzie aby dodać ich więcej lub aby użyć roli do konfiguracji adresów w innym Playbooku do innych zastosowań. Ten sam kod możemy użyć bez przeszkód wiele razy, jeśli włożymy kilka minut w odpowiednie przygotowanie hierarchii katalogów.

Uruchomię teraz ten playbook aby pokazać jak zadziałał nasz szablon konfiguracji sieci. Dodam jednak parametr -D dzięki któremu Ansible pokaże różnice w plikach tekstowych

cd ~/Projects/infra
ansible-playbook -i inv squid.yml -D
PLAY [squid] *********************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************
ok: [192.0.2.1]

TASK [ovh-ipfo-netplan : Configure netplan with IP FO] ***************************************************************
--- before
+++ after: /home/user/.ansible/tmp/ansible-local-25639t55VeF/tmpnWKKb5/51-ipfo.yaml.j2
@@ -0,0 +1,13 @@
+network:
+    version: 2
+    vlans:
+        veth1:
+            id: 1
+            link: ens3
+            dhcp4: no
+            addresses: [192.0.2.2/32]
+        veth2:
+            id: 2
+            link: ens3
+            dhcp4: no
+            addresses: [192.0.2.3/32]

changed: [192.0.2.1]

PLAY RECAP ***********************************************************************************************************
192.0.2.1               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Tu co prawda nie widać kolorów jednak Ansible w standardowych warunkach przygotuje nam kolorowy diff. Log jest czytelny i pokazuje że zostały dodane nowe linie. W tym przypadku pliku jeszcze nie było na serwerze więc został utworzony, zostały mu nadane uprawnienia oraz została wrzucona cała potrzebna treść.

Czas na krótką demonstrację na ile dobrze to działa.

Powiedzmy że na naszym serwerze z bliżej nieokreślonych powodów jest też użytkownik slowpoke. Trochę jak ta osoba która kupuje 15 ton jabłek w zadaniach matematycznych. Ten użyszkodnik na serwerze chce Ci zrobić żart na 1 kwietnia. Usuwa version: 2 z pliku yaml który przygotował nasz Playbook. My całe szczęście jesteśmy safe, na naszym komputerze mamy Playbook, wystarczy odpalić go jeszcze raz tak jak poprzednio

PLAY [squid] ****************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************
ok: [192.0.2.1]

TASK [ovh-ipfo-netplan : Configure netplan with IP FO] **********************************************************
--- before: /etc/netplan/51-ipfo.yaml
+++ after: /home/user/.ansible/tmp/ansible-local-25985jaW7uj/tmpXJUc63/51-ipfo.yaml.j2
@@ -1,4 +1,5 @@
 network:
+    version: 2
     vlans:
         veth1:
             id: 1

changed: [192.0.2.1]

PLAY RECAP ******************************************************************************************************
192.0.2.1               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Jak widać Playbook nie wymagał od nas wiedzy co dokładnie stało się na serwerze. Ja też nie wnikam skąd masz pokemony na serwerze. Ansible wykrył, że brakuje tej linii i ją dodał. Jako admin widzisz plusik przy dodanej linii. Widać też w ostatniej linii changed=1 które mówi że Ansible zmienił coś w jednym zadaniu

Teraz załóżmy że chcemy dodać nowy adres IP do proxy. Wszystko się skaluje więc jeśli chcesz dodać ich 20 to też nie powinno być przeszkód.

~/Projects/infra/squid.yml

- hosts: squid
  become: yes
  vars:
    ipfo_list:
      - "192.0.2.2"
      - "192.0.2.3"
      - "192.0.2.4"
  roles:
    - ovh-ipfo-netplan
    - squid

Dodaliśmy 192.0.2.4 i włączamy Playbooka jeszcze raz

PLAY [squid] *****************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************
ok: [192.0.2.1]

TASK [ovh-ipfo-netplan : Configure netplan with IP FO] *******************************************************************************************************
--- before: /etc/netplan/51-ipfo.yaml
+++ after: /home/user/.ansible/tmp/ansible-local-262563O_cGD/tmpiIsJQ3/51-ipfo.yaml.j2
@@ -11,3 +11,8 @@
             link: ens3
             dhcp4: no
             addresses: [192.0.2.3/32]
+        veth3:
+            id: 3
+            link: ens3
+            dhcp4: no
+            addresses: [192.0.2.4/32]

changed: [192.0.2.1]

PLAY RECAP *************************************************************************************************
192.0.2.1               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Efekt był do przewidzenia, dodało tylko ten nowy adres IP

Ansible - uruchamianie komend

Biorąc pod uwagę użycie netplan, po wpisaniu swojego configa odnośnie sieci wystarczy uruchomić dwie komendy a nowe adresy IP zaczną działać

netplan generate
netplan apply

Te komendy wykonujemy tylko wtedy jeśli coś sie zmieni. Nie trzeba ich uruchamiać jeśli konfiguracja nie ulega zmianie. Nie tylko uruchomimy te komendy za pomocą Ansible, wykryjemy najpierw czy faktycznie trzeba to zrobić. W tym celu sprawdzimy warunek, czy config uległ zmianie.

~/Projects/infra/roles/ovh-ipfo-netplan/tasks/main.yml

- name: Configure netplan with IP FO
  template: src=51-ipfo.yaml.j2 dest=/etc/netplan/51-ipfo.yaml owner=root group=root mode=0644 backup=no
  register: netplan_config_template
 
- name: Generate netplan config
  when: netplan_config_template.changed
  command: netplan generate
 
- name: Apply netplan config
  when: netplan_config_template.changed
  command: netplan apply

register: netplan_config_template

Dopisanie tego do zadania z szablonem tworzy nam zmienną z wynikiem wykonania tego jednego zadania

when: netplan_config_template.changed

W następnych dwóch zadaniach sprawdzamy czy zostały oznaczone jako zmienione. Jeśli się coś zmieniło — odpalamy komendy

command: netplan generate command: netplan apply

Jak widać składnia nie jest trudna jeśli się ją zobaczy kilka razy. W razie czego zawsze można robić kopiuj-wklejkę z tego poradnika ( ͡° ͜ʖ ͡°) Dla bardziej skomplikowanych scenariuszów możemy monitorować co zwróciły komendy. W standardzie Ansible podniesie alarm, jeśli komenda nie zwróciła wyniku 0 i zatrzyma wykonywanie następnych zadań. Przy odpowiedniej konfiguracji błędy można obsługiwać lub ignorować.

Uzupełnienie tasków dla squid

Mamy już w pełni sprawną sieć pod proxy, dokończmy teraz konfigurację Squid, będzie to wyglądać podobnie do konfiguracji sieci co robiliśmy już wcześniej

Domyślna konfiguracja squid w żaden sposób nie rozwiązuje problemów które mamy do rozwiązania. Na dodatek plik konfiguracji z komentarzami ma prawie 8 000 linii więc pozwólcie że nie będę tego tu wklejać. W razie czego zapraszam do dokumentacji na stronie projektu: https://wiki.squid-cache.org/ConfigExamples

Aby konfiguracja się skalowała mamy w sumie dwa w miarę rozsądne rozwiązania:

  • zdefiniować jak ma wyglądać cały nasz config, jeśli taki nie jest obecnie na serwerze to wtedy nadpisujemy tym, co mamy w planach
  • podmieniać wybrane domyślne linie w configu na wybrane przez nas (string replace)

Tu użyje tej pierwszej metody bo wtedy mamy pewność że jest dokładnie tak jak chcemy plus to nie jest oprogramowanie które jakoś specjalnie często się zmienia.

Druga metoda może też mieć uzasadnienie w niektórych przypadkach jak np. wszystko chcemy mieć domyślnie jak przewidzieli twórcy oprócz jednej małej rzeczy. Wtedy zastępowanie całego configa naszą starą wersją może być zbędne.

Okej a więc tak wygląda nowa wersja pliku z zadaniami dla roli squid

~/Projects/infra/roles/squid/tasks/main.yml

- name: Update apt cache if needed and install htop
  apt: pkg=htop state=present update_cache=yes cache_valid_time=3600
 
- name: Install basic tools
  apt:
    name: "{{ packages }}"
  vars:
    packages:
      - htop
      - iotop
      - nload
      - ncdu
 
- name: Install Squid
  apt:
    name: squid
    state: present
 
- name: Configure Squid
  template: src=squid.conf.j2 dest=/etc/squid/squid.conf owner=root group=root mode=0644 backup=no
  register: squid_config_template
 
- name: Apply Squid config
  when: squid_config_template.changed
  systemd:
    name: squid
    state: restarted
    enabled: yes

Nic specjalnie nowego prócz ostatniego taska. Wykorzystujemy tu wbudowany gotowy moduł do obsługi systemd w Ansible. Gdy nasz config się zmieni — zlecamy systemd jej restart oraz włączenie przy starcie jeśli usługa jest nieaktywna.

Uwzględniając nasze zmiany z taskami, Playbook powinien wyglądać teraz tak

~/Projects/infra/squid.yml

- hosts: squid
  become: yes
  vars:
    squid_http_port: 3128
    squid_allowed_nets:
      - "192.0.2.100"
    ipfo_list:
      - "192.0.2.2"
      - "192.0.2.3"
      - "192.0.2.4"
  roles:
    - ovh-ipfo-netplan
    - squid

Użyliśmy małej sztuczki, wszystkie adresy IP dodawane przez rolę do IP FO używane są też przez rolę squida. Nie musimy pisać dwa razy tego samego. Może to być trochę mniej czytelne, jeśli np. dorobimy sobie inną rolę do obsługi innego dostawcy serwerów. Zmienna używana do adresów IP wtedy będzie myląca, gdyż odwołuje się do jednego konkretnego. Są to jednak problemy, których rozwiązanie to temat na inny raz lub akurat jako samodzielne próby nauki :D

Gotowe

Mamy teraz elastyczne proxy z łatwym dodawaniem adresów IP i szybkim stosowaniem zmian. W poradniku jest sporo niedoróbek. Niestety nie byłem w stanie zorganizować więcej czasu niż prawie cały dzień który na niego poświęciłem. Jeśli znajdą się spory odzew w odpowiedziach do tego wątku to rozważę jego poprawę oraz przygotowanie więcej poradników odnośnie Ansible

Przy tworzeniu tego poradnika nie ucierpiał żaden admin

Nieznajomy11
Nieznajomy11 Moderator forum.lvlup.pro

Trochę dokumentacji się naczytałem, ale dzienki działa. 😮

image|369x244