MODUL 1 - LEKCIJA 7

Uvod u Docker i Kontejnerizaciju

Naučite kako pakovati i prenositi aplikacije bez greške "kod mene radi!". Vodič od koncepta i arhitekture do pokretanja povezanog SQL servera i API-ja.

⏱️ Trajanje: ~90 min 📚 Nivo: Srednji / Napredni 🎯 Koncepti: VM vs Docker, Images, Containers, Volumes, Dockerfile, Compose

🐳 Šta je Docker?

Softverski inženjer često napravi aplikaciju i kada je želi pustiti u produkciju ili ustupiti kolegi, javi se klasični problem: "Kod mene na laptopu aplikacija radi savršeno!" Razlog padova na drugim sistemima su različiti operativni sistemi, pogrešne verzije instaliranog softvera (.NET 6 vs .NET 8), nedostatak sistemskih varijabli i slično.

Docker je platforma koja rješava ovaj problem koristeći kontejnerizaciju. Slično kao što teretni brodovi koriste univerzalne brodske kontejnere bez obzira šta je u njima (televizori, igračke ili auti), Docker vam omogućava da "upakujete" vašu aplikaciju i apsolutno sve njene zavisnosti u jedan standardizirani Docker Kontejner koji će raditi identično na svakom kompjuteru na planeti.

⚖️ Virtualne Mašine (VM) protiv Dokera

U prošlosti smo ovaj problem rješavali tako što bismo stvorili cijeli lažni kompjuter ("Virtualnu Mašinu") unutar našeg pravog kompjutera. To je bilo preskupo i presporo.

Tradicionalne Virtualne Mašine

App 1 (1GB)
Guest OS (30GB)
+ RAM (4GB)
App 2 (1GB)
Guest OS (30GB)
+ RAM (4GB)
Hypervisor (VMWare)
Host Operativni Sistem (Windows Server)
Fizički Server (Hardware)

Svaka aplikacija nosi ogroman teret (i licencu) cijelog lažnog operativnog sistema (Guest OS). Pokretanje traje minutama i proždire RAM memoriju.

Docker Kontejneri

Container 1 (50MB)
Biniaries/Libs
Container 2 (50MB)
Biniaries/Libs
Container 3 (10MB)
Biniaries/Libs
Docker Engine
Host Operativni Sistem (Podijeljeni Kernel)
Fizički Server (Hardware)

Kontejneri nemaju Guest OS! Oni "posuđuju" jezgro matičnog OS-a putem Docker Engine-a. Pokreću se za samo nekoliko milisekundi i zauzimaju jedva par megabajta.


💻 Rječnik i Osnovne Komande

Preživljavanje u PowerShell-u
# 1. Prikazuje sve preuzete "Instalacione CDove" na kompjuteru
docker images

# 2. Skini službeni Nginx server Image s interneta
docker pull nginx

# 3. Upali ga u pozadini! (Nginx sada vrti kao Kontejner)
docker run --name moj_web -d -p 8080:80 nginx

# 4. Prikazuje LISTU SVIH AKTIVNIH kontejnera (Traži njegov ID ovdje!)
docker ps

# 5. Zaustavi i Trajno obriši taj kontejner iz memorije i života
docker stop moj_web
docker rm moj_web

Sjeciranje docker run komande

Svaki put kada palimo server, koristimo magičnu "run" komandu sa dva ključna parametra bez kojih bi sve bilo zaključano u crnoj kutiji:

  • -d (Detached mode): Pokreće kontejner "u pozadini" (u mraku). Da nismo stavili -d, PowerShell terminal bi se zaključao ispisujući Nginx logove zauvijek, i ne bi mogli kucati druge komande dok ga ne ugasimo sa u Ctrl+C. Kucanjem -d, Docker nam samo vrati ID kontejnera i terminal je opet naš.
  • -p 8080:80 (Port Mapping - Skretnica): Kontejner je kao sef na dnu okeana. Čak i ako web server unutra sluša na portu 80, on je zaključan i ljevak i desni od zida Windowsa. Komanda -p MOJ_VANJSKI_PORT : NJEGOV_UNUTRASNJI_PORT doslovno probuši rupu kroz Docker zid i kaže: "Kada moj lokalni Chrome posjeti localhost:8080, proslijedi taj upit direktno u kontejner na njegov port 80". Dva Nginxa mogu raditi unutra na portu 80, ali ih vani mapiramo na 8081:80 i 8082:80.

