Docker i docker-compose: wprowadzenie, instalacja

docker i docker-compose wprowadzenie intro

Docker to najpopularniejsza platforma do wirtualizacji. Jest obowi膮zkowym elementem wyposa偶enia wielu profesjonalist贸w i amator贸w IT. Docker pozwala w prosty spos贸b tworzy膰, wsp贸艂dzieli膰 i zarz膮dza膰 kontenerami, wszystko na licencji Open Source. W tym wpisie znajdziesz kr贸tkie wprowadzenie do Dockera. Mam nadziej臋 偶e pozwoli Ci ono zdoby膰 pewne intuicyjne zrozumienie tej technologii, a konkretnie polece艅 docker i docker-compose. Napisz臋 te偶 kr贸tko jak zainstalowa膰 oba narz臋dzia pod Linuxem.

Wprowadzenie

Czym jest Docker?

Docker to platforma s艂u偶膮ca do konteneryzacji (tzw. wirtualizacja na poziomie systemu operacyjnego). Kontener jest uruchomion膮 instancj膮 obrazu Dockera, a wi臋c, w skr贸cie, aplikacji przygotowanej dla tej platformy. Aplikacje dzia艂aj膮ce w kontenerze czuj膮 si臋 w nim jak na w艂asnym, normalnym komputerze. S膮 dzi臋ki temu odizolowane od innych aplikacji, dzia艂aj膮cych w swoich indywidualnych kontenerach.

Kontenery komunikuj膮 si臋 poprzez 艣ci艣le zdefiniowane interfejsy:

  • sieciowe – uruchamiaj膮c kontener podajemy porty poprzez kt贸re mo偶e si臋 komunikowa膰, oraz
  • system plik贸w – kontenery mog膮 posiada膰 tzw. wolumeny, a wi臋c fragment przestrzeni dyskowej host’a. Wsp贸艂dziel膮c ten sam wolumen kontenery mog膮 si臋 wi臋c r贸wnie偶 komunikowa膰.

Ka偶dy obraz zawiera wszystkie niezb臋dne do funkcjonowania danej aplikacji biblioteki i zale偶no艣ci, we w艂a艣ciwych, przetestowanych dla danego wydania wersjach. Jest to ogromna zaleta w procesie tworzenia aplikacji i testowania. Pobieramy obraz z repozytorium, odpalamy go poleceniem docker run (czy docker-compose up) i… 90% problem贸w z kategorii „u mnie dzia艂a艂o” jest wyeliminowanych. Oczywi艣cie stanowi to te偶 korzy艣膰 w trakcie uruchamiania i zarz膮dzania aplikacjami „na produkcji”.

Wszystkimi kontenerami zarz膮dza Docker Engine.

Podsumowuj膮c, najwa偶niejsze cechy kt贸rymi ja opisa艂bym kontenery Docker’a to:

  • izolacja– a wi臋c jasno definiujesz metody kt贸rymi mog膮 si臋 komunikowa膰 mi臋dzy sob膮 oraz z hostem (np. wolumeny, porty sieciowe). Wszystko co jest na zewn膮trz kontenera, w tym inne kontenery z tej samej aplikacji, musz膮 z nich korzysta膰 w komunikacji.
  • samowystarczalno艣膰– zawieraj膮 wszystkie niezb臋dne do uruchomienia Twojej aplikacji zale偶no艣ci. W przeciwie艅stwie do maszyn wirtualnych Docker korzysta jednak z j膮dra systemu operacyjnego maszyny na kt贸rej jest uruchomiony.
  • zarz膮dzalno艣膰– mo偶esz je uruchamia膰, zatrzymywa膰, kasowa膰 i przenosi膰 obrazy kontener贸w. Oznacza to m.in. proste tworzenie, aktualizowanie i nadz贸r nad 艣rodowiskami. Daje to du偶膮 przewag臋 zar贸wno w trakcie developmentu (aplikacje dzia艂aj膮 w przewidywalny spos贸b na wszystkich 艣rodowiskach – produkcja, test, development etc.). Oczywi艣cie przydaje si臋 te偶 w zastosowaniach produkcyjnych, poprzez chocia偶by zapewnienie wysokiej dost臋pno艣ci za pomoc膮 Docker Swarm.
  • nietrwa艂o艣膰 – to mo偶e oczywiste, ale wszystkie zmiany kt贸re wprowadzimy w strukturze plik贸w kontenera zostan膮 stracone w momencie jego zatrzymania. Warto pami臋ta膰, 偶e je艣li chcemy wprowadza膰 zmiany w plikach wewn膮trz kontenera, lub aby kontener m贸g艂 w trwa艂y spos贸b tworzy膰 czy edytowa膰 pliki, to musimy skorzysta膰 w wolumen贸w danych.

Jeden kontener – jedno zadanie

