AWS CodePipeline ile CI/CD Pipeline Kurulumu
Modern yazılım geliştirme süreçlerinde CI/CD pipeline’ı olmadan çalışmak, manuel olarak sunuculara dosya kopyalamakla eş değer. Yani mümkün ama gereksiz yere acı verici. AWS CodePipeline, Amazon’un bu problemi çözmek için sunduğu tam yönetilen sürekli teslim hizmeti. GitHub’dan kodu çek, test et, build al, deploy et. Teoride basit ama pratikte “nasıl doğru yapılandırırım?” sorusu biraz kafa karıştırıcı olabiliyor. Bu yazıda sıfırdan gerçek bir CodePipeline kurulumu yapacağız, yaygın hatalardan kaçınacağız ve production ortamına geçmeden önce dikkat etmeniz gereken noktaları ele alacağız.
AWS CodePipeline Nedir ve Neden Kullanmalısınız?
CodePipeline, AWS’nin CI/CD ekosisteminin merkezinde yer alıyor. Tek başına kullanmıyorsunuz genellikle. Yanında CodeCommit (kaynak kod deposu), CodeBuild (build ve test), CodeDeploy (dağıtım) ve bazen Elastic Beanstalk veya ECS gibi servislerle birlikte çalışıyor.
Neden CodePipeline? Jenkins kuruyorsunuz, bir EC2 instance’ı bakımını yapıyorsunuz, plugin güncellemeleriyle boğuşuyorsunuz. GitHub Actions’ı tercih edebilirsiniz ama AWS ekosisteminde çalışıyorsanız IAM entegrasyonu, VPC erişimi ve diğer AWS servisleriyle native entegrasyon açısından CodePipeline ciddi avantaj sağlıyor. Üstelik pipeline başına ücretlendirme yapılıyor, aktif pipeline olmasa bile ayda 1 dolar. Küçük ekipler için bu oldukça makul.
Ön Gereksinimler
Başlamadan önce şunlara ihtiyacınız var:
- AWS hesabı ve yeterli IAM izinleri
- AWS CLI kurulu ve yapılandırılmış
- Kaynak kodunuz GitHub, Bitbucket veya CodeCommit’te
- Temel AWS kavramlarına hakimiyet (S3, IAM, EC2)
# AWS CLI versiyonunu kontrol edin
aws --version
# Kimlik doğrulama kontrolü
aws sts get-caller-identity
# Gerekli IAM izinlerini kontrol etmek için
aws iam get-user
Mimariyi Anlamak
Gerçek dünya senaryosu olarak şunu düşünelim: Bir Node.js uygulamanız var, GitHub’da geliştiriyorsunuz. main branch’e her push yapıldığında otomatik olarak test edilmesini, Docker image olarak build edilmesini ve ECS Fargate üzerinde deploy edilmesini istiyorsunuz.
Pipeline akışı şu şekilde olacak:
- Source: GitHub repository, main branch
- Build: CodeBuild ile npm test ve Docker image build
- Deploy: ECS Fargate service güncellemesi
IAM Rolleri Oluşturmak
En çok göz ardı edilen kısım IAM rolleri. Yanlış yapılandırılmış IAM, pipeline’ın ortasında patlayan bombaya benziyor. Her servisin kendi rolü olmalı.
# CodePipeline için trust policy dosyası oluştur
cat > codepipeline-trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codepipeline.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
# CodePipeline IAM rolünü oluştur
aws iam create-role
--role-name CodePipelineServiceRole
--assume-role-policy-document file://codepipeline-trust-policy.json
# Gerekli policy'yi ekle
aws iam attach-role-policy
--role-name CodePipelineServiceRole
--policy-arn arn:aws:iam::aws:policy/AWSCodePipeline_FullAccess
CodeBuild için ayrı bir rol oluşturmanız gerekiyor:
# CodeBuild trust policy
cat > codebuild-trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
# CodeBuild rolü oluştur
aws iam create-role
--role-name CodeBuildServiceRole
--assume-role-policy-document file://codebuild-trust-policy.json
# ECR, S3 ve CloudWatch Logs için izinleri ekle
aws iam attach-role-policy
--role-name CodeBuildServiceRole
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
aws iam attach-role-policy
--role-name CodeBuildServiceRole
--policy-arn arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
Önemli not: Production ortamında FullAccess policy’leri kullanmayın. Least privilege prensibini uygulayın. Burada hız için gösteriyorum ama gerçek ortamda custom policy yazmanız şart.
S3 Artifact Bucket Oluşturma
CodePipeline, stage’ler arasında artifact’ları S3 üzerinden taşır. Bu bucket’ı önceden oluşturmanız gerekiyor.
# Artifact bucket oluştur (isim globally unique olmalı)
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
BUCKET_NAME="codepipeline-artifacts-${ACCOUNT_ID}-eu-west-1"
aws s3api create-bucket
--bucket $BUCKET_NAME
--region eu-west-1
--create-bucket-configuration LocationConstraint=eu-west-1
# Versioning aktif et
aws s3api put-bucket-versioning
--bucket $BUCKET_NAME
--versioning-configuration Status=Enabled
# Encryption aktif et
aws s3api put-bucket-encryption
--bucket $BUCKET_NAME
--server-side-encryption-configuration '{
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}
]
}'
echo "Artifact bucket hazır: $BUCKET_NAME"
CodeBuild Projesi Oluşturma
Build aşaması için önce buildspec.yml dosyasını projenizin kök dizininde oluşturmanız gerekiyor. Bu dosya CodeBuild’e ne yapacağını söylüyor.
# buildspec.yml - proje kök dizinine koyun
version: 0.2
env:
variables:
AWS_DEFAULT_REGION: eu-west-1
IMAGE_REPO_NAME: my-nodejs-app
parameter-store:
DOCKER_HUB_TOKEN: /myapp/docker-hub-token
phases:
install:
runtime-versions:
nodejs: 18
commands:
- echo "Bağımlılıklar yükleniyor..."
- npm ci
pre_build:
commands:
- echo "Testler çalıştırılıyor..."
- npm test
- echo "ECR'a login yapılıyor..."
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HASH:=latest}
build:
commands:
- echo "Docker image build ediliyor..."
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo "Image ECR'a push ediliyor..."
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
- echo "imagedefinitions.json oluşturuluyor..."
- printf '[{"name":"my-nodejs-app","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
cache:
paths:
- node_modules/**/*
Şimdi CodeBuild projesini AWS CLI ile oluşturalım:
# CodeBuild proje konfigürasyonu
cat > codebuild-project.json << EOF
{
"name": "my-nodejs-app-build",
"description": "Node.js uygulaması build projesi",
"source": {
"type": "CODEPIPELINE",
"buildspec": "buildspec.yml"
},
"artifacts": {
"type": "CODEPIPELINE"
},
"environment": {
"type": "LINUX_CONTAINER",
"image": "aws/codebuild/standard:7.0",
"computeType": "BUILD_GENERAL1_SMALL",
"privilegedMode": true,
"environmentVariables": [
{
"name": "AWS_ACCOUNT_ID",
"value": "${ACCOUNT_ID}",
"type": "PLAINTEXT"
}
]
},
"serviceRole": "arn:aws:iam::${ACCOUNT_ID}:role/CodeBuildServiceRole",
"cache": {
"type": "S3",
"location": "${BUCKET_NAME}/cache"
},
"logsConfig": {
"cloudWatchLogs": {
"status": "ENABLED",
"groupName": "/aws/codebuild/my-nodejs-app-build"
}
}
}
EOF
aws codebuild create-project
--cli-input-json file://codebuild-project.json
privilegedMode: true ayarına dikkat edin. Docker build yapacaksanız bu zorunlu. Güvenlik açısından değerlendirin, sadece ihtiyacınız varsa açın.
Pipeline Konfigürasyonu
Şimdi her şeyi bir araya getirelim. GitHub bağlantısı için önce CodeStar Connections üzerinden bir connection oluşturmanız gerekiyor. Bu adımı console üzerinden yapmanız daha kolay çünkü OAuth onayı gerektiriyor.
Console’da şu adımları izleyin:
- Developer Tools > Connections > Create connection
- GitHub seçin, bağlantıya isim verin
- GitHub hesabınızı yetkilendirin
- Connection ARN’ını kopyalayın
# Mevcut connections'ları listele
aws codestar-connections list-connections
--provider-type GitHub
--query 'Connections[*].{Name:ConnectionName,ARN:ConnectionArn,Status:ConnectionStatus}'
Connection hazır olduğunda pipeline’ı oluşturabiliriz:
# Pipeline konfigürasyon dosyası
cat > pipeline.json << EOF
{
"pipeline": {
"name": "my-nodejs-app-pipeline",
"roleArn": "arn:aws:iam::${ACCOUNT_ID}:role/CodePipelineServiceRole",
"artifactStore": {
"type": "S3",
"location": "${BUCKET_NAME}"
},
"stages": [
{
"name": "Source",
"actions": [
{
"name": "GitHub_Source",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "CodeStarSourceConnection",
"version": "1"
},
"configuration": {
"ConnectionArn": "arn:aws:codestar-connections:eu-west-1:${ACCOUNT_ID}:connection/BURAYA-CONNECTION-ID",
"FullRepositoryId": "kullaniciadim/my-nodejs-app",
"BranchName": "main",
"OutputArtifactFormat": "CODE_ZIP",
"DetectChanges": "true"
},
"outputArtifacts": [
{"name": "SourceArtifact"}
]
}
]
},
{
"name": "Build",
"actions": [
{
"name": "Build",
"actionTypeId": {
"category": "Build",
"owner": "AWS",
"provider": "CodeBuild",
"version": "1"
},
"configuration": {
"ProjectName": "my-nodejs-app-build"
},
"inputArtifacts": [
{"name": "SourceArtifact"}
],
"outputArtifacts": [
{"name": "BuildArtifact"}
]
}
]
},
{
"name": "Deploy",
"actions": [
{
"name": "Deploy_to_ECS",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "ECS",
"version": "1"
},
"configuration": {
"ClusterName": "my-ecs-cluster",
"ServiceName": "my-nodejs-service",
"FileName": "imagedefinitions.json"
},
"inputArtifacts": [
{"name": "BuildArtifact"}
]
}
]
}
]
}
}
EOF
# Pipeline'ı oluştur
aws codepipeline create-pipeline
--cli-input-json file://pipeline.json
Manuel Onay Adımı Eklemek
Production ortamında staging’den production’a geçerken mutlaka manuel onay isteyin. Otomatik deployment’ın keyfini production’da bozuk kod çıkarınca anlarsınız.
# Mevcut pipeline'a manuel onay stage'i eklemek için
# Önce mevcut pipeline konfigürasyonunu çekin
aws codepipeline get-pipeline
--name my-nodejs-app-pipeline
--query 'pipeline' > current-pipeline.json
# JSON dosyasını düzenleyip yeni stage ekleyin, sonra güncelleyin
# Staging Deploy'dan sonra, Production Deploy'dan önce şu stage'i ekleyin:
cat > approval-stage.json << 'EOF'
{
"name": "Approval",
"actions": [
{
"name": "ManualApproval",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1"
},
"configuration": {
"NotificationArn": "arn:aws:sns:eu-west-1:ACCOUNT_ID:deployment-approvals",
"CustomData": "Production deployment onayı bekleniyor. Staging ortamını kontrol edin.",
"ExternalEntityLink": "https://staging.myapp.com/health"
}
}
]
}
EOF
echo "Bu stage'i pipeline.json'a ekleyip aws codepipeline update-pipeline ile güncelleyin"
Pipeline Durumunu İzlemek
Pipeline’ı kurdunuz, şimdi ne oluyor? Monitoring önemli.
# Pipeline son çalışmalarını listele
aws codepipeline list-pipeline-executions
--pipeline-name my-nodejs-app-pipeline
--max-items 5
# Belirli bir execution'ın detaylarını gör
EXECUTION_ID="buraya-execution-id"
aws codepipeline get-pipeline-execution
--pipeline-name my-nodejs-app-pipeline
--pipeline-execution-id $EXECUTION_ID
# Mevcut pipeline durumunu anlık gör
aws codepipeline get-pipeline-state
--name my-nodejs-app-pipeline
--query 'stageStates[*].{Stage:stageName,Status:latestExecution.status}'
# Build loglarını CodeBuild üzerinden takip et
aws logs tail /aws/codebuild/my-nodejs-app-build
--follow
--format short
Yaygın Sorunlar ve Çözümleri
Artifact boyutu limiti: CodePipeline artifact’ları için 5 GB sınırı var. Node.js projelerinde node_modules dahil her şeyi paketlemeye çalışırsanız sorun yaşarsınız. buildspec.yml‘de artifact’larınızı dikkatli seçin.
IAM permission hatası: “User is not authorized to perform: codepipeline:GetPipeline” gibi hatalar alıyorsanız CodePipeline servis rolünün CrossAccount erişimine ihtiyacı olabilir.
Docker build başarısız: privilegedMode false ise “Cannot connect to the Docker daemon” hatası alırsınız. CodeBuild proje ayarlarından düzeltin.
Cache çalışmıyor: S3 cache için bucket politikası doğru olmalı. CodeBuild rolünün ilgili bucket’a erişimi olduğunu kontrol edin.
# Pipeline'ı manuel tetikleme (test için kullanışlı)
aws codepipeline start-pipeline-execution
--name my-nodejs-app-pipeline
# Başarısız stage'den devam ettirme
aws codepipeline retry-stage-execution
--pipeline-name my-nodejs-app-pipeline
--stage-name Build
--pipeline-execution-id $EXECUTION_ID
--retry-mode FAILED_ACTIONS
Maliyet Optimizasyonu
CodePipeline fiyatlandırması basit: Aktif pipeline başına ayda 1 dolar. Ama CodeBuild build dakikası ücreti ve S3 depolama maliyetleri de var.
- Test ortamları için pipeline’ları kullanmadığınız zaman silin
- CodeBuild cache’i S3’e yazarsanız storage maliyeti oluşur, lifecycle policy ekleyin
- Build dakikalarını azaltmak için cache’i etkin kullanın
BUILD_GENERAL1_SMALLgenellikle Node.js ve Python projeleri için yeterli, büyük Java projelerindeBUILD_GENERAL1_MEDIUMtercih edin
# S3 cache bucket'ına lifecycle policy ekle (30 günde bir temizle)
aws s3api put-bucket-lifecycle-configuration
--bucket $BUCKET_NAME
--lifecycle-configuration '{
"Rules": [
{
"ID": "cache-cleanup",
"Filter": {"Prefix": "cache/"},
"Status": "Enabled",
"Expiration": {"Days": 30}
}
]
}'
Notification Kurulumu
Pipeline başarısız olduğunda haberdar olmak istiyorsunuz. SNS + Slack entegrasyonu en yaygın yöntem.
# SNS topic oluştur
aws sns create-topic --name codepipeline-notifications
# Pipeline bildirimlerini aktif et
aws codestar-notifications create-notification-rule
--name "my-nodejs-app-notifications"
--detail-type FULL
--resource "arn:aws:codepipeline:eu-west-1:${ACCOUNT_ID}:my-nodejs-app-pipeline"
--event-type-ids
"codepipeline-pipeline-pipeline-execution-failed"
"codepipeline-pipeline-pipeline-execution-succeeded"
--targets "[{"TargetType":"SNS","TargetAddress":"arn:aws:sns:eu-west-1:${ACCOUNT_ID}:codepipeline-notifications"}]"
Sonuç
AWS CodePipeline, doğru yapılandırıldığında ekibinizin deployment süreçlerini büyük ölçüde otomatize eder. Ancak “yapılandırıldığında” kelimesinin altını çizmek gerekiyor. IAM rolleri, artifact yönetimi ve build spesifikasyonları en çok zaman harcadığınız yerler olacak.
Pratik önerilerim şunlar: Önce küçük başlayın. Tek bir servis için pipeline kurun, test edin, sonra diğerlerine genişletin. Her stage’de CloudWatch Logs’u açık tutun, başarısız build’larda bu loglar hayat kurtarır. Manuel onay stage’ini production deployments için atlamamanızı şiddetle tavsiye ederim. Hafta sonu patlayan bir production deploy’un kime nasıl döneceğini iyi biliyorum.
Son olarak, CodePipeline’ı Infrastructure as Code olarak yönetin. Terraform veya CloudFormation kullanarak pipeline konfigürasyonunuzu version control altında tutun. Bu yazıda CLI komutlarını gösterdim ama production’da Terraform modülü yazmak çok daha sürdürülebilir. Pipeline’ın kendisi de kod, öyle davranın.
