GitLab Runner Kurulumu ve Pipeline Yapılandırması
Üç yıl önce bir müşterimizin CI/CD altyapısını sıfırdan kuruyordum. Ekip GitHub Actions kullanmak istiyordu ama şirket politikası gereği her şeyin on-premise kalması gerekiyordu. GitLab’a geçtik, runner kurulumuna başladım ve o gün öğrendiklerimi bugün hala kullanıyorum. Bu yazıda hem temel kurulumu hem de gerçek bir pipeline’ı nasıl yapılandıracağınızı anlatacağım.
GitLab Runner Nedir ve Neden Önemli?
GitLab Runner, GitLab CI/CD pipeline’larınızın iş yükünü üstlenen, GitLab sunucusundan bağımsız çalışan bir ajan yazılımıdır. .gitlab-ci.yml dosyanızda tanımladığınız her job, bir runner tarafından execute edilir. Runner’ı GitLab’ın işçisi olarak düşünebilirsiniz: GitLab koordinatör rolünü üstlenirken runner fiilen kodu derler, test çalıştırır, Docker image build eder.
Runner’ların üç farklı scope’u var:
- Shared runners: GitLab.com’un sunduğu, tüm projelerin ortak kullandığı runner’lar
- Group runners: Bir grup içindeki tüm projelerin kullanabildiği runner’lar
- Project runners: Sadece tek bir projeye özgü runner’lar
On-premise kurulumda çoğunlukla project veya group runner kurarsınız. Shared runner mantığını kendi altyapınızda da uygulayabilirsiniz ama bunu daha sonra konuşalım.
Kurulum Öncesi Hazırlık
Runner’ı kuracağınız makinenin gereksinimleri projenize göre değişir. Sadece shell komutları çalıştıracaksanız minimal bir VM yeterli. Docker build yapacaksanız disk alanına ve CPU’ya dikkat etmeniz gerekiyor.
Benim önerim: Production runner’larını GitLab sunucusundan ayrı bir makineye kurun. GitLab sunucusu ile runner’ı aynı makineye koymak kısa vadede pratik görünse de büyük bir build geldiğinde GitLab’ın kendisi yanıt veremez hale gelebilir.
Runner makinesinde şunları kontrol edin:
- Git kurulu olmalı (runner her job öncesinde repo’yu klonlar)
- Eğer Docker executor kullanacaksanız Docker daemon çalışıyor olmalı
- GitLab sunucusuna network erişimi olmalı
- Gerekli portlara güvenlik duvarı izin vermeli (HTTPS için 443)
Linux’ta GitLab Runner Kurulumu
Debian/Ubuntu tabanlı sistemler için resmi repo’yu ekleyip kurulumu yapabilirsiniz:
# GitLab resmi paket deposunu ekle
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
# Runner'ı kur
sudo apt-get install gitlab-runner
# Servis durumunu kontrol et
sudo systemctl status gitlab-runner
RHEL/CentOS/AlmaLinux için:
# Repo ekle
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash
# Kur
sudo dnf install gitlab-runner
# Servisi başlat ve enable et
sudo systemctl enable --now gitlab-runner
Kurulum sonrası gitlab-runner kullanıcısı otomatik oluşturulur. Bu kullanıcının hangi kaynaklara erişeceğini şimdiden düşünmeye başlayın. Docker komutlarını çalıştırması gerekiyorsa docker grubuna eklemeniz gerekecek.
# gitlab-runner kullanıcısını docker grubuna ekle
sudo usermod -aG docker gitlab-runner
# Değişikliğin aktif olması için servisi yeniden başlat
sudo systemctl restart gitlab-runner
Runner’ı GitLab’a Kayıt Ettirme
Bu adım çoğu kişinin takıldığı yer. GitLab arayüzünden registration token almanız gerekiyor. GitLab 15.x ve sonrasında token alma yeri değişti, dikkat edin.
GitLab 16.x ve sonrası için: Settings > CI/CD > Runners bölümüne gidin. “New project runner” butonuna tıklayın, etiketler ve diğer ayarları girin, size bir authentication token verilecek.
Eski yöntem (deprecated ama hala çalışıyor): Settings > CI/CD > Runners > Registration token alanından kopyalayın.
Token’ı aldıktan sonra:
# Interactive kayıt (öğrenme aşaması için iyi)
sudo gitlab-runner register
# Tek satırda kayıt (otomasyon için)
sudo gitlab-runner register
--non-interactive
--url "https://gitlab.sirketiniz.com/"
--token "glrt-xxxxxxxxxxxx"
--executor "docker"
--docker-image "alpine:latest"
--description "production-docker-runner"
--tag-list "docker,production"
--run-untagged="false"
--locked="false"
Executor seçimi kritik bir karar. En çok kullanılanlar:
- shell: Komutları doğrudan runner makinesinin shell’inde çalıştırır. Basit ama riskli, job’lar izole değil
- docker: Her job için temiz bir container başlatır. En çok tercih edilen yöntem
- docker+machine: Talep üzerine bulut VM’i başlatır, enterprise senaryolarda işe yarar
- kubernetes: Job’ları K8s pod’ları olarak çalıştırır, büyük ölçekli yapılar için ideal
Runner Yapılandırması: config.toml
Runner kaydedildikten sonra /etc/gitlab-runner/config.toml dosyası oluşur. Bu dosyayı iyi anlarsanız runner’ı istediğiniz gibi şekillendirebilirsiniz.
concurrent = 4
check_interval = 0
shutdown_timeout = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "production-docker-runner"
url = "https://gitlab.sirketiniz.com/"
id = 12
token = "glrt-xxxxxxxxxxxx"
token_obtained_at = 2024-01-15T10:00:00Z
token_expires_at = 0001-01-01T00:00:00Z
executor = "docker"
[runners.custom_build_dir]
[runners.cache]
MaxUploadedArchiveSize = 0
[runners.docker]
tls_verify = false
image = "alpine:latest"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
network_mtu = 0
Burada dikkat etmeniz gereken birkaç parametre var:
- concurrent: Aynı anda kaç job çalışacağını belirler. Runner makinenizin CPU ve RAM kapasitesine göre ayarlayın
- privileged: Docker içinde Docker build yapmanız gerekiyorsa
trueyapmanız gerekir, ama güvenlik riskini göz önünde bulundurun - volumes: Host’tan container’a mount edilecek dizinler. Cache dizini buraya eklenmeli
İlk .gitlab-ci.yml Dosyası
Artık runner hazır, pipeline yazabiliriz. Basit bir Node.js projesi için örnek:
# .gitlab-ci.yml
image: node:18-alpine
stages:
- install
- test
- build
- deploy
variables:
NODE_ENV: "production"
CACHE_DIR: ".npm"
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
- node_modules/
install_dependencies:
stage: install
script:
- npm ci --cache $CACHE_DIR --prefer-offline
artifacts:
paths:
- node_modules/
expire_in: 1 hour
run_tests:
stage: test
script:
- npm run test:unit
- npm run test:integration
coverage: '/Liness*:s*(d+.?d*)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
build_app:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
only:
- main
- develop
deploy_staging:
stage: deploy
image: alpine:latest
script:
- apk add --no-cache openssh-client rsync
- eval $(ssh-agent -s)
- echo "$STAGING_SSH_PRIVATE_KEY" | tr -d 'r' | ssh-add -
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- rsync -avz --delete dist/ [email protected]:/var/www/uygulama/
environment:
name: staging
url: https://staging.sirket.com
only:
- develop
Bu pipeline’da dikkat edin: cache bloğu node_modules ve npm cache’ini saklar, böylece her build’de sıfırdan indirme yapmaz. artifacts ile stage’ler arası dosya geçişini yönetiyoruz.
Gerçek Dünya Senaryosu: Docker Image Build ve Push
Çoğu ekibin ihtiyacı olan şey: kod commit edildiğinde otomatik Docker image build edip registry’e push etmek.
# Docker build ve push pipeline'ı
stages:
- test
- build
- push
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
LATEST_TAG: $CI_REGISTRY_IMAGE:latest
.docker_auth: &docker_auth
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
unit_tests:
stage: test
image: python:3.11-slim
script:
- pip install -r requirements-dev.txt
- pytest tests/unit/ -v --junitxml=report.xml
artifacts:
reports:
junit: report.xml
build_image:
stage: build
image: docker:24-dind
services:
- docker:24-dind
<<: *docker_auth
script:
- docker build
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
--build-arg VCS_REF=$CI_COMMIT_SHA
--build-arg VERSION=$CI_COMMIT_TAG
-t $IMAGE_TAG
-t $LATEST_TAG .
- docker push $IMAGE_TAG
- docker push $LATEST_TAG
only:
- main
- tags
Burada docker:24-dind (Docker in Docker) kullandık. Runner’da privileged = true olması gerekiyor bu yaklaşım için. Alternatif olarak Kaniko veya Buildah kullanabilirsiniz, privileged gerektirmiyor.
Cache Stratejileri ve Optimizasyon
Pipeline’ların yavaş çalışması en büyük şikayetlerden biri. Cache’i doğru kullanmak build sürenizi ciddi ölçüde kısaltır.
# Gelişmiş cache stratejisi
cache:
- key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull-push
- key: pip-$CI_COMMIT_REF_SLUG
paths:
- .pip-cache/
policy: pull
# Sadece cache'i güncelleyen özel job
update_cache:
stage: .pre
script:
- npm ci
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: push
only:
changes:
- package-lock.json
policy: pull kullandığınızda job cache’i yazar ama okumaz. pull-push hem okur hem yazar. only: changes ile sadece belirli dosyalar değiştiğinde cache güncelleme job’ını çalıştırabilirsiniz.
Runner Etiketleri ve Job Yönlendirme
Birden fazla runner’ınız varsa hangi job’ın hangi runner’da çalışacağını etiketlerle kontrol edersiniz. Örneğin GPU gerektiren ML job’larını sadece GPU’lu makinelere yönlendirebilirsiniz.
# Farklı runner'lara iş yönlendirme
stages:
- build
- test
- ml_train
build_backend:
stage: build
tags:
- docker
- linux
script:
- ./gradlew build
integration_tests:
stage: test
tags:
- docker
- high-memory
script:
- ./gradlew integrationTest
resource_group: integration_tests
train_model:
stage: ml_train
tags:
- gpu
- cuda
script:
- python train.py --epochs 100
timeout: 4 hours
resource_group kullanımına dikkat edin: aynı anda sadece bir job o resource group’ta çalışabilir. Integration testleri gibi paylaşılan kaynakları kullanan işler için hayat kurtarıcı.
Gizli Değişken Yönetimi
Pipeline içinde şifre, API anahtarı gibi hassas bilgileri asla .gitlab-ci.yml dosyasına yazmayın. GitLab’ın CI/CD Variables özelliğini kullanın.
Settings > CI/CD > Variables bölümünden değişken ekleyebilirsiniz. Değişken tiplerini doğru kullanın:
- Variable: Normal metin değişken, loglarda maskelenir
- File: İçerik geçici bir dosyaya yazılır, dosyanın path’i değişkene atanır (kuberentes config, SSL sertifikası için ideal)
deploy_production:
stage: deploy
script:
# File tipinde değişken kullanımı
- export KUBECONFIG=$KUBE_CONFIG_FILE
- kubectl apply -f k8s/
# Masked değişken kullanımı
- echo $DATABASE_URL | grep -o 'postgresql://[^@]*@' # şifreyi göstermez
# Vault entegrasyonu varsa
- export DB_PASS=$(vault kv get -field=password secret/myapp/db)
environment:
name: production
when: manual
only:
- main
when: manual kullanımı production deploy’lar için önerim. Otomatik deploy staging’e kadar gider, production için birinin onaylaması gerekir.
Runner Performans Sorunlarını Gidermek
Sahadan bir kaç senaryo: Runner’ınız job almıyor, queue’da iş birikmiş. İlk kontrol noktalarınız:
# Runner durumunu kontrol et
sudo gitlab-runner status
# Runner'ın GitLab ile iletişimini test et
sudo gitlab-runner verify
# Detaylı loglara bak
sudo journalctl -u gitlab-runner -f
# Hangi job'ların çalıştığını gör
sudo gitlab-runner list
Disk dolması yaygın bir sorun. Docker executor kullanıyorsanız eski image’lar ve volume’lar birikir:
# Kullanılmayan Docker kaynaklarını temizle
docker system prune -af --volumes
# Cron job ile otomatik temizlik ekle
echo "0 2 * * 0 docker system prune -af --volumes >> /var/log/docker-cleanup.log 2>&1" | sudo crontab -u gitlab-runner -
Runner’ın concurrent değeri yanlış ayarlanmışsa job’lar sıraya girer. top veya htop ile CPU ve memory kullanımını izleyin, concurrent değerini buna göre ayarlayın.
Çoklu Ortam için Pipeline Şablonu
Gerçek bir proje genellikle dev, staging ve production ortamlarına sahiptir. Bunu temiz bir şekilde yönetmek için:
# Yeniden kullanılabilir job şablonları
.deploy_template: &deploy_config
image: bitnami/kubectl:latest
before_script:
- kubectl config use-context $KUBE_CONTEXT
script:
- envsubst < k8s/deployment.yaml | kubectl apply -f -
- kubectl rollout status deployment/$APP_NAME -n $NAMESPACE
deploy_dev:
<<: *deploy_config
stage: deploy
variables:
KUBE_CONTEXT: "gitlab-agent/dev-cluster"
NAMESPACE: "development"
APP_NAME: "myapp-dev"
REPLICAS: "1"
environment:
name: development
url: https://dev.sirket.com
only:
- develop
deploy_staging:
<<: *deploy_config
stage: deploy
variables:
KUBE_CONTEXT: "gitlab-agent/staging-cluster"
NAMESPACE: "staging"
APP_NAME: "myapp-staging"
REPLICAS: "2"
environment:
name: staging
url: https://staging.sirket.com
only:
- main
deploy_production:
<<: *deploy_config
stage: deploy
variables:
KUBE_CONTEXT: "gitlab-agent/prod-cluster"
NAMESPACE: "production"
APP_NAME: "myapp"
REPLICAS: "5"
environment:
name: production
url: https://sirket.com
when: manual
only:
- tags
YAML anchor (&deploy_config) ve alias (<<: *deploy_config) kullanımı kod tekrarını önler. Her ortam kendi değişkenlerini override eder, temel deploy mantığı paylaşılır.
Güvenlik Önerileri
Runner güvenliği konusu genellikle atlanıyor. Birkaç kritik nokta:
- Shell executor kullanıyorsanız
gitlab-runnerkullanıcısını sudo yetkisiyle bırakmayın - Privileged mode’u sadece gerçekten gerektiğinde açın, mümkünse Kaniko veya Buildah alternatiflerini değerlendirin
- Runner token’larını düzenli olarak rotate edin, Settings > CI/CD > Runners üzerinden yapabilirsiniz
protectedflag’ini production ortam değişkenlerine mutlaka ekleyin, sadece protected branch’lerde görünür olsun- Runner makinesine SSH erişimini kısıtlayın, sadece sistem yöneticileri erişebilmeli
- Pipeline loglarını düzenli gözden geçirin, hassas bilgi sızıntısı olup olmadığını kontrol edin
Sonuç
GitLab Runner kurulumu teknik olarak basit görünebilir ama iyi bir CI/CD altyapısı inşa etmek detaylara dikkat gerektirir. Executor seçimi, cache stratejisi, gizli değişken yönetimi ve güvenlik konfigürasyonu, pipeline’larınızın hem hızlı hem de güvenilir çalışmasını doğrudan etkiler.
Başlangıç için Docker executor ile single runner’dan başlayın. Ekibinizin CI/CD kültürü olgunlaştıkça group runner’lara, gerekirse Kubernetes executor’a geçiş yapabilirsiniz. Her değişikliği küçük adımlarla yapın ve her adımda pipeline metriklerinizi izleyin. Build süresi, başarısızlık oranı ve kuyruk süresi izlemeniz gereken temel göstergeler.
Bir şeyi baştan doğru yaparsanız sonradan büyük zaman kazanırsınız: runner makinelerini cattle gibi yönetin, pet gibi değil. Her runner aynı şekilde kurulabilmeli, configuration management aracınız (Ansible, Terraform) ile. O müşterimizin altyapısında bugün hala Ansible playbook’larımız çalışıyor, runner makineleri değişse de pipeline’lar dakikalar içinde hazır oluyor.