Z wymienionych powy偶ej wzgl臋d贸w kontener Dockera zazwyczaj tworzymy w taki spos贸b, aby realizowa艂 jedno, podstawowe zadania. Wtedy maksymalnie wykorzystujemy zalety tej technologii. Oznacza to natomiast, 偶e nasza aplikacja zazwyczaj posiada wiele kontener贸w. Jeden na back-end, jeden na relacyjn膮 baz臋 danych, jeden na InfluxDB, jeden na Grafan臋 etc.. Proces podzia艂u aplikacji na kontenery odpowiedzialne za jedno, jasno okre艣lone zadanie okre艣lane jest jako decoupling (roz艂膮czenie). W rezultacie, poza stworzeniem obrazu kontenera, zbudowaniem go oraz uruchomieniem b臋dziemy musieli zarz膮dza膰 tak偶e ustawieniami wielu innych kontener贸w tworz膮cych aplikacj臋 Dockerow膮. Do tego s艂u偶y w艂a艣nie Docker Compose.

Czym s膮 obrazy Docker’a?

Obrazy mo偶emy tworzy膰 sami – za pomoc膮 pliku Dockerfile, o czym kawa艂ek ni偶ej, lub w spos贸b interaktywny, o czym na dzisiaj w og贸le 馃拋鈥嶁檪锔. Najcz臋艣ciej pobieramy gotowe obrazy z repozytorium. Warto zapami臋ta膰, 偶e obraz jest definicj膮 aplikacji, a kontener jego uruchomion膮 instancj膮. Z jednego obrazu mo偶na uruchomi膰 wiele kontener贸w (instancji).

Same obrazy zbudowane s膮 z warstw. Ka偶da kolejna warstwa bazuje na poprzedniej, dodaj膮c co艣 do niej. Przy艣piesza to znacz膮co proces tworzenia obraz贸w i zmniejsza zapotrzebowanie na przestrze艅 dyskow膮. Dwa r贸偶ne obrazy (r贸偶ne aplikacj臋) mog膮 mie膰 sporo wsp贸lnych cz臋艣ci (a wi臋c warstw na kt贸rych bazuj膮). Je艣li chcesz zobaczy膰 ile dok艂adnie zajmuj膮 obrazy na Twoim systemie polecam komend臋

$ docker system df -v

Tam znajdziesz rozbicie rozmiaru ka偶dego obrazu na SIZE, SHARED SIZE i UNIQUE SIZE.

Je偶eli chcesz, mo偶esz sprawdzi膰 z jakich dok艂adnie warstw sk艂ada si臋 dowolny obraz Docker’a, korzystaj膮c z polecenia

$ docker history nazwa_obrazu

W procesie budowania obrazu Docker musi

  1. przebudowa膰 warstw臋 gdzie wprowadzili艣my zmiany, np. w kodzie 藕r贸d艂owym naszej aplikacji, oraz
  2. przebudowa膰 wszystkie warstwy wy偶ej w hierarchii, a wi臋c te kt贸re zale偶膮 od warstwy 1.

W rezultacie najlepsz膮 praktyk膮 aby ten proces przebiega艂 efektywnie jest u艂o偶enie warstw w kt贸rych najcz臋艣ciej wprowadzamy zmiany mo偶liwie najwy偶ej w hierarchii (tzn. jako ostatnie warstwy obrazu). O optymalizacji przeczytasz jeszcze troch臋 ni偶ej, w opisie Dockerfile

W momencie uruchomienia kontenera z obrazu tworzona jest kolejna, ostatnia warstwa, tzw. warstwa kontenera. Ta warstwa istnieje tylko tak d艂ugo jak sam kontener. Wprowadzone tam zmiany zostan膮 zaorane w momencie jego zatrzymania, o czym wspomina艂em powy偶ej.

Repozytoria obraz贸w Docker’a

Tak jak wspomnia艂em obrazy mo偶emy tworzy膰 sami lub pobra膰 z repozytorium. Repozytoria Docker’a dzia艂aj膮 troch臋 podobnie do repozytori贸w git’a, sk艂adnia polece艅 jest r贸wnie偶 podobna (docker pull <nazwa obrazu>, docker push <nazwa obrazu>). Pozwalaj膮 one na przechowywanie i wsp贸艂dzielenie obraz贸w

Warto podzieli膰 repozytoria na 3 kategorie:

  • w艂asne repozytoria, gdzie trzymamy np. obrazy nad kt贸rymi pracujemy (nasze aplikacje)
  • repozytoria firm trzecich (np. Red Hat Quay, Amazon ECR czy Google Container Registery), kt贸rych celem te偶 jest zazwyczaj przechowywanie naszych w艂asnych obraz贸w.
  • Docker Hub – oficjalne repozytorium Docker’a.

Dwa pierwsze punkty pomin臋, warto natomiast powiedzie膰 par臋 s艂贸w na temat Docker Hub. Znajdziemy tam ponad 100 000 obraz贸w, w tym wiele oficjalnych, a wi臋c zarz膮dzanych przez dostawc贸w danej aplikacji. Ka偶dy obraz posiada dokumentacj膮, opisuj膮c膮 zazwyczaj przyk艂adowe sk艂adnie polecenia docker run czy pliku compose. Sama lektura listy dost臋pnych obraz贸w jest bardzo ciekawa, kliknij explore aby j膮 przejrze膰. Mo偶na znale藕膰 fajne narz臋dzia kt贸re w kilku krokach uruchomimy i przetestujemy na w艂asnej maszynie. Dzi臋ki filtrom znajdziesz np. obrazy przystosowane do architektury ARM (a wi臋c dzia艂aj膮ce na np. Raspberry Pi) czy ARM64 (Raspberry Pi wersja 3 lub nowsza).

self-hosting wprowadzenie

self-hosting

W艂a艣nie doda艂em nowy artyku艂 na temat self-hostingu. Je艣li rozpoczynasz zabaw臋 z Dockerem, jest spora szansa 偶e Ci臋 zainteresuje 馃憞

Co to Docker Compose?

Docker Compose to narz臋dzie pozwalaj膮ce w prosty spos贸b zarz膮dza膰 aplikacjami sk艂adaj膮cymi si臋 z wielu kontener贸w. Ma dwa najwa偶niejsze elementy.

Pierwszy z nich to komenda docker-compose. Pozwala ona zarz膮dza膰 stanem aplikacji Dockera – a wi臋c zestawem wszystkich kontener贸w opisanych w pliku z definicj膮. Tym poleceniem startujemy, zatrzymujemy czy sprawdzamy stan aplikacji, analogicznie do polecenia docker dla pojedynczego kontenera.

Drugi element to plik docker-compose.yml kt贸ry definiuje wszystkie us艂ugi oraz ich konfiguracj臋 w ramach jednej aplikacji Dockera. W zasadzie jego nazwa mo偶e by膰 dowolna, natomiast je艣li u偶yjesz w艂a艣nie docker-compose.yml1 to b臋dziesz j膮 m贸g艂 pomija膰 korzystaj膮c z polecenia docker-compose co jest 1) wygodne 2) nie powoduje konflikt贸w, gdy偶 w folderze nadrz臋dnym aplikacji i tak zazwyczaj chcemy mie膰 tylko jeden plik z ustawieniami Docker Compose.

1 Korekta (2022-04): docker-compose.yml wydaje si臋 by膰 obs艂ugiwany, ale jest to nazwa deprecjonowana. Poprawna nazwa pliku dla docker-compose to compose.yaml (preferowane) lub compose.yml.

Plik ten (co podpowiada nam rozszerzenie) u偶ywa notacji YAML. Ciekawostka – YAML jest rekursywnym akronimem frazy „YAML Ain’t Markup Language”.

Je偶eli nie znasz notacji YAML, polecam po prostu poszuka膰 przyk艂adowych plik贸w docker-compose.yml. Zobaczysz, 偶e notacja jest dosy膰 prosta – wa偶ne s膮 indentacje (czyli wci臋cia kodu, a’la Python), sk艂adnia jest intuicyjna, listy dzia艂aj膮 tak jak w JSON.

Struktura pliku docker-compose.yml

Sp贸jrzmy na przyk艂adowy plik docker-compose.yml zawarty w dokumentacji obrazu Redis (baza danych klucz-warto艣膰):

version: '2.0'
services:
  web:
    build: .
    ports:
    - "5000:5000"
    volumes:
    - .:/code
    - logvolume01:/var/log
    links:
    - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

Oto kilka wniosk贸w kt贸re mo偶na wyci膮gn膮膰 z jego lektury.

  • Mamy trzy sekcje: versions (obowi膮zkowo w ka偶dym docker-compose), services (mi臋so), volumes (persystencja danych)2.
  • Mamy dwie us艂ugi, a wi臋c w rezultacie uruchomienia tego pliku powstan膮 dwa kontenery: web, a wi臋c zapewne jaka艣 aplikacja webowa oraz sam redis.
  • Us艂uga web bazuje na budowanym obrazie. Atrybut build ma warto艣膰 . (folder bie偶膮cy), oznacza to 偶e w folderze z plikiem docker-compose.yml znajduje si臋 r贸wnie偶 plik Dockerfile przy pomocy kt贸rego ten obraz zbudujemy.
  • Us艂uga redis posiada tylko wskazany obraz (image: redis). Co to oznacza? Je偶eli nie posiadamy zbudowanego obrazu o nazwie redis, Docker wyszuka tak膮 nazw臋 w serwisie Docker Hub (o kt贸rym kilka s艂贸w poni偶ej). W tym konkretnie przypadku pobierze oficjalny obraz bazy danych Redis.
  • Oba kontenery s膮 ze sob膮 po艂膮czone, natomiast tutaj istotna uwaga. Mechanizm, z kt贸rego tu skorzystano, oparty jest na atrybucie links. Ten atrybut jest deprecjonowany, czyli przestanie by膰 wspierany przez kt贸r膮艣 z kolejnych wersji Dockera. Docker zaleca korzystanie z user-defined networks, czyli w skr贸cie polegamy na portach wystawianych przez poszczeg贸lne us艂ugi oraz, je艣li to konieczne, odr臋bnych, nazwanych sieciach wirtualnych.

Poza deprecjonowanym atrybutem uwag臋 zwraca te偶 Dockerfile us艂ugi web zlokalizowany w folderze nadrz臋dnym (na tym samym poziomie co docker-compose.yml). Nie jest to miejsce na analiz臋 struktury aplikacji ale w moich projektach Dockerfile jest zawsze w podfolderze nazwanym tak samo jak us艂uga. W tym przyk艂adzie mia艂by 艣cie偶k臋 ~/web/Dockerfile a parametr build us艂ugi web wygl膮da艂by tak: build: ./web.

2 Poza wspomnianymi sekcjami versions, services, volumes plik docker-compose mo偶e posida膰 jeszcze sekcje networks, configs i secrets. Dok艂adn膮 specyfikacj臋 pliku compose znajdziesz, jak zawsze, w dokumentacji Dockera.

Uruchamia膰 przez docker czy docker-compose?

TL;DR? Ja wol臋 docker-compose.

W instrukcjach do rozmaitych obraz贸w znajdziesz cz臋sto dok艂adne parametry polecenia docker. Je艣li przyjrzymy si臋 dokumentacji do aktualnej wersji oficjalnego obrazu Elasticsearch, znajdziemy tam dzisiaj nast臋puj膮c膮 metod臋 uruchomienia:

$ docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:tag

Je偶eli potrzebujesz uruchomi膰 Elasticsearch aby co艣 sprawdzi膰, przetestowa膰, czy zrobi膰 ma艂y research, to zapewne b臋dziesz wraca艂 kilka razy do tego polecenia aby uruchomi膰 je z innymi parametrami. Za ka偶dym razem szukaj膮c poprzedniej wersji polecenia w historii pow艂oki (o czym pisa艂em tutaj -> polecenia Linux’a).

Znacznie lepiej wykorzysta膰 plik docker-compose, nawet do uruchomienia aplikacji Docker’a sk艂adaj膮cej si臋 z jednego kontenera. Cz臋sto znajdziesz gotowego compose’a w dokumentacji obrazu, natomiast w przypadku Elasticsearch jej nie znalaz艂em. TBF, klikaj膮c link do dokumentacji znajdziemy bardzo fajny tutorial „Running in Production Mode” a w nim plik compose dla wielow臋z艂owego klastra.

Je艣li nie znajdziesz przyk艂adowego docker-compose mo偶esz go po prostu spr贸bowa膰 napisa膰, lub wspom贸c si臋 polecam webow膮 aplikacj臋 Composerize kt贸ra generuje plik compose z polecenia wklejonego docker run. Poni偶ej tak wygenerowany compose z instrukcji run dla Elasticsearch:

version: '3.3'
services:
    elasticsearch:
        container_name: elasticsearch
        network_mode: somenetwork
        ports:
            - '9200:9200'
            - '9300:9300'
        environment:
            - discovery.type=single-node
        image: 'elasticsearch:tag'

OK ale po co to wszystko?

Przede wszystkim, posiadamy prosty dost臋p do ergonomicznej edycji parametr贸w, korzystaj膮c ze 艣rodowisk IDE czy edytor贸w plik贸w dost臋pnych poprzez ssh jak nano. Po drugie, pliki compose mo偶emy zapisa膰, przes艂a膰 czy wersjonowa膰 przy u偶yciu np. git’a. Dzi臋ki temu ka偶da nowa wersja Twojej aplikacji mo偶e posiada膰 w艂a艣ciwie skonfigurowany plik compose. Dotyczy to zw艂aszcza parametr贸w takich jak:

  • Porty
  • Wolumeny
  • Wersje obraz贸w
  • Zmienne 艣rodowiskowe, o kt贸rych za chwil臋

Zawarto艣膰 pliku compose mo偶esz wykorzysta膰 tak偶e uruchamiaj膮c aplikacj臋 za pomoc膮 mojego ulubionego narz臋dzia do zarz膮dzania kontenerami – Portainer. Tam ka偶dy compose to tzw. stack. Na temat Portainera pisz臋 kr贸tko w artykule o self-hostingu.

Zmienne 艣rodowiskowe kontenera

Uruchamiaj膮c kontener mo偶emy poda膰 mu zmienne 艣rodowiskowe kt贸re zostan膮 inicjalizowane w momencie startu kontenera. W poprzedniej sekcji mo偶emy zobaczy膰 jak wygl膮da deklaracja zmiennych 艣rodowiskowych dla Elasticsearch, zar贸wno za pomoc膮 komendy docker ruin jak i pliku compose (zmienna discovery.type).

Konwencja wskazuje aby nazwy zmiennych 艣rodowiskowych zawsze by艂y pisane du偶ymi literami (jak widzimy Elasticsearch si臋 jej nie trzyma 馃槈).

Dla wielu obraz贸w zmienne 艣rodowiskowe to podstawowy, czy cz臋sto jedyny spos贸b ich konfiguracji. Jest to o tyle wygodne, 偶e zapisuj膮c plik compose, zapisujemy w zasadzie dok艂adn膮 konfiguracj臋 kt贸ra zostanie uruchomiona.

Cz臋sto dostawcy obraz贸w dostarczaj膮 r贸wnie偶 przemy艣lane warto艣ci domy艣lne zmiennych 艣rodowiskowych. Dzi臋ki temu, mo偶emy uruchomi膰 standardow膮 konfiguracj臋 z艂o偶onych aplikacji przy u偶yciu kr贸tkiego polecenia. Nextcloud, kt贸rego plik compose z podstawow膮 konfiguracj膮 ma ~33 linie, mo偶esz uruchomi膰 te偶 poleceniem

$ docker run -d -p 8080:80 nextcloud
Pliki konfiguracyjne

Oczywi艣cie pliki konfiguracyjne do kt贸rych mamy dost臋p poprzez wolumen hosta te偶 bywaj膮 w u偶yciu, zw艂aszcza tam gdzie konfiguracja jest obszerna. Przyk艂ad takiego wykorzystania wolumen贸w danych znajdziemy w dokumentacji do Telegraf gdzie mapujemy plik telegraf.conf z hosta na kontener:

$ docker run -d --name=telegraf --net=influxdb -v $PWD/telegraf.conf:/etc/telegraf/telegraf.conf:ro telegraf

O samym Telegraf pisa艂em w artykule Wprowadzenie do InfluxDB.

Plik .env 馃煝

Kiedy podawanie zmiennych 艣rodowiskowych w pliku compose przestaje by膰 ergonomiczne mo偶emy skorzysta膰 z plik贸w .env. Je艣li umie艣cimy plik o tej nazwie w folderze z kt贸rego uruchamiamy docker-compose, wszystkie zmienne w tym pliku zostan膮 zainicjalizowane po starcie kontenera.

Pliki 艣rodowiska (kt贸re maja oczywi艣cie znacznie szersze wykorzystanie ni偶 tylko Docker) maj膮 bardzo prost膮 struktur臋. Ka偶dy wiersz ma format NAZWA=Warto艣膰, a wi臋c np.:

ZMIENNA_JEDEN=user
ZMIENNA_DWA=1402
DB_PASSWORD=hunter2

Kiedy posiadamy tak zadeklarowane zmienne mo偶emy po prostu wskaza膰 w pliku compose kt贸ra zmienna nale偶y do kt贸rego kontenera, bez podawania ich warto艣ci:

web:
  environment:
    - ZMIENNA_JEDEN
db:
  environment:
    - ZMIENNA_DWA

Mo偶emy tak偶e u偶y膰 tych zmiennych korzyzstaj膮c ze sk艂adni $ZMIENNA lub ${ZMIENNA}, nie tylko w sekcji environment:

db:
  image: baza:${WERSJA}

Cz臋sto wykorzystujemy t膮 sk艂adni臋 aby poda膰 t膮 sam膮 zmienn膮 艣rodowiskow膮 z pliku .env do r贸偶nie nazwanych zmiennych w kilku kontenerach:

db:
  environment:
    - DB_USER_PASSWORD=${DB_PASSWORD}
web:
  environment:
    - DB_PASSWORD=${DB_PASSWORD}

Warto tak偶e powr贸ci膰 do podanego powy偶ej przyk艂adu polecenia docker run dla Telegraf. Tam, przy mapowaniu wolumenu dla pliku konfiguracyjnego, korzystamy ze zmiennej $PWD. Ta zmienna nie zosta艂a dostarczona w pliku – jest to zmienna 艣rodowiskowa dost臋pna we wszystkich pow艂okach kompatybilnych z Posix podaj膮ca 艣cie偶k臋 aktualnego folderu.

Wracaj膮c do .env – mo偶esz oczywi艣cie wskaza膰 inn膮 nazw臋 pliku ze zmiennymi 艣rodowiskowymi. Dla polecenie run b臋dzie to parametr --env-file, a wi臋c np. docker run --env-file secrects.env. Analogicznie w docker-compose mo偶esz u偶y膰 sekcji us艂ugi env_file:

web:
  env_file:
    - secrets.env

Wi臋cej ciekawych patent贸w na zmienne 艣rodowiskowe znajdziesz, jak zawsze, w oficjalnej dokumentacji compose’a: Environment variables in Compose.

Wra偶liwe zmienne 艣rodowiskowe

Jednym ciekawym zastosowaniem dla pliku.env jest przechowywanie w nim wra偶liwych zmiennych 艣rodowiskowych (has艂a do bazy danych, klucze API etc.) i wy艂膮czenia go z kontroli wersji za pomoc膮 .gitignore.

Jest to przydatne i praktykowane w mniejszych projektach, powa偶niejsi u偶ytkownicy skorzystaj膮 z dedykowanych rozwi膮za艅, takich jak chocia偶by Docker Secrets, b臋d膮cego cz臋艣ci膮 Docker Swarm. Swarm to narz臋dzie do orkiestracji, do tego tematu wr贸cimy par臋 akapit贸w ni偶ej.

Arkansas River, CO

Instalacja

docker

W skr贸cie – aby zainstalowa膰 Dockera polecam skorzysta膰 z bardzo intuicyjnej i zrozumia艂ej instrukcji zawartej w dokumentacji Dockera. Ja cz臋sto do niej wracam, opisuje jasno procedur臋 dla wielu system贸w operacyjnych. Czego dok艂adnie szuka膰? Chocia偶by tego artyku艂u: Install Docker Engine on Ubuntu, przynajmniej je艣li tak jak ja pracujesz na Linuxie Mint (czyli dystrybucji bazuj膮cej na Ubuntu). Zwr贸膰 uwag臋 偶e chcemy zainstalowa膰 Docker Engine – czyli mechanizm pozwalaj膮cy korzysta膰 z technologii Docker. Odbywa si臋 to za pomoc膮 deamon’a dockerd czyli pracuj膮cego „w tle” procesu obs艂uguj膮cego rozmaite zadania. Z demonem dockerd komunikujemy si臋 poprzez API oraz CLI.

Dla Windowsa oraz macOS Docker Engine dost臋pny jest w pakiecie Docker Desktop, wcze艣niej znanego jako Docker for Windows i Docker for Mac. Moje do艣wiadczenia z Docker Desktop s膮 kiepskie, chocia偶 dawno ju偶 nie musia艂em z tego rozwi膮zania korzysta膰. Je偶eli nie masz Linuxa polecam odpalenie wirtualnej maszyny w chmurze Google Cloud Platform, Microsoft Azure czy Amazon Web Services. O chmurze Amazona pisa艂em dwukrotnie, w temacie prywatnego, darmowego serwera VPN oraz zdalnego repozytorium GIT. W obu wpisach znajdziesz informacje jak stworzy膰 i po艂膮czy膰 si臋 z instancj膮 EC2 na chmurze Amazona.

Problem z dystrybucj膮 Linuxa

Kiedy instalujesz Dockera na Linuxie jest jedno zastrze偶enie. Dodaj膮c repozytorium Docker’a (Set up the repository, krok 3 z instrukcji powy偶ej) masz wywo艂a膰 nast臋puj膮ce polecenie:

$ sudo add-apt-repository 
  "deb [arch=amd64] https://download.docker.com/linux/ubuntu 
  (lsb_release -cs) 
  stable

To polecenie doda dwa wiersze do pliku /etc/apt/sources.list.d/additional-repositories.list zawieraj膮cego list臋 dodatkowych repozytori贸w. U mnie ten plik wygl膮da tak:

kuba@local:~$ cat /etc/apt/sources.list.d/additional-repositories.list
deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable
deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic test

W obu wierszach musia艂em r臋cznie zmieni膰 codename dystrybucji (tutaj warto艣膰 poprawna – bionic). W moim Linuxie Mint rezultat polecenia lsb_release -cs podaj膮cego tzw. kr贸tki codename dystrybucji to tricia. Dla tej dystrybucji Docker nie przygotowa艂 wydania, dlatego nale偶y odszuka膰 w艂a艣ciwy codename dla dystrybucji nadrz臋dnej (Ubuntu). Je偶eli tak jak ja korzystasz z Mint – sprawd藕 tutaj na jakim distro Ubuntu bazuje Twoja wersja. Je偶eli chcesz zobaczy膰 dla jakich dystrybucji wydawany jest docker wejd藕 tutaj).

docker-compose

Instalacja Docker Compose jest prostsza i r贸wnie偶 polecam zapoznanie si臋 z w艂a艣ciw膮 instrukcj膮 na stronie oficjalnej dokumentacji. Polecenie curl kt贸ra pobiera w艂a艣ciwy plik binarny na Twoj膮 maszyn臋 korzysta z komend uname -s i uname -m kt贸re zwracaj膮 odpowiednio nazw臋 kernela (u mnie Linux) oraz architektury (x86_64). A wi臋c nie ma wspomnianego problemu z dystrybucjami.

Po instalacji

Je偶eli pracujesz na Linuxie i nie chcesz dodawa膰 sudo dla ka偶dego wywo艂ania komendy docker i docker-compose to powiniene艣 zadba膰 o mo偶liwo艣膰 wykonywania Dockera przez zwyk艂ego u偶ytkownika (nie root’a). Tutaj r贸wnie偶 oficjalna dokumentacja jest bardzo pomocna. Wystarczy 偶e zrobisz to dla samego Dockera, Docker Compose domy艣lnie nie wymaga uprawnie艅 root’a.

Zweryfikuj 偶e Docker jest zainstalowany poprawnie uruchamiaj膮c obraz „hello-world” :

kuba@local:~$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete 
Digest: sha256:4cf9c47f86df71d48364001ede3a4fcd85ae80ce02ebad74156906caff5378bc
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
(...)

Teraz przetestujmy jeszcze Docker Compose. Stw贸rz nowy folder oraz dodaj w nim nast臋puj膮cy plik docker-compose.yml:

version: '2.0'
services:
  hello:
    image: hello-world

Jak widzisz ma on bardzo prost膮 struktur臋, z jedn膮 us艂ug膮 (a w zasadzie aplikacj膮) – opart膮 r贸wnie偶 na obrazie testowym hello-world. Teraz mo偶emy wywo艂a膰 komend臋 docker-compose up (pomijamy zazwyczaj wykorzystywany parametr -d, poniewa偶 1) chcemy na bie偶膮co zobaczy膰 output kontenera hello a 2) po wydrukowaniu powitalnego tekstu aplikacja si臋 zatrzyma):

kuba@local:~/test$ docker-compose up
Creating network "test2_default" with the default driver
Creating test2_hello_1 ... done
Attaching to test2_hello_1
hello_1  | 
hello_1  | Hello from Docker!
hello_1  | This message shows that your installation appears to be working correctly.
(...)
hello_1  | For more examples and ideas, visit:
hello_1  |  https://docs.docker.com/get-started/
hello_1  | 
test2_hello_1 exited with code 0
kuba@local:~/test$ cat docker-compose.yml 

Je偶eli u Ciebie wygl膮da to podobnie to gratulacje! 馃帀 Wiem 偶e jest to nieco powierzchowne wprowadzenie ale mam nadziej臋 znalaz艂e艣 tu co艣 pomocnego.

Co dalej?

Dockerfile

