systemd Slice Unit ile Servis Gruplarını İzole Etme ve Kaynak Yönetimi

Üretim ortamında bir web sunucusunu yönetirken, tek bir çılgına dönen PHP-FPM process’inin tüm sistemi çöktürdüğüne şahit olmuş biri olarak söylüyorum: systemd slice unit’lerini öğrenmek, gece uykunuzu kurtarabilir. Çoğu sysadmin systemd’yi servis başlatmak ve durdurmak için kullanır, ama altında yatan cgroup entegrasyonu ve kaynak izolasyonu mekanizmaları genellikle göz ardı edilir. Bu yazıda, slice unit’lerini kullanarak servis gruplarını nasıl izole edeceğinizi ve kaynak yönetimini nasıl yapılandıracağınızı gerçek dünya senaryolarıyla birlikte ele alacağız.

Slice Unit Nedir ve Neden Önemlidir?

systemd, Linux’un cgroup (control groups) altyapısını yönetmek için üç temel unit tipi kullanır: service, scope ve slice. Slice unit’leri bu hiyerarşinin iskeletini oluşturur. Düşünün ki bir şirketin departman yapısı gibi: her departmanın belirli bir bütçesi, belirli kaynakları var ve bir departmanın aşırı harcaması diğerini etkilemiyor.

systemd varsayılan olarak birkaç slice ile gelir:

  • system.slice: Tüm sistem servislerinin varsayılan yeri
  • user.slice: Kullanıcı session’larını barındırır
  • machine.slice: Sanal makineler ve container’lar için
  • -.slice: Her şeyin köküdür, root slice olarak da bilinir

Bu yapıyı görmek için şu komutu çalıştırın:

systemd-cgls

Çıktıda ağaç yapısını göreceksiniz. Aynı şekilde anlık kaynak kullanımına bakmak için:

systemd-cgtop

Bu komut, top gibi çalışır ama cgroup hiyerarşisini gösterir. Hangi slice’ın ne kadar CPU ve RAM yediğini anlık olarak takip edebilirsiniz.

Özel Slice Oluşturma

Senaryo şu: Bir sunucuda hem web uygulamaları (nginx, php-fpm, node.js) hem de veritabanı servisleri (postgresql, redis) çalışıyor. Web katmanının veritabanı kaynaklarını tüketerek yavaşlamasını istemiyoruz. Aynı zamanda geliştirme ortamı servisleri de var ve bunlara kaynak limiti koymak istiyoruz.

İlk adım olarak web uygulamaları için bir slice oluşturalım:

sudo nano /etc/systemd/system/web-apps.slice
[Unit]
Description=Web Uygulamalari Slice
Before=slices.target

[Slice]
# CPU limiti: toplam CPU'nun yuzde 60'i
CPUQuota=60%
# RAM limiti: 4GB
MemoryMax=4G
# Bellek baskisi altinda once buradaki processleri oldur
MemorySwapMax=1G
# IO weight (varsayilan 100, dusuk = daha az oncelik)
IOWeight=80

Veritabanı servisleri için ayrı bir slice:

sudo nano /etc/systemd/system/database.slice
[Unit]
Description=Veritabani Servisleri Slice
Before=slices.target

[Slice]
CPUQuota=80%
MemoryMax=8G
MemorySwapMax=512M
# Veritabani IO'su kritik, yuksek oncelik
IOWeight=200
# Minimum CPU garantisi
CPUWeight=150

Değişiklikleri aktif hale getirin:

sudo systemctl daemon-reload
sudo systemctl start web-apps.slice
sudo systemctl start database.slice

Servisleri Slice’a Atama

Slice’ları oluşturduktan sonra mevcut servisleri bu slice’lara atamamız gerekiyor. Bu işlem için servis unit dosyalarını düzenliyoruz.

nginx’i web-apps.slice’a ekleyelim:

sudo systemctl edit nginx

Bu komut /etc/systemd/system/nginx.service.d/override.conf dosyasını açar. Buraya şunu yazın:

[Service]
Slice=web-apps.slice
# Ek olarak bu servise ozel limitler de koyabilirsiniz
MemoryMax=512M
CPUQuota=20%

PHP-FPM için de aynı işlemi yapalım, ama bu sefer biraz daha fazla kaynak verelim çünkü asıl iş yükü orada:

sudo systemctl edit php8.2-fpm
[Service]
Slice=web-apps.slice
MemoryMax=1G
MemoryHigh=768M
CPUQuota=30%
# Bellek limitine yaklasildiginda yavascama baslasin
MemorySwapMax=256M

MemoryHigh ile MemoryMax arasındaki farka dikkat edin. MemoryHigh yumuşak limit, yani bu değere ulaşınca kernel process’i yavaşlatmaya başlar ama öldürmez. MemoryMax ise sert limit, geçilince OOM killer devreye girer.

Geçici Kaynak Limitleri: systemctl set-property

Bazen production’da kalıcı değişiklik yapmadan test etmek istersiniz. systemctl set-property komutu tam olarak bunun için:

# Gecici limit koy (sistem yeniden basladiginda sifirlanir)
sudo systemctl set-property --runtime nginx.service MemoryMax=256M

# Kalici limit koy
sudo systemctl set-property nginx.service MemoryMax=512M

# Slice seviyesinde limit degistir
sudo systemctl set-property web-apps.slice CPUQuota=50%

Mevcut property değerlerini görmek için:

sudo systemctl show web-apps.slice | grep -E "Memory|CPU|IO"

Bu komutun çıktısı size slice’ın şu anki tüm kaynak parametrelerini gösterir.

Gerçek Dünya Senaryosu: Multi-Tenant Uygulama Sunucusu

Birden fazla müşteriye hizmet veren bir PaaS benzeri ortamda, her müşterinin kaynaklarını izole etmek kritik önem taşır. Müşteri A’nın uygulaması çıldırıp tüm CPU’yu yutmamalı.

Her müşteri için ayrı slice oluşturalım. Önce bir şablon mantığıyla düşünelim:

sudo nano /etc/systemd/system/customer-a.slice
[Unit]
Description=Musteri A Uygulamalari
Before=slices.target

[Slice]
# Toplam kaynaklarin yuzde 25'i
CPUQuota=25%
CPUWeight=100
MemoryMax=2G
MemoryHigh=1500M
# IO limiti: saniyede maksimum 100MB okuma/yazma
IOReadBandwidthMax=/dev/sda 100M
IOWriteBandwidthMax=/dev/sda 100M
# Gorev sayisi limiti (fork bomb koruması)
TasksMax=512

Benzer şekilde customer-b.slice oluşturulur. Şimdi müşteri A’nın Node.js uygulamasını bu slice’a bağlayalım:

sudo nano /etc/systemd/system/customer-a-app.service
[Unit]
Description=Musteri A Node.js Uygulamasi
After=network.target

[Service]
Slice=customer-a.slice
Type=simple
User=customer-a
Group=customer-a
WorkingDirectory=/opt/customer-a/app
ExecStart=/usr/bin/node /opt/customer-a/app/server.js
Restart=on-failure
RestartSec=5

# Servis seviyesinde ek guvenlik
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/opt/customer-a/data

[Install]
WantedBy=multi-user.target

Bu yapıda müşteri A’nın uygulaması ne kadar RAM yerse yesin, sistemi çökertmesi veya müşteri B’yi etkilemesi mümkün değil.

cgroup v2 ile Gelişmiş Bellek Yönetimi

Modern Linux dağıtımları (Ubuntu 22.04, Fedora 31+, RHEL 9) cgroup v2 kullanır ve bu önemli ek özellikler getirir. Hangi sürümü kullandığınızı kontrol edin:

stat -fc %T /sys/fs/cgroup/

Eğer cgroup2fs çıkıyorsa v2 kullanıyorsunuz. cgroup v2 ile MemoryPressure gibi yeni direktifler kullanılabilir. Bunu slice dosyamıza ekleyelim:

