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 Plan | Private Repo | Cloud Entegrasyon | Kullanım |
|---|---|---|---|---|
| Docker Hub | 1 private repo | Sınırlı | Genel | Açık kaynak projeler |
| GitHub GHCR | Unlimited (public) | ✅ | GitHub Actions | GitHub projeleri |
| AWS ECR | 500MB free tier | ✅ | AWS ECS/EKS | AWS altyapısı |
| Google GCR/Artifact Registry | 500MB free tier | ✅ | GKE | GCP altyapısı |
| Azure ACR | Yok | ✅ | AKS | Azure altyapısı |
| GitLab Registry | 5GB | ✅ | GitLab CI | GitLab projeleri |
| Self-hosted (Harbor) | Unlimited | ✅ | Her yere | Tam 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 → HarborDocker 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:latestGitHub 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/arm64GitHub 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-stdinAWS 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:latestGitLab 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
- tagsTag 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:latestImage 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.3Build 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=maxRegistry 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=maxInline 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:latestCache Karşılaştırması
| Cache Türü | Hız | Boyut Limiti | Paylaşım | En İyi Kullanım |
|---|---|---|---|---|
| GHA | Hızlı | 10GB | Aynı repo | GitHub Actions |
| Registry | Orta | Unlimited | Her yerde | Multi-runner, cross-repo |
| Local | Çok hızlı | Disk | Aynı runner | Self-hosted runner |
| Inline | Yavaş | Image boyutu | Her yerde | Basit 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/arm64Self-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 dahilSecurity 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: trueRegistry 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: trueECR 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) —
latestprodcution'da kullanmaHer 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-actionile tutarlı tag stratejisi uygulaPrivate 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
latesttag'ını production deployment'ta kullanmaSecurity scan'i atlamayı
allow_failure: trueile normalleştirmeTek 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-cachekullanma — çok yavaşlatırPush 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-push2. 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 —
latestproduction'da kullanmaBuild 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
AI Asistan
Sorularını yanıtlamaya hazır