🌐 Praktični primjer: Nginx Web Server

Nginx je jedan od najpopularnijih web servera na svijetu. Umjesto da instalirate IIS ili Apache na vaš Windows, možete pokrenuti kompletni Nginx web server jednom komandom i poslužiti vaše HTML/CSS/JS fajlove direktno iz lokalnog foldera.

Pokretanje Nginx-a sa lokalnim fajlovima
docker run --name moj-nginx-test -p 8085:80 -v C:\projekti\web:/usr/share/nginx/html:ro -d nginx

Objašnjenje svakog dijela komande

  • --name moj-nginx-test – Daje kontejneru čitljivo ime umjesto nasumičnog Docker ID-a. Koristi se za docker stop, docker rm itd.
  • -p 8085:80 – Mapira port 8085 na vašem PC-ju na port 80 unutar kontejnera. Otvorite localhost:8085 u browseru i vidjet ćete vaš sajt.
  • -v C:\projekti\web:/usr/share/nginx/html:roBind Mount: Vaš lokalni folder C:\projekti\web se mapira u Nginx-ov interni folder za HTML fajlove. Sufiks :ro (read-only) znači da kontejner može samo čitati fajlove, ali ih ne može mijenjati – dodatna sigurnost!
  • -d – Detached mode, pokreće kontejner u pozadini.
  • nginx – Ime Image-a sa Docker Hub-a. Docker automatski skida najnoviju verziju ako već nije preuzeta.

Rezultat: Svaka promjena u C:\projekti\web\index.html se odmah vidi u browseru na localhost:8085 bez restartanja kontejnera!

📄 Nginx kroz Docker Compose

Umjesto da pamtite dugih komandi, isto možete definisati kroz docker-compose.yml fajl u root folderu vašeg web projekta:

docker-compose.yml (Nginx za lokalni razvoj)
version: '3.8'

services:
  moj-web-server:                      # Proizvoljno ime servisa
    image: nginx:latest                 # Koristi najnoviji Nginx Image
    container_name: nginx-lokalni-test  # Fiksno ime kontejnera
    ports:
      - "8080:80"                       # Mapiranje porta (localhost:8080 → kontejner:80)
    volumes:
      - ./:/usr/share/nginx/html:ro     # Trenutni folder (./) se mapira u Nginx HTML root
    restart: always                     # Automatski restartuj ako padne

Šta znači ./ u volumes?

Oznaka ./ znači "trenutni direktorij" – dakle folder u kojem se nalazi vaš docker-compose.yml fajl. Ako je vaš docker-compose.yml u folderu C:\projekti\web\, onda Docker mapira C:\projekti\web\ u Nginx-ov HTML folder. Sve vaše HTML, CSS i JS datoteke automatski postaju dostupne na web serveru!

⌨️ Docker Compose: Pregled ključnih komandi

Kada imate spreman docker-compose.yml, otvorite terminal u istom folderu i koristite sljedeće komande:

