Node starter projekat (TypeScript + Docker)

Kostur projekta

Pretpostavićemo da će projekat imati više manjih projekata tj. kontejnera, npr. deo za mikroservise, deo za rad sa bazom… Te je dobro da se u root-u projekta naprave podfolderi za svaki manji projekat npr.:

Kreiranje package.json

Prvo ćemo kreirati package.json fajl u root folderu projekta. Ovaj fajl će sadržati sve potrebne informacije o projektu, kao što su naziv, verzija, zavisnosti… Za kreiranje osnovne verzije package.json koristićemo npm init naredbu sa flagom “YES” “-y” da sve sam popuni bez pitanja:

Nakon toga će package.json fajl izgledati ovako:

Za dalji rad je potrebno da kreiramo i definišemo početni JS fajl. Početni JS fajl ćemo krerati u sklopu “src” foldera kojeg ćemo smestiti u okviru “api” foldera (npr. app.js). Nakon toga je potrebno da prepravimo lokaciju i ime početnog fajla u sklopu package.json-a:

Typscript podrška

  1. TypeScript

    Prvo je potrebno da instaliramo sam TypeScript:

  2. tsconfig.json

    tsconfig.json fajl se koristi za konfiguraciju TypeScript kompajlera (tsc). Služi za definisanje različitih opcija kompajliranja koje upravljaju načinom na koji će TypeScript kod biti preveden u JavaScript. Ovaj fajl možete da inicijalizujete naredbom

    Primer

  3. ts-node

    Da bi pokrenuli TypeScript direktno iz Node okruženja bez potrebe za prethodnim kompajliranjem u JS, potrebno je instalirati ts-node paket:

    Zatim kreiramo jednostavnu “start” skriptu za pokretanje aplikacije u produkciji:

    Pozivanjem naredbe iz terminala se pokreće aplikacija:

  4. @types paketi

    Kada koristiš JavaScript biblioteke u TypeScript projektu, onda je potrebno da instaliraš odgovarajuće tipove kako bi otklonio greške na nivou kompajlacije. JavaScript-u je dinamički tipiziran jezik, što znači da nema ugrađene tipove a da bi TypeScript znao koje bi tipove trebo koristi za Node.js (pisan u JS-u), moramo listu tih definicija a koje možemo naći u paketu @types/node.

    Ako npr. koristimo “fs” modul, bez @types/node paketa, TypeScript kompajler ne bi znao ništa o njemu, što bi dovelo do grešaka tokom kompajlacije. Isto važi i za Express, TypeScript ne zna koje bi tipove trebao da koristi za promenjive pisane u Express-u, te je potrebno da mu pomognemo da ne bi izbacivao greške pri kompajliranju tako što ćemo instalirati paket @types/express

  5. Nodemon

    Nodemon paket nam omogućava automatski restart aplikaciju na serveru kada detektuje promene u datotekama u projektu, a instalira se na sledeći način:

Definisanje skripti

Ako imamo instaliran nodemon paket onda možemo da kreiramo skriptu sa kojom ćemo restartovati server svaki put ukoliko dodje do promene koda. Ta skripta se obično naziva “dev” jer se koristi u lokalu pri developmentu:

Ova skripta se poziva iz terminala na sledeći način:.

Nakon startovanja skripte nodemon prati promene u fajlovima unutar src direktorijuma i ako ih primeti nakong toga automatski ponovo pokreće src/app.ts fajl koristeći ts-node biblioteku, kompajlirajući TypeScript kod “u letu”.

NAPOMENA:
Za bildovanje JS projekta možemo kreirati i skriptu npr. "build": "tsc". Ova komanda će pokrenuti TypeScript kompajler koji će prevesti sve .ts fajlove u .js fajlove prema konfiguraciji definisanoj u tsconfig.json fajlu i smestiti ih u izlazni direktorijum (obično dist ili build). Skripta se poziva na sledeći način:.

ENV promenjive

U node.js okruženju kao i u drugim sistemima se koriste promenjive okruženja okupljene u jedan fajl, sa čijim korišćenjem se izbegavaju hardkodirani podaci razbacani po izvornom kod-u na različitim mestima, taj fajl se obeležava sa .env i kreiramo ga u root folderu projekta. Pa će struktura projekta izgledati ovako:

Za učitavanje (environment) promenljivih iz .env fajla u process.env je potrebno da instaliramo dotenv paket:

Potrebno je na početku izvršavanja koda (npr. u početnom fajlu) da se pozove metoda config() iz DotEnv paketa jer ona učitava sadržaj .env datoteke i dodaje svaku promenljivu u “process.env”objekat. Više o env promenjivama pročitajte u članku Promenjive okruženja.

NAPOMENA:
.env datoteku treba dodati u .gitignore fajl kako bi se izbeglo njeno deljenje sa verzionim kontrolama, čime se štite osetljive informacije. Obratite pažnju da se u produkciji ne zaboravi da se “ručno” napravi novi .env fajl jer se on ne šalje sa git-om !!!

Dockerizacija

Dockerfile

Dockerfile je tekstualna datoteka koja sadrži uputstva za kreiranje Docker slike (eng. image). Dockerfile sadrži niz uputstava (instrukcija) koje određuju kako će Docker slika biti kreirana.

Više o Docker-u i njegovom korišćenju pročitajte u članku “Docker: Pokretanje aplikacija svuda”

docker

Kreiranje slike (“build”)

Za kreiranje Docker image-a iz Dockerfile-a se koristi komanda docker “build”. Ova komanda ima nekoliko opcija koje omogućavaju prilagođavanje procesa build-ovanja. Detaljno objašnjenje opcija koje se često koriste sa komandom docker build je sledeće:

Gde je PATH direktorijum koji sadrži Dockerfile, URL može biti URL ka GIT repozitorijumu, a “-“ omogućava čitanje Dockerfile-a iz standardnog ulaza. Evo liste nekih opcija:

  • –tag skraćeno -t: Oznaka za image. Omogućava imenovanje image-a i dodeljivanje verzije (tag-a).
  • –file skraćeno -f: Specifikuje putanju do Dockerfile-a ako nije u trenutnom direktorijumu.
  • –build-arg: Prolaz promenljive kao build argument. Ove promenljive se mogu koristiti unutar Dockerfile-a.
  • –no-cache: Onemogućava korišćenje keša za build, osiguravajući da se svaki sloj ponovo izgradi.
  • –pull: Uvek povlači najnoviji osnovni image pre build-ovanja.
  • –label: Dodaje metapodatke (label-e) image-u.

NAPOMENA:
Kada Docker gradi image, potrebno mu je da ima pristup svim fajlovima koji su navedeni ili korišćeni unutar Dockerfile-a. Ovi fajlovi se nalaze u direktorijumu koji se naziva “kontekstualni direktorijum”. Tačka (“.”) na kraju komande docker build jednostavno znači “koristi trenutni direktorijum kao kontekstualni direktorijum”. To omogućava Docker-u da pročita Dockerfile i sve potrebne resurse iz tog direktorijuma kako bi kreirao image.
Primer:
Tačka (.) u naredbi docker build -f /path/to/Dockerfile .označava trenutni direktorijum kao kontekstualni direktorijum. To znači da Docker koristi sve fajlove iz tog direktorijuma u procesu build-ovanja, dok Dockerfile može biti specificiran bilo gde u sistemu pomoću -f opcije.

Primer

Ova komanda će:

  • Kreirati image pod nazivom my-image sa tag-om (verzijom slike) 1.0
  • Korisiti Dockerfile iz specificiranog puta
  • Postaviti build argument HTTP_PROXY
  • Izvršiti build bez korišćenja keša
  • Uvek povući najnoviji osnovni image
  • Dodati dve oznake (label-e) image-u
  • Kontekstualni direktorijum je trenutni direktorijum

Pre pokretanja naredbe “build” u Windows-u je potrebno da startujemo Docker desktop aplikaciju i poželjno je da u terminalu budete u root-u projekta u kome se nalazi i Dockerfile.
Nakon izvršene komande možete da proverite u “Docker deskop” aplikaciji image, ali to isto možete uraditi i kroz terminal naredbom:

Docker skripte

Ako želimo da ubrzamo rad sa Dockerom, možemo da pripremimo skripte u package.json kao npr.

Objašnjen

  1. Skripta docker:build kreira Docker sliku iz Dockerfile-a, pos imenonm “starter_node:latest”
  2. Skripta docker:run pokreće novi kontejner, dodeljuje mu ime “node_api” na osnovu slike “starter_node”
  3. Skripta docker:stop zaustavlja kontejner sa imenom “node_api”, zatim uklanja sve zaustavljene kontejnera bez traženja potvrde

Više o docker naredbama pogledajte u članku Docker: Pokretanje aplikacija svuda

REZIME

Ovako izgleda struktura projekta:

A ovako package.json fajl:

Pogledajte ceo kod startnog projekta na GitHub-u https://github.com/choslee/node_typescript_doceker_starter


Docker: Pokretanje aplikacija svuda


Uvod u Docker svet

Šta je Docker?

Docker je platforma koja omogućava programerima da jednostavno kreiraju, implementiraju i pokreću aplikacije unutar kontejnera.

Šta su to kontejneri?
Kontejneri se mogu shvatiti kao lagane virtuelne mašine, ali su dosta brži i manje zahtevni od njih, oni sadrže sve što je potrebno za pokretanje aplikacije: kod, biblioteke i podešavanja operativnog sisteme.

docker image

