Cvičení: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.

V tomto cvičení se budeme věnovat několika tématům: podíváme se na asynchronní komunikaci procesů a seznámíme se s několika zajímavými síťovými nástroji. Dále se stručně seznámíme s tím, jak opravit poškozené diskové jednotky.

Ale hlavním tématem je jak nastavit continuous integration na GitLabu takže budete moci kontrolovat zdraví vašeho softwaru s co nejmenší námahou.

Toto cvičení nemá žádný bežící příklad a témata tedy můžete číst a zkoušet v libovolném pořadí.

Předstartovní kontrola

  • Chápete jak jsou navrženy a používány automatické (unit) testy.
  • Víte, co jsou TCP porty.
  • Víte, co je to obraz disku.
  • Pamatujete si, k čemu se program kill.
  • Víte, co jsou to kontejnery a obrazy disků pro Podman/Docker.

GitLab CI

Pokud jste ještě nikdy neslyšeli pojem kontinuální integrace (Continuous Integration — CI), pak je to v kostce následující.

O kontinuální integraci

Abyste zajistili, že software, který vytváříte, je ve zdravém stavu, měli byste na něm často provádět testy a co nejdříve opravovat nefunkční věci (protože náklady na opravu chyb dramaticky rostou s každým dnem, kdy nejsou odhaleny).

To znamená, že vývojář musí při každé revizi spustit testy. Protože je obtížné to vynutit, je lepší to dělat automaticky. CI ve své nejjednodušší podobě označuje stav, kdy se automatické testy (např. na bázi BATS nebo Pythonu) provádějí automaticky po každém odeslání (push), např. po odeslání změn v libovolné větvi do GitLabu.

CI však dokáže mnohem víc: pokud testy projdou, posloupnost příkazů (pipeline) může software zabalit a publikovat jako artefakt (např. jako instalační program). Nebo může spustit úlohu pro nasazení do produkčního prostředí a zpřístupnit jej zákazníkům. A tak dále.

Toto se často označuje jako CI/CD, což znamená kontinuální integrace a kontinuální dodávka (nebo nasazení).

Nastavení CI na GitLabu

V tomto cvičení uvidíte, jak si nastavit GitLab CI podle vašich potřeb.

Důležité je vědět, že GitLab CI může běžet nad Podmanovými kontejnery. Takže pro nastavení GitLabové pipeline si vyberete obraz pro Podman a příkazy, které je potřeba v takovém kontejneru spustit. GitLab pak vytvoří daný kontejner a spustí v něm vaše příkazy.

Podle výsledku celého skriptu (tj. jeho exit kódu) pak označí buď pipeline jako procházející nebo selhanou.

V tomto kurzu se zaměříme na nejjednodušší konfiguraci, kdy chceme provádět testy po každé revizi. GitLab lze nakonfigurovat i pro složitější úlohy, kdy lze software dokonce nasadit na virtuální cloudový stroj, ale to je bohužel mimo náš rozsah.

Pokud vás toto téma zajímá, GitLab má rozsáhlou dokumentaci. Dokumentace je často hustě zaplněna množstvím informací, ale je skvělým zdrojem znalostí nejen o GitLabu, ale o mnoha principech softwarového inženýrství obecně.

.gitlab-ci.yml

Konfigurace GitLab CI je uložena v souboru .gitlab-ci.yml, který musí být uložen v kořenovém adresáři projektu.

Očekáváme, že máte vlastní fork webového úložiště a že jste rozšířili původní soubor Makefile.

Nyní nastavíme úlohu CI, která sestaví pouze web. Bude to ta nejzákladnější CI, jakou si lze představit. Ale alespoň zajistí, že web bude vždy v sestavitelném stavu.

Pro urychlení však z našeho Makefile odstraníme generování PDF, protože instalace OpenOffice vyžaduje stažení 400 MB, což je pro každou revizi poměrně hodně.

image: fedora:37

build:
  script:
    - dnf install -y make pandoc
    - make

Určuje úlohu pipeline build (tento název se zobrazí ve webovém uživatelském rozhraní), která se spustí pomocí obrazu fedora a provede dva příkazy. První nainstaluje závislost a druhý spustí make.

Nepoužíváme Alpine, protože instalace Pandocu do Alpine je trochu složitější. Vyžaduje, abychom ji buď nainstalovali prostřednictvím nástrojů pro správu balíčků programovacího jazyka Haskell, nebo abychom si stáhli předpřipravenou statickou binárku.

Přidejte soubor .gitlab-ci.yml do svého úložiště Git (tj. do svého forku), odevzdejte jej (commit) a odešlete (push).

Pokud otevřete stránku projektu na webu GitLabu, měla by se vedle ní zobrazit ikona pipeline, která by měla nakonec zezelenat.

Log by pravděpodobně vypadal takto.

Running with gitlab-runner 15.11.0 (436955cb)
  on gitlab.mff docker Mtt-jvRo, system ID: s_7f0691b32461
