Komponiranje kontejnera alatom Docker Compose
Do sada smo istraživali korištenje pojedinačnih instanci kontejnera koji se izvode na jednom glavnom računalu, slično kao što programer radi na jednoj usluzi neke aplikacije. Međutim, proizvodne aplikacije obično su mnogo složenije i sastoje se od više usluga, a ovaj model s jednim poslužiteljem neće funkcionirati za koordinaciju 10 ili 100 kontejnera i mrežnih veza između njih, a da ne spominjemo potrebu za osiguravanjem dostupnosti i skalabilnosti.
Docker Compose (GitHub) je alat za definiranje i pokretanje višekontejnerskih aplikacija korištenjem Dockera, tzv. kompozicija (engl. composition). Usluge koje će se pokrenuti definiraju se korištenjem konfiguracije u obliku YAML. YAML je donekle srodan JSON-u, ali je dizajniran kako bi bio čitljiv ljudima (slično kao TOML).
Tip
Službeno proširenje za Docker za Visual Studio Code, između ostalog, podržava i korištenje Docker Composea.
Struktura konfiguracije
Warning
Ubuntu 20.04 LTS ima paket docker-compose koji sadrži Docker Compose verziju 1.25.0. Ta verzija podržava sve značajke koje u nastavku koristimo, ali potrebno je pripaziti da polje version
u YAML datoteci u nastavku bude postavljeno na 3.7 (umjesto 3.9) jer je verzija konfiguracije 3.8 postala dostupna tek u Docker Composeu 1.25.5, a 3.9 u 1.27.1.
Korištenje Docker Composea u osnovi je proces od tri koraka:
- Definiranje okruženja aplikacije s
Dockerfile
tako da se može reproducirati bilo gdje. - Definiranje usluge koje čine aplikaciju u
docker-compose.yml
kako bi se mogle zajedno izvoditi u izoliranom okruženju. - Pokretanje naredbe
docker-compose up
koja pokreće cijelu aplikaciju.
Note
Docker Compose krenuo je kao samostalni alat docker-compose
, ali je u posljednjim verzijama postao dio osnovnog Dockera kao podnaredba compose
koja ima svoje podnaredbe. Stoga, naredbu za pokretanje docker-compose up
moguće je pisati i kao docker compose up
, a analogno vrijedi i za ostale naredbe.
Datoteka docker-compose.yml
koju Docker Compose koristi može biti oblika:
version: "3.8"
services:
mojhttpd:
image: httpd
mojredis:
image: redis
Uočimo vrijednost ključa version
. na stranici Compose file versions and upgrading u tablici možete vidjeti koje verzije datoteke docker-compose.yml
podržavaju određena izdanja Dockera. Koristit ćemo posljednju verziju Docker Enginea, a ona podržava datotečni format Composea verzije 3.8
.
Promatrajući vrijednost ključa services,
uočavamo da ćemo u ovom slučaju pokrenut ćemo dva kontejnera, prvi naziva mojhttpd
temeljen na službenoj slici httpd, a drugi mojredis
temeljen na službenoj slici redis.
Pokretanje, nadzor i zaustavljanje kontejnera
Povucimo slike argumentom pull
(dokumentacija):
$ docker-compose pull
[+] Running 12/12
⠿ mojredis Pulled 21.1s
⠿ a2abf6c4d29d Pull complete 17.3s
⠿ c7a4e4382001 Pull complete 17.5s
⠿ 4044b9ba67c9 Pull complete 17.6s
⠿ c8388a79482f Pull complete 17.9s
⠿ 413c8bb60be2 Pull complete 18.1s
⠿ 1abfd3011519 Pull complete 18.1s
⠿ mojhttpd Pulled 21.6s
⠿ dcc4698797c8 Pull complete 17.6s
⠿ 41c22baa66ec Pull complete 17.6s
⠿ 67283bbdd4a0 Pull complete 18.5s
⠿ d982c879c57e Pull complete 18.7s
Stvaranje kontejnera na temelju preuzetih slika i njihovo pokretanje ćemo izvesti argumentom up
(dokumentacija):
$ docker-compose up
[+] Running 2/0
⠿ Container docker-mojhttpd-1 Created 0.0s
⠿ Container docker-mojredis-1 Created 0.0s
Attaching to docker-mojhttpd-1, docker-mojredis-1
docker-mojhttpd-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.2. Set the 'ServerName' directive globally to suppress this message
docker-mojhttpd-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.2. Set the 'ServerName' directive globally to suppress this message
docker-mojredis-1 | 1:C 03 Jan 2022 22:26:37.086 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
docker-mojredis-1 | 1:C 03 Jan 2022 22:26:37.086 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=1, just started
docker-mojredis-1 | 1:C 03 Jan 2022 22:26:37.086 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.087 * monotonic clock: POSIX clock_gettime
docker-mojhttpd-1 | [Mon Jan 03 22:26:37.087474 2022] [mpm_event:notice] [pid 1:tid 140323163139392] AH00489: Apache/2.4.51 (Unix) configured -- resuming normal operations
docker-mojhttpd-1 | [Mon Jan 03 22:26:37.087533 2022] [core:notice] [pid 1:tid 140323163139392] AH00094: Command line: 'httpd -D FOREGROUND'
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.087 * Running mode=standalone, port=6379.
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.087 # Server initialized
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.087 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.088 * Loading RDB produced by version 6.2.6
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.088 * RDB age 55 seconds
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.088 * RDB memory usage when created 0.77 Mb
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.088 # Done loading RDB, keys loaded: 0, keys expired: 0.
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.088 * DB loaded from disk: 0.000 seconds
docker-mojredis-1 | 1:M 03 Jan 2022 22:26:37.088 * Ready to accept connections
Izvođenje, kao i inače, prekidamo kombinacijom tipki Ctrl+C:
^CGracefully stopping... (press Ctrl+C again to force)
[+] Running 2/2
⠿ Container docker-mojhttpd-1 Stopped 1.2s
⠿ Container docker-mojredis-1 Stopped 0.2s
canceled
Želimo li pokrenuti kompoziciju kontejnera u pozadini, dodat ćemo parametar --detach
, odnosno -d
:
$ docker-compose up -d
[+] Running 2/2
⠿ Container docker-mojredis-1 Started 0.3s
⠿ Container docker-mojhttpd-1 Started 0.3s
Argumentom ps
dobit ćemo popis pokrenutih procesa u pojedinim kontejnerima (dokumentacija):
$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
docker-mojhttpd-1 "httpd-foreground" mojhttpd running 80/tcp
docker-mojredis-1 "docker-entrypoint.s…" mojredis running 6379/tcp
Želimo li više informacija, primjerice koliko je točno procesa pokrenuo web poslužitelj, koliko se dugo izvode i koji su im identifikatori, ispisat ćemo ih argumentom top
, ponovno razdvojene po pojedinim kontejnerima (dokumentacija):
$ docker-compose top
docker-mojhttpd-1
UID PID PPID C STIME TTY TIME CMD
root 870026 869946 0 23:34 ? 00:00:00 httpd -DFOREGROUND
bin 870323 870026 0 23:34 ? 00:00:00 httpd -DFOREGROUND
bin 870324 870026 0 23:34 ? 00:00:00 httpd -DFOREGROUND
bin 870325 870026 0 23:34 ? 00:00:00 httpd -DFOREGROUND
docker-mojredis-1
UID PID PPID C STIME TTY TIME CMD
999 870038 869998 0 23:34 ? 00:00:00 redis-server *:6379
Argumentom logs
dobit ćemo uvid u zapisnike pojedinih kontejnera (dokumentacija):
$ docker-compose logs
docker-mojhttpd-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.2. Set the 'ServerName' directive globally to suppress this message
docker-mojhttpd-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.2. Set the 'ServerName' directive globally to suppress this message
docker-mojhttpd-1 | [Sun Oct 16 17:19:59.639291 2022] [mpm_event:notice] [pid 1:tid 140373418044736] AH00489: Apache/2.4.54 (Unix) configured -- resuming normal operations
docker-mojhttpd-1 | [Sun Oct 16 17:19:59.639568 2022] [core:notice] [pid 1:tid 140373418044736] AH00094: Command line: 'httpd -D FOREGROUND'
docker-mojhttpd-1 | [Sun Oct 16 17:20:49.178083 2022] [mpm_event:notice] [pid 1:tid 140373418044736] AH00492: caught SIGWINCH, shutting down gracefully
docker-mojhttpd-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.3. Set the 'ServerName' directive globally to suppress this message
docker-mojhttpd-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.3. Set the 'ServerName' directive globally to suppress this message
docker-mojhttpd-1 | [Sun Oct 16 17:22:00.688161 2022] [mpm_event:notice] [pid 1:tid 140264025701696] AH00489: Apache/2.4.54 (Unix) configured -- resuming normal operations
docker-mojhttpd-1 | [Sun Oct 16 17:22:00.688314 2022] [core:notice] [pid 1:tid 140264025701696] AH00094: Command line: 'httpd -D FOREGROUND'
docker-mojredis-1 | 1:C 16 Oct 2022 17:19:59.669 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
docker-mojredis-1 | 1:C 16 Oct 2022 17:19:59.669 # Redis version=7.0.5, bits=64, commit=00000000, modified=0, pid=1, just started
docker-mojredis-1 | 1:C 16 Oct 2022 17:19:59.669 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
docker-mojredis-1 | 1:M 16 Oct 2022 17:19:59.670 * monotonic clock: POSIX clock_gettime
docker-mojredis-1 | 1:M 16 Oct 2022 17:19:59.670 * Running mode=standalone, port=6379.
docker-mojredis-1 | 1:M 16 Oct 2022 17:19:59.671 # Server initialized
docker-mojredis-1 | 1:M 16 Oct 2022 17:19:59.671 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
docker-mojredis-1 | 1:M 16 Oct 2022 17:19:59.671 * Ready to accept connections
docker-mojredis-1 | 1:signal-handler (1665940849) Received SIGTERM scheduling shutdown...
docker-mojredis-1 | 1:M 16 Oct 2022 17:20:49.207 # User requested shutdown...
docker-mojredis-1 | 1:M 16 Oct 2022 17:20:49.207 * Saving the final RDB snapshot before exiting.
docker-mojredis-1 | 1:M 16 Oct 2022 17:20:49.209 * DB saved on disk
docker-mojredis-1 | 1:M 16 Oct 2022 17:20:49.209 # Redis is now ready to exit, bye bye...
docker-mojredis-1 | 1:C 16 Oct 2022 17:22:00.645 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
docker-mojredis-1 | 1:C 16 Oct 2022 17:22:00.645 # Redis version=7.0.5, bits=64, commit=00000000, modified=0, pid=1, just started
docker-mojredis-1 | 1:C 16 Oct 2022 17:22:00.645 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.646 * monotonic clock: POSIX clock_gettime
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.646 * Running mode=standalone, port=6379.
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.646 # Server initialized
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.646 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.647 * Loading RDB produced by version 7.0.5
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.647 * RDB age 71 seconds
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.647 * RDB memory usage when created 0.82 Mb
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.647 * Done loading RDB, keys loaded: 0, keys expired: 0.
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.647 * DB loaded from disk: 0.000 seconds
docker-mojredis-1 | 1:M 16 Oct 2022 17:22:00.647 * Ready to accept connections
Zustavljanje izvođenja izvodimo argumentom stop
(dokumentacija)
$ docker-compose stop
[+] Running 2/2
⠿ Container docker-mojredis-1 Stopped 0.2s
⠿ Container docker-mojhttpd-1 Stopped 1.2s
Argumentom down
(dokumentacija) zaustavljamo i uklanjamo kontejnere, mreže, volumene i slike koje je stvorio up
.
$ docker-compose down
[+] Running 3/3
⠿ Container docker-mojredis-1 Removed 0.3s
⠿ Container docker-mojhttpd-1 Removed 1.2s
⠿ Network docker_default Removed 0.1s
Naredba podržava još argumenata, o kojima možemo saznati više proučavanjem službene dokumentacije ili korištenjem parametra --help
:
$ docker-compose --help
Usage: docker compose [OPTIONS] COMMAND
Docker Compose
Options:
--ansi string Control when to print ANSI control characters
("never"|"always"|"auto") (default "auto")
--compatibility Run compose in backward compatibility mode
--env-file string Specify an alternate environment file.
-f, --file stringArray Compose configuration files
--profile stringArray Specify a profile to enable
--project-directory string Specify an alternate working directory
(default: the path of the Compose file)
-p, --project-name string Project name
Commands:
build Build or rebuild services
convert Converts the compose file to platform's canonical format
cp Copy files/folders between a service container and the local filesystem
create Creates containers for a service.
down Stop and remove containers, networks
events Receive real time events from containers.
exec Execute a command in a running container.
images List images used by the created containers
kill Force stop service containers.
logs View output from containers
ls List running compose projects
pause Pause services
port Print the public port for a port binding.
ps List containers
pull Pull service images
push Push service images
restart Restart containers
rm Removes stopped service containers
run Run a one-off command on a service.
start Start services
stop Stop services
top Display the running processes
unpause Unpause services
up Create and start containers
version Show the Docker Compose version information
Run 'docker compose COMMAND --help' for more information on a command.
Zadatak
Napravite "kompoziciju" koja se sastoji od jednog kontejnera website
koristeći sliku web poslužitelja nginx i povežite ga mrežom s domaćinom na kojem se Docker izvodi tako da njegova vrata 80 budu dostupna na vratima koristeći se vratima 8080 na domaćinu. U pregledniku otvorite korijensku web stranicu upravo pokrenutog web poslužitelja.
Zadatak
Napravite kompoziciju dva kontejnera imena po želji od kojih su oba temeljena na slici nginx, neka jedan bude dostupan na vratima 8081, a drugi na vratima 8082. Uvjerite se da možete otvoriti oba web sjedišta.
Zadatak
Napravite kompoziciju dva kontejnera temeljena na slici nginx tako da su u zajedničkoj vlastitoj podmreži i postavite im IP adrese po želji. Van kompozicije pokrenite kontejner temeljen na slici curlimages/curl u istoj mreži i njime pristupite jednom, a zatim drugom web sjedištu.
Pokretanje WordPressa korištenjem Docker Composea
Postavit ćemo usluge koje zahtijeva web sjediše u WordPressu: web poslužitelj, interpreter programskog jezika PHP i bazu podataka MariaDB. Na Docker Hubu već postoji službena slika WordPressa koji, uz programski kod WordPressa, uključuje i web poslužitelj i interpreter programskog jezika PHP. Tako da nam treba samo još službena slika MariaDB-a.
Napravimo novi direktorij wordpress
. U njemu kreirajmo novu docker-compose.yml
datoteku koja će sadržavati dva kontejnera potrebna za WordPress:
version: "3.8"
services:
wordpress:
image: wordpress
ports:
- "5050:80"
mariadb:
image: mariadb
Sada je potrebno definirati odjeljak s parametrima okruženja environment:
koje sadrži parametre specifične za WordPress. Specifično, navesti ćemo sve informacije o bazi podataka na koju će se povezati (varijable WORDPRESS_DB_HOST
, WORDPRESS_DB_USER
i dr.). Također ćemo definirati varijable okruženja i za bazu podataka MariaDB na analogni način:
version: "3.8"
services:
wordpress:
image: wordpress
ports:
- "5050:80"
environment:
WORDPRESS_DB_HOST: mariadb
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: "ide-gas!123"
WORDPRESS_DB_NAME: wordpress
mariadb:
image: mariadb
environment:
MARIADB_DATABASE: wordpress
MARIADB_ROOT_PASSWORD: "ide-gas!123"
Sada vidimo da se instanca WordPressa koju pokrećemo povezuje na uslugu MariaDB i koristi je kao svoj sustav za upravljanje bazom podataka. Korisnik je u ovom jednostavnom primjeru root
, zaporka je ide-gas!123
, a baza podataka koja se koristi je wordpress
.
Warning
U nastavku (na složenijim primjerima i u zadacima) ćemo izbjegavati konfigurirati web aplikacije da pristupaju sustavu za upravljanje bazom podataka korištenjem njegovog korijenskog korisnika jer je to loša sigurnosna praksa. Naime, korijenski korisnik ima sve ovlasti.
Dodajmo još zavisnost WordPressu o MariaDB-u u ključu depends_on
. Docker Compose će zbog toga napraviti i pokrenuti kontejner mariadb
prije pokretanja kontejnera wordpress
.
Također ćemo dodati odjeljak volumes
, koji će nam omogućiti da mapiramo Docker direktorij u direktorij u našem sustavu. Na taj način ako se Docker spremink sruši ili obriše, bez obzira na direktorij, podaci će i dalje biti na domaćinu. Stvorit ćemo volumen imena wordpress_db
i preslikati to unutar kontejnera.
Dodatno možemo eksplicitno navesti mrežu wpnet
koja koristi skup adresa 172.19.0.0/16, a u njoj MariaDB ima adresu 172.19.0.3, dok WordPressov poslužitelj ima 172.19.0.4.
version: "3.8"
services:
wordpress:
image: wordpress
ports:
- "5050:80"
depends_on:
- mariadb
environment:
WORDPRESS_DB_HOST: mariadb
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: "ide-gas!123"
WORDPRESS_DB_NAME: wordpress
networks:
mreza:
ipv4_address: 172.19.0.3
mariadb:
image: mariadb
environment:
MARIADB_DATABASE: wordpress
MARIADB_ROOT_PASSWORD: "ide-gas!123"
volumes:
- ./wordpress_db:/var/lib/mariadb
networks:
mreza:
ipv4_address: 172.19.0.4
networks:
mreza:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.19.0.0/16
Korištenje izgradnje kontejnera u kompoziciji
U kompoziciji je moguće izgraditi neke od kontejnera na temelju datoteka Dockerfile
. Za ilustraciju možemo iskoristiti primjer dan u Get started with Docker Compose, web aplikaciju napisanu u Pythonu korištenjem Flaska koja dohvaća podatke iz Redisa.
Prvo napravite direktorij projekt
i u njemu kreirajte datoteku app.py
sadržaja:
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='mojredis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
Uočimo kako se povezivanje na Redis događa korištenjem imena domaćina mojredis
. To ćemo ime iskoristiti niže kao ime kontejnera.
Jasno je vidljivo iz naredbi import
da ova aplikacija za svoj rad zahtijeva Python module flask
i redis
(podsjetimo se da je modul time
dio standardne biblioteke). Stvorimo datoteku requirements.txt
koju će pip koristiti za preuzimanje potrebnih modula:
flask
redis
Stvorimo Dockerfile
koji će povući sliku kontejnera za Python, dodati u nju datoteku requirements.txt
, instalirati potrebne module (pip install -r requirements.txt
), pokrenuti aplikaciju (flask run
) i otvoriti potrebna vrata (u zadanim postavkama Flask koristi vrata 5000):
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
Ovako definiran Dockerfile
govori Dockeru da redom:
- izgradi sliku počevši od slike Python, verzije 3.7-alpine,
- postavi radni direktorij na
/code
, - postavi varijable okruženja koje koristi naredba
flask
, - instalira potrebne zavisnosti
gcc
,musl-dev
ilinux-headers
naredbomapk
koju Alpine Linux koristi za instalaciju paketa, slično kao što Debian GNU/LInux koristiapt
, a Arch Linuxpacman
, - kopira
requirements.txt
i instalira Pythonove zavisnosti, - doda metapodatke na sliku kako bi opisao da kontejner sluša na portu 5000,
- kopira trenutni direktorij
.
u projektu u radni direktorij.
na slici te - postavi zadanu naredbu za kontejner na
flask run
.
Ovaj kontejner bismo mogli izgraditi naredbom docker build
(dokumentacija), ali tada bismo se morali pobrinuti za Redis. Stoga ćemo napraviti docker-compose.yml
koji će izgraditi kontejner na temelju Dockerfile
-a i pokrenuti ga, izraditi i pokrenuti kontejner s Redisom pod nazivom mojredis
te povezati ta dva kontejnera mrežom:
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
mojredis:
image: "redis:alpine"
Zadatak
Promijenite kontejner tako da koristi Python 3.10 kao osnovnu sliku.
Zadatak
Promijenite korištena vrata na 8080. (Uputa: proučite dokumentaciju sučelja naredbenog retka Flaska.)
Zadatak
Po uzoru na ovu kompoziciju, složite kompoziciju koja koristi službenu sliku za php i povezuje se na Redis.
Author: Vedran Miletić, Matea Turalija