Zašto koristiti Docker?

  1. Rad aplikacije na različitim sistemima

    Kada aplikacija radi u kontejneru, onda ona može raditi bilo gde, i lokalno ili u nekom udaljenom serveru. Kontejneri omogućavaju aplikacijama da rade konzistentno u različitim okruženjima a to se naručito “oseti” kod timova koji razvijaju aplikaciju na različitim računarima koristeći različite operativne sisteme, kao npr.:

    • Programer A (Windows): Razvija aplikaciju u Docker kontejneru.
    • Programer B (Mac): Testira aplikaciju u Docker kontejneru.
    • Server za produkciju (Linux): Pokreće aplikaciju u istom Docker kontejneru.

    Bez Docker-a, svaki programer bi morao instalirati sve potrebne alate i biblioteke na svom računaru, što može dovesti do problema sa kompatibilnošću i reproduktivnošću. A ako se koristi Docker onda svi programeri koriste jednu istu Docker sliku, što im omogućava da razvijaju i testiraju aplikaciju u identičnom okruženju. Na kraju, aplikacija se prenosi na produkciju bez brige o različitim verzijama biblioteka ili softvera na produkciskom serveru.

  2. Izolacija aplikacije

    Kontejneri omogućavaju svakoj aplikaciji da radi u svom odvojenom okruženju, bez međusobnog ometanja. Ovo može biti veoma bitno kod kompanija koja razvijaju ili hostuje dve različite aplikacije a koje zahtevaju različite verzije MySQL baze podataka. U tom slučaju je idealno da se koristi Docker jer se tada svaka aplikacija može smestiti u svoj Docker kontejner sa odgovarajućom verzijom MySQL-a, kao u sledećem primeru:

    • Aplikacija 1 (MySQL 5.7): Pokreće se u Docker kontejneru sa MySQL 5.7
    • Aplikacija 2 (MySQL 8.0): Pokreće se u Docker kontejneru sa MySQL 8.0

    Ili kada tim radi na aplikaciji koja zavisi od specifičnih verzija Node.js-a, NPM-a ili nekih drugih biblioteka. U tom slučaju kada se koristi Docker onda tim kreira Docker sliku koja sadrži sve potrebne zavisnosti, pa se na taj način pokreće aplikacija u potpuno istom okruženju, bez brige o verzijama biblioteka.

  3. Skalabilnost aplikacije

    Docker omogućava lako skaliranje aplikacija horizontalno (dodavanje novih instanci), pa ako kompanija doživljava nagli porast saobraćaja na svojoj e-commerce platformi onda se sistem lako skalira dodavanjem dodatnih kontejnera za rukovanje većim opterećenjem, kao npr.:

    • Web serveri: 10 Docker kontejnera sa Nginx-om
    • API serveri: 15 Docker kontejnera sa aplikacijom
    • Web serveri: 15 Docker kontejnera sa MySQL-om

Osnovni Docker koncepti:

  • Docker Image (Slika): Template za stvaranje kontejnera. Definiše kako će izgledati kontejner.
  • Docker Container (Kontejner): Pokrenut primerak Docker slike.
  • Dockerfile: Datoteka s uputstvima kako kreirati Docker sliku.
  • Docker Hub: Repozitorijum za deljenje i preuzimanje Docker slika.

Instalacija Docker-a

Evo kratkog uputstva za instalaciju:

  1. Provera sistemskih zahteva:

    • Docker Desktop zahteva 64-bitni Windows 10 ili noviji sa verzijom 21H2 ili novijom, ili Windows 11.
    • Virtualizacija mora biti omogućena u BIOS-u (Hyper-V i Windows Container funkcije).
    • Instalacija zahteva najmanje 4GB RAM-a.
  2. Preuzmi Docker Desktop instalacioni program: na ovom linku ili poseti stranicu za instalaciju i klikni na dugme “Download Docker Desktop for Windows”.
  3. Pokreni instalacioni program (npr. “Docker Desktop Installer.exe”) i prati uputstva na ekranu.
  4. Restartuj računar
  5. Proveri Docker instalaciju:

    • Otvori terminal (PowerShell ili Command Prompt) i pokreni komandu:
    • Ako vidiš verziju Dockera, to znači da je instalacija uspela.

NAPOMENA:
Postupak instalacije se može menjati od verzije do verzije, te je najbolje pogledati aktuelno stanje na oficijalnoj stranici.

Korišćenje Docker-a

Docker može da se koristi preko preuzete aplikacije Doker Desktop ili preko terminala, evo par najčešćih komandi za terminal:

  1. Preuzimanje Docker slike sa Docker Huba

    Kao npr:

    ili

  2. Kreiranje slike prema uputstvu iz Dokerfile-a

    U trenutom direktorijumu:

    Ako se DockerFile nalazi na nekoj drugoj lokaciji:

    Gde je:

    • -t: Označava (tag) sliku imenom.
    • -f: Navodi put do Dockerfilea.
  3. Prikazivanje dostupnih Docker slika

    Naredba docker images se koristi za prikazivanje liste svih Docker slika koje su trenutno sačuvane na vašem lokalnom računaru.

    Kada koristite samo docker images bez dodatnih opcija, Docker prikazuje samo najnovije označene slike za svaki repozitorijum. Ovo znači da, ako imate više verzija slike sa različitim oznakama (tagovima), prikazuje se samo poslednja verzija. Kada dodate opciju “-a” ili “–all”, Docker prikazuje sve slike, uključujući sve verzije iste slike kao i tzv. “dangling images” tj. slike koje nemaju oznake i često su rezultat međukoraka tokom procesa građenja slike.

    Primer

    Ova opcija omogućava da vidimo sve slike koje su sačuvane na vašem sistemu, što vam pomaže da upravljate prostorom na disku i odlučite koje slike možete obrisati.

  4. Pokretanje kontejnera

    Kao npr.:

    ili sa dodatnim opcijama:

    Značenje opcija:

    • -d: Pokreće kontejner u pozadini (detached mode)
    • -p: Mapira portove host:container (npr. 8080:80)
    • –name: Dodjeljuje ime “moj-nginx” kontejneru
  5. Spisak pokrenutih kontejnera

    Npr.

    ili sa dodatnim opcijama:

    Opcija -a prikazuje sve kontejnere, uključujući i zaustavljene.

  6. Zaustavljanje pokrenutog kontejnera

    Zaustavljanje po nazivu:

    ili prema id-u:

  7. Brisanje kontejnera

    Brisanje po nazivu:

  8. Brisanje Docker slike

    Npr:

    NAPOMENA:
    “Dangling” slike su one koje nisu povezane ni sa jednim repozitorijumom ili oznakom (tagom). Obično imaju “none” kao naziv repozitorijuma i oznake. Naredba "docker image prune" se koristi za uklanjanje neupotrebljenih (dangling) Docker slika sa vašeg lokalnog sistema.

  9. Izvršavanje naredbe unutar pokrenutog kontejnera:

    Npr.:

  10. Prikazivanje logova kontejnera:

    Npr. prikazivanje svih logova

    Prikazivanje zadnjih 10 linija loga:

Docker Compose

Docker Compose je alat koji olakšava upravljanje višekontejnerskim Docker aplikacijama. Koristeći jednostavni YAML fajl (docker-compose.yml), možemo da definišemo konfiguraciju svih servisa, mreža i volumena koji čine našu aplikaciju, a zatim ih pokrenete i upravljate njima pomoću jedne komande. Docker Compose je uključen u sklopu Docker Dektop instalacije.

Primer

U ovome primeru je prikazan doker-compose.yml fajl sa uputstvima za aplikaciju sa dva servisa: frontend (Vue.js aplikacija) i backend (NestJS API):

Gde je backend servis (NestJS API) opisan u DockerFile:

I frontend servis (Vue.js aplikacija) opisan u DockerFile:

Objašnjenje koraka
  • FROM node:18:

    Koristi zvaničnu Node.js sliku verzije 18 kao osnovu za kreiranje novog kontejnera.

  • WORKDIR /app:

    Postavlja radni direktorijum na /app. Ako direktorijum postavljen sa WORKDIR ne postoji, Docker ga automatski kreira.

    • Sledeće naredbe u Dockerfile-u će se izvršavati relativno prema ovom direktorijumu.
    • Na primer, ako naredba COPY . . sledi nakon WORKDIR /app, sve datoteke će biti kopirane u /app. COPY, ADD i druge naredbe koje koriste fajl ili direktorijum kao argument oslanjaju se na relativne puteve prema trenutnom radnom direktorijumu.
  • COPY package*.json ./:

    Kopira package.json i package-lock.json iz trenutnog direktorijuma na hostu u trenutni radni direktorijum unutar kontejnera (/app).

    • ./ ukazuje na radni direktorijum (postavljen sa WORKDIR).
  • RUN npm install:

    Izvršava npm install unutar trenutnog radnog direktorijuma (/app).

    • Instalira sve Node.js zavisnosti prema package.json.
  • COPY . .:

    Kopira sav preostali kod iz trenutnog direktorijuma na hostu u trenutni radni direktorijum unutar kontejnera (/app).

  • CMD [“npm”, “start”]:

    Postavlja komandnu liniju za pokretanje aplikacije koristeći npm start.

Struktura projekta:

Za pokretanje apilikacije je potrebno da se u terminalu “prebacimo” do lokacije projekta:

A zatim pokrenemo Docker Compose na sledeći način:

Nakon čega možemo da pogledamo aplikaciju u pretraživaču: