Vissza a főoldalra

Docker CI/CD Demo - Reprodukálható lokális CI/CD környezet Vagrant és Docker alapon

Ez a projekt egy teljes CI/CD pipeline saját infrastruktúrán – vagrant up-tól az automatikus deployig. Öt konténer (Gitea, Jenkins, Docker Registry, Nginx Proxy Manager, Portainer) alkotja a stacket; az egyszeri beállítás után minden git push automatikusan végigviszi a buildet a futó alkalmazásig

GitHub repository
Stack Vagrant, VirtualBox, Docker, Docker Compose, Jenkins, Gitea, Docker Registry, Nginx Proxy Manager, Portainer
Szerepem Tervezés, infrastruktúra és pipeline felépítése egyedül

Miért csináltam

Rendszermérnökként a DevOps és a felhőalapú technológiák felé bővítem a tudásomat. Ez a projekt egy DevOps kurzus elméleti anyagának gyakorlati megvalósítása. A célom egy olyan teljes, nulláról felépített CI/CD pipeline létrehozása volt, amely modellezi az éles környezetek valós kihívásait. Ez a tapasztalat – és maga a portfólióelem – fontos mérföldkő a szakmai fejlődésemben.

Mit építettem

A rendszer öt konténerből áll, amelyeket a Vagrant VM egyetlen provisioning lépésben állít fel:

  • Gitea - a demo alkalmazás forráskódjának git szervere
  • Jenkins - egyedi image-ből épített CI/CD szerver, Docker socket hozzáféréssel (Docker-outside-of-Docker modell)
  • Docker Registry - saját, basic auth-hal védett image registry
  • Nginx Proxy Manager - a konténerek névalapú elérése (gitea.local, jenkins.local stb.)
  • Portainer - opcionális Docker felügyeleti UI

Két-repo modell. A projekt tudatosan két repóra van szétválasztva:

Repo Tartalom Hol él
Platform docker-compose, Vagrantfile, provisioning script, Jenkins Dockerfile, NPM konfiguráció, dokumentáció GitHub
Demo app index.html, Dockerfile, Jenkinsfile Gitea (a VM-en belül)

Ez a szétválasztás modellezi az infrastruktúra és az alkalmazáskód valós, egymástól független életciklusát, így a pipeline egy monolitikus repó helyett éles forgatókönyvet tesztel.

Architektúra-ábra

Hogyan dolgoztam

A projektet „walking skeleton” szemlélettel építettem fel: először egy minimális, de végponttól végpontig működő vázat hoztam létre (VM fut, Docker telepítve), majd erre rétegenként húztam fel a funkcionalitást:

  1. Alap infrastruktúra: Vagrantfile, idempotens provisioning script, egyedi Jenkins image, mind az öt konténer elindítása docker-compose-zal
  2. Gitea & Jenkins réteg: Gitea inicializálás, Jenkins pluginek telepítése, webhook bekötése, valamint az Nginx Proxy Manager és a Portainer beállítása
  3. App & Pipeline réteg: a demo alkalmazás fájljainak feltöltése és a Jenkinsfile (build → push → deploy) kialakítása
  4. Validáció: teljes end-to-end tesztelés egy valódi Gitea push-tól a futó konténerig
  5. Dokumentáció & lezárás: README, reprodukciós útmutató és architektúra-dokumentáció elkészítése

Ez a megközelítés biztosította, hogy minden réteg befejezése után egy tesztelhető állapotot kapjak. Így a hibák korán, jól izolálható formában jelentkeztek.

Kihívások és megoldások

Docker socket jogosultság a Jenkins konténerben: A Jenkinsnek hozzá kellett férnie a host Docker socketjéhez az image-ek építéséhez és futtatásához. Az első verzióban a Jenkins rootként futott, ami komoly biztonsági kockázat. A végleges megoldásban az entrypoint.sh futásidőben szinkronizálja a docker csoport GID-jét a host socketjével, így a Jenkins folyamat már a korlátozottabb jogosultságú jenkins felhasználóval indulhat.

Gitea webhook problémák: A Gitea alapértelmezetten blokkolta a belső Docker hálózaton belüli webhook kéréseket (ALLOWED_HOST_LIST). Ezt explicit környezeti változóval kellett engedélyezni a docker-compose fájlban. Ezt követően előfordult, hogy bár a Gitea sikeresen (200 OK) kiküldte a payloadot, a beépített Jenkins Gitea plugin nem reagált. Ezt a problémát a robusztusabb Generic Webhook Trigger plugin használatával hidaltam át.

Registry elérés (konténernév vs. localhost): Mivel a Jenkins a host Docker socketjét mountolja, minden Docker parancs a hoszt hálózati kontextusában fut. Emiatt a Jenkins pipeline-ban a belső registry:5000 helyett a hostra publikált localhost:5001 portot kellett használni – ez egy tipikus hálózati sajátossága a Docker-outside-of-Docker modellnek.

Idempotens provisioning: Cél volt, hogy a vagrant destroy && vagrant up parancsok mindig hiba és emberi beavatkozás nélkül állítsák vissza a teljes stacket. Ehhez minden telepítési és build lépés elé feltételeket (pl. „csak akkor buildeld az image-et, ha még nem létezik”) építettem be, így a script többször is lefuttatható mellékhatások nélkül.

Jenkins pipeline futása

Eredmény

A teljes folyamat végponttól végpontig validált: egy kód push a Gitea-ba automatikusan elindítja a Jenkins pipeline-t, lefut mind a négy szakasz (Checkout, Build image, Push image, Deploy container), és a frissített alkalmazás azonnal elérhetővé válik. A projekt teljes dokumentációval rendelkezik, amely lépésről lépésre leírja a nulláról való reprodukálást:

  • README.md – gyors áttekintés és indítás
  • docs/architecture.md – rendszerfelépítés
  • docs/platform-setup.md – infrastruktúra-telepítés
  • docs/jenkins-gitea-setup.md – CI/CD integráció

Futó demo alkalmazás

Portainer dashboard

Tanulságok

  • A „walking skeleton” szemlélet felgyorsítja a hibakeresést: A réteges építkezés miatt a problémák jól körülhatároltan jelentkeztek, elkerülve a késői integrációs káoszt.
  • A hálózati modell pontos megértése elengedhetetlen: A legtöbb elakadás (registry port, webhook, proxy 502) abból fakadt, hogy melyik konténer milyen hálózati kontextusban működött a Docker-outside-of-Docker modell miatt.
  • Az idempotencia alapkövetelmény: Ha egy provisioning script nem futtatható le biztonságosan többször egymás után, akkor a reprodukálhatóság nem garantálható.

Kód és dokumentáció

A teljes forráskód, a provisioning script és a részletes reprodukciós dokumentáció elérhető a GitHub repóban:

github.com/bwgabor/docker-cicd-demo