Wirtualizacja na poziomie systemu operacyjnego

Wstęp

LXC (Linux Kernel Containers)

Kontenery wspierane przez system Linux (LXC - Linux Containers) są jedną z najnowocześniejszych metod wirtualizacji aplikacji. LXC pozwala na przydział zasobów CPU, pamięci, dysków i sieci dla odizolowanych aplikacji od systemu operacyjnego. LXC separuje drzewa procesów, dostęp do sieci, ID użytkownika, dostęp do plików. LXC jest techniką funkcjonalnie umiejscawianą pomiędzy rozwiązaniem chroot oraz VM. Kontenery Linux są elastyczne, ponieważ pozwalają administratorowi wirtualizować pojedynczą aplikację, a nie cały system operacyjny z wykorzystaniem VM. Nie ma potrzeby zakupów dodatkowych licencji na systemy operacyjne, tak jak ma to miejsce w przypadku wirtualizacji. LXC oferuje także stosunkowo niewielki narzut systemu operacyjnego, ponieważ aplikacje wykorzystują standardowe wywołania systemowe oraz połączenia do interfejsów z lekkim systemem operacyjnym OS. Przykładowo w takich zastosowaniach warto rozważyć dystrybucję CoreOS.

Docker

Docker jest aplikacją pracującą na podbudowie w postaci LXC, która zarządza obrazami oraz asystuje we wdrożeniach wirtualizacji aplikacji. Dostarcza automatyzacji oraz mechanizmów szybkiego tworzenia kontenerów LXC. Dodatkowo udostępnia API, które rozszerza funkcjonalność LXC o możliwość budowania oferty PaaS (Platform as a Service). Docker pierwotnie nazywał się dotCloud, a jego kariera trwa od roku. Aktualnie jest zintegrowany z szeregiem innych narzędzi m.in. Ansible, Chef, OpenStack, Pupper, Salt. Jest też dołączony do RHEL, OpenShift PaaS, Google Compute Engine, Deis, a także Amazon Web Services Elastic Beanstalk. Docker jest aktualnie standardem wirtualizacji aplikacji dla systemu Linux.

Z racji na czasochłonne tworzenie kontenera LXC (ok. 55 minut), proszę przed rozpoczęciem wykonywania ćwiczeń wykonać instrukcje zawarte w punktach:

  • 5.2.
  • 5.3.
  • 5.4.
  • 6.1.II

W trakcie tworzenia kontenera (ostatni podpunkt) można przejść do wykonywania instrukcji od początku.

W przypadku dotarcia do momentu tworzenia kontenera LXC, kontener nie będzie jeszcze gotowy, proszę rozpocząć ćwiczenie dotyczące Docker-a.

Control Groups

1 Montowanie Control Groups

  1. Sprawdzić czy cgroups są zamontowane:
    mount
    1. Jeżeli wszystko jest OK, to wśród zamontowanych systemów plików powinno pojawić się coś na wzór:
      tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
      cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
      cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children)
      cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
      cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
      cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
      cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
      cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
      cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
    2. Jeżeli cgroups nie są zamontowane należy wykonać następujące kroki:
      1. Dodać następującą linię w pliku /etc/fstab
        cgroup  /sys/fs/cgroup  cgroup  defaults  0   0
      2. Spróbować zamontować poleceniem:
        mount /sys/fs/cgroup
      3. W przypadku otrzymania informacji o zajętości zasobów (resource busy problem) należy zrestartować system.

2 Tworzenie nowych grup

  1. Tworzenie nowej grupy polega na stworzeniu folderu w ramach systemu plików cgroup w odpowiednim kontrolerze.
  2. Listę wszystkich kontrolerów można zobaczyć wpisując jedno z poniższych poleceń:
    1. Wpisując mount – każdy punkt montowania to jeden kontroler.
    2. Wyświetlając listę grup procesu init:
      cat /proc/1/cgroup
    3. Wchodząc bezpośrednio do odpowiedniego katalogu np.
      cd /sys/fs/cgroup
      ls -l
  3. Nową grupę można utworzyć używając polecenia cgcreate (przeczytaj manual):
    cgcreate [-h] [-t <tuid>:<tgid>] [-a <agid>:<auid>] [-f mode] [-d mode] [-s mode] -g <controllers>:<path> [-g ...]
  4. Tworzymy nową grupę o nazwie cpulimited w ramach controlera cpu:
    cgcreate -g cpu:/cpulimited
    1. Sprawdzamy czy dana grupa istnieje pobierając wszystkie jej ustawienia:
      cgget -g cpu:/cpulimited
    2. Można też wykonać polecenie:
      ls -l /sys/fs/cgroup/cpu/cpulimited

3 Uruchamianie procesów

  1. Uruchamiamy program cat:
    cat
  2. Używając programu ps odnajdujemy PID uruchomionego procesu programu cat.
  3. Sprawdzamy w jakiej grupie jest uruchomiony powyższy proces:
    cat /proc/PID/cgroup
  4. W folderach, które odpowiadają odpowiednim kontrolerom znajduje się plik tasks. Używając tego pliku sprawdź czy PID powyższego procesu znajduje się w kontrolerach cpu oraz blkio należących do grupy, w której pracuje proces np.:
    cat /sys/fs/cgroup/cpu/CGROUP/tasks | grep PID
  5. Przenieś uruchomiony proces do wcześniej stworzonej grupy cpulimited. Przeniesienie procesu jest możliwe na dwa sposoby (spróbuj wykonać ćwiczenie używając każdego z nich):
    1. Użycie polecenia cgclassify (przeczytaj manual):
      cgclassify -g cpu:/cpulimited PID
    2. Zapisanie PID procesu do pliku tasks:
      echo PID > /sys/fs/cgroup/cpu/cpulimited/tasks
    3. Po każdym przeniesieniu sprawdź czy grupa procesu rzeczywiście się zmieniła:
      Przykład przed:
      # cat /proc/32194/cgroup 
      8:perf_event:/
      7:blkio:/
      6:net_cls,net_prio:/
      5:freezer:/
      4:devices:/user.slice
      3:cpu,cpuacct:/
      2:cpuset:/
      1:name=systemd:/user.slice/user-1000.slice/session-1.scope

      Przykład po:

      # cat /proc/32194/cgroup 
      8:perf_event:/
      7:blkio:/
      6:net_cls,net_prio:/
      5:freezer:/
      4:devices:/user.slice
      3:cpu,cpuacct:/cpulimited
      2:cpuset:/
      1:name=systemd:/user.slice/user-1000.slice/session-1.scope
  6. Aby uruchomić proces od razu w zadanej grupie należy użyć polecenia cgexec (przeczytaj manual).
  7. Zakańczamy działanie uruchomionego procesu cat i uruchamiamy go ponownie, tym razem przy pomocy powyższego polecenia od razu w grupie cpulimited:
    cgexec -g cpu:/cpulimited cat
  8. Analogicznie jak poprzednio: Sprawdzamy czy proces jest uruchomiony w zadanej grupie.
  9. Zakańczamy proces.