Preparing the "docker" executor 00:03
Using Docker executor with image fedora:37 ...
Pulling docker image fedora:37 ...
Using docker image sha256:34354ac2c458e89615b558a15cefe1441dd6cb0fc92401e3a39a7b7012519123 for fedora:37 with digest fedora@sha256:e3012fe03ccee2d37a7940c4c105fb240cbb566bf228c609d9b510c9582061e0 ...
Preparing environment 00:00
Running on runner-mtt-jvro-project-11023-concurrent-0 via gitlab-runner...
Getting source from Git repository 00:01
Fetching changes with git depth set to 20...
Reinitialized existing Git repository in /builds/horkv6am/nswi-177-web/.git/
Checking out 58653aa3 as detached HEAD (ref is master)...
Removing out/index.html
Removing out/main.css
Removing out/rules.html
Removing out/score.html
Removing tmp/
Skipping Git submodules setup
Executing "step_script" stage of the job script 00:33
Using docker image sha256:34354ac2c458e89615b558a15cefe1441dd6cb0fc92401e3a39a7b7012519123 for fedora:37 with digest fedora@sha256:e3012fe03ccee2d37a7940c4c105fb240cbb566bf228c609d9b510c9582061e0 ...
$ dnf install -y make pandoc
Fedora 37 - x86_64                               43 MB/s |  82 MB     00:01
Fedora 37 openh264 (From Cisco) - x86_64        4.3 kB/s | 2.5 kB     00:00
Fedora Modular 37 - x86_64                       17 MB/s | 3.8 MB     00:00
Fedora 37 - x86_64 - Updates                     24 MB/s |  29 MB     00:01
Fedora Modular 37 - x86_64 - Updates            4.8 MB/s | 2.9 MB     00:00
Dependencies resolved.
================================================================================
 Package              Architecture  Version                 Repository     Size
================================================================================
Installing:
 make                 x86_64        1:4.3-11.fc37           fedora        542 k
 pandoc               x86_64        2.14.0.3-18.fc37        fedora         21 M
Installing dependencies:
 gc                   x86_64        8.0.6-4.fc37            fedora        103 k
 guile22              x86_64        2.2.7-6.fc37            fedora        6.5 M
 libtool-ltdl         x86_64        2.4.7-2.fc37            fedora         37 k
 pandoc-common        noarch        2.14.0.3-18.fc37        fedora        472 k
Transaction Summary
================================================================================
Install  6 Packages
Total download size: 29 M
Installed size: 204 M
Downloading Packages:
(1/6): libtool-ltdl-2.4.7-2.fc37.x86_64.rpm     846 kB/s |  37 kB     00:00
(2/6): make-4.3-11.fc37.x86_64.rpm              9.4 MB/s | 542 kB     00:00
(3/6): gc-8.0.6-4.fc37.x86_64.rpm               595 kB/s | 103 kB     00:00
(4/6): pandoc-common-2.14.0.3-18.fc37.noarch.rp 8.4 MB/s | 472 kB     00:00
(5/6): guile22-2.2.7-6.fc37.x86_64.rpm           18 MB/s | 6.5 MB     00:00
(6/6): pandoc-2.14.0.3-18.fc37.x86_64.rpm        56 MB/s |  21 MB     00:00
--------------------------------------------------------------------------------
Total                                            51 MB/s |  29 MB     00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                        1/1
  Installing       : pandoc-common-2.14.0.3-18.fc37.noarch                  1/6
  Installing       : libtool-ltdl-2.4.7-2.fc37.x86_64                       2/6
  Installing       : gc-8.0.6-4.fc37.x86_64                                 3/6
  Installing       : guile22-2.2.7-6.fc37.x86_64                            4/6
  Installing       : make-1:4.3-11.fc37.x86_64                              5/6
  Installing       : pandoc-2.14.0.3-18.fc37.x86_64                         6/6
  Running scriptlet: pandoc-2.14.0.3-18.fc37.x86_64                         6/6
  Verifying        : gc-8.0.6-4.fc37.x86_64                                 1/6
  Verifying        : guile22-2.2.7-6.fc37.x86_64                            2/6
  Verifying        : libtool-ltdl-2.4.7-2.fc37.x86_64                       3/6
  Verifying        : make-1:4.3-11.fc37.x86_64                              4/6
  Verifying        : pandoc-2.14.0.3-18.fc37.x86_64                         5/6
  Verifying        : pandoc-common-2.14.0.3-18.fc37.noarch                  6/6
Installed:
  gc-8.0.6-4.fc37.x86_64               guile22-2.2.7-6.fc37.x86_64
  libtool-ltdl-2.4.7-2.fc37.x86_64     make-1:4.3-11.fc37.x86_64
  pandoc-2.14.0.3-18.fc37.x86_64       pandoc-common-2.14.0.3-18.fc37.noarch
Complete!
$ make
pandoc --template template.html -o out/index.html index.md
pandoc --template template.html -o out/rules.html rules.md
./table.py <score.csv | pandoc --template template.html --metadata title="Score" - >out/score.html
cp main.css out/
Cleaning up project directory and file based variables 00:01
Job succeeded

Všimněte si, že GitLab nejprve připojí úložiště Git do kontejneru a poté provede příkazy uvnitř klonu. Příkazy se provádějí pomocí set -e: první neúspěšný příkaz ukončí celou pipeline.

Pokuste se emulovat výše uvedený běh lokálně. Nápověda. Řešení.

Další drobnosti

Všimněte si, jak je použití pipeline GitLab snadné. Najdete správný obraz (image), zadáte skript a GitLab se postará o zbytek.

Od této chvíle by měl mít každý Váš projekt vytvořený na GitLabu pipeline, která spustí testy (včetně Shellcheck, Pylint atd.). Nastavte ji TEĎ pro své úkoly v jiných předmětech. Nastavte si ji pro svůj ročníkový projekt (NPRG045) v příštím roce. Využijte možnosti nechat si pravidelně testovat svůj kód. Z dlouhodobého hlediska vám to ušetří čas.

