← Kursa Dön
📄 Text · 30 min

Automated Build ve Push — Registry Entegrasyonu

Bir önceki derste CI/CD pipeline'ının temellerini öğrendik — test, build, push ve deploy adımlarını. Şimdi bu pipeline'ın en kritik parçalarından birine derinlemesine dalıyoruz: automated build ve push. Yani Docker image'larının otomatik olarak oluşturulması ve registry'lere dağıtılması.

Bir fırın düşün. Eskiden her sipariş geldiğinde hamuru elle yoğurur, fırına elle koyar, paketler, kuryeye elle verirdin. Şimdi otomatik bir hat kurmuşsun: sipariş gelir (commit), hamur makinesi yoğurur (build), otomatik fırın pişirir (test), paketleme makinesi paketler (tag), kurye sistemi dağıtır (push + deploy). Sen sadece tarifi (Dockerfile) yazıyorsun — gerisini makine hallediyor.

Automated build ve push, Docker image'larının oluşturulması ve dağıtılması sürecini tamamen otomatikleştirir. Her commit'te veya tag'de otomatik olarak image build edilir, test edilir, taranır ve registry'ye push edilir. Manuel müdahale yok, insan hatası yok, tutarlılık var.

Registry Türleri ve Seçimi

Container Registry Karşılaştırması

RegistryÜcretsiz PlanPrivate RepoCloud EntegrasyonKullanım
Docker Hub1 private repoSınırlıGenelAçık kaynak projeler
GitHub GHCRUnlimited (public)GitHub ActionsGitHub projeleri
AWS ECR500MB free tierAWS ECS/EKSAWS altyapısı
Google GCR/Artifact Registry500MB free tierGKEGCP altyapısı
Azure ACRYokAKSAzure altyapısı
GitLab Registry5GBGitLab CIGitLab projeleri
Self-hosted (Harbor)UnlimitedHer yereTam kontrol

Doğru Registry Nasıl Seçilir?

GitHub kullanıyorsan        → GHCR (GitHub Container Registry)
AWS'deysen                  → ECR (Elastic Container Registry)
GCP'deysen                  → Artifact Registry
GitLab kullanıyorsan        → GitLab Container Registry
Açık kaynak proje           → Docker Hub
Self-hosted / air-gapped    → Harbor

Docker Hub — Automated Build

Docker Hub Login ve Push

# Login
docker login
# veya Access Token ile (önerilen)
echo $DOCKERHUB_TOKEN | docker login --username myuser --password-stdin

# Tag ve push
docker build -t myuser/myapp:v1.0 .
docker push myuser/myapp:v1.0

# Multi-tag push
docker tag myuser/myapp:v1.0 myuser/myapp:latest
docker push myuser/myapp:latest

GitHub Actions ile Docker Hub Push

# .github/workflows/dockerhub.yml
name: Docker Hub Push

on:
  push:
    tags: ['v*']
  pull_request:
    branches: [main]

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

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

      - name: Login to Docker Hub
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ secrets.DOCKERHUB_USERNAME }}/myapp
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=
            type=ref,event=branch
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64

GitHub Container Registry (GHCR)

GHCR, GitHub'ın kendi container registry'sidir. GitHub Actions ile entegrasyonu mükemmeldir — GITHUB_TOKEN ile otomatik authentication.

GHCR Push Pipeline

# .github/workflows/ghcr.yml
name: GHCR Build and Push

on:
  push:
    branches: [main]
    tags: ['v*']

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

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write         # GHCR push için gerekli

    steps:
      - uses: actions/checkout@v4

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

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}    # Otomatik — secret eklemeye gerek yok

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
# GHCR'dan image çek (public repo)
docker pull ghcr.io/myorg/myapp:v1.0

# Private repo için login gerekli
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin

AWS ECR — Elastic Container Registry

ECR Repository Oluşturma

# AWS CLI ile ECR repo oluştur
aws ecr create-repository \
    --repository-name myapp \
    --image-scanning-configuration scanOnPush=true \
    --encryption-configuration encryptionType=AES256

