Această pagină poate fi doar citită. Poți vedea sursa, dar nu poți modifica pagina. Consultă administratorul dacă ești de părere că ceva este în neregulă. ====== Dockerfile - Construirea unei imagini proprii de la zero ====== Acest articol explică ce este un **Dockerfile**, cum funcționează sistemul de straturi (layers) al Docker și cum poți construi o imagine personalizată pas cu pas. ---- ===== Ce este un Dockerfile? ===== Un **Dockerfile** este un fișier text simplu, fără extensie, care conține o succesiune de instrucțiuni ce descriu cum se construiește o **imagine Docker**. Procesul de build urmează pașii: - Scrii un ''Dockerfile'' - Rulezi ''docker build'' - Docker produce o **imagine** (read-only, formată din straturi) - Rulezi imaginea ca **container** (adaugă un strat read-write deasupra) - Opțional, distribui imaginea printr-un **Registry** (Docker Hub, ECR etc.) ---- ===== Structura de bază ===== Fiecare linie dintr-un Dockerfile este o **instrucțiune**. Ordinea contează - Docker execută instrucțiunile de sus în jos și creează câte un **strat (layer)** pentru fiecare. <code dockerfile> # Comentariile încep cu # INSTRUCȚIUNE argumente </code> ---- ===== Instrucțiunile esențiale ===== ==== FROM ==== **Obligatorie. Prima instrucțiune.** Specifică imaginea de bază (//base image//). <code dockerfile> FROM ubuntu:22.04 FROM node:20-alpine FROM python:3.12-slim FROM scratch # imagine goală, pentru binare statice </code> > **Recomandare:** Folosește variante ''slim'' sau ''alpine'' pentru imagini mai mici. Evită ''latest'' - fixează versiunea explicit. ==== WORKDIR ==== Setează directorul de lucru pentru toate instrucțiunile următoare. Dacă nu există, îl creează. <code dockerfile> WORKDIR /app </code> Echivalentul unui ''cd /app'' persistent în imagine. ==== COPY și ADD ==== Copiază fișiere din contextul de build în imagine. <code dockerfile> COPY src/ /app/src/ # copiază directorul src/ COPY package.json . # copiază în WORKDIR curent ADD archive.tar.gz /data/ # ADD dezarhivează automat </code> ^ Instrucțiune ^ Dezarhivare automată ^ URL remote ^ | ''COPY'' | Nu | Nu | | ''ADD'' | Da (.tar, .gz) | Da | > **Recomandare:** Preferă ''COPY'' în locul ''ADD'' dacă nu ai nevoie de dezarhivare - comportamentul este mai previzibil. ==== RUN ==== Execută comenzi în shell **la momentul build-ului**. Fiecare ''RUN'' creează un strat nou. <code dockerfile> RUN apt-get update && apt-get install -y curl git RUN npm install RUN pip install -r requirements.txt </code> > **Recomandare:** Combină comenzile cu ''&&'' pe o singură linie ''RUN'' pentru a reduce numărul de straturi și dimensiunea imaginii. <code dockerfile> # Bine RUN apt-get update \ && apt-get install -y curl git \ && rm -rf /var/lib/apt/lists/* # Evită (3 straturi separate) RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y git </code> ==== ENV ==== Setează variabile de mediu disponibile atât la build, cât și la runtime. <code dockerfile> ENV NODE_ENV=production ENV PORT=3000 HOST=0.0.0.0 </code> ==== ARG ==== Variabile disponibile **doar la build-time**, nu în containerul final. <code dockerfile> ARG VERSION=1.0 ARG SECRET_KEY # transmis cu --build-arg SECRET_KEY=xyz </code> > **Atenție:** Nu folosi ''ARG'' pentru secrete - valorile sunt vizibile în istoricul imaginii (''docker history''). ==== EXPOSE ==== Documentează portul pe care aplicația ascultă. Nu publică portul - doar înregistrează intenția. <code dockerfile> EXPOSE 3000 EXPOSE 8080/tcp EXPOSE 5353/udp </code> Publicarea efectivă se face la rulare cu ''-p 3000:3000''. ==== CMD și ENTRYPOINT ==== Definesc comanda implicită la pornirea containerului. ^ Instrucțiune ^ Scop ^ Poate fi suprascris ^ | ''CMD'' | Comandă implicită | Da (''docker run ... mycommand'') | | ''ENTRYPOINT''| Punctul de intrare fix | Greu (''--entrypoint'') | <code dockerfile> # Forma exec (preferată - fără shell intermediar) CMD ["node", "server.js"] ENTRYPOINT ["python", "-m", "gunicorn"] # Forma shell (evită) CMD node server.js </code> **Combinarea lor:** <code dockerfile> ENTRYPOINT ["python", "app.py"] CMD ["--port", "8080"] # argumente implicite, suprascribile </code> ==== VOLUME ==== Declară un punct de montare pentru date persistente. <code dockerfile> VOLUME ["/data", "/logs"] </code> ==== USER ==== Schimbă utilizatorul pentru instrucțiunile următoare și pentru procesul final. <code dockerfile> RUN useradd -m appuser USER appuser </code> > **Recomandare de securitate:** Rulează întotdeauna aplicațiile ca utilizator non-root. ==== LABEL ==== Adaugă metadate la imagine (autor, versiune, descriere). <code dockerfile> LABEL maintainer="echipa@exemplu.ro" LABEL version="2.1.0" LABEL description="API REST pentru serviciul X" </code> ==== HEALTHCHECK ==== Definește o comandă pentru verificarea stării containerului. <code dockerfile> HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1 </code> ---- ===== Exemplu complet: aplicație Node.js ===== <code dockerfile> # ── Etapa 1: dependențe ────────────────────────────────────── FROM node:20-alpine AS deps WORKDIR /app COPY package*.json ./ RUN npm ci --only=production # ── Etapa 2: build ─────────────────────────────────────────── FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # ── Etapa 3: imagine finală ─────────────────────────────────── FROM node:20-alpine AS final LABEL maintainer="dev@exemplu.ro" LABEL version="1.0.0" ENV NODE_ENV=production ENV PORT=3000 WORKDIR /app # Copiază doar ce e necesar COPY --from=deps /app/node_modules ./node_modules COPY --from=builder /app/dist ./dist # Utilizator non-root RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s \ CMD wget -qO- http://localhost:3000/health || exit 1 CMD ["node", "dist/server.js"] </code> Aceasta folosește **multi-stage build** - o tehnică prin care imaginea finală conține doar artefactele necesare, nu întregul toolchain de build. ---- ===== Fișierul .dockerignore ===== Similar cu ''.gitignore'', exclude fișiere din contextul de build (măresc dimensiunea și pot expune date sensibile). <code> # .dockerignore node_modules/ .git/ *.log .env .env.local dist/ coverage/ **/*.test.ts Dockerfile docker-compose*.yml README.md </code> ---- ===== Comenzi de lucru ===== <code bash> # Construiește imaginea cu tag docker build -t myapp:1.0 . # Specifică un Dockerfile cu alt nume docker build -f Dockerfile.prod -t myapp:prod . # Transmite argumente de build docker build --build-arg VERSION=2.0 -t myapp:2.0 . # Fără cache (rebuild complet) docker build --no-cache -t myapp:latest . # Construiește o anumită etapă (multi-stage) docker build --target builder -t myapp:debug . # Rulează containerul docker run -d -p 3000:3000 --name myapp myapp:1.0 # Rulează interactiv (debug) docker run -it --rm myapp:1.0 sh # Inspectează straturile imaginii docker history myapp:1.0 # Publică imaginea docker tag myapp:1.0 utilizator/myapp:1.0 docker push utilizator/myapp:1.0 </code> ---- ===== Bune practici ===== === 1. Ordinea instrucțiunilor - cache eficient === Docker reutilizează layerele din cache dacă instrucțiunile nu s-au schimbat. Pune fișierele care se schimbă rar **sus**, cele care se schimbă des **jos**. <code dockerfile> # Bine: package.json se schimbă rar față de codul sursă COPY package*.json ./ RUN npm ci COPY . . # sursele se schimbă des - copiază la final # Evită: orice schimbare în sursă invalidează și npm ci COPY . . RUN npm ci </code> === 2. Imagini mici === * Folosește imagini ''alpine'' sau ''slim'' ca bază * Curăță cache-ul managerului de pachete în același ''RUN'': <code dockerfile> RUN apt-get update \ && apt-get install -y --no-install-recommends curl \ && rm -rf /var/lib/apt/lists/* </code> === 3. Multi-stage builds === Separă etapa de compilare de imaginea finală de producție. Rezultatul: imagini de 5–10× mai mici. === 4. Securitate === * Nu rula ca root - creează un utilizator dedicat cu ''USER'' * Nu include secrete în Dockerfile - folosește ''--secret'' la build sau variabile de mediu la runtime * Scanează imaginile cu ''docker scout'' sau ''trivy'' === 5. Un proces per container === Un container = un singur proces principal. Evită să rulezi mai multe servicii (nginx + app + db) în același container. ---- ===== Referințe ===== * [[https://docs.docker.com/engine/reference/builder/|Referința oficială Dockerfile]] * [[https://docs.docker.com/develop/develop-images/dockerfile_best-practices/|Best practices - documentație oficială]] * [[https://docs.docker.com/build/building/multi-stage/|Multi-stage builds]] * [[https://docs.docker.com/scout/|Docker Scout - scanare vulnerabilități]] {{tag>docker dockerfile devops container image build linux infrastructura ci-cd securitate}}