Pokud si nejste jisti, který obraz zvolit, oficiální obrazy jsou dobrým začátkem. Skript může mít několik kroků, ve kterých před spuštěním programu nainstalujete chybějící závislosti.

Připomeňme, že nemusíte vytvářet virtuální prostředí: celý počítač je váš (a bude stejně odstraněn), takže můžete instalovat věci globálně.

Může být definováno více úloh, které jsou spouštěny paralelně (ve skutečnosti mezi nimi mohou existovat poměrně složité závislosti, ale v následujícím příkladu jsou všechny úlohy spuštěny najednou).

Následující příklad ukazuje fragment souboru .gitlab-ci.yml, který testuje projekt na více verzích jazyka Python.

# Default image if no other is specified
image: python:3.10

stages:
  - test

# Commands executed before each "script" section (for any job)
before_script:
    # To have a quick check that the version is correct
    - python3 --version
    # Install the project
    - python3 -m pip install ...

# Run unit tests under different versions
unittests3.7:
  stage: test
  image: "python:3.7"
  script:
    - pytest --log-level debug tests/

unittests3.8:
  stage: test
  image: "python:3.8"
  script:
    - pytest --log-level debug tests/

unittests3.9:
  stage: test
  image: "python:3.9"
  script:
    - pytest --log-level debug tests/

unittests3.10:
  stage: test
  image: "python:3.10"
  script:
    - pytest --log-level debug tests/

Signály

Linuxové systémy používají signály pro asynchronní komunikaci mezi běžícími programy (procesy). Slovo asynchronní znamená, že signál může být odeslán (a doručen) bez ohledu na stav procesu. Toto je v kontrastu s komunikací prostřednictvím (například) standardního vstupu, kde program řídí, kdy bude ze standardního vstupu číst (tím, že zavolá odpovídající IO funkce pro čtení).

Signály nicméně nenabízejí příliš bohatou možnost komunikace: jediná informace (kromě toho, že byl vůbec poslán) je jejich číslo. Čísla signálů jsou definována v kernelu, který také některé signály sám zpracovává. V ostatních případech jsou signály doručené aplikaci, která na ně může reagovat. Pokud aplikace na signál nereaguje, je signál zpracován výchozím způsobem. V některých případech se jedná o ukončení aplikace. V ostatních případech jsou signály ignorovány.

Toto je také vyjádřeno tím, že nástroj, kterým se signály posílají, se jmenuje kill (protože obvykle proces ukončí).

Bez dalších parametrů kill posílá signál 15 (nazvaný též TERM), který aplikaci říká, že má být ukončena. Aplikace se může rozhodnout, že signál tzv. odchytí, např. zavře otevřené soubory a teprve pak skončí. Aplikace ale může udělat v podstatě cokoliv a dokonce i signál ignorovat. Kromě TERM můžeme též pomocí kill poslat signál KILL (číslo 9), který je vždy odchycen kernelem a okamžitě (a násilně) program ukončí (v případě, že se aplikace pokusí signál odchytit a zpracovávat, je tento požadavek ignorován).

Většina signálů je poslána procesu v reakci na specifickou událost. Například signál PIPE je poslán, pokud proces zkouší zapsat do roury, jejíž čtecí konec byl uzavřen. (Vzpomeňte si na problém z labu 04.) Ukončení programu pomocí Ctrl-C v terminálu ve skutečnosti pošle signál INT (interrupt, číslo 2). Pokud vás zajímají i ostatní signály, mrkněte na signal(7).

Na příklad pokud se systém vypíná, pošle TERM všem procesům. To jim dá možnost rozumně skončit. Procesy, které po nějaké době stále běží, jsou násilně ukončeny signálem KILL.

Použití kill a pkill

Vzpomeňte si na ukázku z cvičení 5, jak můžeme použít kill k ukončení procesů.

Pro rychlé shrnutí si nyní otevřete dva terminály.

Spusťte sleep v prvním, vyhledejte jeho PID v druhém a zabijte jej pomocí SIGTERM (výchozí signál) a poté cvičení zopakujte pomocí SIGKILL (-9).

Řešení.

Podobně můžete použít pkill k zabíjení procesů podle jména (ale buďte opatrní, protože s velkou mocí přichází i velká zodpovědnost). Další podrobnosti naleznete na stránkách manuálu.

Dále také existuje příkaz killall, který se chová podobně. V některých Unixových systémech (např. Solaris) má tento příkaz zcela jinou sémantiku a používá se k vypnutí celého počítače.

Reakce na signály v Pythonu

Váš program by obvykle reagoval na TERM (výchozí “měkké” ukončení), INT (Ctrl-C z klávesnice) a možná na USR1 nebo USR2 (jediné uživatelsky definované signály). Systémoví démoni také často reagují na HUP znovu-načtením své konfigurace.

Následující program v jazyce Python reaguje na Ctrl-C ukončením. Uložte jej jako show_signals.py a při spuštění ./show_signals.py stiskněte Ctrl-C.

Při zkoušení níže uvedeného kódu se vyhněte časté pasti: neukládejte jej do souboru signal.py, protože by jeho název kolidoval se standardním balíčkem.
#!/usr/bin/env python3

import sys
import time
import signal

# Actual signal callback
def on_signal(signal_number, frame_info):
    print("")
    print("Caught signal {} ({})".format(signal_number, frame_info))
    sys.exit()