sudo nano /etc/systemd/system/web-apps.slice
[Unit]
Description=Web Uygulamalari Slice
Before=slices.target

[Slice]
CPUQuota=60%
CPUWeight=100
MemoryMax=4G
MemoryHigh=3G
MemorySwapMax=1G
# cgroup v2: bellek baskisinda ne yapilacagi
ManagedOOMSwap=auto
ManagedOOMMemoryPressure=auto
ManagedOOMMemoryPressureLimit=80%
IOWeight=80
TasksMax=2048

ManagedOOMMemoryPressure=auto ayarı, systemd-oomd servisinin bu slice’ı izlemesine ve bellek baskısı yüzde 80’i geçtiğinde en savurgan process’leri öldürmesine izin verir. Bu sayede kernel OOM killer devreye girmeden önce systemd daha akıllıca bir müdahale yapabilir.

systemd-oomd servisinin çalıştığını doğrulayın:

sudo systemctl status systemd-oomd

İzleme ve Hata Ayıklama

Slice yapınızın düzgün çalışıp çalışmadığını izlemek için birkaç pratik yöntem var.

Belirli bir slice’ın altındaki tüm process’leri görmek için:

systemd-cgls /web-apps.slice

Anlık CPU ve bellek kullanımı için:

systemd-cgtop -d 2 --depth=3

-d 2 iki saniyede bir yenile, --depth=3 ise üç seviye derinliğe kadar göster demek.

Bir slice’ın mevcut kaynak kullanım istatistiklerini almak için:

sudo systemctl show web-apps.slice -p MemoryCurrent,CPUUsageNSec,TasksCurrent

cgroup dosya sisteminden doğrudan okumak istiyorsanız:

# Slice'in memory kullanimi
cat /sys/fs/cgroup/web-apps.slice/memory.current

# CPU istatistikleri
cat /sys/fs/cgroup/web-apps.slice/cpu.stat

# IO istatistikleri
cat /sys/fs/cgroup/web-apps.slice/io.stat

Journald üzerinden slice ile ilgili logları takip etmek için:

sudo journalctl -u web-apps.slice -f

OOM olaylarını yakalamak için:

sudo journalctl -k | grep -i "oom|killed"

Nested Slice: İç İçe Kaynak Hiyerarşisi

Daha karmaşık senaryolarda slice’ları iç içe geçirebilirsiniz. Örneğin üretim ve geliştirme ortamlarını ayırmak istiyorsunuz:

sudo nano /etc/systemd/system/production.slice
[Unit]
Description=Production Ortami
Before=slices.target

[Slice]
CPUQuota=80%
MemoryMax=12G
IOWeight=200

Şimdi bu slice içinde bir alt slice oluşturalım:

sudo nano /etc/systemd/system/production-web.slice
[Unit]
Description=Production Web Katmani
Before=slices.target

[Slice]
# Bu limitler parent slice limitleri ile sinirlidir
CPUQuota=40%
MemoryMax=6G
IOWeight=150

Alt slice’ın parent’ına bağlanması için unit dosyasında şu direktif yeterli değil, isimlendirme konvansiyonu önemli. systemd, noktalı gösterimle hiyerarşiyi anlar. production-web.slice adı, systemd’ye bunun production.slice altında bir alt slice olduğunu söyler. Bu isimlendirme kuralını mutlaka takip edin.

Servisleri alt slice’a bağlamak için:

sudo systemctl edit nginx
[Service]
Slice=production-web.slice

Transient Unit ile Anlık İzolasyon

Bazen geçici bir iş için kaynak izolasyonu isteyebilirsiniz, örneğin yedekleme işlemi sırasında. systemd-run komutu tam olarak bunun için:

# Gecici slice ile backup scripti calistir
sudo systemd-run 
  --slice=backup.slice 
  --property=CPUQuota=20% 
  --property=MemoryMax=512M 
  --property=IOWeight=50 
  --description="Haftalik Yedekleme" 
  /opt/scripts/weekly-backup.sh

Bu komut geçici bir scope oluşturur, script çalışır ve biter, arkasında hiçbir şey kalmaz. Backup işleminin asla sistemi boğmamasını garanti edersiniz.

Çalışan transient unit’leri görmek için:

systemctl list-units --type=scope

Pratik: Systemd-analyze ile Slice Doğrulama

Yapılandırmanızın sözdizimsel olarak doğru olup olmadığını test etmek için:

systemd-analyze verify /etc/systemd/system/web-apps.slice
systemd-analyze verify /etc/systemd/system/database.slice

Herhangi bir hata veya uyarı yoksa unit’ler hazır demektir. Tüm yapılandırmayı reload etmek için:

sudo systemctl daemon-reload
sudo systemctl restart web-apps.slice database.slice

Önemli Direktif Özeti

Slice unit dosyalarında kullanacağınız en önemli direktifleri kısaca geçelim:

  • CPUQuota: Yüzde cinsinden CPU limiti, 50% şeklinde yazılır, birden fazla core olan sistemlerde 150% gibi değerler mümkündür
  • CPUWeight: 1 ile 10000 arasında öncelik değeri, varsayılan 100’dür
  • MemoryMax: Sert bellek limiti, bu aşılırsa OOM killer devreye girer
  • MemoryHigh: Yumuşak bellek limiti, kernel bu noktadan sonra yavaşlatma başlatır
  • MemorySwapMax: Swap kullanım limiti, 0 yazarsanız swap kullanımını tamamen engellersiniz
  • IOWeight: 1 ile 10000 arasında IO önceliği, yüksek değer daha fazla IO erişimi demektir
  • IOReadBandwidthMax: Okuma bant genişliği limiti, cihaz yolu ve değer belirtilir
  • IOWriteBandwidthMax: Yazma bant genişliği limiti
  • TasksMax: Maksimum thread ve process sayısı, fork bomb saldırılarına karşı önemlidir
  • ManagedOOMSwap: systemd-oomd entegrasyonu için swap baskısı yönetimi
  • ManagedOOMMemoryPressure: systemd-oomd entegrasyonu için bellek baskısı yönetimi

Sonuç

systemd slice unit’leri, Linux kaynak yönetimini başka bir seviyeye taşır. Basit bir servis başlatıp durdurmaktan ibaret olmayan bu altyapı, özellikle çok kiracılı ortamlarda, kritik iş yüklerini izole etmeniz gereken durumlarda ve kapasite planlaması yapmanız gerektiğinde hayat kurtarır.

Pratikte önerim şu yönde: Yeni bir sunucu kurduğunuzda hemen üç temel slice oluşturun: kritik servisler için, iş yükü servisleri için ve düşük öncelikli arka plan işleri için. Her servisi uygun slice’a atayın ve systemd-cgtop ile düzenli olarak izleyin. Zamanla hangi iş yükünün ne kadar kaynak tükettiğini görür ve limitlerini gerçekçi şekilde ayarlarsınız.

Disk IO izolasyonu konusunda uyarı vereyim: IOReadBandwidthMax ve IOWriteBandwidthMax direktifleri SSD’lerde beklediğiniz kadar hassas çalışmayabilir, özellikle NVMe disklerde cgroup IO kontrolünün etkinliği donanıma göre değişir. Bu nedenle kritik ortamlarda mutlaka yük testi yapın ve ölçerek ilerleyin.

Slice tabanlı kaynak yönetimi, ulimit ve benzeri eski yöntemlere kıyasla çok daha dinamik ve yönetilebilir bir yapı sunar. Özellikle systemctl set-property ile anında müdahale edebilmek, production ortamında ne değer biçilemez bir özelliktir. Bir kriz anında servis dosyasını düzenleyip reload etmek yerine tek komutla limiti değiştirmek, o gerilimli anları çok daha yönetilebilir kılar.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir