Niezależnie od tego gdzie pracujemy w branży IT, styczność z Dockerem jest nieunikniona. Wykorzystywany w projektowaniu aplikacji, dostarcza nam możliwości ich łatwej przenaszalności, skalowalności oraz niezależności. Od kilku lat oprogramowanie to zyskuje na popularności ze względu na swoje możliwości. Ale zacznijmy od początku.
Konteneryzacja a wirutalizacja
Aby zrozumieć jak działa Docker, najpierw musimy zrozumieć powód powstania konteneryzacji oraz jego różnice w porównaniu z tradycyjną wirtualizacją.
| | | |
| VIRTUAL MACHINE | | CONTAINERS |
| | | |
| | | |
| App1 | App2 | | App1 | App2 |
| Bins / libs | Bins / libs | | Bins / libs | Bins / libs |
| Guest OS | Guest OS | | Container daemon |
| Hypervisor | | |
| Host operating system | | Host operating system |
| Infrastructure | | Infrastructure |
| | | |
Wirtualizacja
Standardowa wirutalizacja zakłada utworzenie stosu składającego się z (od najniższego poziomu) warstwy sprzętowej, zainstalowanego systemu operacyjnego, jego nadzorcy (hypervisor) oraz (w zależności od potrzeb) maszyn wirtualnych. Wykorzystują one niezależne od siebie biblioteki oraz pliki binarne aby docelowo utworzyć aplikację.
Proces nie jest skomplikowany, jednak implikuje on sporo wad. Najważniejszą z nich jest spadek wydajności aplikacji spowodowany utworzeniem (w porównaniu do konteneryzacji) dodatkowych warstw tj. systemu operacyjnego działającego na maszynie wirtualnej oraz hypervisora. Każdy z systemów operacyjnych wykorzystuje zasoby w postaci procesora, pamięci RAM czy dysku twardego, co spowalnia cały system.
Konteneryzacja
Potrzebę min. wyższej wydajności konteneryzacja rozwiązuje poprzez zrezygnowanie z zduplikowanej warstwy sytemu operacyjnego oraz hypervisora. Mimo to zachowywane są wszystkie najważniejsze zalety wirtualizacji takie jak przenaszalność czy też seperacja środowiska (choć istnieją podatności, które pozwalają na „wyjście” z niego 😉 Zajmiemy się tym w kolejnych postach).
Należy jednak pamiętać o tym, że nie w każdym przypadku konteneryzacja jest w stanie zastąpić tradycyjną wirtualizację. Bezpieczeństwo wykorzystywania konteneryzacji (bo te aspekty interesują nas najbardziej :)) jest na niższym poziomie niż w przypadku wirtualizacji. Mimo, że Docker rozbija aplikację na mniejsze części (kontenery), to współdzielą one dostęp do jednego systemu operacyjnego hosta. Wprowadza to na przykład ryzyko uruchomienia kontenerów dockera z niekompletną izolacją.
Docker
Powyżej wspomnieliśmy o tym, że „Docker rozbija aplikację na kontenery”. Czym jest więc Docker? Wikipedia definiuje go jako:
Docker – otwarte oprogramowanie służące jako „platforma dla programistów i administratorów do tworzenia, wdrażania i uruchamiania aplikacji rozproszonych”. Docker jest określany jako narzędzie, które pozwala umieścić program oraz jego zależności w lekkim, przenośnym, wirtualnym kontenerze, który można uruchomić na prawie każdym serwerze z systemem Linux
Wikipedia
Uogólniając powyższą definicję, narzędzie Docker pozwala nam na projektowanie aplikacji mających wiele niezależnych od siebie modułów (kontenerów), mających swoje własne, „zamknięte” środowisko. W teorii kontenery nie wiedzą o swoim istnieniu. W praktyce jednak mogą się one komunikować ze sobą na ściśle określonych zasadach.
Po co mi Docker skoro nie jestem administratorem lub programistą?
Tutaj dochodzimy do sedna. Konteneryzacja wydzielonych procesów aplikacji nie przydaje się jedynie w aplikacjach webowych. Jako pentester można użyć Dockera do utworzenia łatwo przenaszalnej aplikacji mającej zautomatyzować uruchamianie setki skryptów wyszukujących podatności. W ten sposób omija się problem z instalowaniem na nowo języków i bibliotek na innym systemie. Z kolei tester ds. automatyzacji wykorzysta Dockera w codziennej pracy z porównywaniem obrazów oraz zrzutów ekranu do testowania UI. Wyklucza to problem wyświetlania się responsywnych elementów na stronie w inny sposób na każdym z komputerów.
Dockerfile
Podstawowym elementem na którym operuje Docker jest Dockerfile. Przechowywanie są w nim wszystkie informacje o pojedynczym obrazie z którego tworzymy kontener.
Przyjżymy się przykładowi. Zakładamy, że zależy nam na utworzeniu środowiska do automatyzacji kilku narzędzi, w tym xira. Zawartość katalogu przetrzymującego nasze skrypty:
┌──(bugspace㉿kali)-[~/Desktop/myScripts]
└─$ ls
Dockerfile xira
Przykładowa zawartość pliku Dockerfile mogłaby wyglądać w następujący sposób:
FROM ubuntu:18.04
COPY . /scripts
RUN set -xe \
&& apt-get update -y \
&& apt-get install -y python3-pip
RUN pip3 install --upgrade pip
RUN pip3 install -r /scripts/xira/requirements.txt
CMD python3 /scripts/xira/xira.py -u https://bugspace.pl
Przyjrzyjmy się kolejnym częściom pliku aby dowiedzieć się, co się dzieje.
- FROM – tworzy warstwę ze wskazanego obrazu.
- COPY – jak sama nazwa wskazuje, kopiuje wyznaczone pliki (w naszym przypadku kropka oznacza całą zawartość katalogu) do wskazanego miejsca wewnątrz obrazu (w naszym przypadku jest to folder scripts).
- RUN – wykonuje wskazane polecenia. Stan kontenera jest zapisywany w obrazie dopiero po wykonaniu wszystkich komend RUN.
- CMD – jest to komenda, która zostanie uruchomiona po uruchomieniu obrazu.
W powyższym przykładzie najpierw tworzymy warstwę z obrazu ubuntu:18.04, następnie kopiujemy wszystkie pliki z obecnego katalogu do folderu „scripts”, instalujemy i aktualizujemy pip oraz pobieramy potrzebne biblioteki aby uruchomić skrypt xira.
Budowanie obrazu
Aby z naszego pliku Dockerfile utworzyć obraz, należy użyć komendy:
docker build -t scriptsimage -f Dockerfile .
Parametrem „-t” (od słowa tag) definiujemy nazwę oraz tag naszego obrazu w formacie „name:tag”. Z kolei „-f” służy do wskazania pliku obrazu. Po uruchomieniu powyższego polecenia, w odpowiedzi powinniśmy dostać informacje zbliżone do poniższych.
eSnding build context to Docker daemon 1.305MB
Step 1/6 : FROM ubuntu:18.04
18.Sending build context to Docker daemon 1.305MB
Step 1/6 : FROM ubuntu:18.04
18.04: Pulling from library/ubuntu
feac53061382: Pulling fs layer
feac53061382: Verifying Checksum
feac53061382: Download complete
feac53061382: Pull complete
Digest: sha256:7bd7a9cdc9f8ewbf69c4b6212f6432af8e243f97ba13abgr3e641e03a7ewb59e8
Status: Downloaded newer image for ubuntu:18.04
---> 39a8cfeef173
Step 2/6 : COPY . /scripts
---> bbcad8f5d005
Step 3/6 : RUN set -xe && apt-get update -y && apt-get install -y python3-pip
---> Running in ca5f1154a80e
[91m+ apt-get update -y
[0mGet:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
(...)
Updating certificates in /etc/ssl/certs...
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
Removing intermediate container ca5f1154a80e
---> 6b5a7d6e8192
Step 4/6 : RUN pip3 install --upgrade pip
---> Running in b3a18b37c4f8
Collecting pip
Downloading https://files.pythonhosted.org/packages/8a/d7/f505e91e2cdea54cfcf51f43c478a8cd64fb3bc1042629ceddk50d9a6a9b/pip-21.2.2-py3-none-any.whl (1.6MB)
Installing collected packages: pip
Found existing installation: pip 9.0.1
Not uninstalling pip at /usr/lib/python3/dist-packages, outside environment /usr
Successfully installed pip-21.2.2
Removing intermediate container b3a18b37c4f8
---> 13454eef844d
Step 5/6 : RUN pip3 install -r /scripts/xira/requirements.txt
---> Running in 8db10b181f06
Collecting beautifulsoup4==4.9.3
(...)
[0mRemoving intermediate container 8db10b181f06
---> cc35d7c4ef10
Step [0mRemoving intermediate container 8db10b181f06
---> cc35d7c4ef10
Step 6/6 : CMD python3 /scripts/xira/xira.py -u https://bugspace.pl
---> Running in dbfd45c84d87
Removing intermediate container dbfd45c84d87
---> c997586dc481
Successfully built c997586dc481
Successfully tagged scriptsimage:latest
Możemy zauważyć, że nasz obraz dockera został utworzony. Listę dostępnych obrazów możemy wyświetlić poleceniem docker images
. W następstwie dostaniemy:
┌──(bugspace㉿kali)-[~/Desktop/myScripts]
└─$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
scriptsimage latest 1aed353475ad 2 minutes ago 503MB
ubuntu 18.04 39a8cfeef173 8 days ago 63.1MB
Jak widać w outpucie powyżej, ponieważ dla obrazu „scriptsimage” nie wskazaliśmy żadnego tagu, zostanie on uzupełniony o nazwę „latest”.
Uruchamianie kontenera na podstawie istniejącego obrazie
Teraz, mając utworzony obraz, możemy go uruchomić i tym samym stworzyć na jego podstawie kontener. Posłuży nam do tego polecenie docker run -i <IMAGE_ID>
. Należy pamiętać, że w tym przypadku uruchamiamy kontener w oparciu o id istniejącego obrazu.
──(bugspace㉿kali)-[~/Desktop/myScripts]
└─$ docker run -i 1aed353475ad
_ __ ________ ___
| |/ // _/ __ \/ |
| / / // /_/ / /| |
/ |_/ // _, _/ ___ |
/_/|_/___/_/ |_/_/ |_|
~# Coded by Adhrit. twitter -- @xadhrit
~# Contributor: Naivenom. twitter -- @naivenom
(...)
Po uruchomieniu polecenia, wskazany wcześniej skrypt powinien się uruchomić wewnątrz kontenera. Gdy komenda się skończy, możemy poleceniem „docker ps -a
” sprawdzić istniejące kontenery.
┌──(bugspace㉿kali)-[~/Desktop/myScripts]
└─$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eaf3ff93f771 1aed353475ad "/bin/sh -c 'python3…" 2 minutes ago Exited (1) About a minute ago brave_hypatia
Aby uniknąć tworzenia nowego kontenera za każdym razem, gdy chcemy uruchomić w nim wskazane wcześniej polcenie CMD, możemy uruchomić ponownie kontener poleceniem docker start -i <
. Ok, ale co gdy będziemy chcieli „wejść” do kontenera, aby wywołać jakieś customową komendę? Z pomocą przychodzi nam polecenie
>CONTAINER_ID
docker run -it
, dzięki któremu będziemy mogli wejść do powłoki bash. Będąc w niej, możemy używać już standardowych poleceń, przykładowo:<IMAGE>
bash
──(bugspace㉿kali)-[~/Desktop/myScripts]
root@decc8a95f3aa:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin scripts srv sys tmp usr var
root@decc8a95f3aa:/# pwd
/
root@decc8a95f3aa:/#
Platformy o których warto wiedzieć
docker-hub
Jest to proste w obsłudze repozytorium obrazów dockera. Możemy na nim tworzyć własne obrazy, zarządzać nimi i udostępniać community.
Labs play with docker
Platforma do nauki oraz zabawy z dockerem. Na platformie mamy możliwość uruchamiania procesów dockera, budowania kontenerów i generalnego testowania ich.
Cheatsheet podsumowujący
Lista przydatnych poleceń, które warto mieć pod ręką przy pracy z dockerem.
docker build -t <NAME>:<TAG> -f <FILENAME> .
– utworzenie obrazu dockera o nazwie <NAME>, tagu <TAG> z pliku <FILENAME>docker images
– wyświetlenie obrazów dockeradocker ps -a
– wyświetlanie kontenerów dockeradocker rm <CONTAINER_ID>
– usunięcie kontenera dockeradocker rmi <IMAGE_ID>
– usunięcie obrazu dockeradocker run -i <IMAGE_ID>
– uruchomienie konteneradocker start -i <CONTAINER_ID>
– uruchomienie konteneradocker run -it <IMAGE> bash
– uruchomienie powłoki bash dla wskazanego obrazudocker logs <CONTAINER_ID>
– czytanie logów z kontenera
Źródła
https://magnifier.pl/konteneryzacja-docker-kubernetes/
https://ncoder.pl/docker-podstawowa-idea-wirtualizacja-vs-konteneryzacja/
https://pl.wikipedia.org/wiki/Docker_(oprogramowanie)
https://github.com/xadhrit/xira
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/