# Lifecycle policy — eski image'ları otomatik sil
aws ecr put-lifecycle-policy \
    --repository-name myapp \
    --lifecycle-policy-text '{
      "rules": [
        {
          "rulePriority": 1,
          "description": "Keep last 10 images",
          "selection": {
            "tagStatus": "any",
            "countType": "imageCountMoreThan",
            "countNumber": 10
          },
          "action": {
            "type": "expire"
          }
        }
      ]
    }'

ECR Push Pipeline

# .github/workflows/ecr.yml
name: ECR Build and Push

on:
  push:
    branches: [main]
    tags: ['v*']

env:
  AWS_REGION: eu-west-1
  ECR_REGISTRY: 123456789012.dkr.ecr.eu-west-1.amazonaws.com
  ECR_REPOSITORY: myapp

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build, tag, and push
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
                     $ECR_REGISTRY/$ECR_REPOSITORY:latest
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest

GitLab CI ile Registry Push

# .gitlab-ci.yml
stages:
  - build
  - scan
  - push

variables:
  DOCKER_IMAGE: $CI_REGISTRY_IMAGE
  DOCKER_TAG: $CI_COMMIT_SHORT_SHA

build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  script:
    - docker build -t $DOCKER_IMAGE:$DOCKER_TAG .
    - docker save $DOCKER_IMAGE:$DOCKER_TAG > image.tar
  artifacts:
    paths:
      - image.tar
    expire_in: 1 hour

security-scan:
  stage: scan
  image:
    name: aquasec/trivy:latest
    entrypoint: [""]
  script:
    - docker load < image.tar
    - trivy image --exit-code 1 --severity CRITICAL $DOCKER_IMAGE:$DOCKER_TAG
  allow_failure: false

push:
  stage: push
  image: docker:24
  services:
    - docker:24-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker load < image.tar
    - docker tag $DOCKER_IMAGE:$DOCKER_TAG $DOCKER_IMAGE:latest
    - docker push $DOCKER_IMAGE:$DOCKER_TAG
    - docker push $DOCKER_IMAGE:latest
  only:
    - main
    - tags

Tag Stratejileri — Immutable ve Anlamlı

Doğru Tag Stratejisi

# ✅ İyi tag stratejisi
myapp:v1.2.3              # Semantic version — release'ler için
myapp:abc1234f            # Git commit hash — her build benzersiz
myapp:main-abc1234f       # Branch + commit — hangi branch'ten geldiği belli
myapp:v1.2               # Major.minor — son patch'i otomatik alır

# ❌ Kötü tag stratejisi
myapp:latest              # Hangi versiyon? Değişebilir. Güvenilmez.
myapp:stable              # Ne zaman güncellendi? Belli değil.
myapp:new                 # Yeni olan eski olur.

docker/metadata-action ile Otomatik Tag

# En iyi tag stratejisi — metadata-action
- uses: docker/metadata-action@v5
  with:
    images: ghcr.io/myorg/myapp
    tags: |
      # Git tag'ı v1.2.3 ise → 1.2.3 ve 1.2
      type=semver,pattern={{version}}
      type=semver,pattern={{major}}.{{minor}}

      # Her commit'te → commit hash
      type=sha,prefix=

      # Branch adı → branch tag
      type=ref,event=branch

      # main branch'te → latest
      type=raw,value=latest,enable={{is_default_branch}}

# Sonuç (v1.2.3 tag'ı push edildiğinde):
# ghcr.io/myorg/myapp:1.2.3
# ghcr.io/myorg/myapp:1.2
# ghcr.io/myorg/myapp:abc1234f
# ghcr.io/myorg/myapp:latest

Image Signing — Güvenilir Image'lar

# Cosign ile image imzalama
- name: Install Cosign
  uses: sigstore/cosign-installer@v3

- name: Sign image
  env:
    COSIGN_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
    COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
  run: |
    cosign sign --key env://COSIGN_KEY \
      ghcr.io/myorg/myapp:v1.2.3

# Image doğrulama
# cosign verify --key cosign.pub ghcr.io/myorg/myapp:v1.2.3

Build Cache Stratejileri

