
Mając gotową aplikację webową chcielibyśmy jak najszybciej podzielić się nią ze światem. Niniejszym wpisem przechodzimy do części poświęconej tzw. „deployment’owi” czyli upublicznianiu owoców naszej pracy, w czym nieoceniony będzie Docker.
Uwaga! W tej serii zajmuję się opisowym przedstawieniem tematu, a celem jest zapoznanie z zagadnieniem w sposób luźny i zrozumiały dla nowicjusza. W związku z tym wiele elementów siłą rzeczy musi zostać pominiętych. Tych z was, którzy chcieliby się dowiedzieć więcej, zapraszam do źródeł na końcu artykułu.
„Dziwne… U mnie działa.”
Powyższe słowa powinny trafić jako refren do oficjalnego hymnu wszystkich programistów, testerów i devopsów jeśli takowy kiedykolwiek powstanie.
Podejrzewam, że również i wy mieliście liczne sytuacje gdy próbując skopiować jakieś rozwiązanie na swoją maszynę natrafiliście na bugi, których próżno było szukać u źródła:
- niekompatybilne wersje
- brakujące biblioteki
- konflikty z pracującymi w tle usługami
- kaprysy systemu operacyjnego
- błędy ludzkie popełnianie w trakcie skomplikowanej instalacji
To tylko niektóre z software’owych bolączek trapiących sektor IT. Aby zaadresować te wszystkie problemy powstało narzędzie o nazwie Docker.

Jeśli chcemy w pełni zrozumieć czym jest Docker, musimy wpierw wyjaśnić sobie czym jest
Maszyna wirtualna (VM)
Koncept na szczęście jest bardzo prosty – to system operacyjny odpalany na innym systemie operacyjnym. Narzędzie do kreacji „wirtualek” przydziela określoną przez użytkownika ilość zasobów systemowych dla nowego systemu i odpala go w okienku, dzięki czemu możemy mieć normalnie Windowsa, a na nim takiego „tymczasowego” Linuxa w wybranej przez siebie dystrybucji. Albo na odwrót.
Z pewnością już widzicie benefity jakie taka wirtualizacja ze sobą niesie – od bezpieczeństwa, bo VM jest odseparowana od naszego systemu, po zwykłą wygodę – w końcu na szybko możemy sobie postawić „nowy komputer” bez żadnych nakładów finansowych.
Z kolei przy pracy zespołowej mamy możliwość umówić się z resztą teamu na jeden konkretny system w określonej wersji, z zainstalowanymi potrzebnymi narzędziami i niczym więcej, tak aby środowisko u każdego z członków ekipy było DOKŁADNIE TAKIE SAMO. Tutaj jednak pojawia się pytanie:
Po jakiego grzyba jest mi potrzebny Paint kiedy jedyne co robię to pisanie i odpalanie kodu?
No właśnie, OS-y są przecież uniwersalne w swojej istocie – po to aby można było robić różne rzeczy – od rysowania, przez granie, aż po programowanie. Mają tysiące sterowników, setki dodatkowych ustawień, dziesiątki preinstalowanych aplikacji – rzeczy, które developerom nie są potrzebne, a tylko marnują zasoby i spowalniają system.
W związku z powyższym
Wirtualki zostały pozbawione wszystkich tych zbędnych elementów i wyewoluowały w stronę „kontenerów”.

Dawno, dawno temu, w czasach kiedy aby kupić dżinsy trzeba było wystać swoje na zmiętym kartonie obok bazarowego stoiska, a na słowa „prawa autorskie” dzieciarnia reagowała śmiechem, królem podwórka był ten co miał w domu komputer z nagrywarką CD.
Przy użyciu takich dziadków jak Kazaa albo eDonkey pobierało się z (działającego z zawrotną prędkością 56kb/s) Internetu obrazy gier i programów, które potem mogliśmy nagrać na czyste płyty i pożyczyć kumplom zwiększając tym samym poziom osobistego prestiżu.
Taki obraz, najczęściej z rozszerzeniem „.iso” to była 1:1 kopia płyty na której produkt oryginalnie był sprzedawany, najczęściej ubogacony o tzw. „cracka” usuwającego zabezpieczenia antypirackie. Mając świeżo nagraną płytę mogliśmy śmiało zainstalować grę u siebie i cieszyć się chwilą rozrywki w tym nudnym jak p***a betonowym świecie.
Podsumowując:
Dzięki obrazowi tworzyliśmy instancję danego oprogramowania.

