Pokazywanie postów oznaczonych etykietą Red Hat. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą Red Hat. Pokaż wszystkie posty

Docker - instalacja i podstawy zarządzania kontenerami

Red Hat 7.x / CentOS 7.x
Docker to nowe spojrzenie na wirtualizację. Zamiast tradycyjnych maszyn wirtualnych wprowadza on niezależnie działające kontenery usług oparte o środowisko gospodarza, co zwiększa wydajność i elastyczność środowiska. Teorię można sobie doczytać w internecie, a nic tak nie przybliża do technologii, jak ćwiczenia praktyczne, więc bez zbędnych wstępów zaczynamy.

Instalacja

Jako środowiska bazowego używać będę systemu CentOS 7.2. Poza lokalizacją plików konfiguracyjnych dockera i procesem instalacji samo używanie go w tym srodowisku nie róźni się od innych platform.

Instalację możemy wykonać na trzy sposoby
- używając yum oraz systemowego repozytorium - yum install docker - w ten sposób instalujemy wersję zatwierdzoną przez CentOSa/Red Hata
- używając yum i konfigurując repozytorium dockera
- korzystając ze skryptu instalacyjnego dockera, który doda nam repozytorium i zainstaluje binaria curl -fsSL https://get.docker.com/ | sh

Jeśli zależy nam na aktualnej wersji najlepiej będzie użyć metody trzeciej, a jeśli wystarczy nam wersja wcześniejsza, ale "wygrzana" skorzystać możemy z metody pierwszej. Ja skorzystam z pierwszej.

[root@docker-base ~]# yum install docker

Możemy zweryfikować wersję zainstalowanego pakietu poprzez:

[root@docker-base ~]# docker --version
Docker version 1.8.2-el7.centos, build a01dc02/1.8.2

 Aby nie działać na koncie root-a, ale z drugiej strony nie robić wszystkiego przez sudo możemy dodać swojego użytkownika do grupy docker poprzez usermod -aG docker nasz_user. Jeśli grupa nie istnieje, należy ją utworzyć (groupadd docker).
Domyślnie serwis dockera nie jest wystartowany, więc trzeba do wystartować i zadbać, żeby startował wraz z systemem:

[root@docker-base ~]# systemctl enable docker
[root@docker-base ~]# systemctl start docker

I tu w zasadzie kończy się pierwszy etap - docker jest gotowy do użycia.

Zarządzanie kontenerami

Podstawowym poleceniem dockera, którego używać będziemy najczęściej jest wyświetlanie kontenerów działających na naszym hoście:

[docker@docker-base ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

W wyniku jego wywołania uzyskamy listę działających w środowisku kontenerów. Lista jest wyświetlana w postaci kolumn, które oznaczają odpowiednio:
CONTAINER_ID - identyfikator uruchomionego kontenera
IMAGE - identyfikator obrazu z którego kontener został uruchomiony (o obrazach później)
COMMAND - polecenie wykonywane wewnątrz kontenera podczas jedo inicjalizacji
CREATED - czas utworzenia kontenera
STATUS - obecny status kontenera
PORTS - wykaz mapowanych do kontenera portów (opiszę przy okazji sieci w ramach kontenerów)
NAMES - nazwa naszego kontenera

Polecenie to posiada jeszcze jeden często używany przełącznik docker ps -a, który wyświetla wszystkie kontenery w systemie - również te z różnych powodów nieaktywne.

Jako, że temat zarządzania kontenerami jest ściśle powiązany z innym aspektem - zarządzaniem obrazami - na pierwszy rzut oka polecenia mogą nasuwać wiele pytań. Póki co przyjmij składnię jako pewnego rodzaju kanon, a wszystko rozjaśni się po poznaniu tematu obrazów. Temat ten jest szeroki, więc pewnie pojawi się w osobnym wpisie.

Na początek całą naszą pracę oprzemy o kontener, który dostarczy nam niewielki system operacyjny o nazwie busybox. Rozpocznijmy więc naszą przygodę.

Uruchamianie nowego kontenera


Jako, że tekst jest początkiem początków przygody z Dockerem nie będę tu przedstawiał wszystkich przydatnych opcji uruchomienia kontenera (pewnie później pojawi się zbiorówka tych opcji w osobnym tekście), a jedynie te niezbędne do uruchomienia najprostrzego funkcjonalnego kontenera.

Każdy kontener uruchamiany jest w naszym systemie na bazie posiadanych przez nas obrazów. Obrazy takie - jeśli jeszcze nie istnieją w lokalnym repozytorium są pobierane przez serwer dockera z oficjalnego repozytorium. Proces ten powoduje, że uruchomienie pierwszej instancji kontenera może trwać dość długo, ale nie należy się tym zrażać.
Podstawowa składnia uruchamiająca kontener wyglądanastępująco: docker run nazwa_obrazu. Zastosujmy ją więc praktycznie, opierając się o rzeczywisy obraz systemu wymienianego wcześniej:

[docker@docker-base ~]$ docker run busybox
Unable to find image 'busybox:latest' locally
Trying to pull repository docker.io/library/busybox ... latest: Pulling from library/busybox

56ed16bd6310: Extracting [==================================================>]   676 kB/676 kB
bc744c4ab376: Download complete

Jak zaobserwujemy docker pobiera sobie obraz busybox, aby uruchomić kontener. Po zakończeniu procesu moglibyśmy się spodziewać, że zastaniemy uruchomiony kontener z systemem busybox - sprawdźmy zatem:

[docker@docker-base ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Pusto. Pomimo tego co mogłoby się wydawać, proces uruchomienia kontenera przebiegł właściwie. Wyjaśnienie sytuacji jest proste i widoczne w listingu wszystkich kontenerów:

[docker@docker-base ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
cf7e33398159        busybox             "sh"                3 minutes ago       Exited (0) 3 minutes ago                       dreamy_morse

Zrzut ten pokazuje nam, że kontener został utworzony 3 minuty wcześniej, a w chwili obecnej ma status Exited(0). Kontener wyłączył się, ponieważ operacją jaką miał za zadanie wykonać była egzekucja komendy sh. Udało się ją wykonać, więc kontener zrobił co miał do zrobienia i przestał być potrzebny.
Mój pierwszy kontakt z kontenerami opraty był na tym przykładzie, i szczerze mówiąc bardziej oddalał mnie od tej technologii niż do niej zbliżał. Po co komu "twór", który po uruchomieniu wchodzi do sh i się wyłącza? Kolejny wykopany przykład użycia był równie bezsensowny:

[docker@docker-base ~]$ docker run busybox echo "Echo kontrolne"
Echo kontrolne
[docker@docker-base ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
2033115ffe8c        busybox             "echo 'Echo kontrolne"   3 minutes ago       Exited (0) 3 minutes ago                        berserk_brown
cf7e33398159        busybox             "sh"                     13 minutes ago      Exited (0) 13 minutes ago                       dreamy_morse

Przykład demonstruje jak przekazywać parametry do kontenera - nakazujemy tu odpalenie kontenera i wykonanie w nim echa... po co to komu? i jak to się ma do maszyny wirtualnej? Kontener znów się wyłączył - wykonał swoje zadanie, więc nie jest już do niczego przydatny....
Przykłady te są chyba najsłabszą reklamą technologii jaką widziałem, dlatego warto o nich szybko zapomnieć. Zróbmy więc coś, co przekona nas do Dockera, i udowodni słuszność koncepcji. Założeniem działania Dockera jest szybkie i niskozasobowe dostarczanie usługi, a dobrym przykładem jego realizacji może być prosty serwer www.
Warto tu poznać pierwsze przydatne opcje komendy docker run, a mianowicie -d - pozwala ona uruchomić kontener w tzw. detached mode, czyli oddzielić działanie  kontenera od naszej powłoki shell, oraz -P, które mapuje wszystkie porty kontenera na porty naszego hosta.
Stwórzmy zatem wspomiany kontener hostujący stronę www.

[docker@docker-base ~]$ docker run -d -P httpd
Unable to find image 'httpd:latest' locally
Trying to pull repository docker.io/library/httpd ... latest: Pulling from library/httpd

d8bd0657b25f: Pull complete
a582cd499e0f: Pull complete
4d035354d707: Pull complete
152d136f2b33: Pull complete
36c9fec13bf5: Pull complete
2e56fcbe3fad: Pull complete
ceef08ecfd76: Pull complete
a023717bbace: Pull complete
1fcf3155d7dc: Pull complete
698af7c24a15: Pull complete
cca3d6822dae: Pull complete
c7c39c3750a6: Pull complete
ee4a5faa57f7: Pull complete
Digest: sha256:4aba8de9c396e4f20d682fbc9ee8e06fd91700fdbb88ed36157d6f6487a99da7
Status: Downloaded newer image for docker.io/httpd:latest

22b8aba2db983f1ef6340118ab797a3f8e88680a24e680d3e77c1090264e5357
[docker@docker-base ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                   NAMES
22b8aba2db98        httpd               "httpd-foreground"   8 seconds ago       Up 6 seconds        0.0.0.0:32768->80/tcp   jolly_meitner

Analogicznie do poprzedniego kontenera, obrazu httpd nie było w lokalnym repozytorium, więc został pobrany z sieci. Co się zmieniło w stosunku do poprzedniego przykładu? Po pierwsze, kontener działa, po drugie mamy wpis w kolumnie PORTS.
Kontener działa, więc powinien dostarczać nam usługę httpd. Do sprawdzenia tej teorii potrzebny jest nam drugi element - mapowanie portów. Powyższa forma wpisu oznacza, że port 32768 naszego serwera dockerowego jest mapowany na port 80 kontenera. Weryfikacja jest prosta - otwórz przeglądarkę i udaj się pod adres <ip_serwera_docker>:<port_mapowany> - u mnie to 192.168.1.150:32768. Voila. Apache działa!.

Interakcja z kontenerem


Pierwszym pytaniem jakie mi się nasunęło po uruchomieniu kontenera z httpd było mniej więcej "jak się dostać do środka i zmienić domyślną zawartość index.html". Okazuje się, że są przynajmniej dwie metody podłączenia się z systemem w kontenerze (patrząc z poziomu dockera).
Pierwszą z nich jest docker attach - metoda działa kiedy kontener uruchamia się z aktywnym shellem, i szybko przestałem z niej korzystać.
Druga, to docker exec.
Do podłączenia się do kontenera musimy znać jego id lub nazwę - z pomocą przychodzi nam tu docker ps .

[docker@docker-base ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                   NAMES
b399b5f6b2ce        httpd               "httpd-foreground"   5 seconds ago       Up 4 seconds        0.0.0.0:32769->80/tcp   silly_sinoussi
[docker@docker-base ~]$ docker exec -it silly_sinoussi bash
root@b399b5f6b2ce:/usr/local/apache2#

Polecenie docker exec pozwala na wywołanie wenątrz kontenera komendy podawanej jako argument. Przy interaktywnym podłączeniu istotne są przełączniki użyte powyżej: -i pozwala na interakcję podłączając do kontenera STDIN, -t natomiast uruchamia pseudo terminal TTY dla kontenera. Wewnątrz kontenera poruszamy się tak, jak w zwykłym systemie operacyjnym.
Muszę tu wspomnieć o jednej właściwości dockera w odniesieniu do interakcji z kontenerem. Przy podłączeniu interaktywnym poprzez docker attach, lub uruchomieniu kontenera od razu z opcjami -it, który przeniesie nas do shella kontenera wyjście z niego przy użyciu standardowego exit lub kombinacji ctrl+d zabije nasz kontener - jeśli tego nie chcemy trzeba wyrobić sobie nawyk używania kombinacji wyjścia dockera ctrl+p ctrl+q

Uruchamianie i zatrzymywanie kontenerów


Pierwsze uruchomienie kontenera dodaje go do naszego inwentarza, który przeglądamy poprzez docker ps -a. Znajdujące się tu kontenery możemy zatrzymywać lub uruchamiać, zależnie od potrzeb.

[docker@docker-base ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                   NAMES
b399b5f6b2ce        httpd               "httpd-foreground"   10 minutes ago      Up 3 minutes        0.0.0.0:32770->80/tcp   silly_sinoussi
[docker@docker-base ~]$ docker stop b399b5f6b2ce
b399b5f6b2ce
[docker@docker-base ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS                     PORTS               NAMES
b399b5f6b2ce        httpd               "httpd-foreground"   11 minutes ago      Exited (0) 4 seconds ago                       silly_sinoussi
[docker@docker-base ~]$ docker start silly_sinoussi
silly_sinoussi
[docker@docker-base ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                   NAMES
b399b5f6b2ce        httpd               "httpd-foreground"   11 minutes ago      Up 1 seconds        0.0.0.0:32771->80/tcp   silly_sinoussi

Jak widać powyżej, stosowanie ID i nazwy kontenera jest zamienne i uwarunkowane jedynie preferencjami użytkownika. Dodatkowo, dostępne jest również polecenie docker restart restartujące kontener, a także, w przypadku tworów opornych na polecenie stop - docker kill.

Usuwanie kontenerów


Polecenie docker stop zatrzymuje kontener, ale nie usuwa go z naszego systemu. Kontener taki zajmuje ciągle zasoby dyskowe naszego hosta. Oczywiście istnieje możliwość permanentnego usunięcia nieużywanego już kontenera - wykonać to możemy poprzez

[docker@docker-base ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS                     PORTS                   NAMES
96d2915d0302        busybox             "sh"                 3 seconds ago       Exited (0) 3 seconds ago                           jolly_fermat
b399b5f6b2ce        httpd               "httpd-foreground"   19 minutes ago      Up 4 minutes               0.0.0.0:32772->80/tcp   silly_sinoussi
[docker@docker-base ~]$ docker rm 96d2915d0302
96d2915d0302
[docker@docker-base ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                   NAMES
b399b5f6b2ce        httpd               "httpd-foreground"   19 minutes ago      Up 4 minutes        0.0.0.0:32772->80/tcp   silly_sinoussi

Tu również możemy zamiennie stosować id oraz nazwę kontenera.

Przydatne opcje i polecenia

Jednym z pierwszych przełączników jakich szukałem, był ten umożliwiający nadawanie konkretnych nazw kontenerom. Jeśli chcemy postawić kilka maszyn opartych o ten sam obraz, to nazwy typu "jolly_fermat" i "silly_sinoussi" niewiela nam pomogą przy identyfikacji konkretnej maszyny.
Do nadawania własnych nazw kontenerów używamy przełącznika --name nazwa_kontenera.

Przydatnymi poleceniami mogą okazać się dwa, pozwalające na zatrzymywanie oraz usuwanie WSZYSTKICH maszyn z dockera.
Do zatrzymania wszystkich kontenerów możemy użyć polecenia docker stop $(docker ps -q).
Usunięcie wszystkich kontenerów zrealizujemy w sposób analogiczny: docker rm $(docker ps -qa)

Osobiście w linuksie bardzo lubię fakt, że jeśli czegoś zapomnę, mogę to w bardziej lub mniej łatwy sposób znaleźć bez korzystania za każdym razem z google. Oczywiście mówię tu o helpach i man-ach dla wszelakich poleceń. Trzeba przyznać, że deweloperzy dockera wpisują się bardzo dobrze w zwyczaje dokumentowania binariów. Szczególnie przydatny jest help do dockera, który skonstruowany jest wielopoziomowo:
docker help wyświetli nam ogólnego helpa
docker help komenda daje helpa dla konkretnej opcji (np. docker help run)

[RH7] Konfiguracja fensowania nodów klastra

Red Hat 7.x / CentOS 7.x
Fensowanie jest wymogiem każdego klastra wysokiej dostępności. Zapobiega ono uszkodzeniom struktury danych spowodowanych przez błędnie działający node.
Zależnie od hardware'u na jakim działa klaster procers fensowania może wyłączyć komunikację sieciową uszkodzonego node'a ze współdzielonym zasobem storage'owym, lub zrestartować problematyczny węzeł klastra.

Pierwszym krokiem konfiguracji fensowania jest konfiguracja urządzenia fensującego - może to być np. UPS pod który node jest podłączony,  jego PDU (Power Distribution Unit), urządzenie kontrolujące zasilanie Blade'a.

Listę dostępnych modułów fensowania uzyskamy wydając polecenie:

[root@clusternode1 ~]# pcs stonith list
fence_apc - Fence agent for APC over telnet/ssh
fence_apc_snmp - Fence agent for APC, Tripplite PDU over SNMP
fence_bladecenter - Fence agent for IBM BladeCenter
fence_brocade - Fence agent for HP Brocade over telnet/ssh
fence_cisco_mds - Fence agent for Cisco MDS
fence_cisco_ucs - Fence agent for Cisco UCS
fence_compute - Fence agent for the automatic resurrection of OpenStack compute instances
fence_drac5 - Fence agent for Dell DRAC CMC/5
fence_eaton_snmp - Fence agent for Eaton over SNMP
fence_emerson - Fence agent for Emerson over SNMP
fence_eps - Fence agent for ePowerSwitch
fence_hpblade - Fence agent for HP BladeSystem
fence_ibmblade - Fence agent for IBM BladeCenter over SNMP
fence_idrac - Fence agent for IPMI
fence_ifmib - Fence agent for IF MIB
fence_ilo - Fence agent for HP iLO
fence_ilo2 - Fence agent for HP iLO
fence_ilo3 - Fence agent for IPMI
fence_ilo3_ssh - Fence agent for HP iLO over SSH
fence_ilo4 - Fence agent for IPMI
fence_ilo4_ssh - Fence agent for HP iLO over SSH
fence_ilo_moonshot - Fence agent for HP Moonshot iLO
fence_ilo_mp - Fence agent for HP iLO MP
fence_ilo_ssh - Fence agent for HP iLO over SSH
fence_imm - Fence agent for IPMI
fence_intelmodular - Fence agent for Intel Modular
fence_ipdu - Fence agent for iPDU over SNMP
fence_ipmilan - Fence agent for IPMI
fence_kdump - Fence agent for use with kdump
fence_mpath - Fence agent for multipath persistent reservation
fence_rhevm - Fence agent for RHEV-M REST API
fence_rsa - Fence agent for IBM RSA
fence_rsb - I/O Fencing agent for Fujitsu-Siemens RSB
fence_scsi - Fence agent for SCSI persistentl reservation
fence_virt - Fence agent for virtual machines
fence_vmware_soap - Fence agent for VMWare over SOAP API
fence_wti - Fence agent for WTI
fence_xvm - Fence agent for virtual machines

Każdy z modułów posiada specyficzne opcje, które możemy sprawdzić poprzez:

[root@clusternode1 ~]# pcs stonith describe fence_virt
fence_virt - Fence agent for virtual machines

fence_virt is an I/O Fencing agent which can be used withvirtual machines.

Stonith options:
  debug: Specify (stdin) or increment (command line) debug level
  serial_device: Serial device (default=/dev/ttyS1)
  serial_params: Serial Parameters (default=115200,8N1)
  channel_address: VM Channel IP address (default=10.0.2.179)
  ipport: TCP, Multicast, or VMChannel IP port (default=1229)
  port: Virtual Machine (domain name) to fence
  action: Fencing action (null, off, on, [reboot], status, list, monitor, metadata)
  timeout: Fencing timeout (in seconds; default=30)
  ipaddr: IP address to connect to in TCP mode (default=127.0.0.1 / ::1)
  auth: Authentication (none, sha1, [sha256], sha512)
  hash: Packet hash strength (none, sha1, [sha256], sha512)
  key_file: Shared key file (default=/etc/cluster/fence_xvm.key)
  delay: Fencing delay (in seconds; default=0)
  domain: Virtual Machine (domain name) to fence (deprecated; use port)
  priority: The priority of the stonith resource. Devices are tried in order of highest priority to lowest.
  pcmk_host_map: A mapping of host names to ports numbers for devices that do not support host names.
  pcmk_host_list: A list of machines controlled by this device (Optional unless pcmk_host_check=static-list).
  pcmk_host_check: How to determine which machines are controlled by the device.
  pcmk_delay_max: Enable random delay for stonith actions and specify the maximum of random delay

W przypadku środowiska wirtualnego opartego o VirtualBox problematyczne (jeśli nie niemożliwe) jest skonfigurowanie fensowania opartego o zasilanie maszyny, dlatego użyjemy zasobu dyskowego, który będzie naszym urządzeniem fensującym. W tym celu utworzyłem na dodatkowej maszynie zasób iSCSI, który podłączyłem do wszystkich nodów klastra. (tutaj znajdziesz jak udostępniać zasób iSCSI oraz jak podłączyć serwer do zasobu).
iSCSI pojawiło się na wszystkich maszynach jako /dev/sdb. Teraz musimy zdefiniować w stonith nasze urządzenie fensujące.

[root@clusternode1 ~]# pcs stonith create iscsi-shooter fence_scsi devices=/dev/sdb meta provides=unfencing

Wpis meta provides=unfencing jest opcją, którą należy dodawać w przypadku korzystania z urządzeń fensujących nie opartych o zasilanie. Dba ona, aby przed restartem fensowanego node'a został on "odfensowany", czyli gwarantuje start usług klastrowych i pełne przyłączneie do klastra.

Po dodaniu urządzenia, sprawdzimy, czy utworzyło się ono poprawnie:

[root@clusternode1 ~]# pcs stonith show
 iscsi-shooter  (stonith:fence_scsi):   Started clusternode1

Jak widać, urządzenie działa, więc teoretycznie powinniśmy być w stanie wymusić fensowanie poszczególnych nodów klastra, sprawdźmy zatem:

[root@clusternode1 ~]# pcs cluster status
Cluster Status:
 Last updated: Tue Mar 15 18:30:03 2016         Last change: Tue Mar 15 18:16:10 2016 by root via cibadmin on clusternode3
 Stack: corosync
 Current DC: clusternode1 (version 1.1.13-10.el7_2.2-44eb2dd) - partition with quorum
 3 nodes and 1 resource configured
 Online: [ clusternode1 clusternode2 clusternode3 ]

PCSD Status:
  clusternode1: Online
  clusternode2: Online
  clusternode3: Online
[root@clusternode1 ~]# pcs stonith fence clusternode2
Node: clusternode2 fenced
[root@clusternode1 ~]# pcs stonith fence clusternode3
Node: clusternode3 fenced
[root@clusternode1 ~]# pcs cluster status
Cluster Status:
 Last updated: Tue Mar 15 18:30:18 2016         Last change: Tue Mar 15 18:16:10 2016 by root via cibadmin on clusternode3
 Stack: corosync
 Current DC: clusternode1 (version 1.1.13-10.el7_2.2-44eb2dd) - partition with quorum
 3 nodes and 1 resource configured
 Node clusternode2: pending
 Node clusternode3: pending
 Online: [ clusternode1 ]

PCSD Status:
  clusternode1: Online
  clusternode2: Online
  clusternode3: Online

Jak widać dwa nody odłączyły się od klastra, czyli fensowanie działa. Na takim zfensowanym węźle, próba wyświetlenia statusu klastra zakończy się tak:

[root@clusternode2 ~]# pcs cluster status
Error: cluster is not currently running on this node

W przypadku, kiedy fensowanie nie jest oparte o zasilanie, czyli wadliwa maszyna nie jest restartowana, po usunięciu problemów należy przywrócić działanie takiej maszyny startując usługę klastrową:

[root@clusternode2 ~]# pcs cluster start
Starting Cluster...
[root@clusternode2 ~]# pcs cluster status
Cluster Status:
 Last updated: Tue Mar 15 18:35:12 2016         Last change: Tue Mar 15 18:16:10 2016 by root via cibadmin on clusternode3
 Stack: corosync
 Current DC: clusternode1 (version 1.1.13-10.el7_2.2-44eb2dd) - partition with quorum
 3 nodes and 1 resource configured
 Node clusternode3: pending
 Online: [ clusternode1 clusternode2 ]

PCSD Status:
  clusternode1: Online
  clusternode2: Online
  clusternode3: Online

Testując ten wariant fensowania, zetknąłem się z jednym błędem. Po fensowaniu maszyn clusternode2 i clusternode3 wykonanym z maszyny clusternode1, a następnie ponownym ich przywróceniu do klastra, nie mogłem fensować maszyny clusternode1 z pozostałych maszyn. Problem objawiał się następująco:

[root@clusternode2 ~]# pcs stonith fence clusternode1
Error: unable to fence 'clusternode1'
Command failed: No route to host

Przyczyną okazały się problemy z odświeżaniem informacji o konfiguracji urządzenia fensującego scsi. Rozwiązaniem natomiast okazało się wymuszenie jej odświeżenia:

[root@clusternode2 ~]# pcs stonith cleanup
Waiting for 1 replies from the CRMd. OK

[root@clusternode2 ~]# pcs stonith fence clusternode1
Node: clusternode1 fenced

Okazuje się, że komenda pcs stonith show pokazuje status urządzenia fensującego jako Stopped, gdy którykolwiek z nodów klastra jest niedostępny.
Testowo utworzyłem na każdym z nodów inne urządzenie fensujące, oparte o ten sam dysk - jeden zasób iscsi, i na każdym z nodów wydane polecenie pcs stonith create ze wskazaniem na dysk /dev/sdb, ale różnymi nazwami urządzenia - w rezultacie pcs stonith show pokazywał jako zastopowane jedynie urządzenie na zfensowanym nodzie...

Podłączanie zasobu iSCSI do naszego serwera - iscsiadm

Red Hat 6.x/7.x / CentOS 6.x/7.x
Do podłączenia się do udostępnianego nam zasobu iSCSI będziemy potrzebowali pakietu iscsi-initiator-utils i od jego instalacji musimy rozpocząć procedurę.

[root@iscsi-client ~]# yum install -y iscsi-initiator-utils

Zainstalowanie paczku utworzy nam nazwę inicjatora, która jest potrzebna w przypadku, kiedy wystawiony zasób iSCSI (target) weryfikuje tożsamość hostów podłączających się (ACL).

Naszą nazwę odnajdziemy w pliku /etc/iscsi/initiatorname.iscsi

[root@iscsi-client ~]# cat /etc/iscsi/initiatorname.iscsi
InitiatorName=iqn.1994-05.com.redhat:1feb37be685a

W paczce znajduje się narzędzie, które umożliwia wykrywanie  i podłączanie zasobu - iscsiadm. Manual do niego zawiera w przykładach w zasadzie gotowe do użycia komendy pozwalające przeprowadzić całą procedurę podłączania dysku.

W pierwszej kolejności musimy wyszukać udostępniany nam target:

[root@iscsi-client ~]# iscsiadm --mode discoverydb --type sendtargets --portal 192.168.1.204 --discover
192.168.1.204:3260,1 iqn.2003-01.org.linux-iscsi.server.x8664:sn.ca677b0f8d08

Taki wynik komendy oznacza, że wykryliśmy jeden target udostępniany nam z maszyny o adresie 192.168.1.204.
Aby zainicjować go w systemie, musimy się do niego "zalogować", zmieniając w drugiej komendzie z manualowych przykładów nazwę iqn targetu (wyświetloną w wyniku poprzedniej komendy) oraz adres ip:

[root@iscsi-client ~]# iscsiadm --mode node --targetname iqn.2003-01.org.linux-iscsi.server.x8664:sn.ca677b0f8d08 --portal 192.168.1.204:3260 --login
Logging in to [iface: default, target: iqn.2003-01.org.linux-iscsi.server.x8664:sn.ca677b0f8d08, portal: 192.168.1.204,3260] (multiple)
Login to [iface: default, target: iqn.2003-01.org.linux-iscsi.server.x8664:sn.ca677b0f8d08, portal: 192.168.1.204,3260] successful.

Polecenie pokazuje, że logowanie przebiegło pomyślnie, a to oznacza, że w pliku /proc/partitions powinniśmy zobaczyć nowy twór - nasz dysk iSCSI:

[root@iscsi-client ~]# cat /proc/partitions
major minor  #blocks  name

   8        0    8388608 sda
   8        1     512000 sda1
   8        2    7875584 sda2
  11        0    1048575 sr0
 253        0     839680 dm-0
 253        1    7032832 dm-1
   8       16    1048576 sdb

Wszystko wygląda jak powinno, dysk się pojawił, rozmiar się zgadza. Pozostaje zamountować naszą nową przestrzeń w systemie, utworzyć filesystem i cieszyć się.

W tym miejscu należy się jeszcze jedna uwaga - chodzą słuchy, że system operacyjny potrafi sprawiać niespodzianki w zakresie nazewnictwa dysków, i po podłączeniu do maszyny fizycznego dysku i restarcie (podobno) może zdarzyć się, że nasze iSCSI raptem z sdb stało się np. sdc, dlatego zaleca się mountowanie dysków poprzez ich identyfikator (UUID).

Aby poznać UUID dysku, należy utworzyć filesystem i podmountować go do naszego systemu:

[root@iscsi-client ~]# fdisk /dev/sdb
[... tworzymy partycję ...]
[root@iscsi-client ~]# mkfs.xfs /dev/sdb1
meta-data=/dev/sdb1              isize=256    agcount=4, agsize=65024 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=0        finobt=0
data     =                       bsize=4096   blocks=260096, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=0
log      =internal log           bsize=4096   blocks=853, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
[root@iscsi-client ~]# mount /dev/sdb1 /iscsi/
[root@iscsi-client ~]# blkid /dev/sdb1
/dev/sdb1: UUID="a3719902-0ee5-4095-b713-af768c5fb7e0" TYPE="xfs"

We wpisie w /etc/fstab dla tego dysku należy w pierwszej kolumnie, zamiast jego ścieżki /dev/sdx podać UUID=numer_uuid_z_polecenia_blkid.

[RH7] Tworzenie targetu iSCSI - targetcli

Red Hat 7.x / CentOS 7.x
Zarządzanie targetami iSCSI w wersji 7 odbywa się poprzez narzędzie targetcli. Narzędzie jest rozbudowane i możemy przy jego pomocy zarządzać kilkoma rodzajami storage'u onlineowego (o innych rodzajach może w przyszłości).

Realizację zadania rozpoczniemy oczywiście od instalacji i uruchomienia pakietu targetcli. Ważne, aby pamiętać o systemctl enable - bez tego konfiguracja targetów nie będzie ładowana po restarcie systemu:

[root@server ~]# yum install targetcli
[root@server ~]# systemctl enable target
Created symlink from /etc/systemd/system/multi-user.target.wants/target.service to /usr/lib/systemd/system/target.service.
[root@server ~]# systemctl start target

Narzędzie udostępnia nam swoistą powłokę, w ramach której operujemy na naszych udziałach dyskowych. Wywołanie go daje taki oto efekt:

[root@server ~]# targetcli
targetcli shell version 2.1.fb41
Copyright 2011-2013 by Datera, Inc and others.
For help on commands, type 'help'.

/>

Na pierwszy rzut oka powłaka wydaje się być dziwna, ale z jej poziomu możemy wywołać helpa - i tu niespodzianka, help jest ciekawie zorganizowany - zależnie od poziomu drzewa help wyświetla opcje specyficzne dla tej właśnie lokalizacji.
Powłoka przedstawia nam w formie hierarchicznej konfigurację storage'ów możliwych do skonfigurowania. Część z nich jest dostępna jedynie, w przypadku kiedy wspierający rozwiązanie hardware jest obecny w naszym systemie. Inne znich są dostępne zawsze.
Poruszanie się po drzewie odbywa się analogicznie do poruszania w systemie - ls i cd.

[root@server ~]# targetcli
targetcli shell version 2.1.fb41
Copyright 2011-2013 by Datera, Inc and others.
For help on commands, type 'help'.

/> ls
o- / ..................................................................... [...]
  o- backstores .......................................................... [...]
  | o- block .............................................. [Storage Objects: 0]
  | o- fileio ............................................. [Storage Objects: 0]
  | o- pscsi .............................................. [Storage Objects: 0]
  | o- ramdisk ............................................ [Storage Objects: 0]
  o- iscsi ........................................................ [Targets: 0]
  o- loopback ..................................................... [Targets: 0]
/> cd iscsi
/iscsi> ls
o- iscsi .......................................................... [Targets: 0]
/iscsi>

Backstores

Udostępnianie powierzchni dyskowej dobrze jest rozpocząć od zdefiniowania tego, co i w jakiej ilości chcemy udostępniać. Obiekty takie - zwane backstores możemy zdefiniować w oparciu o cztery komponenty:
- block - to udostępnienie zwykłego urządzenia blokowego, które możemy znaleźć w katalogu /sys/block (np. /dev/sdb)
- fileio - to przestrzeń, którą zaalokujemy w pliku na naszym filesystemie (file I-O)
- pscsi - to urządznie typu pass-through SCSI, czyli np. dysk SAS-owy
- ramdisk - emulujące protokół SCSI na obszarze zarezerwowanym w pamięci RAM.

Tworzenie na podstawie każdego z typów rozpoczyna się identycznie - od przejścia do odpowiedniego "katalogu" w drzewie konsoli, czyli np. jeśli chcemy udostępnić powierzchnię 1GB, która fizycznie będzie się znajdować w pliku na naszym lokalnym dysku, skorzystamy z backstore'a typu fileio:

/> cd backstores/fileio
/backstores/fileio>

Wszystkie opcje dostępne w tej lokalizacji możemy podejrzeć poprzez polecenie help, a detale komendy poprzez help POLECENIE.
Jak łatwo wydedukować na podstawie takiego helpa, tworzenie backstore'a zrealizujemy poleceniem create, który jako parametrów potrzebuje nazwy i lokalizacji pliku, w którym zostanie zaalokowany.

Niestety help zawiera pewną nieścisłość - zaznaczono tam parametr size jako opcjonalny, co może zostać źle zrozumiane. Jego opcjonalność polega bowiem na fakcie, że jest opcjonalny jedynie dla komendy create, przy czym jeśli create tworzyć ma backstore typu fileio, jest on wymagany, a jeśli tyczy się typu block należy go pominąć.

Opcjonalnymi parametrami komendy są:
- write_back - przyjmujący parametr true (domyślnie) jeśli chcemy korzystać z cache lokalnego filesystemu, lub false, jeśli chcemy operować w trybie write_thru. Tryb z włączonym write_back zwiększa co prawda wydajność, ale i możliwość utraty danych, które nie są zapisywane bezpośrednio na dysku.
- sparse - to opcja używana jedynie w przypadku tworzenia nowych plików dla typu fileio - jeśli ustawiona jest na true (domyślnie) fizyczna zajętość dysku będzie rosła wraz z ilością danych w pliku - wartość false zajmie zadeklarowaną powierzchnię na naszym filesystemie od razu. Efekt użycia może zaskoczyć - zmieścimy 20G plik sparse na dysku o pojemności 1G... oczywiście do czasu wykorzystania 1G w udostępnionym później iSCSI.

Stwórzmy zatem nasz backstore:

/backstores/fileio> create my_file_backstore /opt/my_file_backstore 1G write_back=false
Created fileio my_file_backstore with size 1073741824
/backstores/fileio> ls
o- fileio ........................................................... [Storage Objects: 1]
  o- my_file_backstore .......... [/opt/my_file_backstore (1.0GiB) write-thru deactivated]
/backstores/fileio> cd ../..
/> ls
o- / ............................................................................... [...]
  o- backstores .................................................................... [...]
  | o- block ........................................................ [Storage Objects: 0]
  | o- fileio ....................................................... [Storage Objects: 1]
  | | o- my_file_backstore ...... [/opt/my_file_backstore (1.0GiB) write-thru deactivated]
  | o- pscsi ........................................................ [Storage Objects: 0]
  | o- ramdisk ...................................................... [Storage Objects: 0]
  o- iscsi .................................................................. [Targets: 1]
  | o- iqn.2003-01.org.linux-iscsi.server.x8664:sn.ca677b0f8d08 ................ [TPGs: 1]
  |   o- tpg1 ..................................................... [no-gen-acls, no-auth]
  |     o- acls ................................................................ [ACLs: 0]
  |     o- luns ................................................................ [LUNs: 0]
  |     o- portals .......................................................... [Portals: 1]
  |       o- 0.0.0.0:3260 ........................................................... [OK]
  o- loopback ............................................................... [Targets: 0]
/>

Target

Target iSCSI możemy chyba z grubsza opisać jako nazwany obiekt, który dla zdefiniowanych odbiorców zewnętrznych (ip, acl) będzie udostępniał zasoby wewnętrzne serwera (backstore).
 
Tworzenie targetu iSCSI możemy zrealizować w 2 wariantach - z domyślną lub zdefiniowaną nazwą.
Pierwsza opcja polega po prostu na wydaniu wewnątrz "katalogu" iscsi polecenia:

/iscsi> create
Created target iqn.2003-01.org.linux-iscsi.server.x8664:sn.ca677b0f8d08.
Created TPG 1.
Global pref auto_add_default_portal=true
Created default portal listening on all IPs (0.0.0.0), port 3260.

/iscsi> ls
o- iscsi .......................................................... [Targets: 1]
  o- iqn.2003-01.org.linux-iscsi.server.x8664:sn.ca677b0f8d08 ........ [TPGs: 1]
    o- tpg1 ............................................. [no-gen-acls, no-auth]
      o- acls ........................................................ [ACLs: 0]
      o- luns ........................................................ [LUNs: 0]
      o- portals .................................................. [Portals: 1]
        o- 0.0.0.0:3260 ................................................... [OK]
/iscsi>

Stworzenie targetu ze zdefiniowaną nazwą wygląda identycznie, z tym, że po poleceniu create podajemy ową nazwę, np: create iqn.2016-03.server:moj.storage.
Definiowana przez nas nazwa musi być kwalifikowana jako IQN, NAA lub EUI.

TPG oraz iSCSI Portal

TPG, czyli Target Portal Groups pozwalają iSCSI korzystać w wielu konfiguracji w ramach jednego targetu. Jedna grupa TPG jest tworzona domyślnie przy kreowaniu targetu, i w większości przypadków będzie wystarczająca.

W ramach grupy TPG definiowane są tzw. portale, które definiują adres ip oraz port poprzez które target może zostać użyty. Domyślnie w ramach każdego TPG tworzony jest portal domyślny, udostępniający target poprzez wszystkie skonfigurowane adresy IP z użyciem portu 3260.
W przypadku, kiedy chcemy ograniczyć dostępność naszego targetu do jednego adresu skonfigurowanego na maszynie możemy utworzyć taki portal poprzez polecenie create 192.168.1.1 6290 wydane w lokalizacji iscsi/<idn_target>/tpg1/portals/.

LUN-y

Kiedy już wiemy gdzie będziemy udostępniać, przyszła kolej na skonfigurowanie co będziemy udostępniać, poprzez dowiązanie wcześniej  ustawianych backstore'ów do naszego targetu.
Analogicznie do poprzednich kroków, konfiguracji dokonywać będziemy w "katalogu" z nazwą odpowiadającą zagadnieniu, a więc luns w lokalizacji odpowiadającej naszemu targetowi, czyli iscsi/<idn_target>/tpg1/.

Dowiązanienie backstore'a do targetu polega na wskazaniu go jako parametru polecenia create:

/iscsi/iqn.20...d08/tpg1/luns> create /backstores/fileio/my_file_backstore
Created LUN 0.

Szybkie sprawdzenie, czy lun się dowiązał:

/iscsi/iqn.20...d08/tpg1/luns> ls /
o- / ........................................................................................................ [...]
  o- backstores ............................................................................................. [...]
  | o- block ................................................................................. [Storage Objects: 0]
  | o- fileio ................................................................................ [Storage Objects: 1]
  | | o- my_file_backstore ................................. [/opt/my_file_backstore (1.0GiB) write-thru activated]
  | o- pscsi ................................................................................. [Storage Objects: 0]
  | o- ramdisk ............................................................................... [Storage Objects: 0]
  o- iscsi ........................................................................................... [Targets: 1]
  | o- iqn.2003-01.org.linux-iscsi.server.x8664:sn.ca677b0f8d08 ......................................... [TPGs: 1]
  |   o- tpg1 .............................................................................. [no-gen-acls, no-auth]
  |     o- acls ......................................................................................... [ACLs: 0]
  |     o- luns ......................................................................................... [LUNs: 1]
  |     | o- lun0 ............................................. [fileio/my_file_backstore (/opt/my_file_backstore)]
  |     o- portals ................................................................................... [Portals: 1]
  |       o- 0.0.0.0:3260 .................................................................................... [OK]
  o- loopback ........................................................................................ [Targets: 0]

ACLe

Mamy co udostępnić, mamy adres pod którym będzie się to działo, powinniśmy teraz zadbać o to komu będziemy udostępniać.
Każdy klient, łączący się do naszego zasobu iSCSI posiadać powinien unikalną nazwę inicjatora. Nazwa ta może być znalezniona w pliku /etc/iscsi/initiatorname.iscsi na maszynie klienta, a jej znajomość umożliwi nam konfigurację Access Control List dla danego zasobu.

[root@iscsi-client ~]# cat /etc/iscsi/initiatorname.iscsi
InitiatorName=iqn.1994-05.com.redhat:ccfb1428b2a

Znając tą nazwę, możemy utworzyć odpowiedni wpis na naszej liście kontroli dostępu.

/iscsi/iqn.20...d08/tpg1/luns> cd ../acls
/iscsi/iqn.20...d08/tpg1/acls> create iqn.1994-05.com.redhat:ccfb1428b2a
Created Node ACL for iqn.1994-05.com.redhat:ccfb1428b2a
Created mapped LUN 0.

/iscsi/iqn.20...d08/tpg1/acls> ls
o- acls ................................................................................................. [ACLs: 1]
  o- iqn.1994-05.com.redhat:ccfb1428b2a .......................................................... [Mapped LUNs: 1]
    o- mapped_lun0 ........................................................... [lun0 fileio/my_file_backstore (rw)]

Usuwanie definicji

Nie było sensu pisać tego przy każdej pozycji, ponieważ usuwanie elementów konfiguracji w konsoli targetcli zawsze wygląda tak samo.
1. Nawigujemy do lokalizacji odpowiadającej kategorii elementu, który chcemy usunąć
2. Wydajemy polecenie delete nazwa_elementu_do_usuniecia (p.s. działa tabulator)

[RH7] Odzyskiwanie dostępu do konta root - resetowanie hasła

Red Hat 7.x / CentOS 7.x
W wersji 7 systemu z rodziny RedHat odzyskanie hasła root nieco się skomplikowało. Nie wystarczy już dodać "jedynki" jako parametru startowego systemu, a sprowadzenie systemu do trybu rescue czy emergency wymaga od nas nadal znajomości hasła root.
Pocedura odzyskania/resetowania hasła nie jest może bardzo skomplikowana, ale wymaga od nas kilku kroków.
Wymagany jest oczywiście fizyczny dostęp do konsoli serwera, ponieważ nie da się jej zrealizować poprzez ssh.

Pierwszym krokiem będzie restart maszyny i edycja parametrów startowych systemu. Podczas inicjalizacji bootloadera musimy wybrać z menu gruba pozycję odpowiadającą naszemu systemowi (będzie to najczęściej pierwsza pozycja menu) a następnie przejść do edycji parametrów poprzez wciśnięcie e.

Naszym oczom ukaże się kilka linijek konfiguracji - odnajdujemy pierwszą rozpoczynającą się od linux16. Na jej końcu dodajemy wpis, który spowoduje przekazanie nam prompta systemu przed przekazaniem sterowania z initramfs do kernela.

[...]rhgb quiet LANG=en_US.UTF-8 rd.break

Bootujemy system z nowymi parametrami poprzez kombinację ctrl+x.

W tym trybie nasz root filesystem jest podmountowany w trybie ro jako /sysroot, uniemożliwiając zmianę hasła w systemie. Konieczne będzie jego przemountowanie do trybu rw i chrootowanie:

switch_root:/# mount -o remount,rw /sysroot
switch_root:/# chroot /sysroot
sh-4.2#

Od tej chwili operować będziemy na plikach naszego systemu, i wszystkie zmiany będą permanentne.
Istnieje jeszcze jeden element, który uniemożliwiać nam będzie zmianę hasła - reguły selinuxowe. Wyłączamy zatem selinuxa:

sh-4.2# setenforce 0
setenforce: SELinux is disabled

Teraz możemy zmienić hasło root, poprzez zwyczajowe:

sh-4.2# passwd

Pozostaje nam do wykonania jeszcze jedna, bardzo ważna operacja. Modyfikacja pliku /etc/shadow dokonywana poprzez wywołanie komendy passwd usuwa nam konteksty selinuxowe i skutecznie pozbawi nas możliwości korzystania z systemu, dlatego należy pamiętać o ich odtworzeniu:

sh-4.2# touch /.autorelabel

Utworzenie pliku wymusi na systemie odtworzenie kontekstów przed jego wyłączniem.

Kolejnym krokiem będzie przemountowanie filesystemu w tryb ro i restart maszyny:

sh-4.2# mount -o remount,ro /
sh-4.2# exit
exit
switch_root:/# exit
logout

I voila. Odzyskaliśmy kontrolę nad naszym root-em.

[RH7] Konfiguracja i edycja reguł lokalnego firewalla [PT.2 - port rules, masquerading, direct rules, lockdown]

Red Hat 7.x / CentOS 7.x
W pierwszej części było kilka słów teoretycznych i podstawowa konfiguracja firewalld - zony i serwisy. Czas brnąć dalej.

Konfiguracja otwarcia portów

Poza definiowalnymi serwisami pozostaje nam oczywiście możliwość dodawania customowych reguł filtrowania opartych o zwyczajowe numery portów, z jakich chcemy ruch do naszego systemu wpuszczać.
Jak można się domyślać po dwóch poprzednich zagadnieniach, definicja będzie opierać się na podaniu strefy, w której regułe chcemy umieścić, oraz numeru portu - wrażenie jest jak najbardziej prawidłowe, ponieważ wszystkie definicje zostały w firewalld zunifikowane.
Rozpocznijmy od listowania definicji portów, jakie mamy już zdefiniowane w naszych strefach:

[root@server ~]# firewall-cmd --list-ports

Listowanie takie dotyczy, tak jak wcześniej domyślnej strefy (listowanie innej wymaga użycia przełącznika --zone=nazwa_strefy).
Nie uzyskaliśmy żadnej odpowiedzi, co oznacza, że strefa nie zawiera definicji dla portów - ale samo przejście komendy bez echa może być nieco dezorientujące.
I tutaj muszę przypomnieć wcześniejsze Post Scriptum, mianowicie przełącznik --list-ports, tak jak wspominany tam --list-services pokazuje nam jedno, konkretne zagadnienie konfiguracyjne, bez wglądu w inne parametry. Moim zdaniem zapytanie idealnie nadaje się do skryptowania opartego o te reguły, ale w codziennej administracji bardziej przejrzyste jest --list-all.
Krótki rzut oka na pełną konfigurację - dla sprawdzenia reguł portów usunąłem wszystkie serwisy z poprzedniej konfiguracji:

[root@server ~]# firewall-cmd --list-all
dmz (default, active)
  interfaces: enp0s3 enp0s9
  sources:
  services:
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

Tutaj widać wyraźnie - nie ma żadnych definicji portów. Brak serwisu ssh oznacza, że nie mamy otwartego portu 22, i próba połączenia potwierdza to. Zdefiniujmy zatem, dla porównania, otwarcie ruchu do ssh nie używając serwisów:

[root@server ~]# firewall-cmd --add-port=22/tcp
success
[root@server ~]# firewall-cmd --list-all
dmz (default, active)
  interfaces: enp0s3 enp0s8
  sources:
  services:
  ports: 22/tcp
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

Tak jak w poprzednich elementach konfiguracji, usunięcie portu zrealizujemy poprzez przełącznik --remove-port.
Przydatna również może okazać się możliwość otwarcia zakresu portów, np:

[root@server ~]# firewall-cmd --add-port=20-23/tcp
success
[root@server ~]# firewall-cmd --list-all
dmz (default, active)
  interfaces: enp0s3 enp0s8 enp0s9
  sources:
  services:
  ports: 20-23/tcp
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

Tym sposobem możemy otwierać ruch na dowolnym porcie i protokole, zależnie od potrzeby.

Masquerade - port forwarding

Konfiguracja forwardowania portów ma analogiczną składnię, przy czym ilość parametrów, z oczywistych względów jest większa.
Aby forwardowanie miało szansę zadziałać, w pierwszej kolejności wymagane jest uruchomienie usługi masqueradingu:

[root@server ~]# firewall-cmd --add-masquerade
success

Obecny status jest doskonale widoczny w widoku generowanym przez przełącznik --list-all.
Samo przekierowanie portu możemy realizować zarówno na określony port jak i adres ip, lub uwzględniając oba elementy jednocześnie. Składnia przełącznika nie wygląda może zbyt przyjaźnie, ale jest bardzo logicznie uporządkowana - zawiera sekcje port (port na który przychodzi ruch), proto (protokół komunikacji), topotr (docelowy port przekierowania) oraz toaddr (docelowy adres przekierowania):

[root@server ~]# firewall-cmd --add-forward-port=port=50:proto=tcp:toport=22
success
[root@server ~]# firewall-cmd --list-all
dmz (default, active)
  interfaces: enp0s3 enp0s8
  sources:
  services:
  ports:
  masquerade: yes
  forward-ports: port=50:proto=tcp:toport=22:toaddr=
  icmp-blocks:
  rich rules:

W ramach testu działania zamknąłem na maszynie wszystkie porty, otwierając jedynie port 50/TCP - zdefiniowane przekierowanie umożliwiło zalogowanie się ssh na maszynę poprzez port 50 - a odrzuciło połączenie po standardowym 22, więc rezultat jest zgodny z oczekiwaniami.

Dodawanie reguł direct i chain-ów

firewall-cmd pozostawia nam możliwość operowania ra regułach analogicznych do tych, które pamiętamy z iptables. Reguły te są wpisywane bezpośrednio do tablic iptables, a korzystanie z nich wymaga  zrozumienia konstrukcji łańcuchów iptables.
Składnia tego typu definicji wygląda następująco:

[root@server ~]# firewall-cmd [--permanent] --direct --add-rule {ipv4|ipv6|eb} table chain priority args

Przełącznik --permanent odpowiada oczywiście za trwałe zapisanie reguły w systemie. --add-rule jak sugeruje nazwa odpowiada za dodanie reguły. Reguła taka musi mieć określony protokół (ipv4, ipv6 lub ethernet bridges). Kolejnymi elementami wymaganymi są tablica iptables do której reguła ma być dodana oraz odpowiedni chain.
Priorytet wykorzystywany jest do zarządzania kolejnością reguł - 0 oznacza dodanie reguły na początku tablicy, a kolejne numery powodują dodanie jej odpowiednio dalej. Dodanie kilku reguł z jednakowym priorytetem może spowodować przypadkową kolejność ich stosowania (w ramach priorytetu), dlatego, jeśli zależy nam na wymuszeniu kolejności warto zastosować różne priorytety.
Argumentami komendy będą przełączniki stosowane w iptables.
Przykładowe polecenie może wyglądać następująco:

[root@server ~]# firewall-cmd --direct --add-rule ipv4 filter IN_dmz_allow 0 -m tcp -p tcp --dport 2345 -j ACCEPT

Zdefiniowane wcześniej reguły możemy przeglądać poleceniem:

[root@server firewalld]# firewall-cmd --direct --get-all-rules
ipv4 filter IN_dmz_allow 0 -m tcp -p tcp --dport 2345 -j ACCEPT

lub dla konkretnej tablicy:

[root@server firewalld]# firewall-cmd --direct --get-rules ipv4 filter IN_dmz_allow
0 -m tcp -p tcp --dport 2345 -j ACCEPT

Przełącznik --direct umozliwia nam również definiowanie własnych chain-ów w odpowiednich tablicach.

[root@server firewalld]# firewall-cmd --direct --add-chain ipv4 filter test_chain
success

A zdefiniowane przez nas chain-y możemy podejrzeć przez:

[root@server firewalld]# firewall-cmd --direct --get-all-chains
ipv4 filter test_chain

Usuwanie, zarówno reguł jak i chain-ów realizujemy analogicznie, używając odpowiednio przełączników --remove-rule i --remove-chain.

Cały czas pozostaje nam możliwość weryfikowania zdefiniowanych reguł poprzez klasyczne iptables -nL, ale ilość tablic powoduje, że nie są one zbyt czytelne.

Lockdown - blokowanie możliwości zmian reguł przez aplikacje

Aplikacje oraz serwisy, jeśli są uruchomione z uprawnieniami root, mają możliwość zmiany konfiguracji firewalla (np. libvirt). Opcja lockdown daje nam możliwość kontrolowania tych możliwości.
Lockdown konfiguracji wykonujemy poprzez ustawienie wartości yes w pliku /etc/firewalld/firewalld.conf

[root@server firewalld]# vi /etc/firewalld/firewalld.conf
[...]
Lockdown=yes
[...]
[root@server firewalld]# firewall-cmd --reload
success
[root@server firewalld]# firewall-cmd --query-lockdown
yes

Po przeładowaniu, zmian w konfiguracji będą mogły dokonywać jedynie aplikacje zdefiniowane na białej liście, w pliku /etc/firewalld/lockdown-whitelist.xml
Plik posiada predefiniowane reguły, które umożliwiają edycję reguł np. root-owi - do przetestowania działania usunąłem je z pliku - efekt jest zgodny ze spodziewanym:

[root@server firewalld]# firewall-cmd --add-service ftp
Error: ACCESS_DENIED: lockdown is enabled

Lockdown, a w zasadzie reguły whitelist mogą być zdefiniowane w oparciu o kilka elementów:
- context - konteksty selinuxowe danej aplikacji (ps -e --context)
- uid - id użytkownika
- user - nazwa użytkownika
- commands - polecenia
i w takiej też kolejności są przez lockdown sprawdzane.

Zarządzanie odbywa się przez przełączniki:

[root@server firewalld]# firewall-cmd --add-lockdown-whitelist-command='/usr/bin/python -Es /usr/bin/firewall-cmd'
success
[root@server firewalld]# firewall-cmd --add-lockdown-whitelist-context='system_u:system_r:kernel_t:s0'
success
[root@server firewalld]# firewall-cmd --add-lockdown-whitelist-uid=500
success
[root@server firewalld]# firewall-cmd --add-lockdown-whitelist-user=test
success

Usuwać reguły będziemy identycznie, zastępując --add-lockdown-... przez --remove-lockdown...

Weryfikacja odbywać się może poprzez:

[root@server firewalld]# firewall-cmd --list-lockdown-whitelist-user
test
[root@server firewalld]# firewall-cmd --list-lockdown-whitelist-uid
500
[root@server firewalld]# firewall-cmd --list-lockdown-whitelist-context
system_u:system_r:kernel_t:s0
[root@server firewalld]# firewall-cmd --list-lockdown-whitelist-command
/usr/bin/python -Es /usr/bin/firewall-cmd

Konfiguracja i edycja reguł lokalnego firewalla - firewalld i firewall-cmd [PT.1 - zones & services]  

[RH7] Konfiguracja i edycja reguł lokalnego firewalla - firewalld i firewall-cmd [PT.1 - zones & services]

Red Hat 7.x / CentOS 7.x
Wersja 7 systemu spod znaku czerwonego kapelusza w nowy sposób spogląda na obsługę komunikacji sieciowej naszego systemu. Usługą odpowiedzialną za ten kawałek został firewalld.
Moje pierwsze wrażenia? Konfiguracja i definiowanie reguł jest chyba bardziej czytelne, z pewnością bardziej intuicyjne, choć od osoby przyzwyczajonej do iptables wymaga poświęcenia kilku chwil na zaznajomienie się z nowym sposobem operowania w środowisku.

firewalld operuje na zdefiniowanych strefach sieciowych (zonach), które są rodzajem kontenerów reguł sieciowych. Strefa taka określa zasady jakie będą stosowane dla np. interfejsu sieciowego sklasyfikowanego w takiej strefie. Reguły aplikacji korzystających z sieci oparto na innych pseudo kontenerach - serwisach - które w formie plików xml definiują porty, czy też zakresy portów jakie mają pozostać otwarte na naszym serwerze. Ale po kolei...

Obsługa wszystkiego co związane jest z firewalld odbywa się przez jedno z dwóch narzędzi - w trybie graficznym może to być firewall-config, w trybie tekstowym będzie to firewall-cmd. W drugim przypadku, zwyczajowo wszystko mamy ładnie opisane w manualu oraz w helpie dla komendy - przy czym, jak zaobserwowałem, w helpie jest kilka przełączników, których w manualu nie ma...

Wspomniałem o zonach, i od nich wypada rozpocząć szerszy opis przygody z firewalld. Zony, to predefiniowane zestawy reguł, które są stosowane w naszym systemie bez konieczności definiowania czegokolwiek. (możemy zdefiniować własną zonę z naszym zestawem reguł, ale to chyba zabawa dla admina, który cokolwiek już o firewalld wie, więc pominę temat, przynajmniej na razie).
Predefiniowane zony możemy podejrzeć sobie wydając polecenie:

[root@server ~]# firewall-cmd --get-zones
block dmz drop external home internal public trusted work

Poszczególne zony traktują naszą sieć mniej więcej tak:

- block - wypuszczamy wszystkie pakiety, a wpuszczamy jedynie te, które są odpowiedzią na nasze zapytanie
- dmz - jak wskazuje nazwa - wpuszczamy tylko wybrane połączenie w sieci wewnętrznej - domyślnie ssh
- drop - identycznie jak block, z tym, że połączenia są faktycznie dropowane i nie wysyłamy na nie odpowiedzi o zablokowaniu połączenia
- external - nie ufamy nikomu na zewnątrz, więc wpuszczamy tylko zdefiniowany ruch (domyślnie jedynie ssh)
- home - raczej ufamy innym komputerom, więc domyślnie wpuszczamy ssh, sambę, mdns, ipp-client i dhcpv6
- internal - w zasadzie tak samo jak home - sieć wewnątrzna
- public - nie ufamy nikomu... no chyba, że przez ssh albo dhcpv6
- trusted - ufamy wszystkim i wpuszczamy wszystko - taki domyślny ACCEPT na początku tablicy iptables
- work - w zasadzie wierzymy w dobre zamiary innych userów sieci, ale na wszelki wypadek wpuszczamy tylko ssh, ipp i dhcpv6

To tak z grubsza - moim zdaniem za dużo tych definicji, ale jak to mówią od przybytku głowa nie boli, a może są jakieś głąbsze różnice, do których póki co nie dotarłem.
W tym miejscu wspomnę o pierwszej ścieżce systemowej, która jest przydatna w pracy z firewalld, a mianowicie /usr/lib/firewalld. W podkatalogu zones znajdują się pliki xml zawierające definicję i krótki opis każdej ze stref.
W pierwszej kolejności zweryfikujmy, czy nasz deamon działa:

[root@server ~]# firewall-cmd --state
running

Działa. Więc zajmijmy się konfiguracją.

Działania z zonami

Domyślnie nasze interfejsy przypisane są do strefy public - rezultat jest mniej więcej taki sam, jak w przypadku domyślnej konfiguracji iptables w wersjach wcześniejszych systemu.
Przypisanie to możemy sprawdzić poprzez:

[root@server ~]# firewall-cmd --get-active-zones
public
  interfaces: enp0s3

Polecenie pokazuje nam wszystkie aktywne w systemie strefy, oraz przypisane do nich interfejsy.

Wszystkie interfejsy naszej maszyny są dodawane do strefy domyślnej, której definicję sprawdzimy następująco:

[root@server ~]# firewall-cmd --get-default-zone
public

Na dowód takiego zachowania, dodałem w swojej maszynie dwa kolejne interfejsy sieciowe, a efektem jest:

[root@server ~]# firewall-cmd --get-active-zones
public
  interfaces: enp0s3 enp0s8 enp0s9

Strefę domyślną możemy zmienić w bardzo prosty sposób:

[root@server ~]# firewall-cmd --set-default-zone=dmz
success
[root@server ~]# firewall-cmd --get-default-zone
dmz

Każdy z naszych interfejsów możemy przypisać do innej strefy, i definiować dla niego specyficzne reguły, które nie będą działały dla interfejsu w innej strefie.
Przypisanie takie realizujemy poprzez usunięcie go z obecnej strefy i przypisanie do nowej:

[root@server ~]# firewall-cmd --zone=public --remove-interface=enp0s8
success
[root@server ~]# firewall-cmd --zone=dmz --add-interface=enp0s8
success
[root@server ~]# firewall-cmd --get-active-zones
dmz
  interfaces: enp0s8
public
  interfaces: enp0s3

Nie udało mi się jednak w ten sposób uzyskać permanentnego przypisania interfejsów do różnych zone, a restart serwera przywraca wszystko do strefy domyślnej.... może to taki "feature" [firewall-cmd v.0.3.9].
Sam Red Hat podaje jednak alternatywną - działającą - metodę stałego przypisania do zony. W pliku konfiguracyjnym interfejsu /etc/sysconfig/network-scripts/ifcfg-enp0s* należy umieścić parametr ZONE= wraz z nazwą zony.

Serwisy

Domyślne ustawienia stref rzadko kiedy będą zadowalające dla administratora, ze względu na ograniczoną ilość usług, które są sieciowo "przepuszczane". Kolejnym poziomem abstrakcji wprowadzonym przez firewalld, a ułatwiającym korzystanie z niego są serwisy.
Serwis jest definicją portu oraz protokołu sieciowego, który ma zostać przepuszczony przez firewall np. na potrzeby aplikacji. Jest to bardzo ciekawe, i przydatne podejście. Tym bardziej, że definiowanie własnych serwisów jest banalnie proste. Ale po kolei.

Weryfikacja w jaki sposób obsługujemy ruch sieciowy sprowadza się do wylistowania zawartości wszystkich aktywnych zon. Biorąc pod uwagę problem z permanentnym skonfigurowaniem kilku na raz, aktywną pozostaje tylko domyślna, dlatego wystarczy polecenie:

[root@server ~]# firewall-cmd --list-all
dmz (default, active)
  interfaces: enp0s3
  sources:
  services: ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

Jeśli aktywnych byłoby kilka - do powyższej komendy należy dodać przełącznik --zone=nazwa_zone
Jak wynika z powyższego, w zonie dmz mamy skonfigurowany serwis ssh, co oznacza, że ruch na porcie 22 jest otwarty.  Szybka weryfikacja, i faktycznie serwer odpowiada po ssh.

Dodawanie serwisów w danej zonie odbywa się poprzez polecenie:

[root@server ~]# firewall-cmd --zone=dmz --add-service=ftp
success

Analogicznie sytuacja wygląda z usuwaniem serwisu z zony:

[root@server ~]# firewall-cmd --zone=dmz --remove-service=ssh
success

Po tych dwóch operacjach nasze reguły sieciowe przedstawiać się będą następująco:

[root@server ~]# firewall-cmd --list-all
dmz (default, active)
  interfaces: enp0s3
  sources:
  services: ftp
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

I kolejna szybka weryfikacja.... nie zalogujemy się już po ssh (nie sprawdzam portu 21, ponieważ nie mam na tej maszynie ftp-a).

Listę wszystkich predefiniowanych serwisów znajdziemy w formie plików xml w katalogu /usr/lib/firewalld/services, a konfigurację możemy sprawdzić poprzez podejrzenie ich zawartości. Nazwa pliku jest tożsama z nazwą serwisu.
P.S. Jeśli nie potrzebujemy oglądać całej konfiguracji zony, możemy również podejrzeć skonfigurowane serwisy korzystając z przełącznika --list-services, choć nie daje to pełnego obrazu konfiguracji (ale o tym w następnej części).

W tym miejscu należy wspomnieć o jednej, bardzo ważnej właściwości. Ten sposób konfigurowania serwisów nie jest permanentny, i nie przetrwa restartu maszyny.
Do rozwiązania tego problemu możemy użyć dwóch metod:
- używając przełącznika --permanent podczas dodawania lub kasowania serwisu, który każdą wprowadzoną z nim regułę zapisze na stałe w konfiguracji, lub
- zapisując konfigurację zawartą w pamięci, poprzez użycie polecenia firewall-cmd --runtime-to-permanent po wprowadzeniu wszystkich interesujących nas reguł.

Używanie opcji --permanent implikuje z kolei konieczność przeładowania reguł firewalld, ponieważ wprowadzana tak reguła jest zapisywana w konfiguracji, ale nie ładowana do działająego już serwisu. Po dodaniu reguł permanentnych będziemy musieli użyć polecenia firewall-cmd --reload. Używanie jednej z kombinacji jest dowolne, zależnie od preferencji admina.

Tworzenie własnych definicji serwisów


Przydatność serwisów możemy zauważyć, m.in. w przypadku konieczności zdefiniowania własnych reguł aplikacji, która instalowana jest na wielu serwerach (np. aplikacja monitorująca, serwer aplikacyjny itp.). Aplikacje takie niejednokrotnie wymagają otwarcia kilku portów, i na wszystkich maszynach konfiguracja ta jest identyczna, dlatego wygodnie będzie stworzyć własny plik definicji, i dodawać nasz serwis do stref na poszczególnych maszynach. Operacja polegać będzie na skopiowaniu jednego z instniejących plików definicji do katalogu /etc/firewalld/services/ (w nim są przetrzymywane wszystkie definicje serwisów tworzone przez użytkownika), nazwaniu go zgodnie z nazwą serwisu i jego edycji - całość jest bardzo intuicyjna, więc ten temat zakończę na przykładzie, bez zbędnych komentarzy...
Scenariusz przykładu obejmuje: Skonfigurujemy przykładowy plik reguł dla serwera jboss, którego aplikacje nasłuchują na portach (TCP) 80, 8080, 443, 8180, multicast klastra działa na porcie (UDP) 45688, a klaster komunikuje się po porcie (TCP) 7800.

[root@server ~]# cp /usr/lib/firewalld/services/http.xml /etc/firewalld/services/jboss.xml
[root@server ~]# vi /etc/firewalld/services/jboss.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>WWW (HTTP)</short>
  <description>HTTP is the protocol used to serve Web pages. If you plan to make your Web server publicly available, enable this option. This option is not required for viewing pages locally or developing Web pages.</description>
  <port protocol="tcp" port="80"/>
</service>

Zawartość zmieniamy na założoną konfigurację:

<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>Jboss</short>
  <description>Moja definicja Jboss-a</description>
  <port protocol="tcp" port="80"/>
  <port protocol="tcp" port="8080"/>
  <port protocol="tcp" port="443"/>
  <port protocol="tcp" port="8180"/>
  <port protocol="udp" port="45688"/>
  <port protocol="tcp" port="7800"/>
</service>

Pozostaje nam jeszcze załadować serwis do strefy, i gotowe:

[root@server ~]# firewall-cmd --permanent --add-service=jboss
success
[root@server ~]# firewall-cmd --reload
success
[root@server ~]# firewall-cmd --list-all
dmz (default, active)
  interfaces: enp0s3
  sources:
  services: jboss ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

W następnej części zajmiemy się konfiguracją portów, masqueradingu i definicjami direct, bardziej zbliżonymi w swej formie do reguł iptables.

Konfiguracja i edycja reguł lokalnego firewalla [PT.2 - port rules, masquerading, direct rules, lockdown]

[RH7] Tworzenie klastra wysokiej dostępności

Red Hat 7.x / CentOS 7.x
Na oprogramowanie umożliwiające uruchomienie klastra HA w systemach RHEL/Centos 7.x składają się trzy główne komponenty:

pacemaker - komponent odpowiedzialny za całość aktywności klastrowej, np: monitoring członkostwa w klastrze, zarządzanie serwisami i zasobami klastra czy fensowanie członków klastra. Na pacemaker'a składają się cztery główne funkcjonalności:
- Cluster Information Base (CIB) - zawierający konfigurację i informacje o statusie samego klastra jak i wszystkich jego komponentów, przechowywaną w formie XML
- Policy Engine (PEngine) - określający na podstawie informacji zawartych w CIB najlepszą drogę dostępu i optymalną konfigurację klastra
- Cluster Resource Management Deamon (CRMd) - koordynuje zatrzymanie, uruchamianie i sprawdzanie statusu lokalnych demonów zarządzania klastrem (Local Resource Management Deamon - LRMd)
- Shoot the Other Node in the Head (STONITH) - funkcjonalność odpowiedzialna za fensowanie urządzeń klastra skonfigurowanych w CIB

corosync - to framework używany przez pacemaker'a do obsługi komunikacji pomiędzy nodami klastra. Dostarcza on pacemaker'owi informacji o członkostwie i quorum klastra

pcs (Pacemaker Configuration System) - zawiera dwa narzędzia konfiguracji klastra: 
- komenda pcs dostarcza interfejs do tworzenia, konfiguracji i kontroli wszystkich parametrów klastra
- demon pcsd dba o synchronizację konfiguracji nodów oraz dostarcza interfejs tworzenia i konfiguracji klastra.

Dodatkowym oprogramowaniem wymaganym do działania jest pakiet umożliwiający fensowanie urządzeń środowiska klastra. Pakiet ten występuje w wielu wersjach dedykowanych do różnego rodzaju sprzętu. Liczba dedykowanych agentów waha się w okolicach 30, i należałoby dobrać odpowiedni dla swojego środowiska - lub skorzystać z paczki zbiorczej fence-agents-all. Pełna lista dostępna jest w yum:

[root@clusternode1 ~]# yum search fence-agents

Z zagadnień teoretycznych należałoby wspomnieć o portach, któymi klaster komunikuje się sieciowo, gdyż porty te muszą zostać otwarte na lokalnym oraz zewnętrznych firewall'ach. Będą to:
- 5404(UDP), 5405(UDP) - corosync
- 2224(TCP) - pcsd
- 3121(TCP) - pacemaker
- 21064(TCP) - dlm - używany przez GFS2

Selinux w trybie enforcing jest w pełni wspierany, jeśli korzystamy z polityki targeted.

Tyle teorii, zabieramy się więc do tworzenia klastra.

Jak pisałem wcześniej do działania klastra będziemy potrzebować kilku paczek. Część z nich jest zależna od innych i zostanie doinstalowana automatycznie przez yum, dlatego wystarczy, że zainstalujemy tylko dwie z nich:

[root@clusternode1 ~]# yum install pcs fence-agents-all

Na środowiskach produkcyjnych nie jest konieczna instalacja wszystkich agentów i wystarczy dobrać odpowiedni do naszych potrzeb...

Kolejnym etapem będzie umożliwienie komunikacji, poprzez otwarcie lokalnego firewall'a. W wersji 7 nie ma konieczności wprowadzania poszczególnych reguł ruchu, a całość konfiguracji możemy udrożnić dodając odpowiedni serwis do demona firewalld:

[root@clusternode1 ~]# firewall-cmd --permanent --add-service=high-availability
success
[root@clusternode1 ~]# firewall-cmd --reload
success

Oczywiście włączamy demona pcs w systemctl, aby startował wraz z systemem, oraz uruchamiamy go, wykonując na wszystkich node'ach polecenia:

[root@clusternode1 ~]# systemctl enable pcsd
Created symlink from /etc/systemd/system/multi-user.target.wants/pcsd.service to /usr/lib/systemd/system/pcsd.service.
[root@clusternode1 ~]# systemctl start pcsd

Szybka weryfikacja, czy wszystko przebiegło po naszej myśli:

[root@clusternode1 ~]# systemctl is-enabled pcsd
enabled
[root@clusternode1 ~]# systemctl status pcsd
● pcsd.service - PCS GUI and remote configuration interface
   Loaded: loaded (/usr/lib/systemd/system/pcsd.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2016-03-03 20:47:01 CET; 34s ago
 Main PID: 10424 (pcsd)
   CGroup: /system.slice/pcsd.service
           ├─10424 /bin/sh /usr/lib/pcsd/pcsd start
           ├─10428 /bin/bash -c ulimit -S -c 0 >/dev/null 2>&1 ; /usr/bin/ruby -I/usr/lib/pcsd /usr/lib/pcsd/ssl.rb
           └─10429 /usr/bin/ruby -I/usr/lib/pcsd /usr/lib/pcsd/ssl.rb

Mar 03 20:47:00 clusternode1 systemd[1]: Starting PCS GUI and remote configuration interface...
Mar 03 20:47:01 clusternode1 systemd[1]: Started PCS GUI and remote configuration interface.

Operacje powtarzamy na wszystkich node'ach przyszłego klastra.

Po instalacji w systemie zaistniał nowy użytkownik systemowy, który używany będzie do komunikacji w klastrze - Red Hat zaleca konfigurację jednakowych haseł dla tego użytkownika na wszystkich node'ach, choć z punktu widzenia bezpieczeństwa zalecenie to może budzić pewne wątpliwości... Skonfigurujmy więc hasło:

[root@clusternode1 ~]#  echo naszklaster | passwd --stdin hacluster
Changing password for user hacluster.
passwd: all authentication tokens updated successfully.

Jeśli nasze maszyny nie posiadają wpisów w dns, najwygodniej będzie je dodać do /etc/hosts

Jako, że nody klastra wymagają autoryzacji użytkownika hacluster, wykonujemy takową na jednym z node'ów przyszłego klastra:

[root@clusternode1 ~]# pcs cluster auth clusternode1 clusternode2 clusternode3
Username: hacluster
Password:
clusternode2: Authorized
clusternode3: Authorized
clusternode1: Authorized

Do celów automatyzacyjnych/skryptowych przydatne mogą okazać się przełączniki do pcs -u hacluster -p naszklaster.

Zasadniczo podstawowa konfiguracja jest już wykonana, więc możemy spróbować uruchomić nasz klaster:

[root@clusternode1 ~]# pcs cluster setup --start --name PierwszyKlaster clusternode1 clusternode2 clusternode3
Shutting down pacemaker/corosync services...
Redirecting to /bin/systemctl stop  pacemaker.service
Redirecting to /bin/systemctl stop  corosync.service
Killing any remaining services...
Removing all cluster configuration files...
clusternode1: Succeeded
clusternode2: Succeeded
clusternode3: Succeeded
Starting cluster on nodes: clusternode1, clusternode2, clusternode3...
clusternode3: Starting Cluster...
clusternode1: Starting Cluster...
clusternode2: Starting Cluster...
Synchronizing pcsd certificates on nodes clusternode1, clusternode2, clusternode3...
clusternode2: Success
clusternode3: Success
clusternode1: Success

Restaring pcsd on the nodes in order to reload the certificates...
clusternode2: Success
clusternode3: Success
clusternode1: Success

Możemy zweryfikować status naszego klastra:

[root@clusternode1 ~]# pcs cluster status
Cluster Status:
 Last updated: Thu Mar  3 21:24:51 2016         Last change: Thu Mar  3 21:15:09 2016 by hacluster via crmd on clusternode1
 Stack: corosync
 Current DC: clusternode1 (version 1.1.13-10.el7_2.2-44eb2dd) - partition with quorum
 3 nodes and 0 resources configured
 Online: [ clusternode1 clusternode2 clusternode3 ]

PCSD Status:
  clusternode1: Online
  clusternode2: Online
  clusternode3: Online

Musimy pamiętać tu o jednym fakcie. Domyślna konfiguracja powoduje, że w przypadku restartu node'a nie podłącza on się automatycznie do klastra. W takiej sytuacji na działającym node uzyskamy komunikat:

[root@clusternode1 ~]# pcs cluster status
Cluster Status:
 Last updated: Thu Mar  3 21:28:02 2016         Last change: Thu Mar  3 21:15:09 2016 by hacluster via crmd on clusternode1
 Stack: corosync
 Current DC: clusternode1 (version 1.1.13-10.el7_2.2-44eb2dd) - partition with quorum
 3 nodes and 0 resources configured
 Online: [ clusternode1 clusternode2 ]
 OFFLINE: [ clusternode3 ]

PCSD Status:
  clusternode1: Online
  clusternode2: Online
  clusternode3: Online

A na maszynie, która była zrestartowana:

[root@clusternode3 ~]# pcs cluster status
Error: cluster is not currently running on this node

Sytuacja ta jest prosta do naprawienia, wystarczy włączyć automatyczne dołączanie node'a do klastra, poprzez:

[root@clusternode1 ~]# pcs cluster enable --all
clusternode1: Cluster Enabled
clusternode2: Cluster Enabled
clusternode3: Cluster Enabled

Teraz, po restarcie node'a wszystkie znajdować się będą w statusie online.

[TIP] Uruchomienie node'a, który nie dołącza się automatycznie do klastra po restarcie wykonujemy poprzez egzekucję na nim komendy pcs cluster start

Zabezpieczanie filesystemu przy użyciu opcji mountowania - mount i tune2fs

Red Hat 6.x / CentOS 6.x
Dzięki specjalnym opcjom mountowania możemy ograniczyć lub umożliwić pewne zachowania na systemie plików w naszym OS-ie. Część tych opcji, poza standardowym fstab-em może być zdefiniowana w metadanych naszego filesystemu.

Opcje fstab / mount

Podczas mountowania filesystemu możemy wymusić na nim pewne zachowania. Zachowania te definiujemy przez podanie odpowiednich opcji mountowania (zarówno w komendzie mount jak i pliku fstab).

Przytoczmy zatem przykładowy wpis fstaba

/dev/mapper/vg_centos-lv_root /                       ext4    defaults        1 1

 * W pierwszej kolumnie umieszczony jest wskaźnik urządzenia, które chcemy zamountować - w naszym przykładzie to dysk /dev/mapper/vg_centos-lv_root.
* Druga kolumna to punkt mountowania w systemie - tutaj będzie to katalog /.
* Trzecia kolumna określa nam typ filesystemu jaki znajduje się na naszym urządzeniu.
* Czwarta kolumna wpisu to pozycja rozdzielonych przecinkami opcji z jakimi dany filesystem zostanie zamountowany.
* Piąta kolumna informuje system o konieczności zrzucania filesystemu przy okazji wykonywania dump - domyślnie filesystemy nie są zrzucane (backupowane), więc opcja przybiera wartość 0 - w przeciwnym wypadku powinno to być 1.
* Ostatnia kolumna określa kolejność sprawdzania dysku przez fsck podczas restartu maszyny. Root filesystem powinien być opatrzony wartością 1 i być sprawdzany jako pierwszy. Pozostałe punkty mountowania powinny być sprawdzane później (opcja 2), lub jeśli sprawdzanie nie jest konieczne wartość powinna wynieść 0.

Najczęściej spotykaną opcją w czwartej kolumnie pliku fstab jest chyba opcja default. W rzeczywistości opcja jest aliasem dla szeregu innych: rw, suid, dev, exec, auto, nouser, async, realtime. Oczywiście zestaw opcji domyślnych może się zmieniać w zależności od wersji kernela oraz typu filesystemu.

Krótkie wyjaśnienie poszczególnych:
* rw - filesystem zamountowany do odczytu i zapisu
* suid - obsługuje bity set-user-id i set-group-id
* dev - bloki specjalne znajdujące się na takim filesystemie mogą być traktowane jako urządzenia
* exec - zezwala na wykonywanie binariów z danego filesystemu
* auto - powoduje, że filesystem będzie mountowany przy wydaniu komendy mount -a
* nouser - ogranicza możliwość mountowania filesystemu jedynie dla root-a
* async - wszystkie operacje I/O będą prowadzone asynchronicznie
* realtime - zapewnia, że daty dostępu i modyfikacji plików będą odnotowywane w systemie.

Dla większości punktów mountowania opcja default będzie wystarczająca, ale możemy podnieść bezpieczeństwo naszego systemu stosując pewne ograniczenia dla specyficznych filesystemów.
Jak zwykle po pełną listę opcji odeślę do manuala (man mount), ale wymienię kilka, które zdarza mi się wykorzystywać najczęściej:

* noexec - spowoduje, że binaria czy skrypty znajdujące się na takiej partycji nie będą mogły być na niej uruchamiane
* _netdev - informuje system, że dany zasób jest urządzeniem sieciowym i spowoduje, że mountowanie odbędzie się dopiero po uzyskaniu przez system komunikacji sieciowej (podniesienia usługi sieci)
* nosuid - nie pozwoli na zadziałanie bitów setuid i setgid
* acl - dzięki niej będziemy mogli używać kontroli dostępu opartej o ACL-e
* remount - opcja przydatna w przypadku problemów z filesystemem - pozwala na przemountowanie filesystemu z innymi opcjami niż te zawarte w fstabie.

Opcja remount wymieniona powyżej używana jest z komendą mount, i działa w dwojaki sposób, zależnie od użytej składni. Pozwala ona przemountować już zamountowany filesystem z innymi opcjami, przy czym nie zmienia ani urządzenia ani punktu mountowania. Bardzo często używana jest do przywrócenia możliwości zapisu w punkcie mountowania, który np. w wyniku błędów system operacyjny przełączył w tryb ro.

  [root@server ~]# mount -o remount,rw /tmp

Takie użycie opcji spowoduje, że mount pobierze opcje zawarte w fstabie (lub mtabie) i uzupełni je o rw, przemountowując filesystem.
Drugim sposobem użycia, o którym wspominałem jest:

[root@server ~]# mount -o remount,rw /dev/mapper/vg_centos-lv_tmp /tmp

Użycie w poleceniu zarówno ścieżki urządzenia jak i punktu mountowania spowoduje, że mount nie weźmie pod uwagę już zdefiniowanych opcji mountowania (z fstab lub mtab), a przemountuje filesystem jedynie z opcjami podanymi przez nas w komendzie.

Od linuxa 2.4.0 istnieje możliwość stosowania jeszcze jednej ciekawej i nieraz przydatnej opcji. Otóż mamy możliwość bindowania, czyli przemountowywania całości lub części już zamountowanego filesystemu w inne miejsce i z innymi opcjami. Np:

[root@server ~]# mount --bind /tmp /tmp_ro
[root@server ~]# mount | grep tmp
/tmp on /tmp_ro type none (rw,bind)
[root@server ~]# mount -o remount,ro /tmp_ro/
[root@server ~]# mount | grep tmp
/tmp on /tmp_ro type none (ro,bind)

Sposobem tym zawartość katalogu /tmp jest dostępna w systemie również w katalogu /tmp_ro, a dodatkowo ten punkt mountowania działa jedynie w trybie ro. Ten przykład jest może trochę abstrakcyjny, ale bindowanie możemy wykorzystać np: do mountowania katalogu znajdującego się w długiej ścieżce systemowej, do nowego punktu znajdującego się bezpośrednio w katalogu /.
Wpisanie bindowania w /etc/fstab wykonujemy następująco:

/katalog/bindowany      /punkt/mountowania       none        bind     0 0

Niestety bindowanie samo w sobie nie zmienia opcji mountowania, i nowy punkt będzie miał takie same opcje jak ten bindowany. Jeśli dodamy taki wpis w fstabie będziemy musieli pamiętać, aby po restarcie przemountować opcje, lub oskryptować takie działania.

Opcje metadanych

Jak wspomniałem na początku, część opcji mountowania filesystemu znajduje się w jego metadanych. Możemy je podejrzeć poprzez:

[root@rsyslog-server tmp]# tune2fs -l /dev/mapper/vg_centos-lv_root | head
tune2fs 1.41.12 (17-May-2010)
Filesystem volume name:   _CentOS-6.7-x86_
Last mounted on:          /
Filesystem UUID:          ec12b9d0-6f56-47cf-805f-a3fc5286fa12
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
Filesystem flags:         signed_directory_hash
Default mount options:    user_xattr
Filesystem state:         clean

Nie będę się rozpisywał nad znaczeniem ich wszystkich, ponieważ (i o tym trzeba pamiętać) opcje te mogą być nadpisywane przez opcje podawane w fstab czy mount.
Jednak dla porządku, pokażę jak je dodawać i odejmować. (opisy dostępne oczywiście w manualu man tune2fs).

Dodawanie opcji zrealizujemy przez:

[root@rsyslog-server tmp]# tune2fs -o acl /dev/mapper/vg_centos-lv_root
tune2fs 1.41.12 (17-May-2010)
[root@rsyslog-server tmp]# tune2fs -l /dev/mapper/vg_centos-lv_root | grep options
Default mount options:    user_xattr acl

Usuwanie odbywa się poprzez dodanie "daszka" przed opcją, którą chcemy usunąć:

[root@rsyslog-server tmp]# tune2fs -o ^acl /dev/mapper/vg_centos-lv_root
tune2fs 1.41.12 (17-May-2010)
[root@rsyslog-server tmp]# tune2fs -l /dev/mapper/vg_centos-lv_root | grep options
Default mount options:    user_xattr