4 Definiowanie ograniczeń

  1. Wyświetlamy zdefiniowane ograniczenie zużycia procesora w głównej i zdefiniowanej grupie cpulimited:
    cgget -r cpu.shares /
    cgget -r cpu.shares /cpulimited

    Obydwie wartości powinny być sobie równe.

  2. Zmieniamy ograniczenie w naszej zdefiniowanej grupie na wartość 512. Można to zrobić na dwa sposoby:
    1. Przy pomocy echo:
      echo 512 > /sys/fs/cgroup/cpu/cpulimited/cpu.shares
    2. Przy pomocy dedykowanego polecenia cgset (przeczytaj manual):
      cgset -r cpu.shares=512 /cpulimited
  3. Ponownie wyświetlić wartości ustawień w obydwóch grupach aby sprawdzić czy otrzymaliśmy zamierzony efekt.
  4. Testujemy nasze ustawienia:
    1. Instalujemy program matho-primes. Należy samodzielnie znaleźć nazwę pakietu do doinstalowania np. przy pomocy wyszukiwarki na stronie: https://www.debian.org/distrib/packages#search_packages
    2. Sprawdzamy ile mamy procesorów:
      lscpu
    3. Program matho-primes uruchamiamy w głównej grupie tyle razy ile mamy procesorów (aby upewnić się, że każdy będzie obciążony).
    4. W tym celu wywołujemy poniższą komendę zadaną ilość razy:
      matho-primes 0 9999999999 > /dev/null &
    5. Sprawdzamy obciążenie procesora używając top.
    6. Przy pomocy poznanych sposobów uruchamiamy kolejną instancję programu jednak tym razem w grupie cpulimited.
    7. Ponownie sprawdzamy obciążenie procesora i próbujemy zaobserwować różnice w obciążaniu:
       PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
      1556 root      20   0    9116   3248   1356 R  88,4  0,0   0:54.89 matho-primes                                                                                                   
      1557 root      20   0    9116   3220   1316 R  88,1  0,0   0:54.41 matho-primes                                                                                                   
      1558 root      20   0    9116   3208   1308 R  87,4  0,0   0:54.56 matho-primes                                                                                                   
      1555 root      20   0    9116   3260   1364 R  86,1  0,0   0:55.41 matho-primes                                                                                                   
      1554 root      20   0    9116   3220   1316 R  41,6  0,0   0:27.02 matho-primes

      W powyższym przykładzie proces 1554 został uruchomiony w grupie cpulimited. Jak widzimy obciąża on procesor około 50% mniej w porównaniu z innymi procesami.

  5. Zamknąć wszystkie procesy działające w głównej grupie (proszę nie zakańczać procesu z grupy cpulimited):
    1. Jakie jest teraz obciążenie?
    2. Jak wyjaśnić to zjawisko?

LXC

W poniższej instrukcji NAME oznacza wybraną nazwę maszyny wirtualnej.

5 Instalacja

  1. Instalacja LXC jest bardzo prosta. Wszystkie potrzebne pakiety znajdują się w repozytorium.
  2. Aktualizujemy listę pakietów:
    apt-get update
  3. Instalujemy pakiety LXC:
    apt-get install lxc
  4. Dodatkowo można zainstalować (w celu ułatwienia pracy):
    apt-get install bridge-utils libvirt-bin debootstrap
  5. Sprawdzamy konfigurację:
    lxc-checkconfig
  6. Sprawdzamy zainstalowane/dostępne narzędzia:
    lxc-<tab><tab>

6 Tworzenie kontenerów

  1. Tworzenie kontenerów odbywa się przy pomocy polecenia lxc-create:
    1. Przeczytać manual do polecenia.
    2. Tworzymy obraz najnowszej dystrybucji debiana:
      lxc-create -n NAME -t debian -- -r jessie
    3. Po utworzeniu obrazu, wyświetli się nam informacja o konfiguracji w tym hasło root-a do naszej maszyny wirtualnej.
  2. Pliki maszyny wirtualnej znajdują się w katalogu: /var/lib/lxc/NAME/.
  3. Jednym z folderów jest rootfs, który jest montowany w maszynie wirtualnej jako główny system plików.
  4. Wykorzystując ten fakt, zmieniamy hasło root-a do naszej maszyny wirtualnej:
    1. Zmieniamy katalog główny na rootfs:
      chroot rootfs
    2. Wywołujemy polecenie passwd.
    3. Zmieniamy hasło.
    4. Wciskamy Ctrl-D - powłoka powinna powrócić do poprzedniego katalogu głównego.

7 Uruchomienie kontenera

  1. Polecenie lxc-start służy do uruchamiania kontenera:
    1. Przeczytać manual do polecenia, zwrócić uwagę na opcję -d.
    2. Uruchomić maszynę wirtualną poleceniem:
      lxc-start -n NAME
    3. Logujemy się na konto root-a, przy pomocy zmienionego hasła.
  2. Poleceniem lxc-stop (przeczytać manual) można zakończyć działania maszyny wirtualnej w sposób czysty lub wymuszony (opcja -k):
    1. Zatrzymujemy naszą maszynę:
      lxc-stop -n NAME

