# ARTICLE_LOADED
> cat cicd-pipeline-nedir.md
> rendering content...
status: READING_MODE
security_level: PUBLIC
engagement_tracking: ENABLED
article@tektik:/var/blog/cicd-pipeline-nedir$
cat metadata.json
"category": "DevOps"
"date": "2026-01-17"
"read_time": "12 dk okuma"
"author": "TekTık Yazılım DevOps Ekibi"
DevOps17 Ocak 2026

CI/CD Pipeline Nedir? Modern Yazılım Geliştirmede Sürekli Entegrasyon ve Dağıtım

echo"CI/CD Pipeline ile yazılım geliştirme sürecinizi otomatikleştirin. GitLab CI, GitHub Actions, Jenkins gibi popüler araçlarla pratik pipeline örnekleri, best practices ve DevSecOps entegrasyonu."
#CI/CD#DevOps#GitLab CI#GitHub Actions#Jenkins#Automation#DevSecOps
12 dk okuma
TekTık Yazılım DevOps Ekibi
content@tektik:/var/articles/cicd-pipeline-nedir.md$
./render-article --format=html --style=cyber

CI/CD Pipeline Nedir? Modern Yazılım Geliştirmede Sürekli Entegrasyon ve Dağıtım

Yazılım geliştirme dünyasında hızlı ve güvenilir yayınlamalar yapmak artık zorunluluk haline geldi. CI/CD Pipeline'ları, bu zorluk karşısında en etkili çözüm olarak karşımıza çıkıyor. Yazılımı manuel olarak derlemek, test etmek ve yayınlamak yerine, bu işlemleri otomatikleştirerek hız kazanıyoruz.

Bu yazıda, CI/CD Pipeline'ın ne olduğunu, nasıl çalıştığını, popüler araçlarını ve pratik implementasyonlarını inceleyeceğiz.

İçindekiler

  1. CI/CD Nedir ve Neden Önemlidir?
  2. Continuous Integration (CI) - Sürekli Entegrasyon
  3. Continuous Delivery ve Deployment (CD)
  4. CI/CD Pipeline Aşamaları
  5. Popüler CI/CD Araçları Karşılaştırması
  6. GitHub Actions ile Complete Pipeline
  7. GitLab CI/CD Kullanımı
  8. Jenkins Pipeline Konfigürasyonu
  9. Docker ve Kubernetes ile CI/CD
  10. DevSecOps: Pipeline'a Güvenlik Entegrasyonu
  11. Best Practices ve Optimizasyon
  12. Yaygın Hatalar ve Çözümleri

CI/CD Nedir ve Neden Önemlidir? {#cicd-nedir}

Tanım

CI/CD, yazılım geliştirme sürecini otomatikleştiren ve hızlandıran iki önemli pratiğin kombinasyonudur:

  • Continuous Integration (CI): Geliştiricilerin kodlarını merkezi bir repository'ye sık sık merge etmeleri ve her merge'de otomatik testler çalıştırılması
  • Continuous Delivery/Deployment (CD): Otomatik olarak derlenmiş ve test edilmiş kodun production ortamına yayınlanması

Geleneksel vs CI/CD Yaklaşımı

Geleneksel Deployment Süreci (Problemli):

  • Geliştirici aylar boyunca kod yazıyor
  • Tüm kod bir kez merge ediliyor (entegrasyon problemi)
  • Manuel test çalıştırılıyor
  • Manuel deployment yapılıyor
  • Sorunlar production'da keşfediliyor
  • Rollback yapmak zor oluyor

CI/CD Yaklaşımı (Modern):

  • Geliştiriciler her gün kodu merge ediyor
  • Otomatik testler anında çalışıyor
  • Hata bulunursa anında feedback alınıyor
  • Deployment otomatik ve güvenli
  • Sorunlar hızlı çözülüyor
  • Rollback operasyonları basit

İş Değeri ve Faydalar

CI/CD implementasyonunun kanıtlanmış faydaları:

Metrikİyileştirme
**Deployment Sıklığı**Haftada birden günde birkaç kez
**Lead Time**3-6 aydan saatlere
**Mean Time to Recovery**Saatlerden dakikalara
**Change Failure Rate**%46'dan %15'e
**Development Verimliği**%20-30 artış

Bu metrikler DORA (DevOps Research and Assessment) tarafından şirkletlerin verilerine dayanarak hesaplanmıştır.

Continuous Integration (CI) - Sürekli Entegrasyon {#continuous-integration}

CI Prensipleri

Continuous Integration, aşağıdaki prensiplere dayanır:

  1. Sık Code Integration: Geliştiriciler en az günde bir kez kodlarını merge ederler
  2. Otomatik Build: Her merge'de otomatik olarak kod derlenir
  3. Otomatik Test: Birim, entegrasyon ve smoke testleri çalıştırılır
  4. Hızlı Feedback: Sorunlar dakikalar içinde rapor edilir
  5. Kontrol Mekanizmaları: Hata içeren kod merge edilmez

Workflow

text
Developer Code Push
    ↓
Trigger Pipeline
    ↓
Checkout Source Code
    ↓
Dependencies Install
    ↓
Lint & Code Quality
    ↓
Build
    ↓
Unit Tests
    ↓
Integration Tests
    ↓
Code Coverage Check
    ↓
Success/Failure Report

Source Control Integration

Modern CI sistemleri git repository'lere bağlanır:

  • Webhook'lar: Repository'de push gerçekleşince otomatik tetiklenir
  • Branch Protection: CI başarısız olursa merge yasaklanır
  • Status Checks: GitHub/GitLab'da PR'lar CI sonucunu gösterir

Testing Stratejileri

CI Pipeline'da çalıştırılması gereken testler:

  1. Unit Tests - Tek fonksiyon/metod testleri (hızlı, ~2-5 dakika)
  2. Integration Tests - Modüller arası testler (orta hızlı, ~5-10 dakika)
  3. Code Quality - Linting, complexity analysis
  4. Security Scans - SAST araçlarıyla güvenlik taraması
  5. Smoke Tests - Kritik özellikler kontrol edilir

Continuous Delivery ve Deployment (CD) {#continuous-delivery}

Farklar

Continuous Delivery (CD):

  • Kod her zaman production'a hazır
  • Manual onay alındıktan sonra yayınlanır
  • Kontrollü ve güvenli
  • İnsan hatası riski minimum

Continuous Deployment:

  • Her başarıyla tamamlanan CI production'a otomatik yayınlanır
  • Manuel onay gerekmez
  • En hızlı feedback süreci
  • Daha yüksek risk

Çoğu kuruluş Continuous Delivery yaklaşımını tercih eder (manual approval).

Deployment Stratejileri

1. Blue-Green Deployment

  • İki identik ortam: Blue (eski) ve Green (yeni)
  • Trafik Blue'ya yönlendirilir
  • Green test edilir
  • Trafik Green'e switch edilir
  • Sorun varsa Blue'ya geri dönülür

2. Canary Deployment

  • Yeni versiyon küçük bir kullanıcı grubuna yayınlanır (örn: %5)
  • Metrikler izlenir
  • Sorun yoksa gradual olarak artırılır
  • Rollback hızlıdır

3. Rolling Update

  • Pod'lar sırasıyla güncellenir
  • Hizmet kesintisi olmaz
  • Kubernetes'in default stratejisi

4. Feature Flags

  • Özellik kodda var ama disabled
  • Production'da feature flag'i açıp kapatabilirsiniz
  • Rollback gerek olmaz

Environment Management

yaml
Environments:
  - Development: Geliştirici ortamı, sık deploy edilir
  - Staging: Production kopyası, son test aşaması
  - Production: Canlı ortam, manual approval gerekir

CI/CD Pipeline Aşamaları {#pipeline-asamalari}

Bir complete pipeline aşağıdaki aşamaları içerir:

1. Source Aşaması

  • Kod repository'den checkout edilir
  • Git history kontrol edilir
  • Uygun branch'in kullanıldığı doğrulanır

2. Build Aşaması

  • Bağımlılıklar indirilir
  • Kod derlenir
  • Artifacts (JAR, binary, Docker image vs.) oluşturulur

3. Test Aşaması

  • Unit testler çalıştırılır
  • Integration testler çalıştırılır
  • Code coverage kontrol edilir
  • Hata varsa pipeline durdurulur

4. Security Scan Aşaması

  • SAST (Static Application Security Testing) - kaynak kod taraması
  • Dependency scanning - kütüphanelerdeki vulnerabilities
  • Secret scanning - sızıntılı credentials kontrol
  • Container image scanning

5. Deploy Aşaması

  • Artifacts container registry'ye push edilir
  • Staging/Production ortamına deploy edilir
  • Health checks çalıştırılır

6. Post-Deployment Aşaması

  • Smoke tests çalıştırılır
  • Metrikleri monitoring'e bağlanır
  • Slack/Teams'e bildirim gönderilir
  • Rollback hazırlığı yapılır

Popülar CI/CD Araçları Karşılaştırması {#populer-araçlar}

AraçYerleşimKurulumKolaylıkÖlçeklenebilirlikFiyat
**GitHub Actions**GitHub'taÇok kolay★★★★★İyiBedava/ücretli
**GitLab CI/CD**GitLab'taKolay★★★★★Çok iyiBedava/ücretli
**Jenkins**Kendi sunucuZor★★★☆☆MükemmelBedava
**CircleCI**CloudOrta★★★★☆İyiÜcretli
**Azure DevOps**CloudOrta★★★★☆İyiÜcretli
**Tekton**KubernetesZor★★★☆☆MükemmelBedava

Seçim Kriterleri

  • GitHub Actions: GitHub kullanıyorsanız ve basit pipeline istiyorsanız
  • GitLab CI: GitLab kullanıyorsanız veya advanced features istiyorsanız
  • Jenkins: Kurumsal, kompleks ihtiyaçlar ve on-prem çözümleri için
  • Tekton: Kubernetes-native, cloud-agnostic çözümler için

GitHub Actions ile Complete Pipeline {#github-actions}

GitHub Actions, GitHub'ta natively çalışan ve hiçbir kurulum gerektirmeyen güçlü bir CI/CD sistemidir.

Architecture

text
GitHub Repository
  ↓
Webhook (push/PR)
  ↓
Workflow Trigger
  ↓
Runner (Linux/Windows/macOS)
  ↓
Jobs & Steps
  ↓
Artifacts & Results

Tam Pipeline Örneği

yaml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  lint:
    runs-on: ubuntu-latest
    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 ESLint
        run: npm run lint

      - name: Run Prettier
        run: npm run format:check

  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18.x, 20.x, 22.x]
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm run test:unit

      - name: Run integration tests
        run: npm run test:integration

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/coverage-final.json

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: Upload Trivy results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

  build:
    needs: [lint, test, security]
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    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: Build application
        run: npm run build

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to Kubernetes
        run: |
          mkdir -p $HOME/.kube
          echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > $HOME/.kube/config
          kubectl set image deployment/app app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} -n production
          kubectl rollout status deployment/app -n production --timeout=5m

      - name: Run smoke tests
        run: |
          curl -f https://api.example.com/health || exit 1

      - name: Notify Slack
        if: always()
        uses: slackapi/slack-github-action@v1
        with:
          webhook-url: ${{ secrets.SLACK_WEBHOOK }}
          payload: |
            {
              "text": "Deployment ${{ job.status }}",
              "blocks": [{
                "type": "section",
                "text": {
                  "type": "mrkdwn",
                  "text": "*Deployment Status:* ${{ job.status }}\n*Commit:* ${{ github.sha }}\n*Author:* ${{ github.actor }}"
                }
              }]
            }

Temel Konseptler

  • Workflows: .github/workflows/*.yml dosyalarında tanımlanır
  • Jobs: Bir workflow'da paralel veya sequential çalışabilir
  • Steps: Job'un içindeki individual komutlar
  • Actions: Reusable workflow bileşenleri (GitHub Marketplace)
  • Runners: Pipeline'ı çalıştıran compute instances
  • Secrets: Krediler güvenli şekilde saklanan environment variables

GitLab CI/CD Kullanımı {#gitlab-ci}

GitLab CI, en kapsamlı ve kurumsal-ready CI/CD çözümlerinden biridir.

Architecture

text
GitLab Repository
  ↓
.gitlab-ci.yml (tanım dosyası)
  ↓
GitLab Runner (executor: docker/shell/kubernetes)
  ↓
Artifacts, Container Registry, Pages
  ↓
Deployment

Complete Pipeline Örneği

yaml
stages:
  - build
  - test
  - security
  - deploy
  - rollback

variables:
  REGISTRY: registry.gitlab.com
  IMAGE_NAME: $CI_PROJECT_PATH
  DOCKER_HOST: tcp://docker:2375
  DOCKER_TLS_CERTDIR: ""

.docker_build_template: &docker_build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

build:
  <<: *docker_build
  stage: build
  script:
    - docker build -t $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA .
    - docker push $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA
  artifacts:
    reports:
      dotenv: build.env
  cache:
    - key: ${CI_COMMIT_REF_SLUG}
      paths:
        - node_modules/
  only:
    - main
    - develop

test:unit:
  image: node:20
  stage: test
  script:
    - npm ci
    - npm run test:unit -- --coverage
  coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
  artifacts:
    paths:
      - coverage/
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

test:integration:
  image: node:20
  stage: test
  services:
    - postgres:15
    - redis:7
  variables:
    POSTGRES_DB: test_db
    POSTGRES_USER: test_user
    POSTGRES_PASSWORD: test_pass
    REDIS_URL: redis://redis:6379
  script:
    - npm ci
    - npm run test:integration
  timeout: 30 minutes

security:sast:
  image: returntocorp/semgrep
  stage: security
  script:
    - semgrep --config=p/security-audit --json -o sast-report.json .
  artifacts:
    reports:
      sast: sast-report.json

security:container:
  image: aquasec/trivy:latest
  stage: security
  script:
    - trivy image --severity HIGH,CRITICAL --exit-code 1 $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA
  allow_failure: true

security:dependency:
  image: node:20
  stage: security
  script:
    - npm ci
    - npm audit --json > dependency-report.json || true
  artifacts:
    paths:
      - dependency-report.json

deploy:staging:
  image: bitnami/kubectl:latest
  stage: deploy
  environment:
    name: staging
    kubernetes_namespace: staging
  script:
    - kubectl set image deployment/app app=$REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA -n staging
    - kubectl rollout status deployment/app -n staging --timeout=5m
  only:
    - develop
  when: manual

deploy:production:
  image: bitnami/kubectl:latest
  stage: deploy
  environment:
    name: production
    kubernetes_namespace: production
    deployment_tier: production
  script:
    - kubectl set image deployment/app app=$REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA -n production
    - kubectl rollout status deployment/app -n production --timeout=5m
  only:
    - main
  when: manual

rollback:production:
  image: bitnami/kubectl:latest
  stage: rollback
  environment:
    name: production
    kubernetes_namespace: production
  script:
    - kubectl rollout undo deployment/app -n production
    - kubectl rollout status deployment/app -n production --timeout=5m
  when: manual
  only:
    - main

GitLab Runner Setup

GitLab Pipeline'ları çalıştırmak için Runner gereklidir:

bash
# Runner kurulumu
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | bash
sudo apt-get install gitlab-runner

# Runner register (Docker executor ile)
sudo gitlab-runner register \
  --url https://gitlab.com/ \
  --registration-token $RUNNER_TOKEN \
  --executor docker \
  --docker-image ubuntu:latest

Cache ve Artifacts

yaml
# Cache: İş arası paylaşılan veriler (npm modules vb.)
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/
    - .gradle/wrapper/

# Artifacts: İşin çıktısı (test reports, binaries vb.)
artifacts:
  paths:
    - dist/
    - coverage/
  when: always
  expire_in: 30 days

Jenkins Pipeline Konfigürasyonu {#jenkins-pipeline}

Jenkins, kurumsal CI/CD çözümleri için en yaygın açık kaynak platformudur.

Declarative Pipeline Örneği

groovy
pipeline {
    agent any

    environment {
        REGISTRY = 'docker.io'
        IMAGE_NAME = 'tektik/app'
        NODE_ENV = 'production'
        SONARQUBE_URL = 'http://sonarqube:9000'
    }

    options {
        timestamps()
        timeout(time: 1, unit: 'HOURS')
        buildDiscarder(logRotator(numToKeepStr: '10'))
    }

    triggers {
        githubPush()
        pollSCM('H/15 * * * *')
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
                script {
                    sh '''
                        git log --oneline -1
                        echo "Current branch: $(git rev-parse --abbrev-ref HEAD)"
                    '''
                }
            }
        }

        stage('Install Dependencies') {
            steps {
                sh '''
                    node --version
                    npm --version
                    npm ci
                '''
            }
        }

        stage('Lint') {
            parallel {
                stage('ESLint') {
                    steps {
                        sh 'npm run lint -- --format json > eslint-report.json || true'
                    }
                }

                stage('SonarQube') {
                    steps {
                        sh '''
                            npm run build
                            sonar-scanner \
                              -Dsonar.projectKey=tektik \
                              -Dsonar.sources=src \
                              -Dsonar.host.url=$SONARQUBE_URL \
                              -Dsonar.login=$SONARQUBE_TOKEN
                        '''
                    }
                }
            }
        }

        stage('Unit Tests') {
            steps {
                sh '''
                    npm run test:unit -- --coverage --watchAll=false
                '''
                junit 'coverage/junit.xml'
                publishHTML([
                    reportDir: 'coverage',
                    reportFiles: 'index.html',
                    reportName: 'Coverage Report'
                ])
            }
        }

        stage('Security Scans') {
            parallel {
                stage('Dependency Check') {
                    steps {
                        sh '''
                            npm audit --json > audit-report.json || true
                        '''
                    }
                }

                stage('Container Scan') {
                    steps {
                        sh '''
                            docker build -t $IMAGE_NAME:$BUILD_NUMBER .
                            trivy image --severity HIGH,CRITICAL $IMAGE_NAME:$BUILD_NUMBER
                        '''
                    }
                }

                stage('Secret Scan') {
                    steps {
                        sh '''
                            git secrets --scan
                        '''
                    }
                }
            }
        }

        stage('Build') {
            steps {
                sh '''
                    npm run build
                    docker build -t $REGISTRY/$IMAGE_NAME:$BUILD_NUMBER .
                    docker tag $REGISTRY/$IMAGE_NAME:$BUILD_NUMBER $REGISTRY/$IMAGE_NAME:latest
                '''
            }
        }

        stage('Push to Registry') {
            when {
                branch 'main'
            }
            steps {
                sh '''
                    docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY
                    docker push $REGISTRY/$IMAGE_NAME:$BUILD_NUMBER
                    docker push $REGISTRY/$IMAGE_NAME:latest
                '''
            }
        }

        stage('Deploy to Staging') {
            when {
                branch 'develop'
            }
            steps {
                sh '''
                    kubectl set image deployment/app app=$REGISTRY/$IMAGE_NAME:$BUILD_NUMBER -n staging
                    kubectl rollout status deployment/app -n staging --timeout=5m
                '''
            }
        }

        stage('Smoke Tests') {
            when {
                branch 'main'
            }
            steps {
                sh '''
                    sleep 10
                    curl -f http://app-service:3000/health || exit 1
                '''
            }
        }

        stage('Deploy to Production') {
            when {
                branch 'main'
            }
            input {
                message 'Deploy to production?'
                ok 'Deploy'
                submitter 'devops-team'
            }
            steps {
                sh '''
                    kubectl set image deployment/app app=$REGISTRY/$IMAGE_NAME:$BUILD_NUMBER -n production
                    kubectl rollout status deployment/app -n production --timeout=5m
                '''
            }
        }
    }

    post {
        always {
            junit allowEmptyResults: true, testResults: '**/*.xml'

            // Slack bildirimi
            slackSend(
                color: currentBuild.result == 'SUCCESS' ? 'good' : 'danger',
                message: """
                    Jenkins Build ${currentBuild.number} ${currentBuild.result}
                    Job: ${env.JOB_NAME}
                    Branch: ${env.GIT_BRANCH}
                    Commit: ${env.GIT_COMMIT}
                """
            )

            // Cleanup
            cleanWs()
        }

        failure {
            emailext(
                subject: "Jenkins Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                body: """
                    Build Failed
                    Job: ${env.JOB_NAME}
                    Build Number: ${env.BUILD_NUMBER}
                    Build URL: ${env.BUILD_URL}
                    Error: Check console output at ${env.BUILD_URL}
                """,
                to: '${DEFAULT_RECIPIENTS}'
            )
        }
    }
}

Jenkins Installation & Setup

bash
# Docker ile Jenkins başlatma
docker run -d \
  --name jenkins \
  -p 8080:8080 \
  -p 50000:50000 \
  -v jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  jenkins/jenkins:lts

# Kubernetes üzerinde
kubectl create namespace jenkins
helm repo add jenkinsci https://charts.jenkins.io
helm install jenkins jenkinsci/jenkins -n jenkins

Docker ve Kubernetes ile CI/CD {#docker-kubernetes}

Multi-Stage Dockerfile

dockerfile
# Build stage
FROM node:20-alpine AS builder

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci

# Copy source code
COPY . .

# Build application
RUN npm run build

# Production stage
FROM node:20-alpine

WORKDIR /app

# Security: Run as non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001

# Install production dependencies only
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# Copy built application from builder
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3000/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"

# Expose port
EXPOSE 3000

# Switch to nodejs user
USER nodejs

# Start application
CMD ["node", "dist/server.js"]

Kubernetes Deployment & Rolling Update

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: production
  labels:
    app: app
spec:
  replicas: 3

  # Rolling update strategy
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # Fazladan 1 pod
      maxUnavailable: 0    # 0 pod unavailable (zero downtime)

  selector:
    matchLabels:
      app: app

  template:
    metadata:
      labels:
        app: app
        version: v1
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "3000"
        prometheus.io/path: "/metrics"

    spec:
      # Pod scheduling
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - app
              topologyKey: kubernetes.io/hostname

      containers:
      - name: app
        image: registry.example.com/tektik/app:latest
        imagePullPolicy: IfNotPresent

        ports:
        - name: http
          containerPort: 3000
          protocol: TCP

        # Environment variables
        env:
        - name: NODE_ENV
          value: "production"
        - name: LOG_LEVEL
          value: "info"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: database-url

        # Resource management
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

        # Readiness probe (ready mı?)
        readinessProbe:
          httpGet:
            path: /health/ready
            port: http
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3

        # Liveness probe (canlı mı?)
        livenessProbe:
          httpGet:
            path: /health/live
            port: http
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 3
          failureThreshold: 3

        # Startup probe (başlatılıyor mu?)
        startupProbe:
          httpGet:
            path: /health/startup
            port: http
          failureThreshold: 30
          periodSeconds: 10

        # Graceful shutdown
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 15"]

        # Security context
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 1001
          capabilities:
            drop:
              - ALL

        # Volume mounts
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: cache
          mountPath: /app/.cache

      # Service account
      serviceAccountName: app

      # Volumes
      volumes:
      - name: tmp
        emptyDir: {}
      - name: cache
        emptyDir: {}

      # Pod termination grace period
      terminationGracePeriodSeconds: 30

DevSecOps: Pipeline'a Güvenlik Entegrasyonu {#devsecops-entegrasyon}

DevSecOps, güvenliği CI/CD pipeline'ın her aşamasına entegre etme yaklaşımıdır.

Security Stages Örneği

yaml
name: Security Pipeline
on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      # 1. Dependency Vulnerability Scanning
      - uses: actions/checkout@v4

      - name: Dependency check with Snyk
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

      # 2. Static Application Security Testing (SAST)
      - name: SonarQube scan
        uses: sonarsource/sonarqube-scan-action@master
        env:
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

      # 3. Secret Scanning
      - name: Scan for secrets
        uses: gitleaks/gitleaks-action@v2

      # 4. Container Image Scanning
      - uses: actions/setup-docker@v1
      - name: Build and scan image
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
            aquasec/trivy image myapp:${{ github.sha }}

      # 5. License Compliance
      - name: License check
        run: |
          npm install -g license-checker
          license-checker --onlyAllow Apache-2.0,MIT,BSD --json > licenses.json

      # 6. Infrastructure as Code Security
      - name: Scan Terraform
        uses: aquasecurity/tfsec-action@v1
        with:
          working_directory: 'infrastructure/'

DevSecOps Best Practices

  1. Shift Left: Güvenliği geliştirme başında (kod yazarken) kontrol edin
  2. Otomatikleştirin: Tüm güvenlik kontrolleri otomatik olmalı
  3. Fail Secure: Güvenlik testleri başarısız olursa deployment durmalı
  4. Audit & Logging: Tüm işlemler loglanmalı
  5. Secret Management: Credentials kodda olmamalı, Vault gibi araçlar kullanın

Best Practices ve Optimizasyon {#best-practices}

1. Fail Fast Prensibi

Hata erken bulunmalıdır:

yaml
stages:
  - lint        # Paralel çalışır (30 saniye)
  - build       # Lint başarısız olursa çalışmaz
  - test        # Build başarısız olursa çalışmaz
  - deploy      # Test başarısız olursa çalışmaz

2. Pipeline as Code

  • Pipeline'ı kod olarak tanımlayın (YAML, Groovy vb.)
  • Repository'de version control altında tutun
  • Code review için PR process kullanın
  • Infrastructure as Code gibi düşünün

3. Secret Management

yaml
# ❌ YANLIŞ - Secrets hardcoded
password: "super_secret_123"

# ✅ DOĞRU - Secrets environment variable
password: ${{ secrets.DATABASE_PASSWORD }}

# ✅ DAHA DOĞRU - Vault ile
password: vault kv get secret/prod/db_password

4. Caching Strategy

yaml
# NPM cache örneği
cache:
  key:
    files:
      - package-lock.json  # Hash'ine göre cache key
  paths:
    - node_modules/

# Docker layer cache
docker build --cache-from myapp:latest .

5. Parallel Execution

yaml
jobs:
  test:
    strategy:
      matrix:
        node-version: [18.x, 20.x, 22.x]
        os: [ubuntu-latest, macos-latest]
    # 3x2 = 6 parallel job

6. Monitoring ve Observability

Pipeline metriklerini takip edin:

  • Build süresi (trend analizi)
  • Success/failure rates
  • Test coverage
  • Deployment frequency
  • Mean time to recovery (MTTR)

Yaygın Hatalar ve Çözümleri {#yaygın-hatalar}

1. Build Failures

Problem: Lokal'de çalışan kod, pipeline'da başarısız oluyor

Çözüm:

yaml
# Lock files kullanın
npm ci  # npm install yerine (package-lock.json respects)

# Dockerfile ile konsistent environment
docker run --rm -v $(pwd):/app myapp:build npm test

# Exact versions belirtin
node_version: '20.13.0'  # 20.x değil

2. Flaky Tests

Problem: Bazen geçen, bazen başarısız olan testler

Çözüm:

yaml
# Retry logic
- name: Run tests
  run: npm test
  timeout-minutes: 10
  continue-on-error: true

- name: Retry failed tests
  run: npm test -- --testPathPattern=previously-failed
  if: failure()

# Test isolation
# Her test paralel çalışırken kendi database/Redis instance'ı kullanmalı

3. Slow Pipelines

Problem: Pipeline 30+ dakika sürüyor

Çözüm:

yaml
# Parallelization
jobs:
  test:
    parallel: 10  # 10 paralel worker

# Caching optimization
- uses: actions/cache@v3
  with:
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}

# Docker layer caching
- uses: docker/build-push-action@v5
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

4. Deployment Failures

Problem: Deployment başarılı ama application crash oluyor

Çözüm:

yaml
# Health checks
- name: Health check
  run: |
    for i in {1..30}; do
      curl -f http://app:3000/health && exit 0
      sleep 1
    done
    exit 1

# Readiness probes
readinessProbe:
  httpGet:
    path: /ready
    port: 3000
  initialDelaySeconds: 10
  periodSeconds: 5

# Automatic rollback
kubectl rollout status deployment/app --timeout=5m || kubectl rollout undo deployment/app

5. Secret Leakage

Problem: Sensitive bilgiler git history'ye committed oluyor

Çözüm:

yaml
# Secret scanning tools
- name: Detect secrets
  uses: gitleaks/gitleaks-action@v2

# Environment variables maskleme (logs'ta görmez)
- name: Deploy
  env:
    API_KEY: ${{ secrets.API_KEY }}
  run: deploy.sh

# Pre-commit hooks
# .githooks/pre-commit: git secrets --scan
git config core.hooksPath .githooks

Sonuç ve Öneriler

CI/CD Pipeline'ları modern yazılım geliştirmenin temelini oluşturuyor. 2025'te, bir yazılım ekibinin CI/CD olmadan çalışması neredeyse imkansız hale geldi.

Temel Öneriler:

  1. Başlangıç Adımları

    • Basit bir pipeline ile başlayın (GitHub Actions önerilir)
    • Linting ve testleri otomatikleştirin
    • Container image oluşturmayı otomatikleştirin
  2. Güvenlik Odaklı Yaklaşım

    • DevSecOps'u baştan entegre edin
    • Secret management araçları kullanın
    • Dependency scanning ve SAST ekleyin
  3. Ölçeklenebilirlik

    • Infrastructure as Code kullanın
    • GitOps patterns benimseyin
    • Kubernetes üzerinde çalışmayı öğrenin
  4. İzleme ve Optimizasyon

    • Pipeline metriklerini takip edin
    • Slow jobs'u identify ve optimize edin
    • Deployment frequency artırın
  5. Takım Kültürü

    • "Deploy Day" mentality'nden kurtulun
    • Fail fast ve quick recovery kültürü oluşturun
    • Automation'u herkesin sorumluluk alanı yapın

İlgili Yazılar

Kaynaklar


TekTık Yazılım olarak, modern CI/CD mimarilerini tasarlayan ve uygulayan ekip olarak, organizasyonunuzun deployment süreçlerini optimize etmek ve DevOps maturitesini artırmak için size yardımcı olabilir. CI/CD transformation, Kubernetes migration veya DevSecOps implementasyonu hakkında danışmanlık hizmetimizden faydalanabilirsiniz.

İletişim: info@tektik.tr | https://tektik.tr