Dockerfile o kt贸rym ju偶 wcze艣niej wspomina艂em jest sposobem na tworzenie w艂asnych obraz贸w, a w zasadzie pisanie instrukcji ich tworzenia. Naturalne skojarzenie z Makefile (je艣li kiedy艣 mia艂e艣 t膮 przyjemno艣膰) jest zatem jak najbardziej trafne. W oparciu o instrukcj臋 zawart膮 w Dockerfile polecenie docker build zbuduje Tw贸j obraz kt贸ry po uruchomieniu stanie si臋 kontenerem. Nie jest to artyku艂 o Dockerfile natomiast wspomn臋 tu o dw贸ch jego najwa偶niejszych aspektach – strukturze i optymalizacji.

Struktura Dockerfile

Dockerfile to instrukcja wykonania, krok-po-kroku. Sp贸jrzmy na przyk艂adowy plik (na marginesie, wyj臋ty z moje w艂asnego projektu wi臋c nie jest to state-of-the-art):

# pull official base image
FROM python:3.8.3-alpine

# set work directory
WORKDIR /projekt

ADD ./requirements.txt /projekt/requirements.txt
RUN apk --update add --virtual  build-dependencies libffi-dev openssl-dev python3-dev py-pip build-base
RUN pip install -r requirements.txt
ADD . /projekt

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

CMD ["gunicorn", "-c", "gunicorn_config.py", "run:app"]

Ka偶da linia to jedna instrukcja kt贸r膮 Docker zrealizuje w trakcie budowania. Ca艂y plik opisuje proces od skopiowania obrazu python:alpine (a wi臋c popularnego obrazu minimalnej dystrybucji Linuxa z zainstalowanym Pythonem) do uruchomienia mojej w艂asnej aplikacji na serwerze Gunicorn.

Je偶eli interesuje Ci臋 tworzenie obraz贸w z aplikacjami Pythona to warto 艣wiadomie podej艣膰 do wyboru obrazu bazowego. Alpine jest bardzo chudy, co ma swoje zalety (o czym za chwil臋) ale ma te偶 wiele wad. Niekt贸rzy wr臋cz wprost odradzaj膮 u偶ycie Alpine do tworzenia obraz贸w w Pythonie.

Optymalizacja i multi-stage build

W Dockerze wa偶na jest optymalizacja rozmiaru obrazu (w rezultacie r贸wnie偶 czasu jego budowania). Ka偶da instrukcja tworzy warstw臋 (layer) w obrazie. Je偶eli w folderze z aplikacj膮 wprowadzimy drobn膮 zmian臋, a folder ten skopiowali艣my w instrukcji nr 3 z 15, to pomimo braku zale偶no艣ci pomi臋dzy t膮 zmian膮 i kolejnymi instrukcjami Docker i tak przebuduje 12 nast臋pnych warstw. Dlatego kiedy piszemy Dockerfile warto wczyta膰 si臋 w zagadnienie optymalizacji i wieloetapowego budowania (multi-stage build) dzi臋ki kt贸rym oszcz臋dzimy sobie wiele czasu i zasob贸w. W moim przyk艂adowym pliku mam prost膮, zdawa艂oby si臋 oczywist膮, optymalizacj臋. Obecnie fragment instrukcji realizuje nast臋puj膮c膮 sekwencj臋:

  1. Skopiuj plik requirements.txt (wymagane biblioteki kt贸rych u偶y艂em) z folderu aplikacji
  2. Doinstaluj kilka bibliotek do Linuxa Alpine
  3. Pobierz i zainstaluje biblioteki z pliku requirements.txt
  4. Skopiuj reszt臋 folderu aplikacji, ustaw zmienne 艣rodowiskowe, uruchom serwer Gunicorn.

Oznacz to 偶e je偶eli zmieni臋 kod aplikacji (nie zmieniaj膮c u偶ytych modu艂贸w) to przy budowaniu obrazu (a wi臋c przeniesieniu wprowadzonych zmian na aplikacj臋 Dockera) zostanie wykonany tylko krok 4. Nie musz臋 chyba pisa膰 偶e te 3 wcze艣niejsze trwaj膮 zdecydowanie d艂u偶ej. M贸j proces przed t膮 zmian膮 wygl膮da艂 tak:

  1. Skopiuj ca艂y folder aplikacji (w tym requirements.txt)
  2. Doinstaluj, pobierz, zainstaluj etc..
  3. Ustaw zmienne i uruchom serwer Gunicorn

Jak si臋 艂atwo domy艣li膰 – najdrobniejsza zmiana w kodzie powodowa艂a przebudowanie ca艂ego obrazu „od zera”, ponowne pobranie dziesi膮tek bibliotek itd.. Zmiana pozwoli艂a mi to skr贸ci膰 czas z kilku minut do kilku sekund.

Multi-stage z kolei, to proces w kt贸rym, w du偶ym skr贸cie, nasz Dockerfile wielokrotnie u偶ywa s艂owa kluczowego FROM. A wi臋c tworzymy obraz w wersji full tylko po to aby np. zbudowa膰 w nim aplikacj臋 i skopiowa膰 j膮 do drugiego, odchudzonego obrazu. Mo偶emy w ten spos贸b pozby膰 si臋 wielu artefakt贸w. Gdybym przepisa艂 sw贸j w艂asny plik zgodnie z podej艣ciem multi-stage, m贸g艂bym np. wyeliminowa膰 wszystkie zale偶no艣ci kt贸rych u偶y艂em do zbudowania bibliotek z finalnego obrazu. Ale to chyba zagadnienie na inny artyku艂… 馃檯馃徎鈥嶁檪锔

