GCP Container Registry ile Docker Görüntü Yönetimi
Konteyner tabanlı bir altyapı kuruyorsanız, Docker imajlarınızı nerede sakladığınız ve nasıl yönettiğiniz düşündüğünüzden çok daha kritik bir konu. Docker Hub’ı başlangıçta herkes kullanır, ama production ortamına geçince rate limiting, güvenlik açıkları ve network gecikmesi gibi sorunlarla yüzleşmek zorunda kalırsınız. İşte bu noktada GCP Container Registry (ve onun modern halefi Artifact Registry) devreye giriyor. Bu yazıda GCR’yi A’dan Z’ye ele alacağız: kurulumdan temizlik otomasyonuna kadar her şeyi gerçek senaryolarla inceleyeceğiz.
GCP Container Registry Nedir ve Neden Kullanmalısınız?
GCP Container Registry, Google’ın yönetilen Docker imaj deposu hizmetidir. Arka planda Google Cloud Storage kullanır, yani imajlarınız aslında GCS bucket’larında saklanır. Bu mimari beraberinde bazı güzel avantajlar getirir.
Neden GCR tercih edilmeli?
- Düşük gecikme: GKE veya Cloud Run kullanıyorsanız, imajlar aynı ağ içinden çekilir
- IAM entegrasyonu: Google Cloud’un kimlik yönetimiyle doğrudan bütünleşir, ayrı kullanıcı yönetimi gerekmez
- Güvenlik taraması: Vulnerabilities Container Analysis API ile otomatik taranır
- Rate limiting yok: Docker Hub’ın anonymous pull sınırlamalarından kurtulursunuz
- Özel ağ erişimi: VPC içinden private erişim mümkündür
Bir not olarak belirtelim: Google, yeni projeler için Artifact Registry’ye geçiş tavsiye ediyor. Artifact Registry, GCR’nin bir üst seti ve daha fazla özellik sunuyor. Ancak GCR hâlâ yaygın kullanımda ve Artifact Registry aynı docker.pkg.dev formatını desteklediği için aralarında geçiş oldukça kolay.
Kurulum ve Kimlik Doğrulama
Önce temel araçların kurulu olduğunu doğrulayalım.
# gcloud CLI kurulumunu kontrol et
gcloud version
# Docker kurulumunu kontrol et
docker version
# Aktif proje kontrolü
gcloud config get-value project
GCR kullanmak için önce Container Registry API’yi etkinleştirmeniz gerekiyor.
# API'yi etkinleştir
gcloud services enable containerregistry.googleapis.com
# Artifact Registry kullanacaksanız (önerilen)
gcloud services enable artifactregistry.googleapis.com
# Container Analysis için (güvenlik taraması)
gcloud services enable containerscanning.googleapis.com
Şimdi en kritik adıma geliyoruz: Docker’ın GCR’ye kimlik doğrulaması. Bunun birkaç yolu var.
# En basit yol: gcloud credential helper
gcloud auth configure-docker
# Belirli bölgeler için (Artifact Registry)
gcloud auth configure-docker us-central1-docker.pkg.dev,europe-west1-docker.pkg.dev
# Servis hesabı ile kimlik doğrulama (CI/CD için tercih edilir)
gcloud auth activate-service-account --key-file=/path/to/service-account.json
gcloud auth configure-docker
# Docker config'e bakarak doğrula
cat ~/.docker/config.json
Çıktıda gcr.io için credHelpers veya auths bölümünü görmelisiniz. Görmiyorsanız bir şeyler ters gitmiş demektir.
Servis Hesabı Oluşturma (CI/CD İçin)
Production ortamında kendi kullanıcı hesabınızla değil, servis hesabıyla çalışmalısınız. Bu hem güvenlik hem de otomasyon açısından doğru yaklaşım.
# Servis hesabı oluştur
gcloud iam service-accounts create gcr-pusher
--display-name="GCR Image Pusher"
--description="CI/CD pipeline için imaj push/pull yetkisi"
# Değişkene ata
SA_EMAIL="gcr-pusher@$(gcloud config get-value project).iam.gserviceaccount.com"
# Storage Admin rolü ver (GCR için gerekli)
gcloud projects add-iam-policy-binding $(gcloud config get-value project)
--member="serviceAccount:${SA_EMAIL}"
--role="roles/storage.admin"
# JSON key oluştur
gcloud iam service-accounts keys create gcr-pusher-key.json
--iam-account=${SA_EMAIL}
# Bu key'i CI/CD sistemine secret olarak ekleyin!
Önemli: Bu JSON dosyasını kesinlikle Git reponuza commit etmeyin. CI/CD sisteminizin secret yöneticisine ekleyin.
İmaj Push ve Pull İşlemleri
Kimlik doğrulama tamam. Şimdi asıl işe gelelim.
GCR imaj adları şu formatı takip eder: [HOSTNAME]/[PROJECT-ID]/[IMAGE]:[TAG]
Hostname seçenekleri:
- gcr.io: ABD’de barındırılır
- us.gcr.io: ABD bölgesi
- eu.gcr.io: Avrupa bölgesi
- asia.gcr.io: Asya bölgesi
# Örnek uygulama için Dockerfile
cat > Dockerfile << 'EOF'
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["python", "app.py"]
EOF
# İmajı build et
docker build -t my-web-app:v1.0.0 .
# GCR için tag'le
PROJECT_ID=$(gcloud config get-value project)
docker tag my-web-app:v1.0.0 gcr.io/${PROJECT_ID}/my-web-app:v1.0.0
# Push et
docker push gcr.io/${PROJECT_ID}/my-web-app:v1.0.0
# Latest tag'i de push et
docker tag my-web-app:v1.0.0 gcr.io/${PROJECT_ID}/my-web-app:latest
docker push gcr.io/${PROJECT_ID}/my-web-app:latest
Pull işlemi de aynı mantıkla çalışıyor:
# İmajı çek
docker pull gcr.io/${PROJECT_ID}/my-web-app:v1.0.0
# Farklı bir makinede (servis hesabı ile)
docker login -u _json_key -p "$(cat gcr-pusher-key.json)" https://gcr.io
docker pull gcr.io/${PROJECT_ID}/my-web-app:v1.0.0
İmajları Listeleme ve Yönetme
gcloud CLI, imaj yönetimi için güçlü araçlar sunuyor.
# Tüm imajları listele
gcloud container images list --repository=gcr.io/${PROJECT_ID}
# Belirli imajın tag'lerini listele
gcloud container images list-tags gcr.io/${PROJECT_ID}/my-web-app
# Daha fazla bilgi ile
gcloud container images list-tags gcr.io/${PROJECT_ID}/my-web-app
--format="table(digest,tags,timestamp)"
--limit=20
# Belirli imajın detaylarını gör
gcloud container images describe gcr.io/${PROJECT_ID}/my-web-app:v1.0.0
Güvenlik tarama sonuçlarına bakabilirsiniz:
# Vulnerabilities listesi
gcloud container images describe gcr.io/${PROJECT_ID}/my-web-app:v1.0.0
--format='value(image_summary.vulnerability_count)'
# Detaylı güvenlik raporu (Container Analysis API etkinse)
gcloud artifacts docker images describe
gcr.io/${PROJECT_ID}/my-web-app:v1.0.0
--show-package-vulnerability
Gerçek Dünya Senaryosu: Multi-Stage Build ile Optimize İmaj
Production’da imaj boyutu önemli. Hem storage maliyetini etkiler hem de pull süresini. Multi-stage build ile bunu optimize edelim.
# Dockerfile.production
# Stage 1: Build
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# Stage 2: Final
FROM alpine:3.18
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
# Build ve push scripti
#!/bin/bash
set -euo pipefail
PROJECT_ID=$(gcloud config get-value project)
IMAGE_NAME="gcr.io/${PROJECT_ID}/go-api"
VERSION=$(git rev-parse --short HEAD)
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
echo "Building ${IMAGE_NAME}:${VERSION}"
# Build
docker build
-f Dockerfile.production
-t "${IMAGE_NAME}:${VERSION}"
-t "${IMAGE_NAME}:${TIMESTAMP}"
-t "${IMAGE_NAME}:latest"
--label "git-commit=${VERSION}"
--label "build-date=${TIMESTAMP}"
.
# Push all tags
docker push "${IMAGE_NAME}:${VERSION}"
docker push "${IMAGE_NAME}:${TIMESTAMP}"
docker push "${IMAGE_NAME}:latest"
echo "Successfully pushed ${IMAGE_NAME}:${VERSION}"
Lifecycle Policy ve Otomatik Temizlik
Bu konu sysadminlerin çoğunlukla ihmal ettiği ama faturanın şişmesinin temel sebebi olan bir alan. GCR arka planda GCS kullandığı için her imaj storage maliyeti oluşturuyor.
# Mevcut depolama kullanımını kontrol et
gsutil du -sh gs://artifacts.${PROJECT_ID}.appspot.com/
# Detaylı breakdown
gsutil du -s gs://artifacts.${PROJECT_ID}.appspot.com/**
Eski imajları temizlemek için bir script yazalım:
#!/bin/bash
# cleanup-old-images.sh
# Son N tag'i koru, gerisini sil
set -euo pipefail
PROJECT_ID="${1:-$(gcloud config get-value project)}"
IMAGE_NAME="${2:-my-web-app}"
KEEP_COUNT="${3:-10}"
FULL_IMAGE="gcr.io/${PROJECT_ID}/${IMAGE_NAME}"
echo "Cleaning up ${FULL_IMAGE}, keeping last ${KEEP_COUNT} images..."
# Tüm digest'leri timestamp'e göre sırala, en yenileri koru
DIGESTS_TO_DELETE=$(gcloud container images list-tags "${FULL_IMAGE}"
--sort-by="~TIMESTAMP"
--format="get(digest)"
| tail -n +$((KEEP_COUNT + 1)))
if [ -z "${DIGESTS_TO_DELETE}" ]; then
echo "No images to delete. Current count is within limit."
exit 0
fi
echo "Deleting old images..."
while IFS= read -r digest; do
if [ -n "${digest}" ]; then
echo "Deleting ${FULL_IMAGE}@${digest}"
gcloud container images delete
"${FULL_IMAGE}@${digest}"
--quiet
--force-delete-tags
fi
done <<< "${DIGESTS_TO_DELETE}"
echo "Cleanup complete!"
Bu scripti Cloud Scheduler ile otomatikleştirebilirsiniz:
# Cloud Scheduler job oluştur (haftalık temizlik)
gcloud scheduler jobs create http weekly-gcr-cleanup
--schedule="0 2 * * 0"
--uri="https://cloudfunctions.googleapis.com/v1/projects/${PROJECT_ID}/locations/us-central1/functions/gcr-cleanup:call"
--message-body='{"project": "my-project", "image": "my-web-app", "keep": 10}'
--time-zone="Europe/Istanbul"
CI/CD Entegrasyonu: GitHub Actions Örneği
Gerçek hayatta imajları elle push etmezsiniz. CI/CD pipeline’ınız bunu halleder. GitHub Actions örneği:
# .github/workflows/build-and-push.yml
name: Build and Push to GCR
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
IMAGE_NAME: my-web-app
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Configure Docker for GCR
run: gcloud auth configure-docker --quiet
- name: Extract metadata
id: meta
run: |
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "timestamp=$(date +%Y%m%d-%H%M%S)" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: |
gcr.io/${{ env.PROJECT_ID }}/${{ env.IMAGE_NAME }}:latest
gcr.io/${{ env.PROJECT_ID }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.sha_short }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Clean up old images
if: github.event_name != 'pull_request'
run: |
bash scripts/cleanup-old-images.sh ${{ env.PROJECT_ID }} ${{ env.IMAGE_NAME }} 15
Artifact Registry’ye Geçiş
Google, GCR yerine Artifact Registry kullanımını teşvik ediyor. Eğer yeni bir proje başlatıyorsanız doğrudan Artifact Registry’ye gitmenizi öneririm.
# Artifact Registry repository oluştur
gcloud artifacts repositories create docker-repo
--repository-format=docker
--location=europe-west1
--description="Production Docker imajları"
# Repository listele
gcloud artifacts repositories list
# Docker authentication
gcloud auth configure-docker europe-west1-docker.pkg.dev
# Yeni imaj formatı: REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG
docker tag my-web-app:v1.0.0
europe-west1-docker.pkg.dev/${PROJECT_ID}/docker-repo/my-web-app:v1.0.0
docker push europe-west1-docker.pkg.dev/${PROJECT_ID}/docker-repo/my-web-app:v1.0.0
Artifact Registry’nin GCR’den farkları:
- Çoklu format desteği: Docker yanı sıra Maven, npm, Python, Helm chart’ları da saklayabilirsiniz
- Bölge kontrolü: İmajlarınızın hangi fiziksel bölgede saklandığını seçebilirsiniz
- Gelişmiş IAM: Repository seviyesinde daha granular erişim kontrolü
- CMEK desteği: Müşteri tarafından yönetilen şifreleme anahtarları
GCS Bucket Erişim Yönetimi
GCR imajları artifacts.PROJECT_ID.appspot.com bucket’ında saklanır. Bu bucket’a doğrudan erişimi yönetebilirsiniz.
# Bucket adını bul
gsutil ls gs://artifacts.${PROJECT_ID}.appspot.com
# Belirli bir kullanıcıya okuma yetkisi ver
gsutil iam ch user:[email protected]:objectViewer
gs://artifacts.${PROJECT_ID}.appspot.com
# Servis hesabına push yetkisi ver
gsutil iam ch serviceAccount:ci-runner@${PROJECT_ID}.iam.gserviceaccount.com:objectAdmin
gs://artifacts.${PROJECT_ID}.appspot.com
# Mevcut IAM politikasını görüntüle
gsutil iam get gs://artifacts.${PROJECT_ID}.appspot.com
Güvenlik Best Practices
Sysadmin olarak güvenliği göz ardı etmek lüksümüz yok. GCR için uygulamanız gereken temel güvenlik önlemleri:
İmaj Tarama Otomasyonu
# Her push sonrası otomatik tarama için Cloud Build trigger
# cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA']
# Güvenlik taraması bekle
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: 'bash'
args:
- '-c'
- |
gcloud artifacts docker images scan
gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA
--format='value(response.scan)' > /workspace/scan_id.txt
# Critical vulnerability varsa build'i başarısız yap
CRITICAL=$(gcloud artifacts docker images list-vulnerabilities
$(cat /workspace/scan_id.txt)
--format='value(vulnerability.effectiveSeverity)'
| grep -c CRITICAL || true)
if [ "$CRITICAL" -gt "0" ]; then
echo "HATA: ${CRITICAL} kritik güvenlik açığı bulundu!"
exit 1
fi
images:
- 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA'
Güvenlik için dikkat etmeniz gereken diğer noktalar:
- Minimal base image kullanın:
FROM scratchveyadistrolessimajlar saldırı yüzeyini azaltır - Root kullanıcısından kaçının: Dockerfile’da
USER nonrootekleyin - Servis hesabı key rotation: Key’lerinizi düzenli olarak rotate edin
- Binary Authorization: GKE’de sadece imzalı imajların çalışmasına izin verin
- Private registry: Mümkünse VPC Service Controls ile dış erişimi engelleyin
Maliyet Optimizasyonu
GCR’nin maliyeti iki bileşenden oluşur: storage ve network egress. Storage, GCS fiyatlandırmasını takip eder (yaklaşık $0.02/GB/ay). Network egress ise imajların nereye çekildiğine göre değişir.
Maliyeti düşürmek için:
- Aynı bölgede çalıştırın: GKE cluster’ınızla aynı bölgedeki GCR endpoint’ini kullanın. Bölge içi traffic ücretsizdir
- Düzenli temizlik yapın: Yukarıdaki cleanup scriptini işletin
- Layer caching kullanın: Sık değişmeyen katmanları Dockerfile’ın üst kısımlarına taşıyın
- Multi-stage build: Gereksiz build araçlarını final imaja dahil etmeyin
- Compress layer’lar:
.dockerignoredosyasıyla gereksiz dosyaları build context’inden çıkarın
# .dockerignore örneği
.git
.gitignore
*.md
tests/
docs/
.env
.env.*
node_modules/
__pycache__/
*.pyc
.DS_Store
Troubleshooting: Sık Karşılaşılan Sorunlar
“denied: Token exchange failed” hatası
# Token'ı yenile
gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://gcr.io
# Ya da credential helper'ı yeniden kur
gcloud auth configure-docker --quiet
“Repository does not exist” hatası
# Projeyi doğrula
gcloud config list
# API'nin aktif olduğunu kontrol et
gcloud services list --enabled | grep containerregistry
# Bucket'ın var olduğunu kontrol et (ilk push'ta oluşur)
gsutil ls gs://artifacts.${PROJECT_ID}.appspot.com 2>/dev/null || echo "Bucket henüz oluşmamış"
Yavaş push/pull sorunu
# Hangi bölgeden çektiğinizi kontrol edin
# GKE node'undaki bölgeyi görün
gcloud container clusters describe CLUSTER_NAME --format='value(location)'
# O bölgeye uygun endpoint kullanın
# us-central1 -> us.gcr.io
# europe-west1 -> eu.gcr.io
# asia-northeast1 -> asia.gcr.io
Sonuç
GCP Container Registry, özellikle GCP ekosisteminde çalışıyorsanız Docker imaj yönetimi için oldukça sağlam bir çözüm. IAM entegrasyonu, güvenlik tarama yetenekleri ve GKE ile düşük gecikmeli iletişim en büyük avantajları. Ancak birkaç şeyi aklınızın bir köşesinde tutun.
İlk olarak, yeni projeler için direkt Artifact Registry’ye geçin. GCR hâlâ çalışıyor ama Google yatırımını Artifact Registry’ye kaydırdı. İkincisi, imaj temizliğini ihmal etmeyin. Aylık birkaç yüz dolar storage faturasıyla karşılaşmak istemiyorsanız lifecycle policy veya otomatik temizlik scriptleri şart. Üçüncüsü, production’da mutlaka servis hesabı kullanın ve bu hesaplara minimum gerekli yetkiyi verin.
Güvenlik taramasını da pipeline’ınıza entegre etmenizi şiddetle tavsiye ederim. Kritik açık içeren bir imajı production’a deploy etmek, onlarca yıllık sysadmin kariyerini bir gecede mahvedebilir. Binary Authorization ile bunu zorunlu hale getirmek, defense-in-depth stratejinizin önemli bir parçası olabilir.
Son olarak, buraya kadar anlattıklarımı uyguladıktan sonra bir de faturanıza bakın. İlk aydan itibaren ciddi bir tasarruf görmeniz lazım.