CI/CD'de en büyük zaman kaybı image build süresidir. Cache stratejileri ile build süreni %70-90 azaltabilirsin.

GitHub Actions Cache (GHA)

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

Registry Cache

# Registry'yi cache olarak kullan
- uses: docker/build-push-action@v5
  with:
    cache-from: type=registry,ref=ghcr.io/myorg/myapp:buildcache
    cache-to: type=registry,ref=ghcr.io/myorg/myapp:buildcache,mode=max

Inline Cache

# Image'ın kendisini cache olarak kullan
- uses: docker/build-push-action@v5
  with:
    build-args: BUILDKIT_INLINE_CACHE=1
    cache-from: type=registry,ref=ghcr.io/myorg/myapp:latest

Cache Karşılaştırması

Cache TürüHızBoyut LimitiPaylaşımEn İyi Kullanım
GHAHızlı10GBAynı repoGitHub Actions
RegistryOrtaUnlimitedHer yerdeMulti-runner, cross-repo
LocalÇok hızlıDiskAynı runnerSelf-hosted runner
InlineYavaşImage boyutuHer yerdeBasit projeler

Multi-Registry Push — Birden Fazla Registry

Aynı image'ı birden fazla registry'ye push etmek isteyebilirsin: Docker Hub (public erişim) + ECR (production deploy).

# .github/workflows/multi-registry.yml
name: Multi-Registry Push

on:
  push:
    tags: ['v*']

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3

      # Login to all registries
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: eu-west-1

      - uses: aws-actions/amazon-ecr-login@v2
        id: ecr

      - name: Extract version
        id: version
        run: echo "tag=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      # Build once, push to all
      - uses: docker/build-push-action@v5
        with:
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:${{ steps.version.outputs.tag }}
            ghcr.io/${{ github.repository }}:latest
            ${{ secrets.DOCKERHUB_USERNAME }}/myapp:${{ steps.version.outputs.tag }}
            ${{ secrets.DOCKERHUB_USERNAME }}/myapp:latest
            ${{ steps.ecr.outputs.registry }}/myapp:${{ steps.version.outputs.tag }}
            ${{ steps.ecr.outputs.registry }}/myapp:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64

Self-Hosted Registry — Harbor

Kendi registry'ni çalıştırmak istersen Harbor en popüler seçenektir: vulnerability scanning, RBAC, replication, garbage collection dahil.

# docker-compose.yml (Harbor basit kurulum)
# Gerçek kurulumda Harbor'ın kendi installer'ını kullan
# https://goharbor.io/docs/latest/install-config/

# Harbor'a push
docker login harbor.example.com
docker tag myapp:v1.0 harbor.example.com/myproject/myapp:v1.0
docker push harbor.example.com/myproject/myapp:v1.0
# Harbor installer ile kurulum
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
tar xzvf harbor-offline-installer-v2.10.0.tgz
cd harbor

# Konfigürasyon
cp harbor.yml.tmpl harbor.yml
# harbor.yml'de hostname, https, password ayarla

# Kurulum
./install.sh --with-trivy   # Trivy vulnerability scanner dahil

Security Scanning Pipeline

Her push'ta image'ı güvenlik taramasından geçirmek kritiktir.

Trivy ile Scanning

# Build sonrası Trivy scan
- name: Build image
  uses: docker/build-push-action@v5
  with:
    push: false
    load: true              # Lokalde tut (scan için)
    tags: myapp:scan

- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: myapp:scan
    format: 'table'
    exit-code: '1'                    # CRITICAL/HIGH varsa pipeline dursun
    severity: 'CRITICAL,HIGH'
    ignore-unfixed: true              # Fix edilemeyenleri atla

- name: Upload Trivy scan results (SARIF)
  if: always()
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: myapp:scan
    format: 'sarif'
    output: 'trivy-results.sarif'

- name: Upload to GitHub Security tab
  if: always()
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: 'trivy-results.sarif'

Docker Scout (Docker Hub)

- name: Docker Scout scan
  uses: docker/scout-action@v1
  with:
    command: cves,recommendations
    image: myapp:scan
    sarif-file: scout-results.sarif
    summary: true