Komanda Šta zapravo radi?
docker compose up Čita .yml fajl, kreira mrežu, volumene i pokreće kontejnere. Ako slika (image) nije na računaru, prvo će uraditi pull.
docker compose up -d Isto kao gore, ali u pozadini (detached). Terminal vam ostaje slobodan za druge komande.
docker compose up -d --build Ponovo izgradi Image-e iz Dockerfile-a prije pokretanja. Koristite nakon promjene koda.
docker compose stop Samo zaustavlja rad kontejnera. Kontejner i dalje postoji u memoriji, portovi su i dalje rezervisani, ali procesi ne rade.
docker compose start Ponovo pokreće prethodno zaustavljene kontejnere (one koji su stopani kroz stop).
docker compose restart Restartuje sve servise bez brisanja. Ekvivalent stop + start.
docker compose down Uništava sve: zaustavlja kontejnere, briše ih i briše internu mrežu. Ne briše vaše fajlove na disku (HTML, kod), ali briše privremene podatke unutar kontejnera. Volume diskovi ostaju netaknuti.
docker compose down -v ⚠️ OPREZ: Isto kao down, ali dodatno briše i Volume diskove. Svi podaci u bazi se nepovratno gube! Koristite samo kada svjesno želite resetovati sve od nule.
docker compose ps Lista sve servise definirane u vašem .yml fajlu i pokazuje da li su Up (rade) ili Exit (ugašeni).
docker compose logs Prikazuje logove svih pokrenutih servisa. Korisno za debugging kada nešto ne radi.

🏗️ Dockerfile: Kako "ispeći" vlastiti kod

U profesionalnom svijetu mi nećemo samo vući gotove Nginx servere. Naš posao je da Microsoftov ili Node.js kod pretvorimo u novi Custom Image! To radimo kroz tekstualni fajl bez ekstenzije zvani Dockerfile. U njemu dajemo Dockeru upute (recepte) korak-po-korak.