def main():
    # Setting signal callback
    signal.signal(signal.SIGINT, on_signal)
    while True:
        time.sleep(0.5)
        print("Hit Ctrl-C...")

if __name__ == '__main__':
    main()

Cvičení

Napište program, který se pokusí vypsat všechna prvočísla. Při ukončení uloží dosud nalezené nejvyšší číslo a při dalším volání pokračuje od něj.

Řešení.

Reakce na signály v shellu

Reakce na signály v shellu se provádí pomocí příkazu trap.

Všimněte si, že typickou akcí pro obsluhu signálů v shellovém skriptu je úklid dočasných souborů.

#!/bin/bash

set -ueo pipefail

on_interrupt() {
    echo "Interrupted, terminating ..." >&2
    exit 17
}

on_exit() {
    echo "Cleaning up..." >&2
    rm -f "$MY_TEMP"
}

MY_TEMP="$( mktemp )"

trap on_interrupt INT TERM
trap on_exit EXIT

echo "Running with PID $$"

counter=1
while [ "$counter" -lt 10 ]; do
    date "+%Y-%m-%d %H:%M:%S | Waiting for Ctrl-C (loop $counter) ..."
    echo "$counter" >"$MY_TEMP"
    sleep 1
    counter=$(( counter + 1 ))
done

Příkaz trap obdrží jako první argument příkaz, který se má v reakci na signál vykonat. Další argumenty obsahují seznam signálů, na které se má reagovat. Všimněte si, že speciální signál EXIT znamená normální ukončení skriptu. Proto nemusíme po ukončení smyčky volat on_exit.

Pro označení ukončení pomocí obsluhy Ctrl-C použijeme exit 17 (hodnota je sama o sobě libovolná).

Návratovou hodnotu můžete zkontrolovat pomocí echo $? po ukončení příkazu. Speciální proměnná $? obsahuje výstupní kód posledního příkazu.

Pokud váš shellový skript začíná příkazem set -e, budete $? potřebovat jen zřídka, protože jakákoli nenulová hodnota způsobí ukončení skriptu.

Následující konstrukce však zabrání ukončení a umožní vám v případě potřeby větvit kód na základě výstupní hodnoty.

set -e

...
# Prevent termination through set -e
rc=0
some_command_with_interesting_exit_code || rc=$?
if [ $rc -eq 0 ]; then
    ...
elif [ $rc -eq 1 ]; then
    ...
else
    ...
fi

Použití - (pomlčky) místo handleru způsobí, že příslušný handler bude nastaven jako výchozí.

Vaše shellové skripty musí vždy obsahovat obsluhu signálu pro úklid dočasných souborů.

Všimněte si použití $$, které vypíše aktuální PID.

Spusťte výše uvedený skript, poznamenejte si jeho PID a v novém terminálu spusťte následující příkaz.

kill THE_PID_PRINTED_BY_THE_ABOVE_SCRIPT

Skript byl ukončen a byla zavolána rutina pro úklid. Srovnejte se situací, kdy jste příkaz trap odkomentovali.

Spusťte skript znovu, ale předejte -9 do kill, abyste určili, že chcete poslat signál devět (tj. KILL).

Co se stalo? Odpověď.

Signály jsou sice primitivním mechanismem, který předává binární události bez dalších dat, ale jsou hlavním způsobem řízení procesů v systému Linux.

Pokud potřebujete bohatší komunikační kanál, můžete místo něj použít D-Bus.

Rozumná reakce na základní signály je pro serverové aplikace nutností (např. webový server by měl reagovat na TERM dokončením nevyřízených požadavků bez přijímání nových spojení a následným ukončením). V shellových skriptech se považuje za vhodné vždy zajistit úklid dočasných souborů.

Nedostatky v návrhu a realizaci signálu

Signály jsou základním mechanismem pro komunikaci mezi procesy v systémech Unix. Bohužel jejich návrh má několik chyb, které komplikují jejich bezpečné použití.

Nebudeme zabíhat do podrobností, ale měli byste mít na paměti, že manipulace se signály může být složitá v situacích, kdy si nemůžete dovolit ztratit žádný signál nebo kdy signály mohou přicházet rychle jeden za druhým. A při používání signálů ve vícevláknových programech se otevírá celá Pandořina skříňka problémů.

Na druhou stranu pro jednoduché shellové skripty, ve kterých chceme při násilném ukončení provést úklid, postačí přístup, který jsme si ukázali výše. Ve skriptu tím ohlídáme situaci, kdy uživatelé stiskli Ctrl-C, protože zjistili, že pracují se špatnými daty, nebo něco podobného.

Všimněte si však, že takový skript obsahuje chybu pro případ, kdy uživatelé stisknou Ctrl-C velmi brzy během provádění skriptu.

MY_TEMP="$( mktemp )"
# User hits Ctrl-C here
trap on_interrupt INT TERM
trap on_exit EXIT

Dočasný soubor byl již vytvořen, ale obslužná rutina ještě nebyla zaregistrována, a proto nebude soubor odstraněn. Změna pořadí však komplikuje obsluhu signálu, protože musíme otestovat, že $MY_TEMP byl již inicializován.

Skutečnost, že signály mohou být záludné, však neznamená, že bychom měli opustit základní prostředky, které zajistí, že naše skripty po sobě uklidí i v případě, že jsou násilně ukončeny.

V jiných programovacích jazycích je úklid poněkud jednodušší, protože je možné vytvořit dočasný soubor, který se vždy automaticky odstraní, jakmile se proces ukončí.

Spoléhá na šikovný trik, kdy můžeme soubor otevřít (vytvořit) a ihned jej odstranit. Dokud však uchováváme deskriptor souboru (tj. výsledek Pythonovského open), systém zachovává obsah souboru nedotčený. Soubor je však již pryč (ve smyslu označení obsahu) a zavření souboru jej zcela odstraní.

Protože shell je založen na spouštění více procesů, výše uvedený trik pro shellové skripty nefunguje.

Zkontrolujte si vaše porozumění

Vyberte všechna pravdivá tvrzení o signálech a procesech. You need to have enabled JavaScript for the quiz to work.

Network Manager

Existuje několik způsobů, jak konfigurovat síť v Linuxu. Správci serverů často dávají přednost použití holého příkazu ip; na stolních počítačích dnes většina distribucí používá NetworkManager, takže si jej ukážeme i zde. Všimněte si, že stránka ArchLinux Wiki o NetworkManageru obsahuje také mnoho informací.

NetworkManager má grafické rozhraní (pravděpodobně jste použili jeho applet, aniž byste o tom věděli), rozhraní TUI (které lze spustit pomocí nmtui) a nakonec rozhraní CLI.

Zde se (ze zřejmých důvodů) zaměříme na rozhraní příkazového řádku. Bez parametrů zobrazí nmcli informace o aktuálních spojeních:

wlp58s0: connected to TP-Link_1CE4
        "Intel 8265 / 8275"
        wifi (iwlwifi), 44:03:2C:7F:0F:76, hw, mtu 1500
        ip4 default
        inet4 192.168.0.105/24
        route4 0.0.0.0/0
        route4 192.168.0.0/24
        inet6 fe80::9ba5:fc4b:96e1:f281/64
        route6 fe80::/64
        route6 ff00::/8

p2p-dev-wlp58s0: disconnected
        "p2p-dev-wlp58s0"
        wifi-p2p, hw

enp0s31f6: unavailable
        "Intel Ethernet"
        ethernet (e1000e), 54:E1:AD:9F:DB:36, hw, mtu 1500

vboxnet0: unmanaged
        "vboxnet0"
        ethernet (vboxnet), 0A:00:27:00:00:00, hw, mtu 1500

lo: unmanaged
        "lo"
        loopback (unknown), 00:00:00:00:00:00, sw, mtu 65536

DNS configuration:
        servers: 192.168.0.1 8.8.8.8
        interface: wlp58s0

...

Porovnejte výše uvedené údaje s výstupem příkazu ip addr. Všimněte si, že NetworkManager explicitně uvádí výchozí trasy a také vás informuje, že některá rozhraní neovládá (zde lo nebo vboxnet0).

Změna IP konfigurace

Přestože většina sítí nabízí DHCP (alespoň ty, ke kterým se připojujete pomocí stolního počítače), někdy je třeba nastavit IP adresy ručně.

Typickým příkladem je situace, kdy potřebujete dočasně propojit dva počítače, např. pro přenos velkého souboru přes kabelové připojení.

Jediné, o čem musíte rozhodnout, je, jakou síť vytvoříte. Nepoužívejte stejnou, jakou používá váš domácí router; naše oblíbená volba je 192.168.177.0/24.

Následující příkaz přidá na enp0s31f6 připojení s názvem wired-static-temp, přičemž vychází z výše uvedeného názvu:

sudo nmcli connection add \
    con-name wired-static-temp \
    ifname enp0s31f6 \
    type ethernet \
    ip4 192.168.177.201/24

Často je potřebné aktivovat spojení pomocí následujícího příkazu:

sudo nmcli connection up wired-static-temp

Stejný postup proveďte i na druhém hostiteli, ale použijte jinou adresu (např. .202). Nyní byste měli být schopni pingnout druhý počítač:

ping 192.168.177.201

Chcete-li demonstrovat, jak se ping chová při výpadku připojení, můžete zkusit odpojit kabel nebo provést totéž softwarově:

sudo nmcli connection down wired-static-temp

Další síťové nástroje

Nebudeme zde suplovat kurzy síťování, ale zmíníme se o některých základních příkazech, které by vám mohly pomoci při ladění základních problémů souvisejících se sítí.

Již znáte ping: základní nástroj pro zjištění, zda je počítač s danou IP adresou spuštěn (a reaguje na síťový provoz).

ping je základní nástroj pro případ, že náhle ztratíte spojení s nějakým serverem. Pingněte cílový server a také nějaký jiný známý server. Pokud pakety procházejí, víte, že problém je někde jinde. Pokud projdou pouze pakety na dobře známý server, problém je pravděpodobně na daném serveru. Pokud selžou oba, síť pravděpodobně nefunguje.

K dispozici jsou však i pokročilejší nástroje.

traceroute (neboli cesta je cíl)

Někdy se může hodit znát přesnou cestu, kterou pakety procházejí. Pro tento druh úloh můžeme použít traceroute.

Podobně jako v případě ping musíme zadat pouze cíl.

