Bundler ile Ruby Proje Bağımlılıklarını Yönetme
Ruby projelerinde bağımlılık yönetimi, özellikle birden fazla proje üzerinde çalışan sysadminler ve geliştiriciler için ciddi bir baş ağrısı olabilir. Hangi gem hangi versiyonda, hangi Ruby sürümüyle uyumlu, üretim ortamıyla geliştirme ortamı arasındaki farklar… Bundler tam da bu kaosu ortadan kaldırmak için var. Bu yazıda Bundler’ı sıfırdan öğreneceğiz, gerçek dünya senaryolarıyla pekiştireceğiz ve production ortamında nasıl kullanmanız gerektiğini konuşacağız.
Bundler Nedir ve Neden Kullanmalısınız?
Ruby’nin paket yöneticisi RubyGems’tir. gem install rails dediğinizde sisteminize bir gem kurarsınız. Ancak buradaki sorun şu: Sisteminizde global olarak yüklü gemler zamanla çakışmaya başlar. Proje A, Rails 6.1 isterken Proje B, Rails 7.0 ister. İkisi aynı anda sisteminizde barış içinde yaşayamaz.
Bundler, her projenin kendi bağımlılık ortamını izole bir şekilde yönetmesini sağlar. Gemfile adlı bir dosyaya hangi gemlere ihtiyaç duyduğunuzu yazarsınız, Bundler bu gemleri çözer, uyumlu versiyonları bulur ve Gemfile.lock dosyasına kilit koyar. Böylece geliştirme ortamınızda çalışan uygulama, production’da da aynı şekilde çalışır.
Sysadmin perspektifinden bakınca Bundler şu sorunları çözer:
- Versiyon çakışmaları: Farklı projelerin farklı gem versiyonlarına ihtiyaç duyması durumunda izolasyon sağlar
- Tekrar üretilebilir kurulumlar:
Gemfile.locksayesinde her ortamda birebir aynı versiyonlar kurulur - Güvenlik: Hangi gemin hangi versiyonunun kullanıldığını tam olarak bilirsiniz, audit yapabilirsiniz
- CI/CD entegrasyonu: Pipeline’larınızda tutarlı ortamlar oluşturmanızı kolaylaştırır
Bundler Kurulumu
Bundler, RubyGems üzerinden kurulur. Modern Ruby sürümlerinde (2.6+) Bundler zaten dahili gelir, ancak güncellemeniz gerekebilir.
# Bundler'ın yüklü olup olmadığını kontrol edin
bundle --version
# Yoksa veya güncellemek istiyorsanız
gem install bundler
# Belirli bir Bundler versiyonu kurmak için
gem install bundler -v 2.4.10
# Sisteminizde hangi Bundler versiyonları var
gem list bundler
Eğer RVM veya rbenv kullanıyorsanız, her Ruby versiyonu için ayrı ayrı gem kurulumu yapmanız gerektiğini unutmayın. Bundler da bu kapsamda her Ruby ortamına ayrı kurulmalıdır.
# rbenv kullanıyorsanız Ruby versiyonunu ayarlayın
rbenv local 3.2.2
rbenv rehash
# Ardından Bundler kurun
gem install bundler
rbenv rehash
Gemfile Oluşturma ve Yapısını Anlama
Her şey Gemfile ile başlar. Bu dosya projenizin kök dizininde olmalıdır ve hangi gemlere ihtiyacınız olduğunu tanımlar.
# Yeni proje dizini oluşturun
mkdir my-ruby-project
cd my-ruby-project
# Bundler'ın başlangıç Gemfile'ını oluşturmasını sağlayın
bundle init
Oluşturulan temel Gemfile şu şekilde görünür:
# Gemfile içeriği
cat Gemfile
Şimdi gerçekçi bir Gemfile örneği görelim. Diyelim ki bir Sinatra tabanlı web API’si geliştiriyorsunuz:
# frozen_string_literal: true
source "https://rubygems.org"
ruby "3.2.2"
# Web framework
gem "sinatra", "~> 3.0"
gem "puma", "~> 6.0"
# Veritabanı
gem "sequel", "~> 5.70"
gem "pg", "~> 1.5"
# JSON işleme
gem "oj", "~> 3.16"
# Ortam değişkenleri
gem "dotenv", "~> 2.8"
# Geliştirme ve test ortamı
group :development, :test do
gem "rspec", "~> 3.12"
gem "rack-test", "~> 2.1"
gem "rubocop", "~> 1.56", require: false
end
# Sadece geliştirme ortamı
group :development do
gem "pry", "~> 0.14"
gem "rerun", "~> 0.14"
end
# Sadece production ortamı
group :production do
gem "sentry-ruby", "~> 5.12"
gem "lograge", "~> 0.13"
end
Versiyon kısıtlamalarını açıklayalım:
- “~> 3.0”: Pessimistic operator. 3.0 ve üzeri ama 4.0’dan küçük anlamına gelir. En sık kullanılan kısıtlama.
- “>= 2.0”: 2.0 ve üzeri her versiyon.
- “= 1.5.3”: Tam olarak bu versiyon.
- “>= 1.0”, “< 2.0": İki kısıtlamayı birleştirme.
Temel Bundler Komutları
Günlük kullanımda sıklıkla başvuracağınız komutları örneklerle görelim.
# Gemfile'daki tüm gemleri yükle
bundle install
# Sadece production gruplarını yükle (deployment için)
bundle install --without development test
# Gemleri belirli bir dizine yükle (sistem genelinde değil)
bundle install --path vendor/bundle
# Mevcut gem versiyonlarını güncelle
bundle update
# Sadece belirli bir gemi güncelle
bundle update sinatra
# Gemfile.lock'u yeniden oluştur
bundle lock --update
# Hangi gemlerin güncel olmadığını kontrol et
bundle outdated
# Yüklü gemlerin listesini göster
bundle list
# Belirli bir gem hakkında bilgi
bundle info sinatra
bundle install ile bundle update arasındaki farkı mutlaka anlayın. bundle install, Gemfile.lock dosyası varsa onu kullanır ve o versiyonları kurar. bundle update ise Gemfile.lock‘u görmezden gelerek en güncel uyumlu versiyonları bulur ve lock dosyasını günceller. Production’da asla bundle update çalıştırmadan önce test etmeyin.
Gemfile.lock Dosyasını Anlamak
Gemfile.lock dosyası, Bundler’ın çözdüğü bağımlılıkların tam listesini içerir. Bu dosyayı version control’e eklemelisiniz.
# Gemfile.lock'un bir kısmı şöyle görünür
cat Gemfile.lock
GEM
remote: https://rubygems.org/
specs:
mustermann (3.0.0)
puma (6.3.1)
nio4r (~> 2.0)
rack (3.0.8)
sinatra (3.1.0)
mustermann (~> 3.0)
rack (~> 3.0)
rack-protection (= 3.1.0)
tilt (~> 2.0)
PLATFORMS
arm64-darwin-22
x86_64-linux
DEPENDENCIES
sinatra (~> 3.0)
puma (~> 6.0)
BUNDLED WITH
2.4.10
PLATFORMS satırı dikkat çekici. Eğer M1/M2 Mac’te geliştirme yapıyorsunuz ve Linux’ta deploy ediyorsanız, her iki platform da lock dosyasına eklenmelidir. Bunu şu şekilde yaparsınız:
# Linux platformunu lock dosyasına ekle
bundle lock --add-platform x86_64-linux
# Tüm platformları göster
bundle platform
Bu adımı atlarsanız CI/CD pipeline’ınızda veya production sunucunuzda bundle install farklı davranabilir.
Gruplar ve Ortam Yönetimi
Büyük projelerde gem gruplarını doğru yönetmek hem güvenlik hem de performans açısından önemlidir. Production ortamına development gemlerini yüklememek, imaj boyutunu ve saldırı yüzeyini azaltır.
# Sadece belirli grupları yükle
BUNDLE_WITHOUT="development:test" bundle install
# Ortam değişkeniyle belirtme
export BUNDLE_WITHOUT="development test"
bundle install
# .bundle/config ile kalıcı yapılandırma
bundle config set --local without development test
bundle install
Rails projesinde otomatik ortam yönetimi şöyle çalışır:
# Rails uygulamasını production modda başlatırken
RAILS_ENV=production bundle exec rails server
# Rake görevlerinde
RAILS_ENV=production bundle exec rake db:migrate
bundle exec Neden Önemli?
Bu konuyu atlayan pek çok geliştirici ve sysadmin görmüşümdür. bundle exec olmadan bir komut çalıştırdığınızda, sistem genelinde yüklü gem’ler kullanılır. bundle exec ile çalıştırdığınızda ise Gemfile.lock’ta belirtilen versiyonlar kullanılır.
# Yanlış (sistem genelindeki rspec çalışır)
rspec spec/
# Doğru (Gemfile.lock'taki rspec çalışır)
bundle exec rspec spec/
# Rails komutları
bundle exec rails console
bundle exec rails db:migrate
bundle exec sidekiq
# Rake görevleri
bundle exec rake assets:precompile
Bunu her seferinde yazmaktan kaçınmak için binstubs kullanabilirsiniz:
# Binstubs oluştur
bundle binstubs rspec
bundle binstubs rails
# Artık direkt çalıştırabilirsiniz
bin/rspec spec/
bin/rails console
# Tüm gemler için binstubs oluştur
bundle binstubs --all
Gerçek Dünya Senaryosu: Mikroservis Ortamı
Diyelim ki üç farklı Ruby mikroservisiniz var ve bunları tek bir sunucuda çalıştırıyorsunuz. Her servis farklı gem versiyonlarına ihtiyaç duyuyor.
# Dizin yapısı
/opt/services/
auth-service/
Gemfile
Gemfile.lock
payment-service/
Gemfile
Gemfile.lock
notification-service/
Gemfile
Gemfile.lock
Her servis için gemleri izole dizinlere kuruyoruz:
# Auth servisini hazırla
cd /opt/services/auth-service
bundle config set --local path vendor/bundle
bundle config set --local without development test
bundle install --jobs 4 --retry 3
# Payment servisini hazırla
cd /opt/services/payment-service
bundle config set --local path vendor/bundle
bundle config set --local without development test
bundle install --jobs 4 --retry 3
Systemd servis dosyasında Bundler ile çalıştırma:
# /etc/systemd/system/auth-service.service
[Unit]
Description=Auth Service
After=network.target postgresql.service
[Service]
Type=simple
User=deploy
WorkingDirectory=/opt/services/auth-service
Environment=RACK_ENV=production
Environment=BUNDLE_PATH=/opt/services/auth-service/vendor/bundle
ExecStart=/usr/local/bin/bundle exec puma -C config/puma.rb
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Bundler Konfigürasyonu ve .bundle/config
Bundler’ın davranışını proje bazında veya global olarak yapılandırabilirsiniz.
# Global Bundler konfigürasyonu (~/.bundle/config)
bundle config set --global jobs 4
bundle config set --global retry 3
# Proje bazlı konfigürasyon (.bundle/config)
bundle config set --local path vendor/bundle
bundle config set --local without development test
bundle config set --local clean true
# Özel gem kaynağı eklemek (private gem server)
bundle config set --global https://gems.example.com TOKEN123
# Konfigürasyonu görüntüle
bundle config list
# Belirli bir ayarı sil
bundle config unset --local path
.bundle/config dosyasını version control’e eklememeye dikkat edin. Özellikle token içeriyorsa .gitignore‘a ekleyin. Sadece without gibi genel ayarları commit edebilirsiniz.
Private Gem Server Kullanımı
Şirkete özel gemler yazıyorsanız, bunları Gemfile’da tanımlamak için birkaç yöntem var.
# Gemfile'da private server tanımı
source "https://gems.example.com" do
gem "company-auth", "~> 2.0"
gem "internal-utils", "~> 1.3"
end
# Git kaynağından gem
gem "my-private-gem", git: "https://github.com/company/my-gem.git", branch: "main"
# Belirli bir tag veya commit'ten
gem "my-private-gem", git: "https://github.com/company/my-gem.git", tag: "v1.2.0"
# Lokal geliştirme için (production'da kullanmayın)
gem "my-gem", path: "../my-gem"
CI/CD ortamında private gem server kimlik doğrulamasını şöyle yapılandırırsınız:
# GitHub Actions veya GitLab CI'da
export BUNDLE_GEMS__EXAMPLE__COM="oauth_token"
bundle install
# Veya Bundler config ile
bundle config set --local https://gems.example.com $PRIVATE_GEM_TOKEN
Deployment Best Practices
Production deployment’larında Bundler’ı doğru kullanmak kritiktir.
# Capistrano benzeri deployment için
# 1. Önce lock dosyasını güncelle (geliştirme ortamında)
bundle lock --update sinatra
# 2. Tüm testleri çalıştır
bundle exec rspec
# 3. Değişiklikleri commit et
git add Gemfile.lock
git commit -m "Update sinatra to 3.1.0"
# 4. Production'da sadece install çalıştır
bundle install --deployment --without development test
--deployment flag’i önemli bir ayrıntı: Bu flag, Gemfile.lock dosyasının olmadığı durumda hata verir ve gemleri vendor/bundle dizinine yükler. Production güvenliği için idealdir.
# Docker imajı oluştururken
# Dockerfile içinde
RUN bundle config set --local deployment true &&
bundle config set --local without development test &&
bundle install --jobs 4 --retry 3
# Cache optimizasyonu için önce Gemfile'ları kopyala
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
Gem Güvenlik Denetimi
Bundler, yüklü gemlerdeki güvenlik açıklarını kontrol etmek için bundler-audit gemini destekler.
# bundler-audit kur
gem install bundler-audit
# Güvenlik açıklarını kontrol et
bundle audit check
# Veritabanını güncelleyerek kontrol et
bundle audit check --update
# Belirli bir CVE'yi yoksay
bundle audit check --ignore CVE-2023-12345
# Çıktıyı JSON olarak al (CI/CD entegrasyonu için)
bundle audit check --format json
Bunu CI pipeline’ınıza eklemek, her deployment öncesi güvenlik kontrolü yapmanızı sağlar. GitLab CI örneği:
# .gitlab-ci.yml içinde
security_audit:
stage: test
script:
- gem install bundler-audit
- bundle audit check --update
allow_failure: false
Sorun Giderme
Bundler kullanırken karşılaşılan yaygın sorunları ve çözümleri inceleyelim.
# "Your bundle is locked to X but your local Bundler version is Y"
# Çözüm: Bundler versiyonunu lock dosyasıyla eşleştir
gem install bundler -v 2.4.10
bundle _2.4.10_ install
# Native extension derleme hataları (örn. pg gem'i)
# Önce sistem kütüphanelerini kur
sudo apt-get install libpq-dev # Ubuntu/Debian
sudo yum install postgresql-devel # CentOS/RHEL
bundle install
# SSL sertifika hatası
bundle config set --global ssl_ca_cert /path/to/cacert.pem
# Gemfile.lock çakışması (git merge sonrası)
bundle install # Genellikle otomatik çözer
# Çözmezse
bundle lock --update
# "Bundler cannot continue" hatası
bundle env # Ortam bilgisini göster
bundle doctor # Olası sorunları teşhis et
Sonuç
Bundler, Ruby ekosisteminin vazgeçilmez parçalarından biridir. Sysadmin olarak sadece “gem install” yapıp geçiştirebilirsiniz, ancak bu yaklaşım zamanla sizi zor durumda bırakır. Doğru Bundler kullanımı şu sonuçları getirir: production’da sürpriz versiyyon uyumsuzlukları yaşamazsınız, yeni bir geliştirici projeye dahil olduğunda bundle install ile dakikalar içinde çalışır duruma gelir, güvenlik denetimleri otomatikleşir ve Docker imajlarınız optimize olur.
Özellikle dikkat etmeniz gereken üç şey var: Gemfile.lock‘u her zaman version control’e ekleyin, production’da bundle install kullanın bundle update değil, ve bundle exec alışkanlığını edinin. Bu üç kural, olası problemlerin büyük çoğunluğunu önler. Gerisi deneyimle gelir.
