<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>linux &amp;mdash; arek o sofcie</title>
    <link>https://baal.ar76.eu/arek/tag:linux</link>
    <description>Różności na temat tworzenia oprogramowania</description>
    <pubDate>Mon, 04 May 2026 17:19:50 +0200</pubDate>
    <item>
      <title>Intel Arc Pro B50 - zmiany</title>
      <link>https://baal.ar76.eu/arek/intel-arc-pro-b50-zmiany</link>
      <description>&lt;![CDATA[Ciekawa niespodzianka po aktualizacji kernela z 6.14 na 6.17.&#xA;&#xA;Mój pecet nie ma obsługi ReBAR, co znaczy, że CPU miał okno komunikacji z VRAM ograniczone do 256MB. &#xA;&#xA;Okazuje się jednak, że kernel 6.17 potrafi zignorować braki informacji z UEFI  i samodzielnie odgadnąć potrzebę przemapowania zasobów na PCI.&#xA;dmesg pokazuje serię powtarzających się komunikatów o alokacji zasobów PCI poprzetykana wpisami typu&#xA;&#xA;[    0.356844] PCI: No. 4 try to assign unassigned res&#xA;&#xA;I czwarta próba zakończyła się powodzeniem.&#xA;&#xA;LM Studio bez problemu obsłużyło model gtp-oss:20b.&#xA;&#xA;Dodatkowo libva zaczęło funkcjonować poprawnie i w końcu mam sprzętowe dekodowanie video av1.&#xA;&#xA;Jak się okazuje, Linux naprawdę pozwala przywrócić do użycia stare pecety.&#xA;&#xA;linux &#xA;&#xA;@arkr]]&gt;</description>
      <content:encoded><![CDATA[<p>Ciekawa niespodzianka po aktualizacji kernela z 6.14 na 6.17.</p>

<p>Mój pecet nie ma obsługi ReBAR, co znaczy, że CPU miał okno komunikacji z VRAM ograniczone do 256MB.</p>

<p>Okazuje się jednak, że kernel 6.17 potrafi zignorować braki informacji z UEFI  i samodzielnie odgadnąć potrzebę przemapowania zasobów na PCI.
<code>dmesg</code> pokazuje serię powtarzających się komunikatów o alokacji zasobów PCI poprzetykana wpisami typu</p>

<pre><code>[    0.356844] PCI: No. 4 try to assign unassigned res
</code></pre>

<p>I czwarta próba zakończyła się powodzeniem.</p>

<p>LM Studio bez problemu obsłużyło model gtp-oss:20b.</p>

<p>Dodatkowo libva zaczęło funkcjonować poprawnie i w końcu mam sprzętowe dekodowanie video av1.</p>

<p>Jak się okazuje, Linux naprawdę pozwala przywrócić do użycia stare pecety.</p>

<p><a href="/arek/tag:linux" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">linux</span></a></p>

<p><a href="https://mastodon.social/@ark_r" rel="nofollow">@ark_r</a></p>
]]></content:encoded>
      <guid>https://baal.ar76.eu/arek/intel-arc-pro-b50-zmiany</guid>
      <pubDate>Sun, 08 Feb 2026 13:14:10 +0000</pubDate>
    </item>
    <item>
      <title>Power-off problem</title>
      <link>https://baal.ar76.eu/arek/power-off-problem</link>
      <description>&lt;![CDATA[Przy którejś aktualizacji kernela (gdzieś między 5.19, a 6.1) mój pecet nabył ciekawego problemu: wyłączanie kończyło się jak lata temu na windows, gdzie użytkownika żegnał napis &#34;Teraz możesz wyłączyć komputer&#34;.&#xA;&#xA;Cała procedura shutdown-u przechodziła, ale na samym końcu, gdy kernel wywołuje acpipoweroff, proces się zawieszał i musiałem wyłączać zasilanie przytrzymując kilka sekund przycisk na obudowie.&#xA;&#xA;Przy okazji - usypianie (sleep/suspend) również przestało działać, ale tego akurat nie używałem zbyt często, więc przeszkadzało mi znacznie mniej.&#xA;&#xA;Historia poszukiwania rozwiązania jest nudna jak flaki z olejem (ale sporo ciekawych rzeczy o ACPI można się dowiedzieć), więc od razu przejdę do rozwiązania (a przynajmniej czegoś, co u mnie to zadziałało).&#xA;&#xA;Zgodnie z https://www.kernel.org/doc/Documentation/Intel-IOMMU.txt mój oparty na CPU Intela pecet może obsługiwać IOMMU dzięki stosownemu modułowi i zdaje się, że właśnie w kernelu 6.x dostarczanemu z Ubuntu ta funkcjonalność została włączona.&#xA;&#xA;Pecet ma zintegrowaną grafikę Intela (której nie używam) i dodatkowo kartę NVIDIA. I chyba coś z tą nieużywaną grafiką Intela szwankowało.&#xA;&#xA;We wskazanym dokumencie można przeczytać:&#xA;&#xA;Graphics Problems?&#xA;------------------&#xA;If you encounter issues with graphics devices, you can try adding&#xA;option inteliommu=igfxoff to turn off the integrated graphics engine.&#xA;If this fixes anything, please ensure you file a bug reporting the problem.&#xA;&#xA;I właśnie owo inteliommu=igfxoff rozwiązuje problem. Wartość off również, ale podobno iommu poprawia bezpieczeństwo (i przydaje się, gdy się uruchamia maszyny wirtualne).&#xA;&#xA;W każdym razie - zadziałało. Po dodaniu do parametrów kernela w grub problem ustąpił i shutdown -P wyłącza zasilanie. &#xA;&#xA;#linux #poweroff #iommu&#xA;&#xA;@arkr]]&gt;</description>
      <content:encoded><![CDATA[<p>Przy którejś aktualizacji kernela (gdzieś między 5.19, a 6.1) mój pecet nabył ciekawego problemu: wyłączanie kończyło się jak lata temu na windows, gdzie użytkownika żegnał napis “Teraz możesz wyłączyć komputer”.</p>

<p>Cała procedura shutdown-u przechodziła, ale na samym końcu, gdy kernel wywołuje acpi_poweroff, proces się zawieszał i musiałem wyłączać zasilanie przytrzymując kilka sekund przycisk na obudowie.</p>

<p>Przy okazji – usypianie (sleep/suspend) również przestało działać, ale tego akurat nie używałem zbyt często, więc przeszkadzało mi znacznie mniej.</p>

<p>Historia poszukiwania rozwiązania jest nudna jak flaki z olejem (ale sporo ciekawych rzeczy o ACPI można się dowiedzieć), więc od razu przejdę do rozwiązania (a przynajmniej czegoś, co u mnie to zadziałało).</p>

<p>Zgodnie z <a href="https://www.kernel.org/doc/Documentation/Intel-IOMMU.txt" rel="nofollow">https://www.kernel.org/doc/Documentation/Intel-IOMMU.txt</a> mój oparty na CPU Intela pecet może obsługiwać IOMMU dzięki stosownemu modułowi i zdaje się, że właśnie w kernelu 6.x dostarczanemu z Ubuntu ta funkcjonalność została włączona.</p>

<p>Pecet ma zintegrowaną grafikę Intela (której nie używam) i dodatkowo kartę NVIDIA. I chyba coś z tą nieużywaną grafiką Intela szwankowało.</p>

<p>We wskazanym dokumencie można przeczytać:</p>

<pre><code>Graphics Problems?
------------------
If you encounter issues with graphics devices, you can try adding
option intel_iommu=igfx_off to turn off the integrated graphics engine.
If this fixes anything, please ensure you file a bug reporting the problem.
</code></pre>

<p>I właśnie owo <code>intel_iommu=igfx_off</code> rozwiązuje problem. Wartość <code>off</code> również, ale podobno iommu poprawia bezpieczeństwo (i przydaje się, gdy się uruchamia maszyny wirtualne).</p>

<p>W każdym razie – zadziałało. Po dodaniu do parametrów kernela w grub problem ustąpił i <code>shutdown -P</code> wyłącza zasilanie.</p>

<p><a href="/arek/tag:linux" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">linux</span></a> <a href="/arek/tag:poweroff" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">poweroff</span></a> <a href="/arek/tag:iommu" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">iommu</span></a></p>

<p><a href="https://mastodon.social/@ark_r" rel="nofollow">@ark_r</a></p>
]]></content:encoded>
      <guid>https://baal.ar76.eu/arek/power-off-problem</guid>
      <pubDate>Sat, 02 Nov 2024 20:52:49 +0000</pubDate>
    </item>
    <item>
      <title>Linux disk quota</title>
      <link>https://baal.ar76.eu/arek/linux-disk-quota</link>
      <description>&lt;![CDATA[Z jakiś powodów zapragnąłem ustawić w Linuksie limity na użycie dysku. Pominę opis poszukiwania informacji w Internecie, ale w skrócie - z nieznanych mi powodów w zdecydowanej przewadze opisywane są jakieś starocie. W artykule pokrótce jak to wygląda w aktualnych wersjach jądra. !--more--&#xA;&#xA;Kiedyś&#xA;&#xA;Kiedyś było tak: poza filesystemem XFS, informacje o użyciu dysku i inodach był zapisywany w aplikach quota.user i quota.group. Potem coś poprawiono i nowe pliki nazywają się aquota.user i aquota.group.&#xA;&#xA;Żeby limitowanie działało, dysk należy zamontować z opcjami usrquota i grpquota. Kolejna nowość wprowadziła usprawnienie dotyczące sposobu rejestrowania aktualnego użycia dysku dla użytkownika. Opcje montowania zmieniły się na usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv1 i dzięki temu pliki ze zużyciem dysku aktualizowane razem ze zmianami w plikach i przechodzą przez journal.&#xA;&#xA;Potem trzeba uruchomić&#xA;sudo quotacheck -cugv /mnt/   &#xA;i po&#xA;sudo quotaon -ug /mnt&#xA;możliwość limitowania dysku jest włączona.&#xA;&#xA;Ale quotacheck wypisało mi komunikat:&#xA;quotacheck: Your kernel probably supports ext4 quota feature but you are using external quota files. Please switch your filesystem to use ext4 quota feature as external quota files on ext4 are deprecated.&#xA;I tak dowiedziałem się, że ext4 wspiera pliki limitów jako ukryte inody, tylko trzeba tę opcję włączyć.&#xA;&#xA;A tak to wygląda teraz&#xA;&#xA;Zatem, jak to wygląda w na nowym jądrze. &#xA;&#xA;Założenia: mam dysk... np. /dev/loop30, montuję go na /mnt.&#xA;&#xA;Kernel został zbudowany z opcjami&#xA;CONFIGQUOTA=y&#xA;CONFIGQUOTATREE=m&#xA;&#xA;Mamy narzędzia do obsługi limitów (np. apt install quota na Ubuntu)&#xA;&#xA;Mamy niezbędne moduły kernela (sudo modprobe quotav2 działa).&#xA;&#xA;Procedura jest następująca:&#xA;&#xA;Tworzę filesystem (ext4):&#xA;sudo mkfs.ext4 /dev/loop30&#xA;Włączam wewnętrzną obsługę limitów (to jest ta najnowsza nowość)&#xA;&#xA;sudo tune2fs -O quota /dev/loop30&#xA;Feature quota oznacza: &#34;Enable internal file system quota inodes&#34;&#xA;&#xA;A teraz jeszcze jedno: można mieć nie tylko limity per user i per grupa ale również limit per projekt. Nie mam pojęcia jak działa to ostatnie, ale można je wszystkie niezależnie włączać i wyłączać (patrz man tune2fs):&#xA;sudo tune2fs -Q usrquota,grpquota,prjquota /dev/loop30&#xA;Można sprawdzić, że limity zostały włączone wywołując:&#xA;sudo tune2fs -l /dev/loop30 | grep &#34;Filesystem features&#34;&#xA;&#xA;Filesystem features:      hasjournal extattr resizeinode dirindex filetype extent 64bit flexbg sparsesuper largefile hugefile dirnlink extraisize quota metadatacsum project&#xA;Obecność słowa &#34;quota&#34; oznacza, że feature jest włączony.&#xA;&#xA;Kolejny krok: montowanie filesystemu:&#xA;sudo mount -o usrquota,grpquota,prjquota /dev/loop30 /mnt&#xA;Pewnie dobrze jest sobie ten mountpoint wpisać do /etc/fstab, ale chodzi po prostu o to, żeby włączyć dla tego dysku odpowiednie zliczanie limitów.&#xA;&#xA;Żadnych plików limitów w /mnt nie widać, ale możemy uruchomić&#xA;sudo repquota /mnt&#xA;I zobaczymy zużycie  tego dysku przez użytkownika root (bo tylko jego obiekty są na razie w tym katalogu).&#xA;Przy okazji, repquota z parametrem -g pokaże limity dla grup, a -P limity dla projektów (jak ustalę jak zrobić taki projekt, to na pewno to opiszę).&#xA;&#xA;Limity można ustawić poleceniami setquota i edquota (ale to już standard - każdy manual opisuje jak to zrobić).&#xA;&#xA;Wygląda na to, że jeśli użyliśmy podczas montowania opcji usrquota, grpquta etc, to limity są od razu egzekwowane (bez konieczności włączania ich za pomocą quotaon), co można sprawdzić używając opcji -p:&#xA;sudo quotaon -p /mnt&#xA;Limity grupy na /mnt (/dev/loop30) są włączone&#xA;Limity użytkownika na /mnt (/dev/loop30) są włączone&#xA;Limity project na /mnt (/dev/loop30) są włączone&#xA;I działa.&#xA;&#xA;Linki:&#xA;&#xA;(trochę stare już) https://wiki.archlinux.org/title/diskquota&#xA;(trochę nowsze) https://www.digitalocean.com/community/tutorials/how-to-set-filesystem-quotas-on-ubuntu-20-04&#xA;(magiczny feature, o którym tak niewiele) https://ext4.wiki.kernel.org/index.php/Quota&#xA;&#xA;#linux #quota #administration&#xA;&#xA;@arkr]]&gt;</description>
      <content:encoded><![CDATA[<p>Z jakiś powodów zapragnąłem ustawić w Linuksie limity na użycie dysku. Pominę opis poszukiwania informacji w Internecie, ale w skrócie – z nieznanych mi powodów w zdecydowanej przewadze opisywane są jakieś starocie. W artykule pokrótce jak to wygląda w aktualnych wersjach jądra. </p>

<h2 id="kiedyś">Kiedyś</h2>

<p>Kiedyś było tak: poza filesystemem XFS, informacje o użyciu dysku i inodach był zapisywany w aplikach <code>quota.user</code> i <code>quota.group</code>. Potem coś poprawiono i nowe pliki nazywają się <code>aquota.user</code> i <code>aquota.group</code>.</p>

<p>Żeby limitowanie działało, dysk należy zamontować z opcjami <code>usrquota</code> i <code>grpquota</code>. Kolejna nowość wprowadziła usprawnienie dotyczące sposobu rejestrowania aktualnego użycia dysku dla użytkownika. Opcje montowania zmieniły się na <code>usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv1</code> i dzięki temu pliki ze zużyciem dysku aktualizowane razem ze zmianami w plikach i przechodzą przez journal.</p>

<p>Potem trzeba uruchomić</p>

<pre><code class="language-bash">sudo quotacheck -cugv /mnt/   
</code></pre>

<p>i po</p>

<pre><code class="language-bash">sudo quotaon -ug /mnt
</code></pre>

<p>możliwość limitowania dysku jest włączona.</p>

<p>Ale <code>quotacheck</code> wypisało mi komunikat:</p>

<pre><code>quotacheck: Your kernel probably supports ext4 quota feature but you are using external quota files. Please switch your filesystem to use ext4 quota feature as external quota files on ext4 are deprecated.
</code></pre>

<p>I tak dowiedziałem się, że ext4 wspiera pliki limitów jako ukryte inody, tylko trzeba tę opcję włączyć.</p>

<h2 id="a-tak-to-wygląda-teraz">A tak to wygląda teraz</h2>

<p>Zatem, jak to wygląda w na nowym jądrze.</p>

<p>Założenia: mam dysk... np. <code>/dev/loop30</code>, montuję go na <code>/mnt</code>.</p>

<p>Kernel został zbudowany z opcjami
* CONFIG<em>QUOTA=y
* CONFIG</em>QUOTA_TREE=m</p>

<p>Mamy narzędzia do obsługi limitów (np. <code>apt install quota</code> na Ubuntu)</p>

<p>Mamy niezbędne moduły kernela (<code>sudo modprobe quota_v2</code> działa).</p>

<p>Procedura jest następująca:</p>

<p>Tworzę filesystem (ext4):</p>

<pre><code class="language-bash">sudo mkfs.ext4 /dev/loop30
</code></pre>

<p>Włączam wewnętrzną obsługę limitów (to jest ta najnowsza nowość)</p>

<pre><code class="language-bash">sudo tune2fs -O quota /dev/loop30
</code></pre>

<p>Feature <code>quota</code> oznacza: “Enable internal file system quota inodes”</p>

<p>A teraz jeszcze jedno: można mieć nie tylko limity <em>per user</em> i <em>per grupa</em> ale również limit <em>per projekt</em>. Nie mam pojęcia jak działa to ostatnie, ale można je wszystkie niezależnie włączać i wyłączać (patrz <code>man tune2fs</code>):</p>

<pre><code class="language-bash">sudo tune2fs -Q usrquota,grpquota,prjquota /dev/loop30
</code></pre>

<p>Można sprawdzić, że limity zostały włączone wywołując:</p>

<pre><code class="language-bash">sudo tune2fs -l /dev/loop30 | grep &#34;Filesystem features&#34;

Filesystem features:      has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize quota metadata_csum project
</code></pre>

<p>Obecność słowa “quota” oznacza, że feature jest włączony.</p>

<p>Kolejny krok: montowanie filesystemu:</p>

<pre><code class="language-bash">sudo mount -o usrquota,grpquota,prjquota /dev/loop30 /mnt
</code></pre>

<p>Pewnie dobrze jest sobie ten mountpoint wpisać do /etc/fstab, ale chodzi po prostu o to, żeby włączyć dla tego dysku odpowiednie zliczanie limitów.</p>

<p>Żadnych plików limitów w /mnt nie widać, ale możemy uruchomić</p>

<pre><code class="language-bash">sudo repquota /mnt
</code></pre>

<p>I zobaczymy zużycie  tego dysku przez użytkownika root (bo tylko jego obiekty są na razie w tym katalogu).
Przy okazji, <code>repquota</code> z parametrem <code>-g</code> pokaże limity dla grup, a <code>-P</code> limity dla projektów (jak ustalę jak zrobić taki projekt, to na pewno to opiszę).</p>

<p>Limity można ustawić poleceniami <code>setquota</code> i <code>edquota</code> (ale to już standard – każdy manual opisuje jak to zrobić).</p>

<p>Wygląda na to, że jeśli użyliśmy podczas montowania opcji usrquota, grpquta etc, to limity są od razu egzekwowane (bez konieczności włączania ich za pomocą <code>quotaon</code>), co można sprawdzić używając opcji <code>-p</code>:</p>

<pre><code class="language-bash">sudo quotaon -p /mnt
Limity grupy na /mnt (/dev/loop30) są włączone
Limity użytkownika na /mnt (/dev/loop30) są włączone
Limity project na /mnt (/dev/loop30) są włączone
</code></pre>

<p>I działa.</p>

<p>Linki:</p>
<ul><li>(trochę stare już) <a href="https://wiki.archlinux.org/title/disk_quota" rel="nofollow">https://wiki.archlinux.org/title/disk_quota</a></li>
<li>(trochę nowsze) <a href="https://www.digitalocean.com/community/tutorials/how-to-set-filesystem-quotas-on-ubuntu-20-04" rel="nofollow">https://www.digitalocean.com/community/tutorials/how-to-set-filesystem-quotas-on-ubuntu-20-04</a></li>
<li>(magiczny feature, o którym tak niewiele) <a href="https://ext4.wiki.kernel.org/index.php/Quota" rel="nofollow">https://ext4.wiki.kernel.org/index.php/Quota</a></li></ul>

<p><a href="/arek/tag:linux" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">linux</span></a> <a href="/arek/tag:quota" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">quota</span></a> <a href="/arek/tag:administration" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">administration</span></a></p>

<p><a href="https://mastodon.social/@ark_r" rel="nofollow">@ark_r</a></p>
]]></content:encoded>
      <guid>https://baal.ar76.eu/arek/linux-disk-quota</guid>
      <pubDate>Thu, 01 Dec 2022 19:37:22 +0000</pubDate>
    </item>
    <item>
      <title>Czym naprawdę są kontenery</title>
      <link>https://baal.ar76.eu/arek/czym-naprawde-sa-kontenery</link>
      <description>&lt;![CDATA[Prawdopodobnie każdy zainteresowany jakkolwiek tematami IT słyszał już o czymś takim jak kontenery i od razu wie, że chodzi o takie rzeczy jak docker albo kubernetes. Wiadomo, że są obrazy w repozytoriach i można je łatwo pobierać i instalować. &#xA;&#xA;Kontenery przedstawiane są jako lekkie maszyny wirtualne. Taka wirtualka, która nie marnuje RAM-u na utrzymywanie całego systemu operacyjnego gościa - kilka takich &#34;wirtualek&#34; (kontenerów) współdzieli system operacyjny, ale zapewnia separację dla uruchamianych wewnątrz procesów.&#xA;&#xA;Oprogramowanie takie jak docker (zwłaszcza z jego modułem swarm) oraz kubernetes, to całkiem spore kombajny robiące mnóstwo rzeczy, których zakres funkcjonalności przekracza znacznie sam temat uruchamiania kontenera. Do tego obrosły jeszcze w cała gamę narzędzi, które wspierają administratora (lub developera) w procesach wdrażania, monitorowania i zarządzania usługami. Powstały również rozwiązania dla systemów innych niż Linux, np. Docker Desktop dla Windows, czy też jego odpowiednik dla urządzeń Apple&#39;a.&#xA;&#xA;Internet pełen jest artykułów, poradników, prezentacji etc. opisujących zalety konteneryzacji i ogólnie są to bardzo przydatne materiały, ale przy okazji każdej technologii dobrze jest (czasem) wiedzieć czym ona naprawdę jest i jak działa. Taka wiedza przydaje się zwłaszcza, gdy coś przestanie działać i żaden &#34;wizard&#34; i &#34;kreator&#34; nie wiedzieć czemu akurat nie potrafi pomóc.&#xA;&#xA;Czym zatem jest naprawdę kontener i co dzieje się pod tymi wszystkimi warstwami skądinąd bardzo przydatnych narzędzi?!--more--&#xA;&#xA;Jak nie trudno się domyślić, mam zamiar odpowiedzieć na to pytanie, ale żeby nie zanudzić ewentualnego czytelnika, zminimalizuję teorię i postaram się przedstawić praktyczny przykład utworzenia kontenera korzystając wyłącznie z dwóch narzędzi dostępnych praktycznie w każdym systemie Linux.&#xA;&#xA;Tymi narzędziami są:&#xA;&#xA;ip - program z pakietu iproute2 (na pewno jest już zainstalowany jeśli środowisko ma konfigurowaną sieć),&#xA;unshare program z pakietu util-linux (przynajmniej tak pakiet ten nazywa się w Ubuntu, jeśli which unshare nie potrafi go znaleźć, to należy go doinstalować poleceniem sudo apt-get install util-linux).&#xA;&#xA;Zatem wracam do pytania...&#xA;&#xA;Co to jest kontener?&#xA;&#xA;Na poziomie systemu operacyjnego, kontener to nic innego jak jeden lub kilka procesów, które zostały uruchomione taki sposób, żeby nie współdzielić globalnych przestrzeni nazw z innymi procesami.&#xA;&#xA;O co chodzi z tymi przestrzeniami nazw?&#xA;&#xA;Na to pytanie odpowiada dokumentacja linuksa, man 7 namespaces:&#xA;&#xA;  A namespace wraps a global system resource in an abstraction that&#xA;  makes it appear to the processes within the namespace that they&#xA;  have their own isolated instance of the global resource.  Changes&#xA;  to the global resource are visible to other processes that are&#xA;  members of the namespace, but are invisible to other processes.&#xA;  One use of namespaces is to implement containers.&#xA;&#xA;Zatem system operacyjny ma ileś typów zasobów i te zasoby dostępne są dla procesu w ramach przestrzeni nazw. Jeśli utworzymy dla procesu dedykowaną przestrzeń nazw, to nie będzie on widział identyfikatorów zasobów z przestrzeni globalnej i nie będzie mógł jej zmieniać. Dla określonego typu zasobów, proces ma swój odizolowany świat i nie widzi niczego poza nim.&#xA;&#xA;Te typy zasobów, to:&#xA;&#xA;Cgroup - grupy kontrolne; mechanizm w Linuksie, który pozwala stwarzać ograniczenia dla procesów z grupy, np. limitować CPU lub RAM,&#xA;IPC - obiekty System V IPC i kolejki POSIX (tymi pierwszymi można zarządzać przy pomocy poleceń ipcs, ipcmk i ipcrm),&#xA;Network - Urządzenia sieciowe (np. eth0), stosy, porty, tablice routingu, etc),&#xA;Mount - punkty montowania (czyli w wydzielonej przestrzeni nazw system plików może wyglądać zupełnie inaczej niż w przestrzeni globalnej - nawet katalog główny /)&#xA;PID - identyfikatory procesów (można mieć wiele procesów o tym samym identyfikatorze, jeśli procesy te są w różnych przestrzeniach nazw)&#xA;Time - czas systemowy&#xA;User - identyfikatory uzytkowników i grup (UID-y i GID-y); np. w dedykowanej przestrzeni nazw można mieć użytkownika o identyfikatorze zero, który wcale nie jest root&#39;em w systemie),&#xA;UTS - nazwa hosta&#xA;&#xA;Czym zatem jest kontener? To proces, któremu wydzielimy namespace&#39;y. &#xA;&#xA;Weźmy na przykład przestrzenie PID, Mount i Network. Taki proces w wyizolowanych przestrzeniach dostanie PID 1 (jeśli jest pierwszym procesem w tej przestrzeni) - zupełnie jak proces init w przestrzeni globalnej. Jeśli ma oddzielną przestrzeń Mount, to znaczy, że może mieć zupełnie własną strukturę plików, poczynając od katalogu głównego. W przestrzeni Network będzie widział inne interfejsy sieciowe, z innymi adresami i może na nich otwierać porty, które nie będą kolidowały z otwartymi portami w innych przestrzeniach (czyli robię dwie przestrzenie i w obu uruchamiam nginx&#39;a na porcie 80 i oba procesy uruchamiają się i działają jednocześnie).&#xA;&#xA;Dokładając kolejne przestrzenie rzeczywiście dochodzimy do poziomu, gdzie świat procesu jest prawie tak autonomiczny jak w przypadku maszyn wirtualnych (o ile oczywiście w implementacji Linuksa nie ma błędów i proces nie ma możliwości odkłamania tej rzeczywistości).&#xA;&#xA;W zasadzie mechanizm działania kontenerów powinien być już jasny. Taki docker czy kubernetes (a dokładniej containerd, z którego korzystają, a jeszcze dokładniej program runc, który jest uruchamiany podczas startu każdego kontenera) właśnie zajmuje się na samym dole wydzieleniem namespaceów dla nowego kontenera i uruchomieniem w tej dedykowanej przestrzeni głównego procesu naszego kontenera.&#xA;&#xA;Ale żeby to powtórzyć nie jest potrzebny ani docker, ani containerd, ani nawet wspomniany runc (kiedyś napiszę o tych narzędziach). &#xA;&#xA;Za cały proces wydzielania przestrzeni nazw odpowiada praktycznie jedna funkcja systemowa Linuksa: unshare. A wspomniane na wstępie narzędzie o tej nazwie jest wygodną nakładką, dzięki której można z niej skorzystać z poziomu shella i nie trzeba od razu programować w C.&#xA;&#xA;Coś o tworzeniu procesów w Linuksie&#xA;&#xA;Jak można przeczytać w każdej mądrej książce o Uniksie, w tym systemie każdy nowy proces powstaje poprzez uruchomienie funkcji systemowej fork (wyjątkiem jest pierwszy proces - ten z pid&#39;em 1; jego jakoś magicznie tworzy kernel).&#xA;&#xA;Funkcja fork tworzy koncepcyjnie idealną kopię procesu, który ją wywołał - współdzielą wszystko - otwarte pliki, pamięć, kontekst użytkownika (realny i efektywny UID etc).&#xA;&#xA;Jedyna różnica, która się między nimi pojawia (o ile o czymś nie zapomniałem) to PID i PPID (parent pid). W momencie powrotu z tej funkcji, do procesu rodzica zwracany jest pid nowoutworzonego procesu potomka, a w procesie potomka zwracane jest zero. Dzięki temu, w kodzie wiemy, w którym procesie jesteśmy (bo wszystko - np. wartości zmiennych - jest takie samo). Oczywiście PID procesu rodzica jest niezmieniony, a potomek dostał swój nowy pid i jego parent pid wskazuje na proces rodzica.&#xA;&#xA;W pewnym momencie w Linuksie taki mechanizm okazał się dobry, ale niewystarczający - Linux zaczął wspierać wątki.&#xA;&#xA;Powstała więc bardziej ogólna funkcja systemowa clone, w której można wyspecyfikować co proces potomny ma współdzielić ze swoim przodkiem, a co ma mieć osobne. Stary fork jest szczególnym przypadkiem wywołania clone (w kernelu oba syscalle obsługuje ten sam kod).&#xA;&#xA;A potem (nie analizowałem dokładnej chronologii) powstała kolejna funkcja systemowa - unshare - która pozwala w większym stopniu odciąć się od procesu nadrzędnego. Już nie tylko w momencie wykonywania forka.&#xA;&#xA;I na wykorzystaniu tej funkcji skupię się w części praktycznej tego artykułu.&#xA;&#xA;Prymitywny kontener&#xA;&#xA;Kontenery znane są przede wszystkim z tego, że mają osobny system plików (wczytywany z obrazu), ale to jest akurat najbardziej czasochłonne w przygotowaniu - trzeba mieć albo statycznie zlinkowany program, albo przygotować całą strukturę /usr/lib z bibliotekami, a na koniec jeszcze zatroszczyć się o zawartość katalogów /etc czy /dev, bez których niewiele rzeczy zadziała. A ogólnie jest to najmniej interesujące, bo po prostu można zamontować gdzieś cała strukturę systemu plików (np. z obrazu ISO) i tu się kończy magia osobnego filesystemu.&#xA;&#xA;Przy okazji - wyizolowanie systemu plików to najstarszy klocek &#34;konteneryzacji&#34;. We wszystkich implementacjach Unix jest polecenie chroot, które właśnie odpowiada za uruchomienie procesu z innym systemem plików.&#xA;&#xA;Skupię się więc na uruchomieniu procesu w jego własnej przestrzeni identyfikatorów procesów (PIDy) a potem dodam do tego wyizolowaną sieć.&#xA;&#xA;Zatem &#34;pierwszy kontener&#34;:&#xA;&#xA;sudo unshare -p -f bash&#xA;echo $$&#xA;1&#xA;Pierwsza linijka to uruchomienie bash&#39;a w wydzielonej przestrzeni PID, o czym mówi parametr -p. Parametr -f instruuje program unshare, że powinien zrobić fork&#39;a przed uruchomieniem docelowego programu, co z różnych powodów jest bardzo wskazane.&#xA;&#xA;Tu od razu skierowanie do manuala programu unshare. Jak widać z listy parametrów możemy nim wydzielić każdą dostępną w systemie przestrzeń.&#xA;&#xA;Wracając do powyższej sesji z pracy kontenera: polecenie echo $$ (czyli wyświetl mój pid, gdzie &#34;mój&#34; dotyczy uruchomionego shella) pokazuje &#34;1&#34;.&#xA;Sukces! Shell działa we własnej przestrzeni identyfikatorów procesów.&#xA;&#xA;To sprawdźmy jakie inne procesy są (nie powinno być żadnego):&#xA;&#xA;ps ax&#xA;&#xA;I tu niespodzianka - ps pokazuje wszystkie procesy w systemie. Co ciekawe pid 1 wcale nie jest naszym bashem, tylko programem /sbin/init.&#xA;Wyjaśnienie tego jest dość proste, jeśli wiemy skąd program ps bierze informacje o procesach - z katalogu /proc (i wszystkich podkatalogów, których nazwy są numerkami - każdy numerek to pid programu).&#xA;&#xA;Czyli widać procesy, bo ps może je przeczytać z katalogu /proc, co nie znaczy, że nasz proces naprawdę widzi te PIDy. To jest łatwo sprawdzić wybierając sobie dowolny pid i wykonując polecenie kill:&#xA;&#xA;kill 4451&#xA;bash: kill: (4451) - Nie ma takiego procesu&#xA;&#xA;Polecenie ps wypisało miedzy innymi jakiś proces o PID 4451, ale próba wysłania sygnału do tego procesu kończy się błędem - w kontenerze takiego procesu nie ma.&#xA;&#xA;Te błędne informacje z polecenia ps jest bardzo łatwo poprawić i unshare ma na to specjalną opcję:  --mount-proc=/proc. Dodanie tego parametru spowoduje, że w przestrzeni nazw naszego procesu zostanie zamontowany jego własny katalog /proc, który będzie prawidłowo opisywał jego rzeczywistość.&#xA;&#xA;Zatem wychodzę z mojego kontenera poleceniem exit i uruchamiam jeszcze raz:&#xA;&#xA;$ sudo unshare -p --mount-proc=/proc -f bash&#xA;echo $$&#xA;1&#xA;ps ax&#xA;  PID TTY      STAT   TIME COMMAND&#xA;    1 pts/3    S      0:00 bash&#xA;    8 pts/3    R+     0:00 ps ax&#xA;I tak oto mam kontener! W moim kontenerze jest tylko proces bash i procesy, które z niego uruchamiam. Innych procesów nie widać.&#xA;&#xA;Uruchomienie polecenia ps poza kontenerem (w innym terminalu) pokaże, że system ma o wiele więcej uruchomionych procesów i pokaże również tego basha, który jest potomkiem programu unshare i wcale nie ma PIDu różnego 1.&#xA;&#xA;Przy okazji tak mimochodem wydzieliliśmy mu również osobną przestrzeń montowania - ma filesystem identyczny z globalnym ale z jedną różnica - jego katalog /proc jest inny niż /proc w systemie.&#xA;&#xA;Sieć w kontenerze&#xA;&#xA;Dodanie sieci od naszego kontenera wymaga kilku dodatkowych poleceń, ale wcale nie będzie dużo bardziej skomplikowane.&#xA;&#xA;Po pierwsze zobaczmy jakie interfejsy sieciowe widać w kontenerze:&#xA;&#xA;ip link&#xA;1: lo: LOOPBACK,UP,LOWERUP mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000&#xA;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&#xA;2: enp4s0: BROADCAST,MULTICAST,UP,LOWERUP mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000&#xA;....&#xA;&#xA;Ogólnie - te same, które są w systemie. Uruchamiam więc jeszcze raz  kontener ale z dodatkowym parametrem -n, co spowoduje wydzielenie dla procesu osobnej przestrzeni nazw dla sieci:&#xA;&#xA;exit&#xA;$ sudo unshare -p --mount-proc=/proc -f -n bash&#xA;ip link&#xA;1: lo: LOOPBACK mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000&#xA;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&#xA;&#xA;I teraz jest już tylko urządzenie loopback - nie jest nawet podniesione: state DOWN. Sieć nie jest zainicjalizowana.&#xA;&#xA;Do kontenera (czyli namespace procesu) można dodawać interfejsy sieciowe za pomocą polecenie ip: &#xA;sudo ip link set dev device netns namespace &#xA;&#xA;Zamiast device podajemy faktyczną nazwę urządzenia (np. eth2). Pewnym problemem jest kwestia nazwy namespace: utworzona przestrzeń jest anonimowa i nie ma nazwy (ip netns ls nie pokaże jej). Ale nie jest to dużym problemem. Zamiast nazwy można podać pid (ten &#34;prawdziwy&#34;, z głównej przestrzeni nazw) procesu wewnątrz kontenera. Wystarczy za pomocą htop top czy ps axf odszukać proces bash, który jest potomkiem programu unshare. W moim wypadku jest to 5030.&#xA;&#xA;I jeszcze druga kwestia - jaki interfejs przypisać od kontenera. W zasadzie da się dowolny, ale żeby miało to bardziej praktyczne zastosowanie można wykorzystać specjalny interfejs wirutalny - veth.&#xA;&#xA;Zacznijmy od jego utworzenia. Robimy to &#34;poza kontenerem&#34;. &#xA;&#xA;$ sudo ip link add siec0 type veth peer name siec1&#xA;&#xA;Polecenie ip link pokaże nam, że interfejs został utworzony (a nawet dwa interfejsy!):&#xA;&#xA;169: siec1@siec0: BROADCAST,MULTICAST,M-DOWN mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000&#xA;    link/ether 2a:14:f2:52:01:0d brd ff:ff:ff:ff:ff:ff&#xA;170: siec0@siec1: BROADCAST,MULTICAST,M-DOWN mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000&#xA;    link/ether aa:e1:2b:2a:b3:27 brd ff:ff:ff:ff:ff:ff&#xA;&#xA;Interfejs veth jest specyficzny. Otóż posiada dwa końce (nadałem im nazwy siec0 i siec1). Wysłanie ramki na jeden z końców powoduje, że ramka pojawia się natychmiast na drugim. Jak widać - system widzi oba końce pod nazwami siec0@siec1 i siec1@siec0.&#xA;&#xA;Zaleta tego interfejsu jest taka, że końce mogą być w różnych przestrzeniach nazw.  Przypisuję jeden z końców do mojego kontenera (którego przestrzeń nazw jest identyfikowana pidem procesu, który w nim działa):&#xA;&#xA;sudo ip link set dev siec0 netns 5030&#xA;&#xA;Ponowne wywołanie ip link pokaże, że interfejs siec0 (id 170) zniknął, a interfejs siec0 (id 169) nieznacznie zmienił nazwę:&#xA;&#xA;169: siec1@if170: BROADCAST,MULTICAST mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000&#xA;    link/ether 2a:14:f2:52:01:0d brd ff:ff:ff:ff:ff:ff link-netnsid 3&#xA;&#xA;Za to uruchomienie ip link w kontenerze pokaże:&#xA;&#xA;ip link&#xA;1: lo: LOOPBACK mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000&#xA;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&#xA;170: siec0@if169: BROADCAST,MULTICAST mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000&#xA;    link/ether aa:e1:2b:2a:b3:27 brd ff:ff:ff:ff:ff:ff link-netnsid 0&#xA;&#xA;Jak widać, interfejs o id 170 został przeniesiony do kontenera (a dokładniej do jego przestrzeni nazw).&#xA;&#xA;Jak wykorzystać ten interfejs? Można zrobić most (bridge). W systemie (w globalnej przestrzeni nazw) wyknuję kolejno:&#xA;&#xA;$ sudo ip link add most type bridge&#xA;$ sudo ip link set master most dev siec1&#xA;Pierwsze polecenie tworzy most, a drugie przypina do niego urządzenie siec1 (czyli koniec veth, widoczny w systemie). Teraz należy nadać adres.&#xA;&#xA;$ sudo ip addr add 10.11.0.1/24 dev most&#xA;$ sudo ip link set dev most up&#xA;$ sudo ip link set dev siec1 up&#xA;&#xA;Kolejno:&#xA;&#xA;nadawany jest adres ip dla mostu,&#xA;urządzenie mostu jest aktywowane,&#xA;aktywowany jest interfejs urządzenia veth.&#xA;&#xA;W kontenerze natomiast uruchamiam:&#xA;&#xA;ip addr add 10.11.0.2/24 dev siec0&#xA;ip link set dev siec0 up&#xA;&#xA;Nadawany jest adres dla interfejsu siec0 (inny niż mostu, ale z tej samej podsieci) i również podnoszony jest interfejs.&#xA;&#xA;Od tego momentu polecenie ping 10.11.0.1 uruchomione na kontenerze oraz ping 10.11.0.2 uruchomione poza kontenerem pokazuje, że pakiety docierają na drugi koniec.&#xA;&#xA;Co więcej, jeśli w kontenerze uruchomię nc -l 80 (netcat - otwarcie portu 80), to poza kontenerem tego otwartego portu nie widać (nie pojawia się w wynikach polecenia sudo ss -tnlp), ale można się z nim połączyć na adresie kontenera, tj. 10.11.0.2.&#xA;&#xA;Mam pełnoprawny kontener z wirtualną siecią.&#xA;&#xA;Co dalej?&#xA;&#xA;Można w ten sam sposób tworzyć kolejne kontenery. Dla każdego dodawać kolejne urządzenie veth, dołączone jednym końcem do mostu.&#xA;&#xA;W systemie można włączyć przekazywanie pakietów i dodać maskaradę w iptables, co spowoduje, że kontener będzie mógł komunikować się nie tylko z innymi kontenerami i systemem, ale także z siecią LAN (i Internetem).&#xA;&#xA;Można również za pomocą iptables i jego tablicy nat przekierować przychodzące pakiety do uruchomionych kontenerów.&#xA;&#xA;Na koniec można też wykorzystać system plików OverlayFS i utworzyć dla kontenera warstwy struktur katalogów ze wspólną bazą read only.&#xA;&#xA;Dokładnie takie kroki wykonywane są przez serwer dockera, gdy uruchamiamy polecenie docker run.&#xA;&#xA;Doker przykrywa to zgrabnym zestawem parametrów w CLI.&#xA;Ale pod spodem wykonuje się właśnie to, co opisałem w niniejszym artykule i jak widać, nie jest to zbyt skomplikowane.&#xA;&#xA;Tagi: #kontener #docker #kubernetes #linux&#xA;&#xA;@arkr]]&gt;</description>
      <content:encoded><![CDATA[<p>Prawdopodobnie każdy zainteresowany jakkolwiek tematami IT słyszał już o czymś takim jak kontenery i od razu wie, że chodzi o takie rzeczy jak docker albo kubernetes. Wiadomo, że są obrazy w repozytoriach i można je łatwo pobierać i instalować.</p>

<p>Kontenery przedstawiane są jako lekkie maszyny wirtualne. Taka wirtualka, która nie marnuje RAM-u na utrzymywanie całego systemu operacyjnego gościa – kilka takich “wirtualek” (kontenerów) współdzieli system operacyjny, ale zapewnia separację dla uruchamianych wewnątrz procesów.</p>

<p>Oprogramowanie takie jak docker (zwłaszcza z jego modułem swarm) oraz kubernetes, to całkiem spore kombajny robiące mnóstwo rzeczy, których zakres funkcjonalności przekracza znacznie sam temat uruchamiania kontenera. Do tego obrosły jeszcze w cała gamę narzędzi, które wspierają administratora (lub developera) w procesach wdrażania, monitorowania i zarządzania usługami. Powstały również rozwiązania dla systemów innych niż Linux, np. Docker Desktop dla Windows, czy też jego odpowiednik dla urządzeń Apple&#39;a.</p>

<p>Internet pełen jest artykułów, poradników, prezentacji etc. opisujących zalety konteneryzacji i ogólnie są to bardzo przydatne materiały, ale przy okazji każdej technologii dobrze jest (czasem) wiedzieć czym ona naprawdę jest i jak działa. Taka wiedza przydaje się zwłaszcza, gdy coś przestanie działać i żaden “wizard” i “kreator” nie wiedzieć czemu akurat nie potrafi pomóc.</p>

<p>Czym zatem jest naprawdę kontener i co dzieje się pod tymi wszystkimi warstwami skądinąd bardzo przydatnych narzędzi?</p>

<p>Jak nie trudno się domyślić, mam zamiar odpowiedzieć na to pytanie, ale żeby nie zanudzić ewentualnego czytelnika, zminimalizuję teorię i postaram się przedstawić praktyczny przykład utworzenia kontenera korzystając wyłącznie z dwóch narzędzi dostępnych praktycznie w każdym systemie Linux.</p>

<p>Tymi narzędziami są:</p>
<ul><li><code>ip</code> – program z pakietu <strong>iproute2</strong> (na pewno jest już zainstalowany jeśli środowisko ma konfigurowaną sieć),</li>
<li><code>unshare</code> program z pakietu <strong>util-linux</strong> (przynajmniej tak pakiet ten nazywa się w Ubuntu, jeśli <code>which unshare</code> nie potrafi go znaleźć, to należy go doinstalować poleceniem <code>sudo apt-get install util-linux</code>).</li></ul>

<p>Zatem wracam do pytania...</p>

<h2 id="co-to-jest-kontener">Co to jest kontener?</h2>

<p>Na poziomie systemu operacyjnego, kontener to nic innego jak jeden lub kilka procesów, które zostały uruchomione taki sposób, żeby nie współdzielić globalnych przestrzeni nazw z innymi procesami.</p>

<p>O co chodzi z tymi przestrzeniami nazw?</p>

<p>Na to pytanie odpowiada dokumentacja linuksa, <a href="https://man7.org/linux/man-pages/man7/namespaces.7.html" rel="nofollow">man 7 namespaces</a>:</p>

<blockquote><p>A namespace wraps a global system resource in an abstraction that
makes it appear to the processes within the namespace that they
have their own isolated instance of the global resource.  Changes
to the global resource are visible to other processes that are
members of the namespace, but are invisible to other processes.
One use of namespaces is to implement containers.</p></blockquote>

<p>Zatem system operacyjny ma ileś typów zasobów i te zasoby dostępne są dla procesu w ramach przestrzeni nazw. Jeśli utworzymy dla procesu dedykowaną przestrzeń nazw, to nie będzie on widział identyfikatorów zasobów z przestrzeni globalnej i nie będzie mógł jej zmieniać. Dla określonego typu zasobów, proces ma swój odizolowany świat i nie widzi niczego poza nim.</p>

<p>Te typy zasobów, to:</p>
<ul><li>Cgroup – grupy kontrolne; mechanizm w Linuksie, który pozwala stwarzać ograniczenia dla procesów z grupy, np. limitować CPU lub RAM,</li>
<li>IPC – obiekty System V IPC i kolejki POSIX (tymi pierwszymi można zarządzać przy pomocy poleceń <code>ipcs</code>, <code>ipcmk</code> i <code>ipcrm</code>),</li>
<li>Network – Urządzenia sieciowe (np. eth0), stosy, porty, tablice routingu, etc),</li>
<li>Mount – punkty montowania (czyli w wydzielonej przestrzeni nazw system plików może wyglądać zupełnie inaczej niż w przestrzeni globalnej – nawet katalog główny <code>/</code>)</li>
<li>PID – identyfikatory procesów (można mieć wiele procesów o tym samym identyfikatorze, jeśli procesy te są w różnych przestrzeniach nazw)</li>
<li>Time – czas systemowy</li>
<li>User – identyfikatory uzytkowników i grup (UID-y i GID-y); np. w dedykowanej przestrzeni nazw można mieć użytkownika o identyfikatorze zero, który wcale nie jest root&#39;em w systemie),</li>
<li>UTS – nazwa hosta</li></ul>

<p>Czym zatem jest kontener? To proces, któremu wydzielimy namespace&#39;y.</p>

<p>Weźmy na przykład przestrzenie PID, Mount i Network. Taki proces w wyizolowanych przestrzeniach dostanie PID 1 (jeśli jest pierwszym procesem w tej przestrzeni) – zupełnie jak proces <code>init</code> w przestrzeni globalnej. Jeśli ma oddzielną przestrzeń Mount, to znaczy, że może mieć zupełnie własną strukturę plików, poczynając od katalogu głównego. W przestrzeni Network będzie widział inne interfejsy sieciowe, z innymi adresami i może na nich otwierać porty, które nie będą kolidowały z otwartymi portami w innych przestrzeniach (czyli robię dwie przestrzenie i w obu uruchamiam nginx&#39;a na porcie 80 i oba procesy uruchamiają się i działają jednocześnie).</p>

<p>Dokładając kolejne przestrzenie rzeczywiście dochodzimy do poziomu, gdzie świat procesu jest prawie tak autonomiczny jak w przypadku maszyn wirtualnych (o ile oczywiście w implementacji Linuksa nie ma błędów i proces nie ma możliwości odkłamania tej rzeczywistości).</p>

<p>W zasadzie mechanizm działania kontenerów powinien być już jasny. Taki docker czy kubernetes (a dokładniej <code>containerd</code>, z którego korzystają, a jeszcze dokładniej program <code>runc</code>, który jest uruchamiany podczas startu każdego kontenera) właśnie zajmuje się na samym dole wydzieleniem namespaceów dla nowego kontenera i uruchomieniem w tej dedykowanej przestrzeni głównego procesu naszego kontenera.</p>

<p>Ale żeby to powtórzyć nie jest potrzebny ani docker, ani containerd, ani nawet wspomniany runc (kiedyś napiszę o tych narzędziach).</p>

<p>Za cały proces wydzielania przestrzeni nazw odpowiada praktycznie jedna funkcja systemowa Linuksa: <code>unshare</code>. A wspomniane na wstępie narzędzie o tej nazwie jest wygodną nakładką, dzięki której można z niej skorzystać z poziomu shella i nie trzeba od razu programować w C.</p>

<h2 id="coś-o-tworzeniu-procesów-w-linuksie">Coś o tworzeniu procesów w Linuksie</h2>

<p>Jak można przeczytać w każdej mądrej książce o Uniksie, w tym systemie każdy nowy proces powstaje poprzez uruchomienie funkcji systemowej <code>fork</code> (wyjątkiem jest pierwszy proces – ten z pid&#39;em 1; jego jakoś magicznie tworzy kernel).</p>

<p>Funkcja <code>fork</code> tworzy koncepcyjnie idealną kopię procesu, który ją wywołał – współdzielą wszystko – otwarte pliki, pamięć, kontekst użytkownika (realny i efektywny UID etc).</p>

<p>Jedyna różnica, która się między nimi pojawia (o ile o czymś nie zapomniałem) to PID i PPID (parent pid). W momencie powrotu z tej funkcji, do procesu rodzica zwracany jest pid nowoutworzonego procesu potomka, a w procesie potomka zwracane jest zero. Dzięki temu, w kodzie wiemy, w którym procesie jesteśmy (bo wszystko – np. wartości zmiennych – jest takie samo). Oczywiście PID procesu rodzica jest niezmieniony, a potomek dostał swój nowy pid i jego parent pid wskazuje na proces rodzica.</p>

<p>W pewnym momencie w Linuksie taki mechanizm okazał się dobry, ale niewystarczający – Linux zaczął wspierać wątki.</p>

<p>Powstała więc bardziej ogólna funkcja systemowa <code>clone</code>, w której można wyspecyfikować co proces potomny ma współdzielić ze swoim przodkiem, a co ma mieć osobne. Stary <code>fork</code> jest szczególnym przypadkiem wywołania <code>clone</code> (w kernelu oba syscalle obsługuje ten sam kod).</p>

<p>A potem (nie analizowałem dokładnej chronologii) powstała kolejna funkcja systemowa – <code>unshare</code> – która pozwala w większym stopniu odciąć się od procesu nadrzędnego. Już nie tylko w momencie wykonywania forka.</p>

<p>I na wykorzystaniu tej funkcji skupię się w części praktycznej tego artykułu.</p>

<h2 id="prymitywny-kontener">Prymitywny kontener</h2>

<p>Kontenery znane są przede wszystkim z tego, że mają osobny system plików (wczytywany z obrazu), ale to jest akurat najbardziej czasochłonne w przygotowaniu – trzeba mieć albo statycznie zlinkowany program, albo przygotować całą strukturę <code>/usr/lib</code> z bibliotekami, a na koniec jeszcze zatroszczyć się o zawartość katalogów <code>/etc</code> czy <code>/dev</code>, bez których niewiele rzeczy zadziała. A ogólnie jest to najmniej interesujące, bo po prostu można zamontować gdzieś cała strukturę systemu plików (np. z obrazu ISO) i tu się kończy magia osobnego filesystemu.</p>

<p>Przy okazji – wyizolowanie systemu plików to najstarszy klocek “konteneryzacji”. We wszystkich implementacjach Unix jest polecenie <code>chroot</code>, które właśnie odpowiada za uruchomienie procesu z innym systemem plików.</p>

<p>Skupię się więc na uruchomieniu procesu w jego własnej przestrzeni identyfikatorów procesów (PIDy) a potem dodam do tego wyizolowaną sieć.</p>

<p>Zatem “pierwszy kontener”:</p>

<pre><code>sudo unshare -p -f bash
# echo $$
1
# 
</code></pre>

<p>Pierwsza linijka to uruchomienie bash&#39;a w wydzielonej przestrzeni PID, o czym mówi parametr <code>-p</code>. Parametr <code>-f</code> instruuje program unshare, że powinien zrobić fork&#39;a przed uruchomieniem docelowego programu, co z różnych powodów jest bardzo wskazane.</p>

<p>Tu od razu skierowanie do <a href="https://man7.org/linux/man-pages/man1/unshare.1.html" rel="nofollow">manuala programu unshare</a>. Jak widać z listy parametrów możemy nim wydzielić każdą dostępną w systemie przestrzeń.</p>

<p>Wracając do powyższej sesji z pracy kontenera: polecenie <code>echo $$</code> (czyli wyświetl mój pid, gdzie “mój” dotyczy uruchomionego shella) pokazuje “1”.
Sukces! Shell działa we własnej przestrzeni identyfikatorów procesów.</p>

<p>To sprawdźmy jakie inne procesy są (nie powinno być żadnego):</p>

<pre><code># ps ax
</code></pre>

<p>I tu niespodzianka – <code>ps</code> pokazuje wszystkie procesy w systemie. Co ciekawe pid 1 wcale nie jest naszym bashem, tylko programem <code>/sbin/init</code>.
Wyjaśnienie tego jest dość proste, jeśli wiemy skąd program <code>ps</code> bierze informacje o procesach – z katalogu <code>/proc</code> (i wszystkich podkatalogów, których nazwy są numerkami – każdy numerek to pid programu).</p>

<p>Czyli widać procesy, bo ps może je przeczytać z katalogu <code>/proc</code>, co nie znaczy, że nasz proces naprawdę widzi te PIDy. To jest łatwo sprawdzić wybierając sobie dowolny pid i wykonując polecenie <code>kill</code>:</p>

<pre><code># kill 4451
bash: kill: (4451) - Nie ma takiego procesu
#
</code></pre>

<p>Polecenie ps wypisało miedzy innymi jakiś proces o PID 4451, ale próba wysłania sygnału do tego procesu kończy się błędem – w kontenerze takiego procesu nie ma.</p>

<p>Te błędne informacje z polecenia <code>ps</code> jest bardzo łatwo poprawić i <code>unshare</code> ma na to specjalną opcję:  <code>--mount-proc=/proc</code>. Dodanie tego parametru spowoduje, że w przestrzeni nazw naszego procesu zostanie zamontowany jego własny katalog <code>/proc</code>, który będzie prawidłowo opisywał jego rzeczywistość.</p>

<p>Zatem wychodzę z mojego kontenera poleceniem <code>exit</code> i uruchamiam jeszcze raz:</p>

<pre><code>$ sudo unshare -p --mount-proc=/proc -f bash
#echo $$
1
# ps ax
  PID TTY      STAT   TIME COMMAND
    1 pts/3    S      0:00 bash
    8 pts/3    R+     0:00 ps ax
# 
</code></pre>

<p>I tak oto mam kontener! W moim kontenerze jest tylko proces <code>bash</code> i procesy, które z niego uruchamiam. Innych procesów nie widać.</p>

<p>Uruchomienie polecenia <code>ps</code> poza kontenerem (w innym terminalu) pokaże, że system ma o wiele więcej uruchomionych procesów i pokaże również tego basha, który jest potomkiem programu <code>unshare</code> i wcale nie ma PIDu różnego 1.</p>

<p>Przy okazji tak mimochodem wydzieliliśmy mu również osobną przestrzeń montowania – ma filesystem identyczny z globalnym ale z jedną różnica – jego katalog <code>/proc</code> jest inny niż <code>/proc</code> w systemie.</p>

<h2 id="sieć-w-kontenerze">Sieć w kontenerze</h2>

<p>Dodanie sieci od naszego kontenera wymaga kilku dodatkowych poleceń, ale wcale nie będzie dużo bardziej skomplikowane.</p>

<p>Po pierwsze zobaczmy jakie interfejsy sieciowe widać w kontenerze:</p>

<pre><code># ip link
1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp4s0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
....
#
</code></pre>

<p>Ogólnie – te same, które są w systemie. Uruchamiam więc jeszcze raz  kontener ale z dodatkowym parametrem <code>-n</code>, co spowoduje wydzielenie dla procesu osobnej przestrzeni nazw dla sieci:</p>

<pre><code># exit
$ sudo unshare -p --mount-proc=/proc -f -n bash
# ip link
1: lo: &lt;LOOPBACK&gt; mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
#
</code></pre>

<p>I teraz jest już tylko urządzenie loopback – nie jest nawet podniesione: <code>state DOWN</code>. Sieć nie jest zainicjalizowana.</p>

<p>Do kontenera (czyli namespace procesu) można dodawać interfejsy sieciowe za pomocą polecenie <code>ip</code>:</p>

<pre><code>sudo ip link set dev &lt;device&gt; netns &lt;namespace&gt; 
</code></pre>

<p>Zamiast <em>device</em> podajemy faktyczną nazwę urządzenia (np. eth2). Pewnym problemem jest kwestia nazwy namespace: utworzona przestrzeń jest anonimowa i nie ma nazwy (<code>ip netns ls</code> nie pokaże jej). Ale nie jest to dużym problemem. Zamiast nazwy można podać pid (ten “prawdziwy”, z głównej przestrzeni nazw) procesu wewnątrz kontenera. Wystarczy za pomocą <code>htop</code> <code>top</code> czy <code>ps axf</code> odszukać proces bash, który jest potomkiem programu <code>unshare</code>. W moim wypadku jest to 5030.</p>

<p>I jeszcze druga kwestia – jaki interfejs przypisać od kontenera. W zasadzie da się dowolny, ale żeby miało to bardziej praktyczne zastosowanie można wykorzystać specjalny interfejs wirutalny – <code>veth</code>.</p>

<p>Zacznijmy od jego utworzenia. Robimy to “poza kontenerem”.</p>

<pre><code>$ sudo ip link add siec0 type veth peer name siec1
</code></pre>

<p>Polecenie <code>ip link</code> pokaże nam, że interfejs został utworzony (a nawet dwa interfejsy!):</p>

<pre><code>169: siec1@siec0: &lt;BROADCAST,MULTICAST,M-DOWN&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 2a:14:f2:52:01:0d brd ff:ff:ff:ff:ff:ff
170: siec0@siec1: &lt;BROADCAST,MULTICAST,M-DOWN&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether aa:e1:2b:2a:b3:27 brd ff:ff:ff:ff:ff:ff
</code></pre>

<p>Interfejs veth jest specyficzny. Otóż posiada dwa końce (nadałem im nazwy siec0 i siec1). Wysłanie ramki na jeden z końców powoduje, że ramka pojawia się natychmiast na drugim. Jak widać – system widzi oba końce pod nazwami <code>siec0@siec1</code> i <code>siec1@siec0</code>.</p>

<p>Zaleta tego interfejsu jest taka, że końce mogą być w różnych przestrzeniach nazw.  Przypisuję jeden z końców do mojego kontenera (którego przestrzeń nazw jest identyfikowana pidem procesu, który w nim działa):</p>

<pre><code>sudo ip link set dev siec0 netns 5030
</code></pre>

<p>Ponowne wywołanie <code>ip link</code> pokaże, że interfejs <code>siec0</code> (id 170) zniknął, a interfejs siec0 (id 169) nieznacznie zmienił nazwę:</p>

<pre><code>169: siec1@if170: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 2a:14:f2:52:01:0d brd ff:ff:ff:ff:ff:ff link-netnsid 3
</code></pre>

<p>Za to uruchomienie <code>ip link</code> w kontenerze pokaże:</p>

<pre><code># ip link
1: lo: &lt;LOOPBACK&gt; mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
170: siec0@if169: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether aa:e1:2b:2a:b3:27 brd ff:ff:ff:ff:ff:ff link-netnsid 0
</code></pre>

<p>Jak widać, interfejs o id 170 został przeniesiony do kontenera (a dokładniej do jego przestrzeni nazw).</p>

<p>Jak wykorzystać ten interfejs? Można zrobić most (bridge). W systemie (w globalnej przestrzeni nazw) wyknuję kolejno:</p>

<pre><code>$ sudo ip link add most type bridge
$ sudo ip link set master most dev siec1
</code></pre>

<p>Pierwsze polecenie tworzy most, a drugie przypina do niego urządzenie siec1 (czyli koniec veth, widoczny w systemie). Teraz należy nadać adres.</p>

<pre><code>$ sudo ip addr add 10.11.0.1/24 dev most
$ sudo ip link set dev most up
$ sudo ip link set dev siec1 up
</code></pre>

<p>Kolejno:</p>
<ul><li>nadawany jest adres ip dla mostu,</li>
<li>urządzenie mostu jest aktywowane,</li>
<li>aktywowany jest interfejs urządzenia veth.</li></ul>

<p>W kontenerze natomiast uruchamiam:</p>

<pre><code># ip addr add 10.11.0.2/24 dev siec0
# ip link set dev siec0 up
</code></pre>

<p>Nadawany jest adres dla interfejsu siec0 (inny niż mostu, ale z tej samej podsieci) i również podnoszony jest interfejs.</p>

<p>Od tego momentu polecenie <code>ping 10.11.0.1</code> uruchomione na kontenerze oraz <code>ping 10.11.0.2</code> uruchomione poza kontenerem pokazuje, że pakiety docierają na drugi koniec.</p>

<p>Co więcej, jeśli w kontenerze uruchomię <code>nc -l 80</code> (netcat – otwarcie portu 80), to poza kontenerem tego otwartego portu nie widać (nie pojawia się w wynikach polecenia <code>sudo ss -tnlp</code>), ale można się z nim połączyć na adresie kontenera, tj. 10.11.0.2.</p>

<p>Mam pełnoprawny kontener z wirtualną siecią.</p>

<h2 id="co-dalej">Co dalej?</h2>

<p>Można w ten sam sposób tworzyć kolejne kontenery. Dla każdego dodawać kolejne urządzenie <code>veth</code>, dołączone jednym końcem do mostu.</p>

<p>W systemie można włączyć przekazywanie pakietów i dodać maskaradę w iptables, co spowoduje, że kontener będzie mógł komunikować się nie tylko z innymi kontenerami i systemem, ale także z siecią LAN (i Internetem).</p>

<p>Można również za pomocą iptables i jego tablicy <code>nat</code> przekierować przychodzące pakiety do uruchomionych kontenerów.</p>

<p>Na koniec można też wykorzystać system plików OverlayFS i utworzyć dla kontenera warstwy struktur katalogów ze wspólną bazą read only.</p>

<p>Dokładnie takie kroki wykonywane są przez serwer dockera, gdy uruchamiamy polecenie <code>docker run</code>.</p>

<p>Doker przykrywa to zgrabnym zestawem parametrów w CLI.
Ale pod spodem wykonuje się właśnie to, co opisałem w niniejszym artykule i jak widać, nie jest to zbyt skomplikowane.</p>

<p>Tagi: <a href="/arek/tag:kontener" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">kontener</span></a> <a href="/arek/tag:docker" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">docker</span></a> <a href="/arek/tag:kubernetes" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">kubernetes</span></a> <a href="/arek/tag:linux" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">linux</span></a></p>

<p><a href="https://mastodon.social/@ark_r" rel="nofollow">@ark_r</a></p>
]]></content:encoded>
      <guid>https://baal.ar76.eu/arek/czym-naprawde-sa-kontenery</guid>
      <pubDate>Fri, 02 Apr 2021 19:33:58 +0000</pubDate>
    </item>
  </channel>
</rss>