Sunucunda bir uygulama aniden tüm CPU’yu veya belleği yutmaya başladığında, sistem geneline yayılan bu kaos herkesi etkiler. İşte tam bu noktada Linux çekirdeğinin sunduğu cgroups (control groups) mekanizması devreye girer ve sana granüler düzeyde kaynak kontrolü sağlar. Yıllardır container teknolojilerinin (Docker, LXC, Kubernetes) altında sessizce çalışan bu yapıyı doğrudan komut satırından kullanmayı öğrenmek, hem sorun giderme hem de kaynak planlaması açısından sysadmin araç kutuna ciddi bir güç katar.
cgroups Nedir ve Neden Önemlidir?
cgroups, Linux çekirdeğine 2008 yılında (2.6.24 ile) entegre edilmiş, süreçleri hiyerarşik gruplar halinde organize edip bu grupların kaynak kullanımını sınırlandırmaya, izlemeye ve önceliklendirmeye yarayan bir çekirdek özelliğidir. CPU zamanı, bellek, disk I/O, ağ bant genişliği gibi kaynakların ne kadarının hangi süreç grubuna tahsis edileceğini belirleyebilirsin.
Bugün iki aktif sürüm mevcut:
- cgroups v1: Eski sistemlerde yaygın, her kaynak türü için ayrı hiyerarşi
- cgroups v2: Modern yaklaşım, birleşik hiyerarşi, systemd ile tam entegrasyon, Ubuntu 21.10+, RHEL 9+ üzerinde varsayılan
Hangi sürümü kullandığını anlamak için:
grep cgroup /proc/filesystems
mount | grep cgroup
ls /sys/fs/cgroup/
Eğer /sys/fs/cgroup/ altında cpu, memory gibi ayrı dizinler görüyorsan v1 varsın. Tek bir hiyerarşik yapı görüyorsan v2.
Temel Kavramlar
Çalışmaya başlamadan önce birkaç kavramı netleştirmek gerekiyor.
Subsystem (Alt sistem): Belirli bir kaynak türünü kontrol eden modül. cpu, memory, blkio, cpuset bunların en çok kullanılanları.
Hierarchy (Hiyerarşi): cgroup ağacının bir mount noktasına bağlı versiyonu. Bir subsystem yalnızca bir hiyerarşiye bağlanabilir.
Task: cgroup bağlamında süreç veya thread anlamına gelir.
Controller: v2 terminolojisinde subsystem’ın karşılığı.
cgroupfs ile Doğrudan Çalışmak
En saf haliyle cgroups, bir dosya sistemi arayüzüdür. Çekirdekle konuşmak için özel dosyaları okur ve yazarsın.
cgroups v1 ile Manuel Yapılandırma
Önce gerekli araçları kur:
# Debian/Ubuntu
sudo apt install cgroup-tools libcgroup1
# RHEL/CentOS/Rocky
sudo dnf install libcgroup libcgroup-tools
CPU kullanımını sınırlamak için basit bir örnek:
# cpu subsystem'ı altında yeni bir cgroup oluştur
sudo mkdir /sys/fs/cgroup/cpu/web_app
# Bu cgroup'un CPU payını ayarla (varsayılan 1024, yarıya indir)
echo 512 | sudo tee /sys/fs/cgroup/cpu/web_app/cpu.shares
# Mevcut bir süreci bu gruba ekle (PID'i öğren)
echo $$ | sudo tee /sys/fs/cgroup/cpu/web_app/tasks
# Doğrula
cat /sys/fs/cgroup/cpu/web_app/tasks
Bellek sınırı koymak için:
# memory subsystem altında grup oluştur
sudo mkdir /sys/fs/cgroup/memory/db_process
# 512MB bellek sınırı koy
echo $((512 * 1024 * 1024)) | sudo tee /sys/fs/cgroup/memory/db_process/memory.limit_in_bytes
# OOM killer'ı etkinleştir (sınır aşılınca süreci öldürsün)
echo 1 | sudo tee /sys/fs/cgroup/memory/db_process/memory.oom_control
# Bir PID ekle
echo 12345 | sudo tee /sys/fs/cgroup/memory/db_process/tasks
cgexec ve cgclassify Araçları
cgroup-tools paketi daha pratik komutlar sunar:
# Bir uygulamayı doğrudan cgroup içinde başlat
sudo cgexec -g cpu,memory:web_app /usr/bin/nginx
# Mevcut bir süreci cgroup'a taşı
sudo cgclassify -g cpu:web_app 18923
# cgroup bilgilerini görüntüle
cgget -r cpu.shares web_app
cgget -r memory.limit_in_bytes db_process
cgroups v2 ile Çalışmak
Modern sistemlerde v2 hakimdir ve çok daha temiz bir arayüz sunar. Hiyerarşi birleşiktir, her şey /sys/fs/cgroup/ altındadır.
# Mevcut cgroup yapısını gör
systemd-cgls
# Ya da direkt dosya sistemine bak
ls /sys/fs/cgroup/
cat /sys/fs/cgroup/cgroup.controllers
v2’de bir cgroup oluşturup CPU ve bellek sınırı koymak:
# Yeni cgroup dizini oluştur
sudo mkdir /sys/fs/cgroup/my_app
# CPU controller'ı etkinleştir (üst cgroup'tan devral)
echo "+cpu +memory" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
# CPU kotası: 100ms periyotta 50ms kullanabilsin (yüzde 50)
echo "50000 100000" | sudo tee /sys/fs/cgroup/my_app/cpu.max
# Bellek sınırı: 256MB
echo $((256 * 1024 * 1024)) | sudo tee /sys/fs/cgroup/my_app/memory.max
# Süreci ekle
echo $$ | sudo tee /sys/fs/cgroup/my_app/cgroup.procs
# Anlık istatistikleri izle
cat /sys/fs/cgroup/my_app/cpu.stat
cat /sys/fs/cgroup/my_app/memory.current
Gerçek Dünya Senaryoları
Senaryo 1: Kontrolden Çıkan Build Süreci
Ekipten biri büyük bir derleme işi başlattı ve sunucu neredeyse dondu. Artık bir daha böyle bir şey yaşamamak için CI build süreçlerini kısıtlamak istiyorsun:
# Build kullanıcısı için cgroup oluştur
sudo mkdir /sys/fs/cgroup/build_jobs
# CPU: en fazla 4 çekirdek kullanabilsin (4 * 100000)
echo "400000 100000" | sudo tee /sys/fs/cgroup/build_jobs/cpu.max
# Bellek: 4GB sınırı
echo $((4 * 1024 * 1024 * 1024)) | sudo tee /sys/fs/cgroup/build_jobs/memory.max
# Swap kullanımını da sınırla
echo $((1 * 1024 * 1024 * 1024)) | sudo tee /sys/fs/cgroup/build_jobs/memory.swap.max
# Build komutunu bu cgroup içinde çalıştır
sudo cgexec -g cpu,memory:build_jobs sudo -u builder make -j8
# Ya da v2'de direkt:
echo $$ | sudo tee /sys/fs/cgroup/build_jobs/cgroup.procs
make -j8
Senaryo 2: Çok Kiracılı Uygulama Sunucusu
Aynı makinede birden fazla müşterinin uygulaması çalışıyor ve kaynak izolasyonu şart:
#!/bin/bash
# multi_tenant_setup.sh
TENANTS=("tenant_a" "tenant_b" "tenant_c")
CPU_SHARES=(512 256 256) # Toplam 1024 birim
MEM_LIMITS=("2G" "1G" "1G")
for i in "${!TENANTS[@]}"; do
TENANT="${TENANTS[$i]}"
MEM="${MEM_LIMITS[$i]}"
# cgroup oluştur
mkdir -p /sys/fs/cgroup/${TENANT}
# CPU paylaşım ağırlığı (weight, 1-10000 arası, varsayılan 100)
echo "${CPU_SHARES[$i]}" > /sys/fs/cgroup/${TENANT}/cpu.weight
# Bellek sınırı
case $MEM in
*G) BYTES=$(( ${MEM%G} * 1024 * 1024 * 1024 )) ;;
*M) BYTES=$(( ${MEM%M} * 1024 * 1024 )) ;;
esac
echo $BYTES > /sys/fs/cgroup/${TENANT}/memory.max
echo "${TENANT} için cgroup hazır: CPU weight=${CPU_SHARES[$i]}, Bellek=${MEM}"
done
Senaryo 3: Veritabanı Sunucusu Disk I/O Kontrolü
PostgreSQL ve diğer servisler aynı diskten okuyup yazıyor, PostgreSQL’in diğerlerini bastırmasını istemiyorsun:
# Disk cihazını bul
lsblk
# Varsayalım ki sda kullanıyoruz, major:minor = 8:0
# PostgreSQL cgroup'u
sudo mkdir /sys/fs/cgroup/postgresql
# I/O ağırlığı (varsayılan 100, yüksek öncelik için 200)
echo "8:0 200" | sudo tee /sys/fs/cgroup/postgresql/io.weight
# Saniyede maksimum okuma: 200MB
echo "8:0 rbps=209715200" | sudo tee /sys/fs/cgroup/postgresql/io.max
# Saniyede maksimum yazma: 100MB
echo "8:0 wbps=104857600" | sudo tee /sys/fs/cgroup/postgresql/io.max
# PostgreSQL PID'lerini ekle
for pid in $(pgrep -u postgres); do
echo $pid | sudo tee /sys/fs/cgroup/postgresql/cgroup.procs
done
systemd ile cgroup Entegrasyonu
Modern sistemlerde cgroup’ları doğrudan yönetmek yerine systemd üzerinden yönetmek çok daha temiz bir yaklaşım. systemd zaten her servis için otomatik cgroup oluşturur.
# Bir servisin cgroup konumunu bul
systemctl status nginx | grep CGroup
# Tüm cgroup ağacını gör
systemd-cgls
# Kaynak kullanım özetini gör
systemd-cgtop
Servis dosyasına kaynak sınırı eklemek için:
# /etc/systemd/system/myapp.service
sudo systemctl edit myapp.service --force
Açılan editöre şunları ekle:
[Service]
# CPU kotası: yüzde 50
CPUQuota=50%
# CPU ağırlığı (nice benzeri, 1-10000)
CPUWeight=100
# Bellek sınırı
MemoryMax=512M
# Bellek yüksek su işareti (bu noktadan sonra throttle)
MemoryHigh=400M
# Swap sınırı
MemorySwapMax=256M
# I/O ağırlığı
IOWeight=50
# Başlangıç önceliği
Nice=10
Değişikliği uygula:
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
# Uygulanan limitleri doğrula
systemctl show myapp.service | grep -E "CPU|Memory|IO"
systemd-run ile Geçici Kısıtlı Süreçler
Kalıcı servis dosyası oluşturmadan geçici olarak kısıtlı süreç başlatmak için:
# CPU ve bellek kısıtlı geçici servis
systemd-run --scope --slice=limited.slice
-p CPUQuota=25%
-p MemoryMax=256M
/usr/bin/python3 heavy_script.py
# Dilersen adını ver ve takip et
systemd-run --unit=my_job
-p CPUQuota=50%
-p MemoryMax=1G
-p IOWeight=25
bash -c "tar czf backup.tar.gz /data"
# Çalışan geçici servisi izle
systemctl status my_job
journalctl -u my_job -f
cgroup İzleme ve Raporlama
Kaynak kullanımını takip etmek en az sınırlamak kadar önemli.
# systemd-cgtop ile anlık özet (htop gibi ama cgroup bazlı)
systemd-cgtop -d 2 # 2 saniye aralıkla güncelle
# Belirli bir cgroup'un anlık bellek kullanımı
cat /sys/fs/cgroup/my_app/memory.current
# CPU istatistikleri
cat /sys/fs/cgroup/my_app/cpu.stat
# Detaylı bellek istatistikleri
cat /sys/fs/cgroup/my_app/memory.stat | head -20
# OOM olayları yaşandı mı?
cat /sys/fs/cgroup/my_app/memory.events
# I/O istatistikleri
cat /sys/fs/cgroup/my_app/io.stat
# Tüm cgroup hiyerarşisindeki süreçleri gör
systemd-cgls --no-pager | less
Hızlı bir izleme scripti:
#!/bin/bash
# cgroup_monitor.sh - Belirli cgroup'ları periyodik izle
CGROUP_PATH="/sys/fs/cgroup/my_app"
LOG_FILE="/var/log/cgroup_monitor.log"
while true; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
MEM_CURRENT=$(cat ${CGROUP_PATH}/memory.current 2>/dev/null || echo "N/A")
MEM_MAX=$(cat ${CGROUP_PATH}/memory.max 2>/dev/null || echo "N/A")
CPU_USAGE=$(cat ${CGROUP_PATH}/cpu.stat | grep usage_usec | awk '{print $2}')
# Bayt'tan MB'a çevir
if [[ "$MEM_CURRENT" != "N/A" ]]; then
MEM_MB=$((MEM_CURRENT / 1024 / 1024))
else
MEM_MB="N/A"
fi
echo "${TIMESTAMP} | Bellek: ${MEM_MB}MB | CPU toplam usec: ${CPU_USAGE}" | tee -a $LOG_FILE
# Bellek sınırına yaklaşıldıysa uyar
if [[ "$MEM_CURRENT" != "N/A" && "$MEM_MAX" != "N/A" && "$MEM_MAX" != "max" ]]; then
PERCENT=$((MEM_CURRENT * 100 / MEM_MAX))
if [ $PERCENT -gt 85 ]; then
echo "UYARI: ${CGROUP_PATH} bellek kullanimi %${PERCENT}" | logger -p warning
fi
fi
sleep 10
done
Yaygın Sorunlar ve Çözümleri
Sorun: Controller aktif değil
v2’de bazı controller’lar üst cgroup’tan açıkça etkinleştirilmelidir:
# Hangi controller'lar mevcut?
cat /sys/fs/cgroup/cgroup.controllers
# Hangileri aktif?
cat /sys/fs/cgroup/cgroup.subtree_control
# cpu ve memory controller'larını etkinleştir
echo "+cpu +memory +io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
Sorun: “cgroup: fork rejected by pids controller” hatası
# pids sınırını kontrol et
cat /sys/fs/cgroup/my_app/pids.max
# Limiti artır
echo 1000 | sudo tee /sys/fs/cgroup/my_app/pids.max
# Ya da sınırı kaldır
echo max | sudo tee /sys/fs/cgroup/my_app/pids.max
Sorun: OOM killer süreçleri öldürüyor
# OOM olaylarını incele
dmesg | grep -i "oom|killed process"
# Belirli cgroup'un OOM geçmişi
cat /sys/fs/cgroup/my_app/memory.events
# memory.oom.group ayarı - tüm grubu birden öldür
echo 1 | sudo tee /sys/fs/cgroup/my_app/memory.oom.group
Kalıcı Yapılandırma için cgconfig
Sistem yeniden başladığında cgroup’ların kaybolmaması için /etc/cgconfig.conf kullanılabilir (v1 sistemlerde):
# /etc/cgconfig.conf
group web_apps {
cpu {
cpu.shares = 512;
}
memory {
memory.limit_in_bytes = 1073741824; # 1GB
}
}
group batch_jobs {
cpu {
cpu.shares = 256;
}
memory {
memory.limit_in_bytes = 536870912; # 512MB
}
}
Servisi başlat:
sudo systemctl enable cgconfig
sudo systemctl start cgconfig
Modern systemd tabanlı sistemlerde ise yukarıda gösterdiğimiz gibi unit dosyaları üzerinden yönetmek çok daha sağlıklı.
Güvenlik ve İzolasyon Notları
cgroups kaynak sınırlaması yapar ama tam izolasyon için tek başına yeterli değildir. Birkaç önemli nokta:
- Namespace’lerle birlikte kullan: PID, network, mount namespace’leri süreç izolasyonunu tamamlar
- Seccomp politikaları: Sistem çağrısı kısıtlaması için gerekli
- cgroup v2 + systemd: Bu kombinasyon güvenlik açısından en güçlü yerleşik seçenek
- Bellek sınırları gerçekçi olmalı: Çok düşük sınırlar sürekli OOM’a yol açar, uygulama davranışını analiz et önce
# Bir uygulamanın tipik bellek kullanımını ölç
/usr/bin/time -v my_application 2>&1 | grep "Maximum resident"
# Ya da smem ile daha detaylı
smem -P my_app -c "name pid pss uss rss"
Sonuç
cgroups, sysadmin dünyasında genellikle “container altyapısının altında çalışan bir şey” olarak algılanır. Ama onu doğrudan kullanmayı öğrendiğinde, kontrolden çıkan build süreçlerini frenlemek, çok kiracılı sistemlerde izolasyon sağlamak veya bir veritabanının disk I/O’sunu dengelemek gibi gerçek sorunlara zarif çözümler üretebiliyorsun.
Başlangıç için pratik bir yol haritası şöyle çizilebilir: Önce systemd-cgtop ve systemd-cgls ile mevcut sistemindeki cgroup yapısını tanı. Sonra kritik servislerin systemd unit dosyalarına CPUQuota, MemoryMax gibi direktifleri ekleyerek başla; bu en az riskli yol. Oradan ilerledikçe v2’nin doğrudan cgroupfs arayüzüne geçip daha granüler kontrol sağlayabilirsin.
Docker ya da Kubernetes kullansan bile temel cgroups bilgisi container davranışını anlamak, --cpus ve --memory bayraklarının arka planda ne yaptığını kavramak için vazgeçilmez. Bir container OOM’a düşüp düşmediğini anlamak istediğinde ya da bir pod’un neden CPU throttle yediğini araştırdığında, çekirdeğin /sys/fs/cgroup altındaki o sade metin dosyalarına dönüyorsun. Ve artık ne aradığını biliyorsun.