Dockerfile – Szkoła Dockera https://szkoladockera.pl Najlepsze miejsce dla entuzjastów konteneryzacji i Dockera Wed, 17 Jun 2020 11:52:17 +0000 pl-PL hourly 1 https://wordpress.org/?v=5.7.15 https://szkoladockera.pl/wp-content/uploads/2019/07/cropped-Shelftons-5-32x32.png Dockerfile – Szkoła Dockera https://szkoladockera.pl 32 32 Tworzenie lekkich i optymalnych obrazów dockerowych https://szkoladockera.pl/tworzenie-lekkich-i-optymalnych-obrazow-dockerowych/ https://szkoladockera.pl/tworzenie-lekkich-i-optymalnych-obrazow-dockerowych/#respond Wed, 17 Jun 2020 08:24:12 +0000 https://szkoladockera.pl/?p=1869 Tworzenie obrazów dockerowych Tworzenie obrazów dockerowych to temat rzeka. Dla każdej technologii obraz będzie wyglądał nieco inaczej. Są jednak pewne „wspólne” dobre praktyki, które możemy stosować niezależnie od technologii, w której stworzona została aplikacja. Przypomnijmy, że to obraz to działająca instancja kontenera. Jeżeli chcesz, by Twoje kontenery po uruchomieniu były Dowiedz się więcej

Artykuł Tworzenie lekkich i optymalnych obrazów dockerowych pochodzi z serwisu Szkoła Dockera.

]]>
Tworzenie obrazów dockerowych

Tworzenie obrazów dockerowych to temat rzeka. Dla każdej technologii obraz będzie wyglądał nieco inaczej. Są jednak pewne „wspólne” dobre praktyki, które możemy stosować niezależnie od technologii, w której stworzona została aplikacja.

Przypomnijmy, że to obraz to działająca instancja kontenera. Jeżeli chcesz, by Twoje kontenery po uruchomieniu były lekkie i bezpieczne — koniecznie stosuj dobre praktyki podczas tworzenia obrazów

Zacznij od ustalenia konkretnej wersji obrazu bazowego

Może to się wydawać oczywiste, ale wiele osób zapomina o ustaleniu konkretnej wersji obrazu bazowego. Dlaczego jest to takie ważne?

Ten kod:

FROM node

WORKDIR /app

COPY package.json .
RUN npm install
COPY . .

CMD ["node", "server.js"]

Zastąp tym:

FROM node:14.4

WORKDIR /app

COPY package.json .
RUN npm install
COPY . .

CMD ["node", "server.js"]

Jeżeli nie podasz konkretnej wersji, to domyślną wersją obrazu będzie latest, która każdej chwili może zostać zaktualizowana na Docker Hubie.

W chwili przeprowadzania testów wszystko działało, a nagle po upływie kilku miesięcy…

NIE DZIAŁA.

Zastanawiasz się, co może być przyczyną. Spędzasz masę czasu na znalezienie problemu. Okazuje się, że obraz z tagiem :latest został w tym czasie kilkukrotnie zaktualizowany (w obrazie wprowadzono zmiany), co spowodowało, że Twoja aplikacja zachowuje się teraz inaczej.

Poszukaj minimalnego obrazu bazowego

Ten kod:

FROM node:14.4.0

WORKDIR /app

COPY package.json .
RUN npm install
COPY . .

CMD ["node", "server.js"]

Zastąp tym:

FROM node:14.4.0-buster-slim

WORKDIR /app

COPY package.json .
RUN npm install
COPY . .

CMD ["node", "server.js"]

Dzięki temu otrzymujemy oszczędność około ~250MB na rozmiarze obrazu, co oznacza szybszy czas budowania, pobierania i pushowania do Docker Registry.

Korzystaj z pliku .dockerignore

.git
node_modules
build

Na pewno kojarzysz i wiesz do czego służy plik .gitignore w przypadku systemu kontroli wersji Git. Aby wykluczyć niepotrzebne pliki z procesu budowania obrazu (bez konieczności zmian twojego katalogu), użyj pliku .dockeringore. Docker tworząc to rozwiązanie, wzorował się na .gitignore (które to podejście zapewne doskonale znasz). Przykładem mogą być zbudowane paczki lub pliki wykonywalne (.dll, .exe, katalog node_modules itp), utworzone lokalnie podczas developmentu aplikacji.

Przygotowując kontener, nie chcemy ich kopiować. Chcemy, by zostały one zbudowane na nowo wewnątrz kontenera.

Już to wiem, ale jak ogarnąć resztę?

Są dwie drogi.

Pierwsza z nich to dołączenie do programu Docker Maestro, czyli najbardziej obszernego kursu online z Docker po polsku. Budowanie własnych obrazów to tylko mały fragment tego co znajduje się w całym kursie. Łącznie znajdziesz tam:

12 modułów
89 lekcji
Wiedza w pigułce
Praktyka & teoria


Od podstaw aż po zaawansowane tematy.

