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.
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
.
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.
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)
.
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
).
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.
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.
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í.
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.
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.
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í
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
.
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
.
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.
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
atraceroute
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