Orkiestracja

Tematem kt贸rego w tym wpisie nie poruszy艂em jest tzw. orkiestracja (orchestration). Tak jak Docker Compose pozwala nam zarz膮dza膰 aplikacjami z艂o偶onymi z wielu kontener贸w uruchomionych na jednej maszynie (lub, aby by膰 bardziej precyzyjnym, na jednym Docker Engine), tak orkiestracja jest technologi膮 pozwalaj膮c膮 na zarz膮dzanie aplikacjami Docker dzia艂aj膮cymi na wielu maszynach, w rozproszonych 艣rodowiskach. Jest ona wi臋c kluczowa do osi膮gni臋cia skalowalno艣ci i wysokiej dost臋pno艣ci Twojej aplikacji. W zwi膮zku z tym ten temat ma znacznie je偶eli Twoje aplikacje s膮 ju偶 „na produkcji”.

Najpopularniejszymi narz臋dziami s艂u偶膮cymi do orkiestracji s膮 Docker Swarm (a wi臋c kolejny system autorstwa Dockera) oraz Kubernetes (w skr贸cie – K8s). Kubernetes wywodzi si臋 ze stajni Google, natomiast obecnie jest zarz膮dzany przez Linux Foundation.

Kubernetes posiada opinie systemu bardzo z艂o偶onego i raczej trudnego w opanowaniu, zw艂aszcza bez wcze艣niejszej wiedzy z dziedziny wirtualizacji, konteneryzacji czy w艂a艣nie orkiestracji. Docker Swarm, przynajmniej s膮dz膮c po lekturze dokumentacji, posiada nieco mniejsz膮 barier臋 wej艣cia.

Na koniec wspomn臋 偶e je偶eli jeste艣 u偶ytkownikiem chmury, to ka偶dy z wi臋kszych dostawc贸w posiada swoje w艂asne narz臋dzia do zarz膮dzania orkiestracj膮 kontener贸w:

  • Amazon Elastic Container Service (Amazon ECS )
  • Google Kubernetes Engine (GKI)
  • Azure Container Instances, Azure Container Services czy Azure Kubernetes Services (ACI, ACS, AKS)

Oczywi艣cie pod mask膮 (co jest ewidentne patrz膮c na ich nazwy) te us艂ugi korzystaj膮 cz臋sto z Swarm lub K8s, natomiast ich przewag膮 jest ergonomia, integracja oraz jednolity, w odniesieniu do pozosta艂ych modu艂贸w chmury, interfejs u偶ytkownika.

TL;DR?

  • Naucz si臋 podstaw komend docker i docker-compose, je艣li chcesz i艣膰 dalej i budowa膰 w艂asne obrazy to szukaj w google o plikach Dockerfile
  • Czytaj oficjaln膮 dokumentacj臋 Dockera 猸愨瓙猸愨瓙猸
  • Testuj oficjalne obrazy dost臋pne na Docker Hub. Z odrobin膮 znajomo艣ci docker-compose mo偶na w prosty spos贸b zbudowa膰 z艂o偶one aplikacje.
  • Sprawdzaj list臋 najpopularniejszych repozytori贸w na GitHubie. Mo偶esz j膮 filtrowa膰 po j臋zyku – zar贸wno programistycznym jak i tym m贸wionym. Jest to o tyle wa偶ne 偶e ostatnio spora cz臋艣膰 czo艂贸wki to pliki w j臋zyku chi艅skim.

Na koniec warto wspomnie膰 o repozytorium awesome-docker na GitHubie w kt贸rym znajdziesz ca艂膮 mas臋 produkt贸w, obraz贸w, 藕r贸de艂, zasob贸w itd. przydatnych w pracy i nauce Dockera. W tej chwili repozytorium posiada ponad 18000 猸 wi臋c z pewno艣ci膮 jest tam wiele cennych informacji.

P.S.

Zdj臋cie tytu艂owe zrobi艂em aparatem Nikon D200 gdzie艣 w ko艂o 2010 roku, w okolicach Alamosa w Kolorado, USA 鉂わ笍 馃嚭馃嚫. Zdj臋cie w 艣rodku artyku艂u to trasa 50, biegn膮ca wzd艂u偶 rzeki Arkansas, gdzie艣 pomi臋dzy Ca帽on City i Texas Creek . Poni偶ej bonus, zdj臋cie ponownie trasa w okolicy Alamosy, innego dnia:

Alamosa, Colorado, USA

Autor: kuba

Pracuj臋 w IT, sprzedaj膮c oprogramowanie klasy APS/MES firmom produkcyjnym. Nie jestem software developerem ale odk膮d zacz膮艂em w Turbo Pascalu zawsze co艣 pisz臋 - ostatnio w Pythonie. Lubi臋 technologie, mocn膮 kaw臋 i dobre zdj臋cia 馃

W odpowiedzi na “Docker i docker-compose: wprowadzenie, instalacja”

Dodaj komentarz

Tw贸j adres e-mail nie zostanie opublikowany.