traceroute 1.1.1.1
traceroute to 1.1.1.1 (1.1.1.1), 30 hops max, 60 byte packets
 1  _gateway (10.16.2.1)  2.043 ms  1.975 ms  1.948 ms
 2  10.17.0.1 (10.17.0.1)  1.956 ms  1.971 ms  1.961 ms
 3  gw.sh.cvut.cz (147.32.30.1)  1.947 ms  1.973 ms  1.977 ms
 4  r1sh-sush.net.cvut.cz (147.32.252.238)  2.087 ms  2.262 ms  2.527 ms
 5  r1kn-konv.net.cvut.cz (147.32.252.65)  1.856 ms  1.849 ms  1.847 ms
 6  kn-de.net.cvut.cz (147.32.252.57)  1.840 ms  1.029 ms  0.983 ms
 7  195.113.144.172 (195.113.144.172)  1.894 ms  1.900 ms  1.885 ms
 8  195.113.235.99 (195.113.235.99)  4.793 ms  4.748 ms  4.723 ms
 9  nix4.cloudflare.com (91.210.16.171)  2.264 ms  2.807 ms  2.814 ms
10  one.one.one.one (1.1.1.1)  1.883 ms  1.800 ms  1.834 ms

První sloupec odpovídá počtu skoků. Druhý sloupec představuje adresu tohoto skoku a za ním jsou uvedeny tři časy oddělené mezerou v milisekundách. Příkaz traceroute odešle na cíl tři pakety a každý z časů označuje dobu, za kterou paket na cíl dorazí. Z výše uvedeného výstupu tedy vidíme, že balíčky na své cestě mezi místním počítačem a cílem provedly celkem 10 skoků.

Tento nástroj je užitečný zejména tehdy, když máte potíže se sítí a nejste si jisti, kde je problém.

traceroute to 1.1.1.1 (1.1.1.1), 30 hops max, 60 byte packets
 1  10.21.20.2 (10.21.20.2)  0.798 ms  0.588 ms  0.699 ms
 2  10.21.5.1 (10.21.5.1)  0.593 ms  0.506 ms  0.611 ms
 3  192.168.88.1 (192.168.88.1)  0.742 ms  0.637 ms  0.534 ms
 4  10.180.2.113 (10.180.2.113)  1.696 ms  4.106 ms  1.483 ms
 5  46.29.224.17 (46.29.224.17)  14.343 ms  13.749 ms  13.806 ms
 6  * * *
 7  * * *
 8  * * *
 9  * * *
10  * * *
11  * * *
12  * * *
13  * * *
14  * * *
15  * * *
16  * * *
17  * * *
18  * * *
19  * * *
20  * * *
21  * * *
22  * * *
23  * * *
24  * * *
25  * * *
26  * * *
27  * * *
28  * * *
29  * * *
30  * * *

Z tohoto protokolu vidíme, že poslední navštívený skok byl 46.29.224.17, takže se můžeme zaměřit na tento síťový prvek.

nmap (též známý jako průzkum vaší sítě)

nmap je velmi mocný nástroj. Bohužel i nevinné – ale opakované – použití může být snadno chybně interpretováno jako škodlivé skenování zranitelností, které jsou náchylné k útoku. Používejte tento nástroj opatrně a experimentujte ve své domácí síti. Bezohledné skenování univerzitní sítě může ve skutečnosti vašemu počítači na nějakou dobu zakázat jakékoli připojení.

nmap je základní nástroj pro skenování sítě. Pokud chcete zjistit, které síťové služby jsou na počítači spuštěny, můžete se zkusit připojit ke všem jeho portům a zjistit, které jsou otevřené. Nmap to umí a ještě mnohem více.

Zkuste nejprve zkontrolovat zařízení zpětné smyčky (loopback), zda v počítači nejsou spuštěny interní služby:

nmap localhost

Výsledek by mohl vypadat takto (počítač má tiskový server a HTTP proxy server):

