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 ­čĄÖ

Dodaj komentarz

Tw├│j adres e-mail nie zostanie opublikowany.