Przytoczona anegdotka jest kluczowa w celu zrozumienia istoty Dockera, czyli pojęć:
Obraz & Kontener
W kursie Dockera na kanale Zaprogramuj Życie padło bardzo obrazowe porównanie powyższych słów do pożywienia, które teraz podkradnę i będę maglował do końca artykułu:
Obraz to ziemniak, podczas gdy kontener to frytka
Surowy ziemniak do jedzenia się zbytnio nie nada, ale potrzebujemy go, żeby zrobić frytki. Dokładnie tak samo jest z obrazem i kontenerem.
Obraz, jak ta płyta CD jest „read-only” – jedynie do odczytu, nie skorzystamy z niego inaczej jak tylko do stworzenia kontenera. Na bazie jednego obrazu możemy stworzyć niezliczoną ilość kontenerów, a każdy z nich to taka odchudzona na maxa maszyna wirtualna zdolna do wykonania tylko jednej funkcji. Jeśli obraz zawiera np. bazę danych SQL (czy raczej DBMS-a) to jego instancje/kontenery też zaoferują nam tylko tę bazę danych*.
(*) Ale np. każdy kontener może mieć inne ustawienia bazy tj. dane logowania albo port, kontenery mogą być parametryzowane w czasie uruchamiania.

Ale, ale. Żeby z tego ziemniaka zrobić frytki, trzeba go najpierw mieć. A żeby go mieć musimy go albo kupić albo wyhodować. Tutaj z pomocą przychodzą:
Docker Hub & Dockerfile
Ten pierwszy odnosi się do globalnego repozytorium gdzie znajdziemy obrazy najpopularniejszych aplikacji, takich właśnie jak DBMS-y, serwery (np. Tomcat czy Nginx), czy środowiska programistyczne (np. OpenJDK, Node). To wszystko jest za darmo – możemy pobierać do woli i wykorzystywać we własnych projektach.
Docker Hub oferuje również opcję założenia własnego konta/repozytorium (płatnego lub darmowego z ograniczeniami) z miejscem na nasze własne obrazy.
Ale jak stworzyć swój własny obraz?
Dockerfile
Jest to plik (zawyczaj nazywany właśnie „Dockerfile” bez żadnego rozszerzenia), który tworzymy w katalogu głównym (inaczej: „root„) naszego projektu. Wewnątrz, za pomocą specjalnej dockerowej składni, piszemy zestaw instrukcji jak wyhodować sobie własnego ziemniaka czyli stworzyć obraz projektu.
Przykład (najprostszy z możliwych) wrzucam poniżej:

Bądźcie świadomi, że zabawa z Dockerem to taka trochę gra wyobraźni.
Owszem, istnieje coś takiego jak Docker Desktop z graficznym interfejsem użytkownika, ale koniec końców i tak będziecie zmuszeni pisać komendy w konsoli, próbując logicznie poukładać sobie w łepetynie ciąg wydarzeń.
docker run --name mongodb
-e MONGO_INITDB_ROOT_USERNAME=user
-e MONGO_INITDB_ROOT_PASSWORD=123456
-v data:/data/db
--rm
-d
--network myappnetwork
mongo
Tak wygląda przykładowe polecenie jakie możecie wydać Dockerowi w celu uruchomienia kontenera na bazie danego obrazu. Przejdźmy sobie przez wszystkie elementy:
- „docker run … mongo”
uruchamia oficjalny obraz bazy MongoDB (jeśli go nie mamy to automatycznie pobierze najnowszą wersję z DockerHub), to wszystko co jest pomiędzy słowami „run” i „mongo” to dodatkowe ustawienia tego kontenera (poniżej) - „-e”
oznacza zmienne środowiskowe, w naszym przypadku ustawiamy tutaj login i hasło użytkownika bazy - „-v”
to skrót od „volume” (wolumen), Docker tworzy folder na naszej maszynie gdzie będzie zapisywał dane, które kontener wrzuci do bazy Mongo, dzięki czemu nie przepadną gdy zatrzymamy/usuniemy kontener - „–rm”
sprawia, że kontener jest tymczasowy – jak tylko go zatrzymamy to zostaje trwale usunięty - „-d”
(skrót od „detached” – odłączony) mówi Dockerowi, żeby nie zalewał nam konsoli logami z tego co się aktualnie dzieje w kontenerze - „–network myappnetwork”
tworzy wewnętrzną sieć co oznacza, że tych kontenerów ma być więcej i mają się ze sobą komunikować
Mam nadzieję, że teraz jest jasne co jest czym, ale musicie sobie uzmysłowić, że powyższy to i tak jeden z łatwiejszych przykładów, a do tego specjalnie – dla polepszenia czytelności – go sformatowałem „1 polecenie = 1 linia”. W terminalu będzie to wyglądało tak:
docker run --name mongodb -e MONGO_INITDB_ROOT_USERNAME=user -e MONGO_INITDB_ROOT_PASSWORD=123456 -v data:/data/db --rm -d --network myappnetwork mongo

Ale i tutaj nie jesteśmy zdani sami na siebie, bo z pomocą przychodzi
Docker Compose
Podsumujmy co mamy na tę chwilę:
- instrukcję jak wyhodować ziemniaka: Dockerfile
- warzywniak: Docker Hub
- ziemniaka: obraz
- frytkę: kontener
Samymi kartoflami się cywilizowany człowiek nie naje.
A gdzie schabowy, gdzie ogórki kiszone, gdzie kompocik?
We wcześniejszym akapicie wspomniałem o czymś takim jak wewnętrzna sieć Dockera, w której uruchomione kontenery mogą się ze sobą komunikować. Na jej bazie możemy stworzyć sobie zestaw połączonych kontenerów odpalanych jedną prostą komendą:
docker-compose up
I tyle. Te trzy słówka potrafią zastąpić grube linie pisane w terminalu. Ale nie zadziałają bez uprzedniego przygotowania.
O ile Dockerfile mówi Dockerowi jak stworzyć obraz z naszego projektu, o tyle plik „docker-compose.yaml” mówi Docker Compose w jaki sposób te potrawy ze sobą połączyć aby razem utworzyły jedno, przepyszne danie.
Spójrzcie na przykład pliku docker-compose.yaml:
version: "3.8"
services:
server:
image: 'nginx:stable-alpine'
ports:
- '8000:80'
volumes:
- ./src:/var/www/html
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- php
- mysql
php:
build:
context: ./dockerfiles
dockerfile: php.dockerfile
volumes:
- ./src:/var/www/html:delegated
mysql:
image: mysql:5.7
env_file:
- ./env/mysql.env
composer:
build:
context: ./dockerfiles
dockerfile: composer.dockerfile
volumes:
- ./src:/var/www/html
# artisan:
# npm:
Jeśli teraz liczycie na to, że będę tłumaczył w/w plik linijka po linijce to chyba was…ekhem… to się przeliczycie. Po pierwsze: będzie to nudne jak flaki z olejem, a po drugie – jeśli jesteście na początku waszej programistycznej ścieżki* (a do takich osób kieruję ten cykl), to ta wiedza będzie wam potrzebna najwcześniej gdzieś dopiero za rok.
(*) Jeśli znajdujecie się na etapie szukania pracy, to zwyczajnie wrzućcie sobie ten kod w ChatGPT i poproście o wytłumaczenie. Ogólnie plik zawiera instrukcje uruchomienia serwera nginx dla kodu napisanego w php i połączonego z bazą MySQL.
Ok, myślę że na chwilę obecną wystarczy i nie ma co głębiej wchodzić w szczegóły – od tego są źródła na dole tego wpisu.
Na koniec chciałbym zostawić was z przemyśleniem:
Projekt zaczyna się od Gita, a kończy na Dockerze
Git i Docker wbrew pozorom mają ze sobą wiele wspólnego – są to zewnętrzne narzędzia, które mają uprościć życie developerów zdejmując z nich masę koniecznej, ale niewygodnej roboty. Choć Git jest o wiele popularniejszy, to Docker szybko nadrabia zaległości i z roku na rok coraz częściej widać go w wymaganiach nawet na pozycje juniorskie.
Kolejne zagadnienia z działu „Deployment” jakie będziemy przerabiać czyli Kubernetes, Jenkins oraz AWS to wciąż albo sekcja „mile widziane”, albo w ogóle tematy zarezerwowane dla midów i seniorów. Natomiast starając się o pierwszą pracę jako programista naprawdę warto (chociaż z teorii) znać Dockera. To czy już na starcie będziemy z niego korzystać to już inna brocha.

Źródła:
Kurs Docker na Zaprogramuj Życie YT/POL
Przemek Bykowski: Szkolenie z Dockera YT/POL
Kurs Docker & Kubernetes na Udemy (płatne) ANG