8 Przestrzenie nazw i Control groups

  1. Uruchamiamy maszynę wirtualną.
  2. Po jej uruchomieniu sprawdzamy identyfikator procesu init:
    ps uax | grep init
  3. W systemie gospodarza wykonujemy identyczną komendę:
    1. Ile procesów init pojawia się na liście?
    2. Który z nich dotyczy maszyny wirtualnej?
  4. Sprawdzamy identyfikatory przestrzeni nazw dla obydwóch procesów:
    ls -l /proc/1/ns
    ls -l /proc/INITPID/ns
    1. Czy identyfikatory są różne?
    2. Które z nich są takie same?
    3. Zwróć uwagę na identyfikator net, czy są one sobie równe?
  5. W maszynie wirtualnej uruchamiamy proces cat:
    cat
    1. W systemie gospodarza próbujemy wyszukać ten proces w liście wszystkich procesów:
      ps aux | grep cat
    2. Czy taki proces istnieje?
    3. Jeżeli tak, to:
      1. Wyświetl identyfikatory jego przestrzeni nazw.
        1. Czy są one takie same jak w przypadku któregoś z procesów init?
        2. Co to oznacza?
      2. Spróbuj go zakończyć przy pomocy kill.
        1. Czy proces w maszynie wirtualnej zakończył się?
    4. Jeżeli nie, to jak to wyjaśnić?
  6. Sprawdź czy LXC rzeczywiście korzysta z Control Groups:
    1. Wejdź do katalogu /sys/fs/cgroup/ w wyszukaj w nim pliki o nazwie NAME:
      find . -name \*NAME\*
    2. Jeżeli pojawiają się jakieś wyniki, to znaczy, że LXC tworzy swoje grupy.
    3. Spróbuj także użyć polecenia lxc-cgroup (przeczytaj manual) np.:
      lxc-cgroup -n NAME cpu.shares
  7. Sprawdź czy oprócz tworzenia swoich grup, LXC uruchamia procesy w tych grupach:
    1. Wyświetl plik tasks np. w kontrolerze cpu grupy odnoszącej się do uruchomionej maszyny wirtualnej:
      cat /sys/fs/cgroup/cpu/lxc/NAME/tasks
      1. Czy wyświetla się jakiś identyfikator?
      2. Jakiego procesu on dotyczy?

9 Konfiguracja sieci

  1. Sprawdź czy jest połączenie z internetem w twojej maszynie wirtualnej. Możesz to zrobić wywołując polecenie:
    apt-get update
  2. Sprawdź jakie interfejsy sieciowe są aktywne w twoim systemie wirtualnym.
  3. W systemie gospodarza, sprawdź konfigurację sieci twojej maszyny wirtualnej:
    1. Wyświetl plik konfiguracyjny:
      less /var/lib/lxc/NAME/config
    2. Jaka jest wartość opcji lxc.network.type?
    3. Co ona oznacza? (sprawdź manual lxc.container.conf(5))
  4. Zmień ustawienie na none.
    1. Wykonaj restart maszyny wirtualnej przy pomocy polecenie lxc-stop a później lxc-start.
    2. Sprawdź jakie interfejsy sieciowe są w systemie wirtualnym.
    3. Ponownie wykonaj test czy jest połączenie z internetem.
    4. Na maszynie gospodarza ponownie porównaj identyfikatory przestrzeni nazw net, czy są one takie same?
  5. Zatrzymaj swoją maszynę wirtualną przy pomocy lxc-stop.
    1. Czy działa połączenie z internetem w systemie gospodarza?
    2. Czy takiego efektu można było się spodziewać.
  6. Skonfiguruj ponownie swoje interfejsy w systemie gospodarza - doprowadź sieć do działania.
  7. Zmień konfigurację sieci twojego kontenera:
    1. Zmień ustawienie parametru lxc.network.type na veth.
    2. Poniżej dodaj dwa kolejne wpisy:
      1. Automatyczne aktywowanie interfejsów sieciowych podczas startu maszyny: lxc.network.flags = up
      2. Nazwa mostka sieciowego przez który maszyna będzie się łączyć z siecią: lxc.network.link = lxcbr0
  8. Następnie należy dostosować konfigurację systemu gospodarza:
    1. Stwórz i skonfiguruj mostek sieciowy:
      brctl addbr lxcbr0
      brctl addif lxcbr0 eth0
    2. Usuń przypisane IP z dotychczasowego interfejsu i przypisz go do mostka:
      ifconfig eth0 0.0.0.0
      dhclient lxcbr0
  9. Uruchom maszynę wirtualną i sprawdź dostęp do sieci w systemie gospodarza i na maszynie wirtualnej.
  10. Zatrzymaj maszynę wirtualną i sprawdź dostęp do sieci w systemie gospodarza.