Jeżeli jednak z jakichś powodów nie możesz dołączyć do Docker Maestro, to zachęcam do obejrzenia poniższego wideo, gdzie na przykładzie dwóch aplikacji — ReactJS oraz NodeJS pokazuję jak optymalizować swoje obrazy.

Jest to tylko przykład i nawet jeśli na co dzień używasz innych technologii, poprzez analogię możesz przenieść te dobre praktyki do siebie.

1. Co robić by po zbudowaniu nie ważyły setek megabajtów?
2. Jak zadbać o ich bezpieczeńśtwo?
3. A co z obrazami bazującymi na Alpine Linux
4. Jakich narzędzi używać, by ciągle pilnować ich jakości?

Zapraszam do oglądania!

Jak już obejrzysz, to wpadnij na stronę Docker Maestro i sprawdź TRZY darmowe lekcje DEMO.

Dzięki i do usłyszenia!



Artykuł Tworzenie lekkich i optymalnych obrazów dockerowych pochodzi z serwisu Szkoła Dockera.

]]>
https://szkoladockera.pl/tworzenie-lekkich-i-optymalnych-obrazow-dockerowych/feed/ 0
Zmienne środowiskowe w Dockerze https://szkoladockera.pl/zmienne-srodowiskowe-w-dockerze/ https://szkoladockera.pl/zmienne-srodowiskowe-w-dockerze/#respond Thu, 19 Mar 2020 07:08:15 +0000 https://szkoladockera.pl/?p=1508 Przeglądając grupy tematyczne związane z Dockerem, zauważyłem, że wiele osób ma problem z zrozumieniem, w jaki sposób można przekazywać zmienne środowiskowe do obrazu i do kontenera. Zmotywowało mnie to do stworzenia tego artykułu. Z artykułu dowiesz się WSZYSTKO na temat zmiennych środowiskowych w Dockerze. Oczywiście wszystko na przykładach, tak by Dowiedz się więcej

Artykuł Zmienne środowiskowe w Dockerze pochodzi z serwisu Szkoła Dockera.

]]>
Przeglądając grupy tematyczne związane z Dockerem, zauważyłem, że wiele osób ma problem z zrozumieniem, w jaki sposób można przekazywać zmienne środowiskowe do obrazu i do kontenera. Zmotywowało mnie to do stworzenia tego artykułu.

Z artykułu dowiesz się WSZYSTKO na temat zmiennych środowiskowych w Dockerze. Oczywiście wszystko na przykładach, tak by łatwiej było zrozumieć, a następnie móc zastosować u siebie.

Post ten jest kontynuacją serii, w której opisują poszczególne instrukcje Dockerfile. Jeżeli jeszcze nie widziałeś, koniecznie sprawdź COPY vs ADD oraz CMD vs ENTRYPOINT.


Zastosowania instrukcji ENV

Wewnątrz pliku Dockerfile, instrukcja ENV służy do tworzenia zmiennych środowiskowych. Podczas uruchamiania kontenera na podstawie obrazu, możemy nadpisać ich wartość. Taka praktyka jest bardzo często stosowana.

Do czego mogą służyć zmienne środowiskowe? Niemal każda aplikacja ma jakąś konfigurację.

Przykłady:

  • adres IP do bazy danych
  • adres IP do innych usług (Elasticsearch, Vault etc.)
  • zmienne związane z technologią (NODE_ENV, JAVA_VERSION etc)
  • zmienne związane z logiką biznesową


Sposoby na przekazywanie zmiennych środowiskowych

1. Uruchamianie kontenera z terminala

Jeżeli tworzysz kontener korzystając z terminala, do nadpisania lub ustawienia zmiennej środowiskowej służy argument -e KLUCZ=WARTOŚĆ. Poniżej tworzymy kontener WordPressa, przekazując w zmiennych środowiskowych połączenie do bazy danych.

docker run -e WORDPRESS_DB_HOST=10.1.2.3:3306 \
    -e WORDPRESS_DB_USER=user \
    -e WORDPRESS_DB_PASSWORD=somepassword \
    -d wordpress

2. Uruchamianie z pomocą docker-compose

Ten sam efekt możesz uzyskać wewnątrz pliku docker-compose.yml

version: '3.1'

services:

  wordpress:
    image: wordpress
    environment:
       - WORDPRESS_DB_HOST: 10.1.2.3:3306
       - WORDPRESS_DB_USER: user 
       - WORDPRESS_DB_PASSWORD: somepassword
       - WORDPRESS_DB_NAME: exampledb

3. Bezpośrednio w Dockerfile

Istnieją pewne przypadki, dla których na stałe chcemy ustawić zmienną środowiskową. Jak już wyżej wspominałem, może dotyczyć to technologii, w której tworzymy aplikację. Przykładowo, chcemy na stałe ustawić zmienną środowiskową NODE_VERSION. Przykład poniżej.

FROM scratch

# set up node
ENV NODE_VERSION 8.9.4
ENV NODE_DOWNLOAD_SHA 21fb4690e349f82d708ae766def01d7fec1b085ce1f5ab30d9bda8ee126ca8fc
RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \
    && echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \
    && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \
    && rm nodejs.tar.gz \
    && ln -s /usr/local/bin/node /usr/local//nodejs


Stosowanie plików .env

Istnieje możliwość stworzenia pliku o nazwie .env. Zawartość tego pliku może posłużyć do nadpisania wartości znajdujących się w pliku docker-compose.yml. Warto zaznaczyć, że oba pliki muszą znajdować się w tym samym katalogu.

Zawartość pliku .env defniujemy za pomocą schematu klucz=wartość

DB_PORT=5434
DB_USER=myuser

Przykład pliku docker-compose, gdzie to zadziała. Zwróć uwagę w jaki sposób określamy, że zmienna ma zostać podstawiona. Jest to składnia ${DB_PORT} oraz ${DB_USER}

version: '3.2'

services:
  postgres:
    image: postgres:9.6
    ports:
      - ${DB_PORT}:5432
    environment:
        POSTGRES_PASSWORD: secretpassword
        POSTGRES_USER: ${DB_USER}

Aby upewnić się, że wartości są podstawiane prawidłowo wystarczy użyć komendy docker-compose config

$ docker-compose config                  
services:                                
  postgres:                              
    environment:                         
      POSTGRES_PASSWORD: secretpassword  
      POSTGRES_USER: myuser              
    image: postgres:9.6                  
    ports:                               
    - published: 5434                    
      target: 5432                       
version: '3.2'

W miejsce ${DB_USER} została podstawiona wartość myuser, a w miejsce ${DB_PORT} wartość 5434. Wartości te pochodzą z pliku .env. Warto podkreślić, że oba pliki muszą znajdować się w tym samym katalogu.

Gdzie takie podejście może mieć zastosowanie?

Plik docker-compose.yml jest dodawany do kontroli wersji, natomiast plik .env już nie. Dzięki temu, każdy z członków zespołu może mieć inną zawartość pliku .env według własnych potrzeb, co nie będzie powodować żadnych zmian w repozytorium.


Zmienne środowiskowe na hoście nadpisują wpisy w pliku .env

Jeżeli na hoście istnieje zmienna o takiej samej nazwie jak wewnątrz pliku .env, wartość zostanie pobrana z zmiennej środowiskowej hosta! Sprawdźmy to na przykładzie. Na hoście ustawiłem wartość zmiennej środowiskowej JAVA_HOME na C:\Program Files\Java\jdk1.8.0_221

Mamy następujące plik docker-compose.yml, w którym odwołujemy się do ${JAVA_HOME}

version: '3.2'

services:
  myapp:
    image: openjdk:7
    environment:
        JAVA_DIRECTORY: ${JAVA_HOME}

W tym samym katalogu znajduje się również plik .env zawierający zmienną JAVA_HOME=D:/java

JAVA_HOME=D:/java

Teraz pozostaje sprawdzić całość za pomocą polecenia docker-compose config

$ docker-compose config
services:
  myapp:
    environment:
      JAVA_DIRECTORY: C:\Program Files\Java\jdk1.8.0_221
    image: openjdk:7
version: '3.2'

Rezulat? Zamiast wartości zdefiniowanej w pliku .env, została podstawiona wartość zmiennej środowiskowej z hosta! (C:\Program Files\Java\jdk1.8.0_221)


Zmienne w terminalu nadpisują wszystko

Nie można było o tym nie wspomnieć. Definiując zmienną środowiskową wewnątrz terminala, ma ona najwyższy priorytet.

Ustawiamy zmienną środowiskową (system Windows):

$ set JAVA_HOME=D:\shell_variable_path

Obecny stan zmiennych o nazwie JAVA_HOME:

  • na hoście: C:\Program Files\Java\jdk1.8.0_221
  • w pliku .env:  D:\java
  • w terminalu: D:\shell_variable_path

Tradycyjnie sprawdzamy rezultat poleceniem docker-compose config

$ docker-compose config
services:
  myapp:
    environment:
      JAVA_DIRECTORY: D:\shell_variable_path
    image: openjdk:7
version: '3.2'

Tym sposobem mamy potwierdzenie, iż zmienne środowiskowe zdefiniowane w terminalu mają najwyższy priorytet!

UWAGA: Plik .env nie ma nic wspólnego z instrukcjami ENV wewnątrz Dockerfile oraz argumentem `-e` w terminalu!  Oznacza to, że wartości zdefiniowane w pliku .env, NIE trafią bezpośrednio do kontenera, a służą tylko do podstawiania wartości w pliku docker-compose.yml


Inne sposoby na przekazywanie zmiennych środowiskowych do kontenera

Po zbudowaniu obrazu, możemy uruchamiać kontenery i przekazywać do nich zmienne środowiskowe na kilka sposobów. W każdym przypadku, spowoduje to nadpisanie domyślnych wartości zdefiniowanych w Dockerfile.