Starting Nmap 7.91 ( https://nmap.org ) at 2021-05-04 16:38 CEST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00011s latency).
Other addresses for localhost (not scanned): ::1
rDNS record for 127.0.0.1: localhost.localdomain
Not shown: 998 closed ports
PORT     STATE SERVICE
631/tcp  open  ipp
3128/tcp open  squid-http

Nmap done: 1 IP address (1 host up) scanned in 0.11 seconds

Pokud chcete zobrazit více informací, můžete zkusit přidat parametr -A.

nmap -A localhost

A pokud jej spustíte pod rootem (tj. sudo nmap -A localhost), může se nmap pokusit detekovat i vzdálený operační systém.

Ve výchozím nastavení nmap skenuje pouze porty často používané síťovými službami. Pomocí volby -p můžete zadat jiný rozsah:

nmap -p1-65535 localhost

Tento příkaz dává nmap pokyn ke skenování všech portů TCP (-p1-65535) na localhost.

Opět: neskenujte všechny porty TCP na počítačích v univerzitní síti!

Cvičení: Který webový server se používá v našem GitLabu? A který je na našich univerzitních webových stránkách? Řešení.

nc (netcat)

Podívejme se, jak vytvořit síťové připojení ze shellu. To je nezbytné pro ladění síťových služeb, ale je to také užitečné pro použití sítě ve skriptech.

Švýcarský armádní nůž síťového skriptování se nazývá netcat nebo nc.

Bohužel existuje více implementací netcatu, které se liší možnostmi a schopnostmi. My si ukážeme ncat, který je ve Fedoře standardně nainstalován. Ve vašem systému může být nainstalována jiná varianta.

Nejprve triviální věci: chcete-li se připojit k danému portu TCP na vzdáleném počítači, můžete spustit příkaz nc machine port. Tím se naváže spojení a připojí se k němu stdin a stdout. Můžete tedy komunikovat se vzdáleným serverem.

Netcat se často připojuje k jiným příkazům pomocí rour. Napišme si základního HTTP klienta:

echo -en "GET / HTTP/1.1\r\nHost: www.kernel.org\r\n\r\n" | nc www.kernel.org 80

Používáme \r\n, protože protokol HTTP vyžaduje řádky ukončené CR+LF. Hlavička Host: je povinná, protože protokol HTTP podporuje více webových stránek běžících na stejné kombinaci IP adresy a portu.

Vidíme, že http://www.kernel.org/ nás přesměruje na https://www.kernel.org/, a tak to zkusíme znovu pomocí HTTPS. Naštěstí naše verze programu netcat umí pracovat s protokolem TLS (transport-layer security), který se používá k šifrování:

echo -en "GET / HTTP/1.1\r\nHost: www.kernel.org\r\n\r\n" | nc --ssl www.kernel.org 443

Nyní vytvoříme jednoduchý server. Bude naslouchat na portu TCP 8888, a když se k němu někdo připojí, server pošle na toto připojení obsah daného souboru:

nc --listen 8888 <path-to-file

Můžeme otevřít nový shell a zkusit soubor přijmout:

nc localhost 8888

Soubor obdržíme, ale netcat se neukončí – stále čeká na vstup ze stdin. Stisknutí klávesy Ctrl-D funguje, ale je jednodušší říct netcatu, aby pracoval pouze jedním směrem:

nc localhost 8888 --recv-only

OK, pro přenos jednoho souboru po síti to funguje. (Mějte však na paměti, že přenos není šifrovaný, takže není rozumné jej používat ve veřejné síti.)

Po přenosu souboru se server ukončí. Co když chceme spustit server, který zvládne více připojení? Zde přesměrování nestačí, protože potřebujeme soubor číst vícekrát. Místo toho můžeme požádat netcat, aby pro každé spojení spustil příkaz shellu a propojil spojení se jeho stdin a stdout:

nc --listen 8888 --keep-open --sh-exec 'cat path-to-file'

To lze samozřejmě využít k mnohem zajímavějším věcem, než je odesílání souboru. Můžete vzít libovolný program, který komunikuje přes stdin a stdout, a udělat z něj síťovou službu.

Opakované spouštění úloh s Cronem

V systému je mnoho úloh, které je třeba pravidelně provádět. Mnohé z nich souvisejí s údržbou systému, například rotace protokolů (odstraňování zastaralých protokolů), ale i běžní uživatelé mohou chtít provádět pravidelné úlohy.

Typickým příkladem může být zálohování adresáře $HOME nebo každodenní změna tapety na ploše.

Z pohledu správce je třeba nainstalovat démona cron a spustit jej. Ve Fedoře se balíček jmenuje cronie, ale služba se stále jmenuje crond.

Úlohy (tasks) pro celý systém jsou definovány v souboru /etc/cron.*/, kam můžete přímo umístit své skripty. Chcete-li například provádět denní zálohování počítače, můžete umístit skript backup.sh přímo do souboru /etc/cron.daily/. Samozřejmě existují specializované zálohovací nástroje (např. duplicity), vaše řešení z Labu 06 je pro domácí postup docela dobrý začátek.

Pokud chcete přesnější specifikaci, než jakou nabízí adresáře cron.daily nebo cron.hourly, můžete ji zadat ve vlastním souboru uvnitř /etc/cron.d/.

V něm každý řádek specifikuje jednu úlohu: požadavek na spuštění zadaného příkazu v zadaném čase pod zadaným uživatelem (obvykle root). Čas se zadává jako minuta (0-59), hodina (0-23), den v měsíci (1-31), měsíc (1-12) a den v týdnu (0-6, kde 0 je neděle). V každém poli můžete použít * pro “libovolný”. Další podrobnosti naleznete v crontab(5).

Následující příkaz tedy spustí /usr/local/bin/backup.sh každý den 85 minut po půlnoci (tj. v 1:25). Druhý řádek bude volat big-backup.sh každou neděli ráno.

25 1 * * * root /usr/local/bin/backup.sh
0  8 * * 0 root /usr/local/bin/big-backup.sh

Všimněte si, že cron.d obvykle obsahuje speciální volání následujícího tvaru, které zajišťuje, že se skripty cron.hourly spustí (tj. samotný deamon cronie hledá pouze uvnitř /etc/cron.d/, použití cron.daily nebo cron.monthly je řešeno speciálními úlohami).

01 * * * * root run-parts /etc/cron.hourly

Spuštění pod obyčejným uživatelem

Běžní (tj. non-root) uživatelé nemohou upravovat soubory v adresáři /etc/cron.d/. Místo toho mají k dispozici příkaz crontab, který lze použít k úpravě jejich osobní tabulky cronu (tj. seznamu úloh cronu).

Voláním crontab -l zobrazíte aktuální obsah tabulky cron. Pravděpodobně nevypíše nic.

Chcete-li upravit tabulku cronu, spusťte příkaz crontab -e. Spustí se váš oblíbený editor, do kterého můžete přidávat řádky ve výše uvedeném formátu, tentokrát bez zadání uživatele.

Přidáním následující položky se například každý den změní pozadí pracovní plochy:

1 1 * * * /home/intro/bin/change_desktop_background.sh

Samozřejmě za předpokladu, že takový skript v daném umístění máte. Pokud to chcete opravdu vyzkoušet, následující skript funguje pro Xfce a používá Lorem Picsum.

#!/bin/bash

# Update to your hardware configuration
screen_width=1920
screen_height=1080

wallpaper_path="$HOME/.wallpaper.jpg"

curl -L --silent "https://picsum.photos/$screen_width/$screen_height" >"$wallpaper_path"

# Xfce
# Select the right path from xfconf-query -lvc xfce4-desktop
xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitor0/workspace0/last-image -s "$wallpaper_path"

# LXDE
pcmanfm -w "$wallpaper_path"

# Sway
# For more details see `man 5 sway-output`
# You can also set a different wallpaper for each output (display)
# Run `swaymsg -t get_outputs` for getting specific output name
swaymsg output '*' bg "$wallpaper_path" fill

Oprava poškozených disků

Hlavní nástroj systému Linux pro opravu poškozených svazků se nazývá fsck (kontrola souborového systému). Příkaz fsck je vlastně jednoduchý wrapper, který vybírá správnou implementaci podle typu souborového systému. Pro rodinu souborových systémů ext2/ext3/ext4 systému Linux se implementace nazývá e2fsck. Může být užitečnější volat přímo e2fsck, protože specializovanější volby se nepředávají prostřednictvím obecného fsck.

Jak jsme se již krátce zmínili ve cvičení 10, je bezpečnější pracovat na kopii svazku, zejména pokud máte podezření, že je svazek vážně poškozen. Tímto způsobem neriskujete jeho ještě větší poškození. To může být poměrně náročné z hlediska diskového prostoru: nakonec jde vždy o peníze – stojí data za víc než nákup dalšího disku nebo dokonce kompletní přenesení do profesionální firmy zaměřené na tento druh práce.

Případně můžete nejprve spustit e2fsck -n, který kontroluje pouze chyby, a sami posoudit jejich závažnost.

Někdy je disk příliš poškozený na to, aby ho fsck opravil. (Ve skutečnosti se to u souborových systémů ext stává zřídka – byli jsme svědky úspěšných oprav disků, jejichž prvních 10 GB bylo zcela přepsáno. Ale u souborových systémů DOS/Windows, jako jsou vfat a ntfs, jsou automatické opravy méně úspěšné.)

I v takovém případě je stále velká šance na obnovení mnoha souborů. Naštěstí, pokud nebyl disk příliš zaplněn, byla většina souborů ukládána průběžně. Můžeme tedy použít jednoduchý program, který prohledá celý diskový obraz a vyhledá signatury běžných formátů souborů (připomeňte si například, jak vypadá formát GIF). Tím samozřejmě neobnovíme názvy souborů ani hierarchii adresářů.

První program, který si ukážeme, je photorec (sudo dnf install testdisk). Před jeho spuštěním si připravte prázdný adresář, do kterého budete ukládat výsledky.

Přijímá jediný argument: soubor s diskovým obrazem, který se má skenovat. Poté spustí interaktivní režim, ve kterém vyberete, kam se mají obnovené soubory uložit, a také odhadnete typ souborového systému (ve většině případů to bude FAT nebo NTFS). Poté se pokusí soubory obnovit. Nic víc, nic míň.

photorec dokáže obnovit spoustu souborových formátů včetně souborů JPEG, MP3, ZIP (včetně ODT a DOCX) nebo dokonce RTF.

Dalším nástrojem je recoverjpeg, který se zaměřuje na obnovu fotografií. Na rozdíl od photorec pracuje recoverjpeg zcela neinteraktivně a nabízí několik dalších parametrů, které umožňují jemné nastavení procesu obnovy.

Balíček recoverjpeg není pro Fedoru připraven: můžete si ho zkusit nainstalovat ručně nebo si hrát pouze s photorec (a doufat, že ho nikdy nebudete potřebovat).

Úlohy k ověření vašich znalostí

Očekáváme, že následující úlohy vyřešíte ještě před příchodem na cvičení, takže se budeme moci o vašich řešeních na cvičení pobavit.

Přidejte do GitLabu vlastní pipeline, která kontroluje, zda jako shebang nikdy nepoužíváte /usr/bin/python.

Nápověda.

Řešení.

Učební výstupy

Učební výstupy podávají zhuštěný souhrn základních konceptů a dovedností, které byste měli umět vysvětlit a/nebo použít po každém cvičení. Také obsahují absolutní minimum, které je potřebné pro pochopení navazujících cvičení (a dalších předmětů).

Znalosti konceptů

Znalost konceptů znamená, že rozumíte významu a kontextu daného tématu a jste schopni témata zasadit do většího rámce. Takže, jste schopni …

  • vysvětlit, proč je použití nmap obvykle omezeno/zakázáno správci sítě

  • vysvětlit, co je to signál v kontextu Linux procesů

  • vysvětlit principy continous integration

  • vysvětlit výhody používání continous integration

  • vysvětlit v širším smyslu, jak funguje GitLab CI

Praktické dovednosti

Praktické dovednosti se obvykle týkají použití daných programů pro vyřešení různých úloh. Takže, dokážete …

  • použít nc pro základní operace

  • použít pgrep pro vyhledání konkrétních procesů

  • poslat signál běžícímu procesu

  • použít nmap pro základní skenování sítě

  • použít ip k dotazování na stav aktuální síťové konfigurace

  • použít utilit ping a traceroute jako základních nástrojů pro ladění síťových problémů

  • nastavit GitLab CI pro jednoduché projekty

  • volitelné: použít NetworkManager k nastavení statických IP adres

  • volitelné: opravit poškozené souborové systémy pomocí programů z rodiny fsck

  • volitelné: používat photorec k obnově souborů z poškozeného souborového systému