Registry Temizlik Otomasyonu

Registry'ler zamanla şişer. Otomatik temizlik şart.

GHCR Temizlik

# .github/workflows/cleanup.yml
name: Cleanup old images

on:
  schedule:
    - cron: '0 2 * * 0'   # Her pazar gece 2'de

jobs:
  cleanup:
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - uses: actions/delete-package-versions@v5
        with:
          package-name: myapp
          package-type: container
          min-versions-to-keep: 10
          delete-only-untagged-versions: true

ECR Lifecycle Policy

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "Remove untagged images after 1 day",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 1
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 2,
      "description": "Keep only last 20 tagged images",
      "selection": {
        "tagStatus": "tagged",
        "tagPatternList": ["*"],
        "countType": "imageCountMoreThan",
        "countNumber": 20
      },
      "action": { "type": "expire" }
    }
  ]
}

Best Practices

Yap:

  • Immutable tag kullan (commit hash veya semver) — latest prodcution'da kullanma

  • Her push'ta security scan çalıştır (Trivy, Scout)

  • Build cache kullan — CI build sürelerini düşür

  • Multi-platform build yap (amd64 + arm64) — ARM sunucuları artıyor

  • Registry lifecycle policy ile eski image'ları temizle

  • Image signing ile güvenilirliği doğrula (Cosign)

  • docker/metadata-action ile tutarlı tag stratejisi uygula

  • Private registry'de RBAC ayarla — herkes her yere push edemesin

  • Artifact attestation ile build provenance kaydet (SBOM)

Yapma:

  • Secret'ları build arg olarak geçme — image history'de görünür

  • latest tag'ını production deployment'ta kullanma

  • Security scan'i atlamayı allow_failure: true ile normalleştirme

  • Tek büyük monolith image build etme — stage'le ve optimize et

  • Registry'yi temizlemeden bırakma — disk ve maliyet şişer

  • CI/CD'de docker build --no-cache kullanma — çok yavaşlatır

  • Push edilen image'ın tag'ını silip yeniden kullanma (tag mutability)

Yaygın Hatalar ve Çözümleri

1. "denied: access forbidden" — Push Hatası

# GHCR'da: Repository permissions kontrol et
# Settings → Packages → Package settings → Manage Actions access

# Docker Hub'da: Access Token yetkisi yeterli mi?
# Account → Security → Access Tokens → Read/Write

# ECR'da: IAM policy kontrol et
aws iam get-user-policy --user-name ci-user --policy-name ecr-push

2. Build Cache Çalışmıyor

# Dockerfile'da sıralama doğru mu?
# Sık değişen katmanlar SONA gelmeli

# ❌ Cache her seferinde bozulur
COPY . .
RUN npm install

# ✅ Cache verimli
COPY package*.json ./
RUN npm ci
COPY . .

3. Multi-Platform Build Yavaş

# QEMU emülasyonu yavaş — native runner kullan
# GitHub Actions: self-hosted ARM64 runner
# Veya: buildx ile remote builder

docker buildx create --name fast-builder \
    --driver docker-container \
    --platform linux/amd64
docker buildx create --name fast-builder \
    --append \
    --driver docker-container \
    --platform linux/arm64 \
    --node arm-builder \
    ssh://user@arm-server

Özet

  • Automated build ve push, her commit/tag'de otomatik image oluşturma ve dağıtma sürecidir

  • Registry seçimi: GitHub → GHCR, AWS → ECR, GCP → Artifact Registry, self-hosted → Harbor

  • Tag stratejisi: Semantic versioning + commit hash — latest production'da kullanma

  • Build cache (GHA, registry, inline) ile CI sürelerini %70-90 azalt

  • Security scanning (Trivy, Scout) her build'de zorunlu olmalı

  • Multi-platform build (amd64 + arm64) geleceğe hazırlık

  • Multi-registry push ile farklı ortamlara dağıt (Docker Hub + ECR + GHCR)

  • Registry temizliği lifecycle policy ile otomatikleştir — disk ve maliyet yönetimi

  • Image signing (Cosign) ile supply chain güvenliği sağla