1. Zmienne środowiskowe z hosta

Jak to działa? Standardowo korzystamy z argumentu -e, ale z małą różnicą. Podajemy tylko nazwę zmiennej, bez wartości. W ten sposób docker pobiera zmienną środowiskową zdefiniowaną w terminalu (jeżeli istnieje) lub w systemie hosta.

Obecnie w terminalu wartość zmiennej JAVA_HOME=D:\shell_variable_path

Uruchamiamy kontener w trybie interaktywnym z konsolą, wskazując -e JAVA_HOME. Następnie wewnątrz kontenera, wyświetlamy zawartość zmiennej.

$ docker run -it -e JAVA_HOME alpine sh
/ # $JAVA_HOME 
sh: D:\shell_variable_path

Widzimy, że wewnątrz kontenera została podstawiona wartość z hosta (a dokładnie z terminala hosta).

2. Wartości z pliku (env_file)

Tworzymy plik o dowolnej nazwie, a jego zawartość powinna opierać się o wpisach typu klucz=wartość. Dla przykładu, plik ten będzie miał nazwę my_env_file (bez żadnego rozszerzenia), a jego zawartość będzie następująca:

VARIABLE_NAME=variable_value

Uruchamiamy kontener z parametrem env (spowoduje wyświetlenie wszystkich zmiennych środowiskowych) wskazując na plik my_env_file:

$ docker run --env-file=my_env_file alpine env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=77929624bad0
VARIABLE_NAME=variable_value
HOME=/root

Możemy również wykorzystać to samo podejście w pliku docker-compose.yml, dodając wpis env_file: <ścieżka_do_pliku>

version: '3.2'

services:
  alpine:
    image: alpine
    env_file: my_env_file


Podsumowanie

Stosowanie zmiennych środowiskowych w Dockerze jest czymś naturalnym i często wykorzystywanym. Na przykładzie WordPressa widzimy, że nawet oficjalne obrazy, które możemy znaleźć na Docker Hubie stosują ten mechanizm. Warto go zatem znać i rozumieć jak działa.

Mam nadzieję, że nieco pomogłem zrozumieć Ci jak działają zmienne środowiskowe w Dockerze, lub bynajmniej post ten spowodował odświeżenie Twojej dotychczasowej wiedzy. Ważne, że w razie wątpliwości można tutaj wrócić i rozwiać swoje wątpliwości, do czego gorąco zachęcam!



Artykuł Zmienne środowiskowe w Dockerze pochodzi z serwisu Szkoła Dockera.

]]>
https://szkoladockera.pl/zmienne-srodowiskowe-w-dockerze/feed/ 0
Jak uruchomić jednocześnie dwa procesy w kontenerze? https://szkoladockera.pl/jak-uruchomic-jednoczesnie-dwa-procesy-w-kontenerze-przyklad-z-zycia/ https://szkoladockera.pl/jak-uruchomic-jednoczesnie-dwa-procesy-w-kontenerze-przyklad-z-zycia/#comments Thu, 27 Feb 2020 06:36:16 +0000 https://szkoladockera.pl/?p=1300 Dzisiaj wpis o tym, w jaki sposób uruchomić jednocześnie dwa procesy w jednym kontenerze. Początkowo może to brzmieć jak anti-pattern, ale pozwól, że wyjaśnię Ci dlaczego czasami warto to rozważyć – oczywiście na prawdziwym przykładzie 🙂 Kontener = Jeden proces Ogólnie panująca zasada mówi: Kontener powinien być zgodny z zasadą Dowiedz się więcej

Artykuł Jak uruchomić jednocześnie dwa procesy w kontenerze? pochodzi z serwisu Szkoła Dockera.

]]>
Dzisiaj wpis o tym, w jaki sposób uruchomić jednocześnie dwa procesy w jednym kontenerze. Początkowo może to brzmieć jak anti-pattern, ale pozwól, że wyjaśnię Ci dlaczego czasami warto to rozważyć – oczywiście na prawdziwym przykładzie 🙂

Kontener = Jeden proces

Ogólnie panująca zasada mówi:

Kontener powinien być zgodny z zasadą SRP i mieć pojedynczą odpowiedzialność.

W 100 % się z tym zgadzam, a nawet namawiam do tego w moim poradniku na temat tworzenia Dockerfile.

Główny proces kontenera określamy w pliku Dockerfile za pomocą instrukcji ENTRYPOINT lub CMD. O różnicach pomiędzy tymi instrukcjami pisałem w ostatnim poście.

Na tej podstawie, główny proces kontenera jest odpowiedzialny za zarządzanie pozostałymi procesami, które tworzy. Jeżeli główny proces kończy swoje działanie, automatycznie powoduje to zatrzymanie kontenera.

Chcąc uruchomić kontener składający się z wielu procesów, nie ma do tego jednoznacznego rozwiązania. Nawet jeśli przyszło Ci do głowy, że można by zdefiniować w Dockerfile dwa razy instrukcję ENTRYPOINT lub CMD – niestety, ale to nie zadziała.

Tylko ostatnia instrukcja ENTRYPOINT i/lub CMD jest brana pod uwagę,

Realny Problem

W pewnym projekcie, gdzie używamy bazy danych PostgreSQL w Dockerze do celów testowo-developerskich, potrzebowaliśmy tworzyć joby, które będą wykonywać się co określony interwał czasowy. Problem w tym, że PostgreSQL domyślnie nie posiada takiego mechanizmu, który by na to pozwalał.

Do wyboru mamy dwa pluginy: pg_cron oraz pgAgent.

Początkowo naturalnym wydawał się pg_cron, jednak po zagłębieniu się w jego specyfikację, okazało się, że wymaga on podania na sztywno w pliku konfiguracyjnym nazwy bazy danych.

Niestety nie sprostało to naszym wymaganiom, ponieważ w obecnym projekcie tworzymy dynamicznie bazy danych i na etapie konfiguracji nawet nie znamy ich nazw.

Chyba mam rozwiązanie

Szybki research na temat pgAgenta. „Tak, to zadziała” – pomyślałem.

Wystarczy znaleźć na Docker Hubie gotowy obraz Postgresa z pgAgentem i temat załatwiony. Niestety, jak to w życiu bywa – nie ma tak łatwo. Próby poszukiwania oficjalnego obrazu (lub stabilnego) zakończyły się fiaskiem. Na Githubie również pustki (lub to co znalazłem, mnie nie przekonywało)

Docker - dwa procesy w kontenerze. Jak to zrobić?

Trzeba to zrobić samemu, ale jak?

No dobra, trzeba coś wymyślić samemu. Pierwszym krokiem było rozmontowanie na części pierwsze oficjalnego Dockerfile Postgresa (open-source rulez!)

Pomijając instalację niezbędnych paczek, czy konfiguracje uprawnień, interesowało mnie, co jest głównym procesem kontenera.

FROM debian:stretch-slim

. . . 
. . .

ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 5432
CMD ["postgres"]

Plik docker-entrypoint.sh wykonuje się zawsze jako pierwszy (jeszcze przed startem głównego procesu) i odpowiada za inicjalizację i konfigurację serwera, by następnie uruchomić serwer postgresa.

Własny Dockerfile

W oficjalnym obrazie Postgresa, niestety nie ma zainstalowanego pgAgenta. Trzeba zatem zrobić to samemu 🙂

FROM postgres:10

RUN apt-get update && apt-get -y install pgagent

OK – mam już pgAgenta w obrazie, co dalej? Sama instalacja nie wystarczy. PgAgent musi zostać uruchomiony. Potrzebujemy więc uruchomić dwa procesy na raz. Jeden to pochodzący z oficjalnego obrazu skrypt docker-entrypoint.sh, drugi to pgAgent.

Rozwiązanie

Aby uruchomić jednocześnie pgAgenta oraz serwer Postgresa, potrzebujemy skryptu, który to za nas zrobi. Skrypt ten nazwałem entrypoint.sh.

W pierwszym kroku, uruchomimy pgAgenta, który będzie działać w tle.

/usr/bin/pgagent dbname=postgres user="$POSTGRES_USER"
status=$?
if [ $status -ne 0 ]; then
  echo "Failed to start first process: pg_agent: $status"
  exit $status
fi

Drugi krok, to uruchomienie Postgresa. Uruchomimy docker-entrypoint.sh (domyślny plik znajdujący się w oficjalnym obrazie) oraz pozwolimy na przekazanie dodatkowego argumentu, który zostanie określone w pliku Dockerfile, za pomocą instrukcji CMD.

/docker-entrypoint.sh "$@"
status=$?
if [ $status -ne 0 ]; then
  echo "Failed to start second process: postgres engine: $status"
  exit $status
fi

Na koniec, dodajemy logikę, która w momencie gdy któryś z procesów przestanie działać, wyślę sygnał exit 1.

while sleep 60; do
  ps aux |grep pgagent |grep -q -v grep
  PROCESS_1_STATUS=$?
  ps aux |grep postgres |grep -q -v grep
  PROCESS_2_STATUS=$?
  # If the greps above find anything, they exit with 0 status
  # If they are not both 0, then something is wrong
  if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
    echo "One of the processes has already exited."
    exit 1
  fi
done

Mamy to. Skrypt jest gotowy. Całość znajdziesz na moim Githubie.

Finalny Dockerfile

Mając plik entrypoint.sh, możemy wymusić jego uruchomienie. Poniżej znajdziesz finalny Dockerfile:

FROM postgres:10

RUN apt-get update && apt-get -y install pgagent

COPY create_extension.sh /docker-entrypoint-initdb.d
COPY entrypoint.sh /usr/local/bin/

RUN chmod +x /usr/local/bin/entrypoint.sh \
    && ln -s /usr/local/bin/entrypoint.sh /

ENTRYPOINT ["entrypoint.sh"]
CMD ["postgres"]

Pewnie zastanawiasz się, co kryje się w pliku create_extension.sh. Już tłumaczę. W tym pliku znajduje się polecenie, które wykona skrypt SQL bezpośrednio na bazie danych, a dokładniej, utworzy rozszerzenie pgagent.

psql -U "$POSTGRES_USER" -W postgres -c "CREATE EXTENSION pgagent"

Jak z tego korzystać?

Wystarczy zbudować obraz poleceniem docker build i korzystać z niego dokładnie tak samo jak w przypadku oficjalnego obrazu postgres’a.

Docker Build:

$ docker build . -t postgres-pgagent 

Docker Run:

$ docker run -d --name postgresdb -e POSTGRES_USER=postgres -e \
 POSTGRES_PASSWORD=passsword123 -p 5432:5432 \
 --restart=always postgres-pgagent

Podsumowanie

W niektórych przypadkach uruchomienie dwóch procesów kontenerze ma sens. Należy pamiętać, o odpowiednim przygotowaniu i zarządzaniu tymi procesami. Kluczowa kwestia to sytuacja w której jeden proces nagle kończy swoje działanie. Musisz odpowiedzieć sobie na pytanie:

„Co powinno się wtedy wydarzyć?”

Przykład omawiany w artykule nie jest przeznaczony do rozwiązań produkcyjnych. Jeżeli chcesz z niego skorzystać, rób to z głową!


Artykuł Jak uruchomić jednocześnie dwa procesy w kontenerze? pochodzi z serwisu Szkoła Dockera.

]]>
https://szkoladockera.pl/jak-uruchomic-jednoczesnie-dwa-procesy-w-kontenerze-przyklad-z-zycia/feed/ 2
Dockerfile – ENTRYPOINT vs CMD https://szkoladockera.pl/dockerfile-entrypoint-vs-cmd/ https://szkoladockera.pl/dockerfile-entrypoint-vs-cmd/#comments Thu, 20 Feb 2020 05:48:01 +0000 https://szkoladockera.pl/?p=1211 Ostatnio jeden z czytelników bloga zadał mi pytanie: Co powinienem użyć w moim Dockerfile? ENTRYPOINT czy CMD? Postanowiłem, że odpowiem na to pytanie w formie artykułu – tak byś i Ty mógł/mogła z tego skorzystać. Post dołącza do serii A vs B, gdzie w jednym z poprzednich artykułów omawialiśmy różnicę pomiędzy ADD i COPY. Dowiedz się więcej

Artykuł Dockerfile – ENTRYPOINT vs CMD pochodzi z serwisu Szkoła Dockera.

]]>
Ostatnio jeden z czytelników bloga zadał mi pytanie: Co powinienem użyć w moim Dockerfile? ENTRYPOINT czy CMD? Postanowiłem, że odpowiem na to pytanie w formie artykułu – tak byś i Ty mógł/mogła z tego skorzystać.

Post dołącza do serii A vs B, gdzie w jednym z poprzednich artykułów omawialiśmy różnicę pomiędzy ADD i COPY. Link do tego artykułu znajdziesz TUTAJ.

Jak sam tytuł wskazuje, dzisiaj przyjrzymy się bardziej szczegółowo instrukcjom ENTRYPOINT oraz CMD.

Wprowadzenie

Kiedy uruchamiamy kontener, uruchamiamy go na podstawie obrazu. Dzięki temu jesteśmy w stanie przenosić zachowanie kontenerów pomiędzy środowiskami. To, co znajdzie się wewnątrz obrazu oraz jak będzie się zachowywać kontener po uruchomieniu, określamy w pliku Dockerfile. Plik Dockerfile to zbiór instrukcji, gdzie zwykle każda z instrukcji dodaje kolejną warstwę do finalnego obrazu.

Z reguły w pierwszej warstwie obrazu znajdują się pliki systemowe. Kolejne polecenia takie jak RUN, COPY, ADD tworzą następne warstwy, by finalnie stworzyć kompletny obraz naszej aplikacji.

Przykładowo — nasz obraz może być zbudowany na podstawie Ubuntu (polecenie FROM). W kolejnej warstwie instalujemy dodatkowe pakiety, które są niezbędne do działa naszej aplikacji. Następnie kopiujemy artefakty aplikacji i wskazujemy proces startowy kontenera za pomocą polecenia CMD.

TL;DR;

A czy możemy użyc tutaj ENTRYPOINT zamiast CMD?

Odpowiedź brzmi: TAK, możemy. Nawet powinniśmy!

FROM ubuntu:18.04
RUN apt update && apt install python3
COPY my_script.py /
CMD ["python", "my_script.py"]

Instrukcje ENTRYPOINT oraz CMD pozwalają określić punkt startowy dla kontenera, ale istnieje znaczna różnica pomiędzy nimi. Może zatem pojawić się dylemat, którą instrukcję wybrać albo która instrukcja jest rekomendowana.


Shell vs Exec

Zarówno instrukcja CMD, jak i ENTRYPOINT mogą występować w dwóch formach. Shell oraz Exec.

Exec

Składnia formy Exec (rekomendowanej) jest następująca:

<instruction> ["executable", "param1", "param2"]

FROM ubuntu:18.04
CMD ["/bin/ping", "localhost"]

Gdy polecenie jest wykonywane, następuje odwołanie bezpośrednio do pliku wykonywalnego. W tym przypadku jest to /bin/ping localhost.

Shell

Składnia formy Shell jest następująca:

<instruction> <command>

FROM ubuntu:18.04
CMD ping localhost

Podczas wykonywania instrukcji zbudowanej w formie Shell, zamiast bezpośredniego odwołania do pliku wykonywalnego, wywoływane jest procesowanie przez shell:
/bin/sh -c 'ping localhost'

Abyś mógł lepiej zrozumieć różnicę, uruchomiłem dwa kontenery i nadałem im nazwy odpowiadające formom Shell i Exec. Całość wygląda następująco:

CONTAINER ID        COMMAND                    STATUS              NAMES
dac330dd8ff6        "/bin/sh -c 'ping lo…"     Up 30 seconds       shell-form
b83174ca6fb0        "/bin/ping localhost"      Up 40 seconds       exec-form

Jeżeli chcesz poznać najlepsze praktyki tworzenia Dockerfile, zachęcam do lektury
„10 Najlepszych Praktyk Tworzenia Dockerfile”


CMD

Instrukcja CMD pozwala na określenie domyślnego polecenia, które zostanie wykonane tylko podczas uruchamiania kontenera bez podawania dodatkowych argumentów. Jeżeli uruchomisz kontener z dodatkowym argumentem, domyślne polecenie zostanie zignorowane. Jeżeli Twój Dockerfile posiada więcej niż jedną instrukcję CMD, tylko ostatnia jest brana pod uwagę. Pozostałe są ignorowane.

CMD może występować w trzech formach:

  • CMD ["executable","param1","param2"] (forma Exec, rekomendowana)
  • CMD ["param1","param2"] (pozwala na przekazanie dodatkowych parametrów do instrukcji ENTRYPOINT)
  • CMD command param1 param2 (forma Shell)

Załóżmy następujący Dockerfile:

FROM ubuntu:18.04
CMD echo "Hello world"

Gdy uruchomimy kontener bez przekazywania dodatkowych argumentów, za pomocą polecenia:

docker run -it <image> otrzymamy następujący rezultat:

Hello world

Jeżeli natomiast dodamy dodatkowy argument /bin/bash i uruchomimy kontener za pomocą polecenia docker run -it <image> /bin/bash, polecenie echo „Hello world” zostaje zignorowane i uruchamiany jest terminal.

root@8cb0763a478d:/#


ENTRYPOINT

Instrukcja ENTRYPOINT również służy do określenia głównego procesu kontenera. Wygląda bardzo podobnie do CMD, ponieważ pozwala na określenie polecenia wraz z parametrami.
Istnieje jednak znacząca różnica w działaniu ENTRYPOINT. Mianowicie, po uruchomieniu kontenera z dodatkowymi parametrami, domyślne polecenie NIE jest pomijane, jak w przypadku CMD.

ENTRYPOINT występuje w dwóch formach:

  • ENTRYPOINT ["executable", "param1", "param2"] (forma Exec)
  • ENTRYPOINT command param1 param2 (forma Shell)

Exec

Exec jest rekomendowanym podejściem. Dlaczego tak jest?

Forma exec instrukcji ENTRYPOINT pozwala na zdefiniowanie dodatkowych parametrów polecenia, które w zależności od potrzeb mogą ulec zmianie. Odbywa się to za pomocą komendy CMD.


Jak to wygląda? Argumenty zawarte w instrukcji ENTRYPOINT zawsze się wykonają. Z kolei argumenty instrukcji CMD są opcjonalne i mogą zostać nadpisane podczas uruchamiania kontenera.

ENTRYPOINT ["/bin/echo", "Hej"]
CMD ["Damian"]

Gdy uruchomimy kontener bez przekazywania dodatkowych argumentów, za pomocą polecenia:

docker run -it <image> otrzymamy następujący rezultat:

Hej Damian

Jeżeli natomiast dodamy dodatkowy argument 'Filip’ i uruchomimy kontener za pomocą polecenia docker run -it <image> Filip, otrzymamy:

Hej Filip

Shell

Postać Shell instrukcji ENTRYPOINT nie bierze pod uwagę argumentów przekazanych przez CMD.

Omówmy sobie to na tym samym przykładzie:

ENTRYPOINT echo Hej
CMD Damian

Gdy uruchomimy kontener bez przekazywania dodatkowych argumentów, za pomocą polecenia:

docker run -it <image> otrzymamy następujący rezultat:

Hej

Dodając argument 'Filip’ do polecenia docker run -it <image> Filip, otrzymamy nadal to samo:

Hej

Podsumowanie

Jeżeli masz dylemat czy użyć CMD, czy ENTRYPOINT jako punkt startowy twojego kontenera, odpowiedz sobie na następujące pytanie.

Czy zawsze moje polecenie MUSI się wykonać?

Jeśli odpowiedź brzmi tak, użyj ENTRYPOINT. Co więcej, jeśli potrzebujesz przekazać dodatkowe parametry, które mogą być nadpisane podczas uruchomienia kontenera — użyj również instrukcji CMD.


Artykuł Dockerfile – ENTRYPOINT vs CMD pochodzi z serwisu Szkoła Dockera.

]]>
https://szkoladockera.pl/dockerfile-entrypoint-vs-cmd/feed/ 1
Dockerfile – COPY vs ADD https://szkoladockera.pl/dockerfile-copy-vs-add/ https://szkoladockera.pl/dockerfile-copy-vs-add/#respond Fri, 03 Jan 2020 06:59:08 +0000 https://szkoladockera.pl/?p=769 Tworząc Dockerfile podstawową kwestią jest kopiowania plików, czy to kodu źródłowego aplikacji, czy plików konfiguracyjnych. Zarówno poleceniem ADD jak i COPY można skopiować pliki/katalogi do określonej lokalizacji do Docker Image. COPY Polecenie COPY pozwala TYLKO na kopiowanie plików/katalogów do określonej lokalizacji wewnątrz Docker Image. Składnia COPY jest następująca: Warto tutaj Dowiedz się więcej

Artykuł Dockerfile – COPY vs ADD pochodzi z serwisu Szkoła Dockera.

]]>
Tworząc Dockerfile podstawową kwestią jest kopiowania plików, czy to kodu źródłowego aplikacji, czy plików konfiguracyjnych.

Zarówno poleceniem ADD jak i COPY można skopiować pliki/katalogi do określonej lokalizacji do Docker Image.

COPY

Polecenie COPY pozwala TYLKO na kopiowanie plików/katalogów do określonej lokalizacji wewnątrz Docker Image.

Składnia COPY jest następująca:

COPY [--chown=<user>:<group>] <src>... <dest>

Warto tutaj zwrócić uwagę na opcjonalny parametr --chown, służący do nadawania praw do kopiowanych plików/katalogów.

Domyślnie prawa do kopiowanych plików/katalogów ma użytkownik root.

Gdy użyjemy następującego polecenia:

COPY /source/file/path  /destination/path

Prawa do /destination/path zostaną nadane użytkownikowi root.

Gdy chcemy nadać prawa np. użytkownikowi patrick, polecenie będzie następujące:

COPY --chown=patrick /source/file/path  /destination/path


UWAGA: Funkcja --chown jest obsługiwana tylko do budowania kontenerów Linuxowych i nie działa w Windows Containers.


ADD

Polecenie ADD robi to samo co COPY, ale oprócz tego obsługuję dodatkowe przypadki.

Po pierwsze:

Możesz użyć adresu URL zamiast lokalnego pliku / katalogu.

ADD http://example.com/file.png /home

Po drugie:

Możesz wyodrębnić plik tar ze źródła bezpośrednio do miejsca docelowego.

ADD myfile.tar.gz /home/extracted_myfile 

Pomimo, że kopiowanie bezpośrednio z URL za pomocą ADD jest możliwe, Docker zaleca by tego NIE ROBIĆ.


O ile to możliwe, unikaj korzystania z ADD, by nie być podatnym na ataki za pośrednictwem nieporządanych URL


Jeżeli chcesz poznać najlepsze praktyki tworzenia Dockerfile, zachęcam do lektury
„10 Najlepszych Praktyk Tworzenia Dockerfile”


Rekomendowanym podejściem jest użycie komendy RUN + curl/wget.

Przykładowo, zamiast

ADD http://example.com/big.tar.xz /usr/src/things/<br>
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things<br>
RUN make -C /usr/src/things all

Należy użyć

RUN mkdir -p /usr/src/things \
     && curl -SL  http://example.com/big.tar.xz \
     | tar -xJC /usr/src/things \
     && make -C /usr/src/things all


Podsumowanie

Prawidłowym i rekomendowanym wykorzystaniem ADD jest przypadek, w którym chcemy rozpakować lokalne archiwum tar do określonego katalogu wewnątrz Docker Image. Praktycznie, ma to zastosowanie w oficjalnym obrazie alpineADD rootfs.tar.gz

Dla wszystkich przypadków gdzie nie wymagane jest automatyczne rozpakowanie pliku tar, należy używać polecenia COPY.

Artykuł Dockerfile – COPY vs ADD pochodzi z serwisu Szkoła Dockera.

]]>
https://szkoladockera.pl/dockerfile-copy-vs-add/feed/ 0