Dobrodošli u deveti deo serijala na ITNetwork.rs! Do sada smo prošli kroz monolit i mikroservise, frontend, API-je, baze, serverless, performanse, i bezbednost. Danas ulazimo u srž modernog inženjeringa – DevOps prakse, CI/CD pipeline-ove i observability. Naučićemo kako automatizovati sve od koda do produkcije, kako pratiti sistem u realnom vremenu, i kako reagovati pre nego što korisnik primeti problem.
Šta je DevOps?
„DevOps nije alat – to je kultura.“
Cilj: Ukloniti silos između razvoja i operacija.
| Tradicionalno | DevOps |
|---|---|
| „Baci preko zida“ | „You build it, you run it“ |
| Ručni deploy | Automatski, ponovljivi |
| Downtime od 2h | Zero-downtime |
| „Radi na mom računaru“ | Radi svuda |
1. Infrastructure as Code (IaC)
Alati:
| Alat | Tip | Kada koristiti |
|---|---|---|
| Terraform | Deklarativni | Multi-cloud, kompleksni sistemi |
| Pulumi | Imperativni (JS/TS) | Programeri koji vole kod |
| AWS CDK | Programski (TS) | AWS-only |
| Dockerfile | Za aplikaciju | Svuda |
# main.tf
provider "aws" {
region = "eu-central-1"
}
resource "aws_s3_bucket" "static" {
bucket = "itnetwork-static-${random_id.id.hex}"
}
resource "random_id" "id" {
byte_length = 4
}
resource "aws_cloudfront_distribution" "cdn" {
origin {
domain_name = aws_s3_bucket.static.bucket_regional_domain_name
origin_id = "S3-static"
}
enabled = true
is_ipv6_enabled = true
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "S3-static"
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
output "cdn_url" {
value = aws_cloudfront_distribution.cdn.domain_name
}
Pokretanje: terraform init → plan → apply
2. CI/CD Pipeline – od commit-a do produkcije
Faze:
Primer: GitHub Actions za Next.js + Node.js mikroservis
# .github/workflows/ci-cd.yml
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: testdb
ports: [5432:5432]
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Run tests
run: npm test
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/testdb
- name: Build
run: npm run build
security-scan:
runs-on: ubuntu-latest
needs: build-test
steps:
- uses: actions/checkout@v4
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
deploy-staging:
runs-on: ubuntu-latest
needs: security-scan
environment: staging
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod --env=staging'
deploy-production:
runs-on: ubuntu-latest
needs: deploy-staging
environment: production
steps:
- name: Manual Approval
uses: trstringer/manual-approval@v1
with:
approvers: admin1,admin2
- name: Deploy to Production
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-args: '--prod'
| Stub | Šta meri | Alat |
|---|---|---|
| Metrics | Brojevi (CPU, latency) | Prometheus |
| Logs | Tekstualni događaji | Loki / ELK |
| Traces | Put zahteva kroz servise | Jaeger / OpenTelemetry |
prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'node-app'
static_configs:
- targets: ['app:3000']
- job_name: 'postgres'
static_configs:
- targets: ['postgres-exporter:9187']
{
"title": "API Health",
"panels": [
{
"type": "stat",
"title": "P95 Latency",
"targets": [
{
"expr": "histogram_quantile(0.95, sum(rate(http_request_duration_ms_bucket[5m])) by (le))"
}
]
}
]
}
docker-compose.yml (loki + promtail)
services:
loki:
image: grafana/loki:2.9
ports: ["3100:3100"]
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:2.9
volumes:
- ./promtail.yaml:/etc/promtail/config.yml
- /var/log:/var/log
command: -config.file=/etc/promtail/config.yml
scrape_configs:
- job_name: app
static_configs:
- targets: [localhost]
labels:
job: app
__path__: /var/log/app/*.log
Node.js instrumentacija
// tracing.ts
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { PrismaInstrumentation } from '@opentelemetry/instrumentation-prisma';
const provider = new NodeTracerProvider();
const exporter = new OTLPTraceExporter({
url: 'http://jaeger:4318/v1/traces'
});
provider.addSpanProcessor(new BatchSpanProcessor(exporter));
provider.register();
registerInstrumentations({
instrumentations: [
new HttpInstrumentation(),
new ExpressInstrumentation(),
new PrismaInstrumentation()
]
});
Rezultat: Vidimo gde zahtev „gubi“ vreme – DB? Redis? External API?
4. GitOps – deklarativni deployment
Alat: ArgoCD
# argocd-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: web-app
spec:
project: default
source:
repoURL: https://github.com/itnetwork-rs/web-app.git
targetRevision: HEAD
path: k8s/prod
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
Promeniš YAML u Git-u → ArgoCD automatski deploy-uje
5. Praktičan primer: Kompletan CI/CD + Observability
Poređenje: Ručni vs Automatski deploy
| Aspekt | Ručni | CI/CD + GitOps |
|---|---|---|
| Vreme deploy-a | 30 min | 3 min |
| Greške | Česte | Retke |
| Rollback | Ručno | Jedan klik |
| Audit trail | Ne postoji | Git log |
| Timska saradnja | Blokirana | Paralelna |
| Sloj | Alat |
|---|---|
| IaC | Terraform / Pulumi |
| CI/CD | GitHub Actions / GitLab CI |
| Deploy | ArgoCD / Flux |
| Metrics | Prometheus + Grafana |
| Logs | Loki |
| Traces | OpenTelemetry + Jaeger |
| Alerting | Alertmanager → Slack/Email |
Zlatno pravilo: „Ako ne možeš da deploy-uješ sa jednim klikom – još nisi završio.“
Šta dalje?
U desetom i poslednjem delu ulazimo u arhitekturu budućnosti:
- WebAssembly
- AI-driven aplikacije
- Progressive Web Apps
- Zero-Trust, Quantum-ready
Tvoj izazov
- Napravi GitHub Actions pipeline za svoj projekat
- Postavi Prometheus + Grafana lokalno (docker-compose up)
- Dodaj OpenTelemetry u svoj Node.js servis
- Komentariši ispod: „Koliko često deploy-uješ na produkciju?“
Autor: Dušan Antonijević – saradnik ITNetwork.rs
Hvala na čitanju! Podeli tekst sa kolegama.