10 Uruchamianie procesów w kontenerze

  1. W kontenerze można pracować jak w na normalnym systemie operacyjnym. Pierwszym zadaniem będzie pobranie, skompilowanie i uruchomienie aplikacji z github-a:
    1. Uruchom kontener.
    2. Zainstaluj git-a:
      apt-get install git
    3. Sklonuj repozytorium:
      git clone https://github.com/fsuplicy/bubbleSort.git
    4. Spróbuj skompilować:
      gcc -o bubbleSort bubbleSort.c

      W razie braku kompilatora doinstaluj odpowiednie pakiety.

    5. Uruchom program:
      ./bubbleSort test_data/extralarge.txt
  2. Wykonaj restart kontenera i sprawdź czy po ponownym uruchomieniu pliki programu bubbleSort dalej istnieją w kontenerze.
  3. Drugim przypadkiem użycia kontenerów jest uruchamianie procesów w odizolowanym środowisku.
    1. Używając polecenia lxc-execute (przeczytać manual) uruchom program echo. Przykładowa komenda do uruchomienia może wyglądać następująco:
      echo "test OK"
      1. W przypadku komunikatów o błędzie, doinstaluj pakiet zawierający brakujący plik - nazwę pakietu możesz wyszukać tutaj.
      2. Po znalezieniu nazwy pakietu użyj następującego polecenia aby go zainstalować:
        apt-get install --no-install-recommends NAZWA_PAKIETU
    2. Jeżeli instalacja przebiegła pomyślnie, to ponownie spróbuj uruchomić powyższy przykład. Jeżeli na konsoli pojawi się napis test OK to oznacza, że wszystko przebiegło pomyślnie.
    3. Używając polecenia lxc-execute uruchom program bubbleSort. Przykładowa komenda do uruchomienia może wyglądać następująco:
      lxc-execute -n NAME -- /root/bubbleSort/bubbleSort /root/bubbleSort/test_data/large3.txt

Docker

11 Instalacja

  1. Poniższa instrukcja dotyczy instalacji narzędzia na systemie Debian w wersji Jessie.
  2. Wszystkie instrukcje należy wykonać jako root.
  3. Weryfikacja wersji jądra: Docker wymaga jądra w wersji co najmniej 3.10. W celu weryfikacji czy system spełnia to wymaganie należy wpisać:
    uname -r
  4. Wyczyść ewentualne stare wersje pakietów:
    apt-get purge lxc-docker* docker.io*
  5. Narzędzie Docker jest pobierane z osobnej lokalizacji (nie ze standardowych repozytoriów Debiana), dlatego należy dodać nowe repozytorium do pliku: /etc/apt/sources.list:
    1. Otwieramy plik w dowolnym edytorze i na jego końcu dodajemy wpis:
      # Docker
      deb https://apt.dockerproject.org/repo debian-jessie main
    2. Generujemy nowy klucz dla programu apt, który posłuży do weryfikacji pakietów instalowanych z repozytorium Docker-a:
      apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
    3. Wykonujemy aktualizację listy pakietów:
      apt-get update
    4. Jeżeli wystąpiły błędy mówiące o tym, że nie ma sterownika dla metody przesyłania pakietów https, należy go doinstalować:
      1. Najpierw należy zakomentować dodany URL repozytorium dodając na początku linii znak #.
      2. Wykonujemy aktualizację pakietów.
      3. Instalujemy brakujący sterownik:
        apt-get install apt-transport-https
      4. Usuwamy znak komentarza.
      5. Ponownie wykonujemy aktualizację pakietów.
  6. Instalujemy pakiety Docker-a:
    apt-get install docker-engine
  7. Uruchamiamy demona systemowego:
    service docker start
  8. Na stronie https://docs.docker.com/engine/installation/debian znajduje się szczegółowa instrukcja dotycząca instalacji Docker-a. Można na niej znaleźć informacje dotyczące instalacji na innych wersjach Debiana a także innych systemach operacyjnych (w tym na Windows-ie).

12 Pobranie obrazu i uruchomienie kontenera

  1. Aby stworzyć kontener przy pomocy Docker-a, należy uruchomić odpowiedni obraz. Jeżeli obraz nie istnieje lokalnie to zostanie on pobrany ze zdalnego repozytorium.
  2. Uruchamiamy kontener o nazwie debian:
    docker run -it debian
    1. Przeczytać w manualu co oznaczają opcje powyższego polecenia.
    2. W drugiej konsoli sprawdź:
      1. Listę obrazów:
        docker images
      2. Listę kontenerów:
        docker ps
    3. Uruchom kolejny kontener z tego samego obrazu:
      docker run -it debian

      Ponownie sprawdź listę obrazów i kontenerów.

    4. Jaka jest relacja pomiędzy kontenerem a obrazem?
  3. Zatrzymaj jeden z kontenerów przy pomocy polecenia docker stop CONTAINER:
    1. W celu uzyskania identyfikatora kontenera CONTAINERID wywołaj
      docker ps
    2. Zatrzymaj kontener:
      docker stop CONTAINERID

13 Przestrzenie nazw i Control groups

  1. Uruchamiamy kontener.
  2. Po jego uruchomieniu uruchamiamy dowolny program np. cat:
    cat
  3. Na maszynie hosta wyszukujemy uruchomiony proces:
    ps uax | grep cat

    Czy taki proces istnieje?

  4. Sprawdzamy identyfikator procesu cat (CATPID).
  5. Wyświetlamy identyfikatory przestrzeni nazw w których pracuje:
    ls -l /proc/CATPID/ns

    Czy są one takie same jak w przypadku procesu init?

  6. Sprawdzamy Control Groups w jakich uruchomiony został proces:
    cat /proc/CATPID/cgroup
  7. Sprawdź czy oprócz tworzenia swoich grup, Docker uruchamia procesy w tych grupach:
    1. Wyświetl plik tasks np. w kontrolerze cpu grupy odnoszącej się do uruchomionej maszyny wirtualnej.
      1. Czy oprócz identyfikaotra procesu cat wyświetla się jeszcze jakiś identyfikator?
      2. Jakiego procesu on dotyczy?
  8. Spróbuj zakończyć proces cat przy pomocy kill. Czy proces w maszynie wirtualnej zakończył się?
  9. Analogicznie spróbuj zakończyć drugi proces. Co się stało z maszyną wirtualną?

14 Stan kontenera

  1. W uruchomionym kontenerze przejdź do katalogu domowego.
  2. Stwórz w tym katalogu dowolny plik.
  3. Zatrzymaj kontener przy pomocy docker stop CONTAINER.
  4. Uruchom kontener ponownie przy pomocy docker run.
  5. Sprawdź czy stworzony plik nadal znajduje się w kontenerze.
  6. Jak wytłumaczyć tą sytuację?
  1. Wykonaj ponownie powyższe kroki 1. i 2.
  2. Sprawdź jakie zmiany zostały dokonane:
    docker diff CONTAINERID
  3. Aby zapamiętać stan kontenera należy użyć polecenia docker commit:
    1. Przeczytaj manual do polecenia.
    2. Zapamiętaj zmiany:
      docker commit CONTAINERID debian:newfile
  4. Sprawdź jakie obrazy są teraz do dyspozycji. Czy wśród nich znajduje się właśnie wykonany commit?
  5. Aby uruchomić kontener z obrazu stworzonego przy pomocy powyższego commit-a wykonujemy następujące polecenie:
    docker run -ti debian:newfile

    Po uruchomieniu się kontenera sprawdź czy stworzony wcześniej plik nadal się w nim znajduje.

15 Uruchamianie sortowania

  1. Sprawdzamy czy sieć wewnątrz kontenera działa. W tym celu spróbuj zaktualizować pakiety:
    apt-get update
  2. Zainstalować git-a:
    apt-get install git
  3. Sklonować repozytorium:
    git clone https://github.com/fsuplicy/bubbleSort.git
  4. Spróbować skompilować:
    gcc -o bubbleSort bubbleSort.c

    W razie braku kompilatora doinstalować odpowiednie pakiety.

  5. Uruchomić program:
    ./bubbleSort test_data/extralarge.txt
  6. Można wykonać commit.
  7. Zatrzymaj kontener przy pomocy docker stop.

16 Tworzenie obrazów

Tworzenie swoich obrazów może być zautomatyzowane poprzez stworzenie tzw. Dockerfile, pliku który zawiera wszystkie instrukcje potrzebne do osiągnięcia zamierzonego celu. Plik ten definiuje takie kwestie jak:

  • Obraz bazowy - na bazie którego dalsze instrukcje będą wykonywane.
  • Instrukcje służące do skonfigurowania środowiska.
  • Polecenia do uruchomienia przy tworzeniu kontenera.
  • Na stronie http://docs.docker.com/engine/reference/builder można przeczytać specyfikację tworzenia plików Dockerfile:
    • Proszę szczególnie zwrócić uwagę na takie instrukcje jak: FROM, ENV, RUN, ADD, CMD.
  1. Pierwszym zadaniem będzie stworzenie pliku Dockerfile, który zautomatyzuje pobieranie oraz uruchamianie programu do sortowania:
    1. Tworzymy folder o dowolnej nazwie (DDIR).
    2. W tym folderze tworzymy plik o nazwie Dockerfile:
      1. Pierwszą kwestią jaką musimy określić jest obraz bazowy. W naszym przypadku wybieramy obraz o nazwie debian:
        FROM debian
      2. Aby ułatwić sobie utrzymanie pliku, definiujemy kilka zmiennych przy pomocy instrukcji ENV:
        1. dir zmienna określająca nazwę folderu w którym znajduje się program bubbleSort. Ustawiamy jej wartość na bubbleSort.
        2. exe nazwa pliku wykonywalnego. Ustawiamy jej wartość również na bubbleSort.
        3. tos nazwa pliku do posortowania. Wybieramy jeden plik z katalogu bubbleSort/test_data np. large1.txt.
      3. Korzystając z instrukcji RUN konfigurujemy środowisko kontenera:
        1. Instalujemy niezbędne pakiety:
          # Update repositories and install dependencies
          RUN apt-get -y update && \                                                                                                                    
              apt-get install -y gcc git
        2. Również przy pomocy RUN wywołujemy komendę pobierającą kod z repozytorium.
        3. Przy pomocy kolejnego wykorzystania RUN kompilujemy nasz program:
          # Build executables from source files
          RUN cd $dir && gcc *.c -o $exe 
        4. Ostatnim krokiem jest zdefiniowanie domyślnej komendy uruchamianej przy tworzeniu kontenera. W naszym przypadku uruchamiamy sortowanie wybranego pliku:
          # Run sorting
          CMD cd $dir && ./$exe test_data/$tos 
    3. Możemy teraz przystąpić do budowania naszego obrazu:
      1. W tym celu używamy polecenia docker build.
      2. Używamy opcji -t w celu zdefiniowania nazwy i tagu naszego obrazu:
        1. Nazwa: bubblesort.
        2. Tag: 1.0.
      3. Kompletne polecenie wygląda następująco:
        docker build -t bubblesort:1.0 DDIR

        Zakładamy, że wywołanie powyższej komendy odbywa się z poziomu katalogu w którym znajduje się nasz folder DDIR.

    4. Jeżeli, proces budowania zakończył się pomyślnie, to wyświetlając dostępne obrazy powinniśmy móc zobaczyć także nasz obraz:
      docker images

      Przykładowy rezultat:

      REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
      bubblesort          1.0                 3677d1f27a16        23 minutes ago      309.3 MB
      debian              newfile             1c11dae8ea4b        47 hours ago        309.3 MB
      ubuntu              latest              e9ae3c220b23        9 days ago          187.9 MB
      debian              latest              91bac885982d        9 days ago          125.1 MB
    5. Spróbuj teraz uruchomić stworzony obraz:
      docker run bubblesort:1.0

      Czy wyświetlane są rezultaty sortowania?

    6. Możemy także oglądnąć historię naszego obrazu:
      docker history bubblesort:1.0
  2. Drugim zadaniem będzie rozbudowa naszego Dockerfile-a o instrukcję pozwalającą na dodanie własnych plików do budowanego obrazu.
    1. W pierwszym kroku przechodzimy do folderu DDIR i tworzymy własny plik do posortowania:
      CNT=10 && echo $CNT > myfile.txt && for I in `seq 1 $CNT` ; do echo $RANDOM ; done | tr '\n' ' ' >> myfile.txt

      Wartość zmiennej CNT możemy zmieniać według upodobań.

    2. W celu weryfikacji poprawności wygenerowanego pliku otwórz go i sprawdź czy:
      1. Pierwsza linia to ilość liczb do posortowania.
      2. Druga linia zawiera różne (losowe) wartości w liczbie określonej w pierwszej linii.
    3. W pliku Dockerfile zmieniamy wartość zmiennej tos na myfile.txt.
    4. Przy pomocy instrukcji ADD dodajemy nasz plik do obrazu:
      1. Poniższą instrukcję musimy umieścić pomiędzy kolonowaniem repozytorium a instrukcją CMD:
        # Clone generated file to bubblesort directory
        ADD $tos $dir/test_data
    5. Analogicznie jak powyżej tworzymy kolejny obraz. Tym razem naszemu obrazowi nadajemy tag 2.0.
    6. Sprawdzamy czy obraz znajduje się na naszej liście.
    7. Próbujemy stworzyć kontener i obserwujemy czy wartości w naszym pliku zostały posortowane.

17 Zarządzanie plikami w kontenerze

Ostatnim zadaniem będzie skopiowanie pliku myfile.txt z uruchomionego obrazu bubblesort:2.0 do uruchomionego obrazu bubblesort:1.0 i posortowanie go. W tym celu wykonujemy następujące kroki:

  1. Tworzymy dwa kontenery po jednym na podstawie każdego z utworzonych obrazów. Tym razem zamiast sortowania uruchamiamy interaktywny kontener z powłoką /bin/bash:
    docker run -ti bubblesort:1.0 /bin/bash
    docker run -ti bubblesort:2.0 /bin/bash
  2. Ponieważ kopiowanie plików pomiędzy kontenerami nie jest wspierane należy najpierw skopiować plik myfile.txt na dysk lokalny a dopiero następnie do docelowego kontenera.
  3. Przy pomocy docker ps sprawdzamy identyfikator kontenera stworzonego z obrazu bubblesort:2.0.
  4. Wykonujemy kopiowanie przy pomocy docker cp.
  5. Analogicznie, przy pomocy docker ps sprawdzamy identyfikator kontenera stworzonego z obrazu bubblesort:1.0.
  6. Ponowanie wykonujemy kopiowanie przy pomocy docker cp.
  7. W docelowym kontenerze sprawdzamy czy nasz plik istnieje.
  8. Jeżeli tak to próbujemy go posortować.
pl/dydaktyka/sitw/2015/lab6/start.txt · ostatnio zmienione: 2019/06/27 15:50 (edycja zewnętrzna)
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0