Dockerfile (Staviti u root foldera vašeg C# API-ja)
# FAZA 1: Kompajliranje koda ("Build" platforma - ogromna)
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /App

# Kopiraj samo cs-proj (da Docker kešira ovisnosti i ubrza stvar) i instaliraj pakete (NuGet)
COPY *.csproj ./
RUN dotnet restore

# Sada iskopiraj ostatak svih C# kodova i KOMPAJLIRAJ API (publish)
COPY . ./
RUN dotnet publish -c Release -o out

# FAZA 2: Produkcijsko Pokretanje (Ekstremno sitna i laka platforma)
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App

# Preuzmi samo pre-kompajlirane .dll fajlove iz ogromne prve faze
COPY --from=build-env /App/out .

# Kada se kontejner upali, komanduj Windows/Linux serveru da pokrene taj API
ENTRYPOINT ["dotnet", "MojaAplikacija.dll"]

Sada kucamo build (ispeci Image) i run (upali ga!).

Izrada i Pokretanje
docker build -t moja_aplikacija .
docker run -d -p 5000:8080 moja_aplikacija

🗄️ Trajnost Podataka: Volumes

U Dockeru postoji jedno neumoljivo pravilo: Sve što se napravi ili promijeni unutar kontejnera se trajno briše onog trenutka kada se kontejner ugasi (Stateless arhitektura).
Ovo je katastrofalno ako vrtimo SQL Server kontejner. Ako se takav kontejner resetuje – svi podaci korisnika nestaju zauvijek!

Ovo se sprječava koristeći Volumene (Volumes) – trajne dijelove diska koji su "povezani" sa kontejnerom. Kontejner misli da zapisuje po svojim internim folderima, ali Docker te foldere transparentno mapira na vaš lokalni hard-disk!

📦 Dva tipa Volume mapiranja

SQL Server Primjer u Dockeru

Nema instalacije i 40 minuta registry muke! Jedna komanda diže potpuno funkcionalan MS SQL Server uz mapiranje porta (1433) lokalno, postavljanje lozinke za SuperAdmina (-e), i trajno čuvanje podataka kroz volume (-v)!

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=NasaLozinka!2026" --name moj_sql_server -p 1433:1433 -v sql_server_podaci:/var/opt/mssql -d mcr.microsoft.com/mssql/server:2022-latest

Gdje su moji .mdf i .ldf fajlovi?

Kada se koristi Named Volume (kao gore: sql_server_podaci), SQL Server interno čuva .mdf i .ldf fajlove na Linux putanji /var/opt/mssql/data/ unutar kontejnera. Docker te fajlove automatski sprema na vaš disk, ali na lokaciji koja nije jednostavno dostupna kroz Windows Explorer.

Ako želite direktan pristup tim fajlovima sa svog PC-ja, koristite Bind Mount umjesto Named Volume-a:

docker run ... -v C:/MojiSQLPodaci:/var/opt/mssql -d mcr.microsoft.com/mssql/server:2022-latest

Sada otvarate C:\MojiSQLPodaci\data\ u Windows Exploreru i tamo vidite .mdf i .ldf fajlove kao da je SQL Server instaliran lokalno!

💾 Backup i Restore baze kroz SSMS

Jedan od najčešćih scenarija: imate SQL Server u kontejneru, koristite SQL Server Management Studio (SSMS) sa svog Windows PC-ja, i želite napraviti backup baze ili restore postojećeg .bak fajla.

Problem je što SSMS komunicira sa SQL Serverom unutar kontejnera, pa sve putanje za backup/restore moraju biti Linux putanje unutar kontejnera, a ne Windows putanje vašeg PC-ja!

Rješenje: Bind Mount za Backup folder

Prilikom pokretanja SQL kontejnera, dodajte još jedan -v parametar koji mapira lokalni Windows folder na backup folder unutar kontejnera:

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=NasaLozinka!2026" --name moj_sql_server -p 1433:1433 -v sql_server_podaci:/var/opt/mssql -v X:/db_backup:/var/opt/mssql/backup -d mcr.microsoft.com/mssql/server:2022-latest

Backup baze iz SSMS-a: Otvorite SSMS, spojite se na localhost,1433, desni klik na bazu → Tasks → Back Up... i za Destination upišite: /var/opt/mssql/backup/MojaBaza.bak. Fajl će se pojaviti u X:\db_backup\MojaBaza.bak na vašem PC-ju!

Restore baze iz SSMS-a: Kopirajte vaš .bak fajl u folder X:\db_backup\ na PC-ju. U SSMS-u kliknite Restore Database, i za Source → From device upišite: /var/opt/mssql/backup/MojaBaza.bak. SQL Server unutar kontejnera automatski vidi taj fajl jer je folder mapiran!

🔄 Životni ciklus volumena

Volumeni žive nezavisno od kontejnera. To znači:

Kako vidjeti sve volumene na računaru?

Koristite komandu docker volume ls da vidite listu svih volumena. Za brisanje jednog volumena: docker volume rm ime_volumena. Za brisanje svih nekorištenih volumena: docker volume prune.

📋 Definisanje volumena u Docker Compose

U docker-compose.yml fajlu volumeni se definišu na dva mjesta:

Struktura volumena u Compose fajlu
services:
  moj-servis:
    image: mcr.microsoft.com/mssql/server:2022-latest
    volumes:
      # 1. Named Volume (Docker upravlja lokacijom)
      - baza_podaci:/var/opt/mssql

      # 2. Bind Mount (vi birate Windows folder)
      - C:/backup:/var/opt/mssql/backup

# Svi Named Volumeni MORAJU biti registrovani ovdje na dnu:
volumes:
  baza_podaci:        # Docker kreira i čuva ovaj volumen automatski

🔗 Dijeljenje volumena između dva kontejnera

Jedan od najmoćnijih koncepata: isti volumen može biti priključen na više kontejnera istovremeno. Ovo omogućava scenarije poput: ASP.NET API piše logove u folder, a drugi kontejner (npr. servis za monitoring) čita te iste logove u realnom vremenu.

docker-compose.yml – Dijeljeni volumen za logove
version: '3.8'

services:
  # Kontejner 1: .NET API koji PIŠE logove
  api-aplikacija:
    build: ./MojaAplikacija
    container_name: dotnet_api
    ports:
      - "5000:8080"
    volumes:
      - app_logovi:/app/logs    # Piše log fajlove u /app/logs

  # Kontejner 2: Servis koji ČITA logove (npr. monitoring, analitika)
  log-reader:
    image: alpine:latest
    container_name: citac_logova
    volumes:
      - app_logovi:/data/logs:ro   # Čita ISTE logove, :ro = samo čitanje!
    command: tail -f /data/logs/*.log

volumes:
  app_logovi:   # Oba kontejnera dijele ovaj volumen!

Kako ovo funkcioniše?

Docker kreira jedan jedini Named Volume app_logovi na disku. Kada api-aplikacija upiše fajl u /app/logs/app-2026-03-15.log, taj fajl je istog trenutka vidljiv u kontejneru log-reader na putanji /data/logs/app-2026-03-15.log. Oba kontejnera gledaju u isti fizički folder na disku!

📝 Pristup logovima .NET aplikacije sa PC-ja

Ako želite vidjeti logove vaše ASP.NET aplikacije direktno u Windows Exploreru (bez ulaska u kontejner), koristite Bind Mount:

Pokretanje .NET API-ja sa logovima vidljivim na PC-ju
# Logovi se pišu u C:\projekti\logovi na vašem Windowsu
docker run -d -p 5000:8080 -v C:\projekti\logovi:/app/logs moja_aplikacija

Sada otvorite C:\projekti\logovi\ u Windows Exploreru i vidjet ćete sve .log fajlove koje vaša .NET aplikacija generira unutar kontejnera. Promjene se vide u realnom vremenu!

Konfiguracija logiranja u .NET-u

Da bi .NET aplikacija pisala logove u folder /app/logs unutar kontejnera, potrebno je konfigurirati Serilog ili NLog u vašem appsettings.json da koristi putanju /app/logs/ kao destinaciju. Docker će automatski proslijediti sve upisane fajlove na vaš lokalni Windows folder kroz Bind Mount!


🌐 Umrežavanje Kontejnera (Docker Networks)

Imamo upaljen SQL Server u jednom kontejneru (nazvanom moj_sql_server). Sada želimo podići i naš C# API kontejner koji će čitati podatke iz njega. Ovdje dolazimo do najveće zamke za početnike!

⚠️ OPREZ: localhost ne znači isto unutar kontejnera!

Kontejneri su potpuno izolirani procesi. Vaš ASP.NET API kontejner nema pristup localhostu vašeg Windows PC-ja. Ako u appsettings.json stavite Server=localhost;Database=..., API će tražiti bazu na svom vlastitom internom localhostu unutar svog kontejnera. Tamo baze naravno nema i aplikacija će pasti!

Rješenje je stvoriti Docker Mrežu (Network) – zamišljeni LAN kabal (switch) u koji ćemo uključiti oba kontejnera kako bi se međusobno vidjela po dodijeljenom imenu kontejnera.

Povezivanje 2 kontejnera ručno
# 1. Kreirajmo virtuelni switch (mrežu)
docker network create backend_mreza

# 2. Ugasimo prethodni SQL Server i pokrenemo ga ponovo, ali sada priključen na našu mrežu
docker rm -f moj_sql_server
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=NasaLozinka!2026" --name moj_sql_server --network backend_mreza -d mcr.microsoft.com/mssql/server:2022-latest

# 3. Sada palimo i naš API, i on se također priključuje na 'backend_mreza'
# Umjesto localhost u Connection Stringu koristi se "moj_sql_server" (ime prvog kontejnera)!
docker run -d -p 5000:8080 --name moja_aplikacija --network backend_mreza -e "ConnectionStrings__DefaultConnection=Server=moj_sql_server;Database=Porezi;User Id=sa;Password=NasaLozinka!2026;TrustServerCertificate=True" moja_aplikacija

Sada Docker igra ulogu DNS servera – kada C# kod zovne Server=moj_sql_server, Docker prevodi to u pravu internu IP adresu baze u mreži backend_mreza i naš API uspješno čita podatke!

🎼 Orkestracija: Docker Compose

Kucanje PowerShell komandi sličnih onoj iznad svaki put je put u ludilo, naročito ako vaša aplikacija zahtijeva 1 SQL bazu, 1 Redis Keš server, i 3 Mikroservisa (API-ja) da rade istovremeno! Docker Compose vadi stvar tako što vam dopušta da cijelu infrastrukturu opišete u jednoj yaml datoteci, te upalite cijeli ekosistem odjednom!

docker-compose.yml (SQL Server sa detaljnim objašnjenjem)
version: '3.8' # Verzija standarda compose dokumenta

services: # Ovdje definišemo šta sve 'podižemo'
  
  baza-poreske-uprave: # Naš proizvoljan naziv servisa (kontejnera)
    image: mcr.microsoft.com/mssql/server:2022-latest # Odakle skidamo sistem
    container_name: sql_server_poreska # Fiksno ime kontejnera kada se upali
    environment:
      - ACCEPT_EULA=Y # Microsoftov EULA pristanak
      - MSSQL_SA_PASSWORD=NasaLozinka!2026 # Šifra za 'sa' korisnika
    ports:
      - "2433:1433" # Mapiranje porta (Host:Kontejner) 
    volumes:
      - poreska_baza_podaci:/var/opt/mssql # Trajno čuvanje same baze (VOLUME)
      - X:/db_backup:/var/opt/mssql/backup # Fizički folder za bekap
    restart: always # Pravilo: Ako padne kontejner ili se PC restarta, upali ga sam!

volumes: # Definisani volumeni ('Virtualni tvrdi diskovi') moraju biti ovdje pobrojani
  poreska_baza_podaci:

Objašnjenje svake linije

  • version: '3.8' – Verzija Docker Compose specifikacije. Novije verzije podržavaju više funkcionalnosti.
  • services: – Sekcija u kojoj nabrajamo svaki kontejner (servis) koji želimo pokrenuti.
  • baza-poreske-uprave: – Proizvoljno ime servisa. Docker Compose koristi ovo ime kao interni DNS naziv za mrežnu komunikaciju sa drugim kontejnerima.
  • image: – Određuje koji Image se skida sa Docker Hub-a. Ovo je oficial Microsoft SQL Server 2022.
  • container_name: – Fiksno ime kontejnera vidljivo u docker ps. Bez ovoga Docker generira nasumično ime.
  • environment: – Varijable okoline koje se proslijeđuju u kontejner pri pokretanju (EULA pristanak i lozinka).
  • ports: "2433:1433" – Mapiranje porta. Vaš SSMS se spaja na localhost,2433, a Docker prosljeđuje na interni port 1433 unutar kontejnera.
  • volumes: poreska_baza_podaci:/var/opt/mssql – Named Volume koji trajno čuva .mdf/.ldf fajlove baze čak i nakon brisanja kontejnera.
  • volumes: X:/db_backup:/var/opt/mssql/backup – Bind Mount koji mapira fizički Windows folder X:\db_backup u kontejnerov /var/opt/mssql/backup. SSMS backup sa putanjom /var/opt/mssql/backup/MojaBaza.bak automatski sprema fajl na vaš PC!
  • restart: always – Ako kontejner padne ili se računar restartuje, Docker ga automatski ponovo pokreće.
  • volumes: (na dnu) – Volumeni navedeni unutar servisa moraju biti registrovani na dnu fajla.

Da biste upalili cijeli sistem iz docker-compose.yml fajla, u folderu kucate:

Jedna linija za cijelu IT infrastrukturu
docker compose up -d

Za gašenje i brisanje kontejnera: docker compose down (Volume diskovi ostaju netaknuti!). Za potpunu listu svih Compose komandi pogledajte sekciju ⌨️ Docker Compose: Pregled ključnih komandi iznad.

🏁 Zaključak kursa

Ovladavanje Docker konceptom je bitno. Ako vaša arhitektura radi preko Compose fajla kod vas na laptopu – ona mora raditi identično i na produkcionom serveru bilo kojeg klijenta. To se zove Uvezana isporuka (CI/CD) i sa tim prestaje mit o "Radi kod mene!".