Bir servisi her zaman ayakta tutmak zorunda mısınız? Sistem boot olduğunda onlarca servis ayağa kalkıyor, RAM yiyor, CPU kullanıyor ama belki o an hiçbiri aktif bağlantı almıyor. İşte systemd’nin socket activation özelliği tam bu sorunu çözmek için tasarlandı. Servisi sürekli çalıştırmak yerine, gelen bir bağlantı olduğunda otomatik olarak uyandırıyorsunuz. Bu yazıda socket activation’ın ne olduğunu, nasıl çalıştığını ve gerçek dünyada nasıl kullanabileceğinizi detaylıca inceleyeceğiz.
Socket Activation Nedir?
Socket activation, bir network servisinin sürekli çalışır halde tutulmak yerine, o servise gelen bir bağlantı isteği geldiğinde systemd tarafından otomatik olarak başlatılması mekanizmasıdır. Bu fikir aslında yeni değil. Eski Unix dünyasında inetd ve sonrasında xinetd aynı prensibi kullanıyordu. Systemd bu konsepti modern Linux sistemlerine taşıdı ve çok daha gelişmiş hale getirdi.
Normal bir servis modelinde şöyle düşünün: Nginx boot’ta başlar, port 80 ve 443’ü dinler, bağlantı bekler. Bağlantı gelmese bile process ayaktadır. Socket activation’da ise systemd önce soketi açar ve dinlemeye başlar. Servis henüz çalışmıyor. Bir bağlantı isteği geldiğinde systemd servisi başlatır ve soketi ona devreder. Servis işini bitirince tekrar kapanabilir, soket yine systemd’de kalır.
Bu yaklaşımın sağladığı avantajlar şunlardır:
- Lazy başlatma: Servis sadece ihtiyaç duyulduğunda başlar, gereksiz kaynak tüketimi olmaz
- Paralel boot: Servisler birbirini beklemeden paralel başlayabilir çünkü soketler hemen hazır hale gelir
- Otomatik yeniden başlatma: Servis çökse bile soket systemd’de kalmaya devam eder, yeni bağlantılar geldiğinde servis tekrar başlatılır
- Sıfır bağlantı kaybı: Servis yeniden başlatılırken gelen bağlantılar kaybolmaz, soket tampona alır
Socket ve Service Unit Dosyaları
Socket activation için iki ayrı unit dosyasına ihtiyacınız var: .socket ve .service. İsimlendirme önemli. myapp.socket ve myapp.service şeklinde aynı ismi paylaşmaları gerekiyor. Systemd bu isim eşleşmesine bakarak ikisini birbirine bağlıyor.
Önce basit bir soket unit dosyasına bakalım:
# /etc/systemd/system/myapp.socket
[Unit]
Description=MyApp Socket
Documentation=man:myapp(8)
[Socket]
ListenStream=8080
Accept=no
[Install]
WantedBy=sockets.target
Buna karşılık gelen servis dosyası:
# /etc/systemd/system/myapp.service
[Unit]
Description=MyApp Service
Requires=myapp.socket
After=myapp.socket
[Service]
Type=simple
ExecStart=/usr/local/bin/myapp
StandardInput=socket
[Install]
WantedBy=multi-user.target
Burada dikkat etmeniz gereken nokta StandardInput=socket satırı. Bu sayede servis başladığında stdin’den soket üzerinden veri okuyabilir.
Socket Unit Parametreleri
.socket dosyasındaki [Socket] bölümünde kullanabileceğiniz temel parametreler:
- ListenStream: TCP soketi için dinlenecek adres ve port. Örnek:
ListenStream=127.0.0.1:9000 - ListenDatagram: UDP soketi için kullanılır
- ListenSequentialPacket: Unix domain socket için sıralı paket dinleme
- ListenFIFO: FIFO (named pipe) dosyası dinler
- Accept:
yesolursa her bağlantı için ayrı servis instance’ı başlatılır,noolursa tüm bağlantılar tek servise gider - Backlog: Kuyrukta bekleyebilecek maksimum bağlantı sayısı
- SocketUser ve SocketGroup: Soketin sahibi olacak kullanıcı ve grup
- SocketMode: Unix soket dosyasının izin bitleri
- Service: Farklı bir servis ismi belirtmek için, varsayılan olarak aynı isimli servis kullanılır
Accept=yes ile Accept=no Farkı
Bu iki mod arasındaki fark kritik ve hangisini kullanacağınızı bilmek önemli.
Accept=no (varsayılan ve önerilen): Systemd soketi dinler, bir bağlantı geldiğinde servisi başlatır ve soketi o servise devreder. Tüm bağlantılar tek bir servis instance’ı tarafından yönetilir. Servisin kendi içinde concurrent bağlantıları handle etmesi gerekir.
Accept=yes: Her yeni bağlantı için systemd ayrı bir servis instance’ı başlatır. Bu eski inetd davranışına benzer. Her instance bağlantısını işleyip kapanır. Yüksek bağlantı sayısında çok fazla process oluşabilir, dikkatli kullanın.
# Accept=yes örneği - basit bir echo servisi için
# /etc/systemd/system/[email protected]
[Unit]
Description=Echo Service Instance %i
[Service]
Type=simple
ExecStart=/bin/cat
StandardInput=socket
StandardOutput=socket
StandardError=journal
# /etc/systemd/system/echo.socket
[Unit]
Description=Echo Socket
[Socket]
ListenStream=7
Accept=yes
[Install]
WantedBy=sockets.target
Accept=yes kullandığınızda servis dosyası @ ile template formatında olmalıdır.
Gerçek Dünya Senaryosu 1: PHP-FPM ve Nginx
Web sunucusu kurulumlarında PHP-FPM ile Nginx arasındaki iletişimi socket activation ile yönetebilirsiniz. Genellikle TCP port yerine Unix domain socket tercih edilir, çünkü daha hızlı ve güvenlidir.
# /etc/systemd/system/php-fpm.socket
[Unit]
Description=PHP FastCGI Process Manager Socket
PartOf=php-fpm.service
[Socket]
ListenStream=/run/php-fpm/php-fpm.sock
SocketUser=nginx
SocketGroup=nginx
SocketMode=0660
[Install]
WantedBy=sockets.target
Bu konfigürasyonla /run/php-fpm/php-fpm.sock dosyası systemd tarafından oluşturulur ve nginx kullanıcısına devredilir. PHP-FPM servisi çalışmıyor olsa bile Nginx soket dosyasını bulur. İlk istek geldiğinde PHP-FPM otomatik başlar.
Soket Aktivasyonunun Uygulamasında sd_notify
Servisiniz socket activation’ı desteklediğini systemd’ye bildirmek için sd_notify protokolünü kullanabilir. Bu özellikle Type=notify servislerinde önemlidir.
# /etc/systemd/system/mywebapp.service
[Unit]
Description=My Web Application
Requires=mywebapp.socket
After=mywebapp.socket
[Service]
Type=notify
ExecStart=/usr/local/bin/mywebapp --systemd
NotifyAccess=main
WatchdogSec=30s
Restart=on-failure
RestartSec=5s
User=webapp
Group=webapp
[Install]
WantedBy=multi-user.target
Uygulama READY=1 mesajını göndererek hazır olduğunu bildirir. Watchdog aktifken belirli aralıklarla WATCHDOG=1 göndermesi gerekir, aksi halde systemd servisi yeniden başlatır.
Gerçek Dünya Senaryosu 2: SSH Benzeri Servis
Bir SSH proxy servisi düşünelim. Normalde sürekli ayakta çalışır. Socket activation ile sadece bağlantı geldiğinde başlatabilirsiniz:
# /etc/systemd/system/sshproxy.socket
[Unit]
Description=SSH Proxy Socket
Documentation=https://example.com/sshproxy
[Socket]
ListenStream=2222
Accept=no
NoDelay=yes
DeferAcceptSec=1
[Install]
WantedBy=sockets.target
# /etc/systemd/system/sshproxy.service
[Unit]
Description=SSH Proxy Service
Requires=sshproxy.socket
After=sshproxy.socket network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/sshproxy
User=sshproxy
Group=sshproxy
PrivateTmp=yes
NoNewPrivileges=yes
ProtectSystem=strict
Restart=on-failure
RestartSec=3s
[Install]
WantedBy=multi-user.target
NoDelay=yes TCP Nagle algoritmasını devre dışı bırakır, gecikme hassas uygulamalar için önemlidir. DeferAcceptSec=1 ise sadece veri geldiğinde bağlantıyı kabul eder, boş TCP bağlantıları için servis başlatmaz.
Systemd Socket Activation’ı Etkinleştirme ve Yönetme
Konfigürasyonları yazdıktan sonra sisteme tanıtma işlemleri şöyle yapılır:
# Unit dosyalarını yeniden yükle
sudo systemctl daemon-reload
# Sadece soketi etkinleştir ve başlat
sudo systemctl enable --now myapp.socket
# Servisin kendisini enable etmeye gerek yok, soket halleder
# Ama istiyorsanız enable edebilirsiniz
sudo systemctl enable myapp.service
# Soket durumunu kontrol et
sudo systemctl status myapp.socket
# Servisi test et (bağlantı simüle et)
curl http://localhost:8080/
# Soket ve servis loglarını birlikte gör
journalctl -u myapp.socket -u myapp.service -f
Soket aktif durumda iken servis durumunu kontrol ettiğinizde şunu görürsünüz: soket listening modunda, servis ise inactive (dead). Bağlantı gelince servis active (running) olur.
# Mevcut soketleri listele
systemctl list-sockets
# Sadece aktif soketleri gör
systemctl list-sockets --state=active
# Soket üzerinden kaç bağlantı geldiğini gör
systemctl show myapp.socket -p NAccepted -p NConnections
Soket Dosya Tanımlayıcıları ve LISTEN_FDS
Socket activation’ı destekleyen bir uygulama yazmak istiyorsanız, systemd tarafından geçirilen soket file descriptor’larını nasıl alacağınızı bilmeniz gerekir.
Systemd, soket tanımlayıcılarını iki ortam değişkeni ile bildirir:
- LISTEN_FDS: Geçirilen soket sayısı. İlk soket her zaman fd=3’ten başlar
- LISTEN_PID: Hangi process için bu soketlerin hazırlandığı PID bilgisi
Basit bir Python örneği ile gösterelim:
#!/usr/bin/env python3
# /usr/local/bin/myapp
import os
import socket
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler
class SimpleHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(b"Merhaba! Socket activation calisiyor.n")
def log_message(self, format, *args):
pass # Sessiz mod
def get_systemd_socket():
"""Systemd tarafindan gecirilen soketi al"""
listen_fds = int(os.environ.get('LISTEN_FDS', 0))
listen_pid = int(os.environ.get('LISTEN_PID', 0))
if listen_fds == 0 or listen_pid != os.getpid():
return None
# Ilk soket fd=3'ten baslar
fd = 3
sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(True)
return sock
if __name__ == '__main__':
sock = get_systemd_socket()
if sock:
print("Systemd soketinden basliyorum...", file=sys.stderr)
server = HTTPServer(server_address=('', 0), RequestHandlerClass=SimpleHandler)
server.socket.close()
server.socket = sock
else:
print("Normal modda basliyorum...", file=sys.stderr)
server = HTTPServer(('127.0.0.1', 8080), SimpleHandler)
server.serve_forever()
Gerçek projelerde systemd Python kütüphanesini veya C’de libsystemd‘yi kullanmak çok daha temiz bir çözüm sunar. Go için coreos/go-systemd, Rust için systemd crate mevcut.
Paralel Boot Avantajı Detaylı İnceleme
Socket activation’ın en güçlü yanlarından biri paralel boot’tur. Klasik durumda şöyle bir bağımlılık zinciri oluşur:
Veritabanı başlar -> Uygulama başlar -> Web sunucusu başlar
Herbiri bir öncekinin tamamen hazır olmasını bekler. Socket activation ile:
Web sunucusu soketi hazır, Uygulama soketi hazır, Veritabanı soketi hazır (hepsi aynı anda)
İlk istek geldiğinde gerçekten ihtiyaç duyulan servisler başlar. Boot süresi dramatik şekilde kısalır.
# Boot süresini analiz et
systemd-analyze blame
# Socket activation etkisini gör
systemd-analyze critical-chain myapp.service
# Detaylı timing grafiği
systemd-analyze plot > boot-analysis.svg
Sorun Giderme
Socket activation ile ilgili en sık karşılaşılan sorunlar ve çözümleri:
Soket dosyası oluşmuyor veya izin hatası var:
# Soket birim durumunu detaylı gör
systemctl status myapp.socket -l
# Soket dosyasının varlığını ve izinlerini kontrol et
ls -la /run/myapp/
# SELinux veya AppArmor engeli olabilir
ausearch -m avc -ts recent | grep myapp
Servis başlamiyor, soket var ama bağlantı reddediliyor:
# Servisin soket fd'yi alıp almadığını loglardan gör
journalctl -u myapp.service --since "5 minutes ago"
# Ortam değişkenlerini kontrol et
systemctl show myapp.service | grep Environment
# Servisi manuel olarak soket ile test et
systemd-socket-activate -l 8080 /usr/local/bin/myapp
systemd-socket-activate komutu son derece kullanışlıdır. Gerçek bir servis oluşturmadan socket activation’ı test etmenizi sağlar.
Bağlantılar kuyrukta kalıyor, servis yavaş başlıyor:
# Backlog ayarını artır
# .socket dosyasına ekle:
# Backlog=512
# Servis başlama süresini izle
systemd-analyze critical-chain myapp.service
# TimeoutStartSec değerini artır
# .service dosyasına ekle:
# TimeoutStartSec=30
Güvenlik Avantajları
Socket activation güvenlik açısından da önemli avantajlar sunar. Soket systemd tarafından root yetkisiyle oluşturulur ve ardından servis düşük yetkili kullanıcıyla başlatılır. Bu sayede 80 gibi privileged portları dinlemek için root yetkisine ihtiyaç kalmaz.
# /etc/systemd/system/webapp.socket
[Unit]
Description=Web Application Socket
[Socket]
ListenStream=80
ReusePort=yes
[Install]
WantedBy=sockets.target
# /etc/systemd/system/webapp.service
[Unit]
Description=Web Application
Requires=webapp.socket
[Service]
Type=notify
ExecStart=/usr/local/bin/webapp
User=www-data
Group=www-data
# Servis düşük yetkili kullanıcıyla çalışır
# Ama port 80'i dinleyebilir çünkü soketi systemd hazırladı
DynamicUser=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/webapp
[Install]
WantedBy=multi-user.target
DynamicUser=yes ile systemd her çalıştırmada geçici bir kullanıcı oluşturur. Bu en yüksek güvenlik seviyesini sağlar.
Birden Fazla Soket Kullanımı
Bir servis birden fazla soket dinleyebilir. Örneğin hem HTTP hem HTTPS, veya hem IPv4 hem IPv6:
# /etc/systemd/system/dualstack.socket
[Unit]
Description=Dual Stack Web Socket
[Socket]
ListenStream=0.0.0.0:8080
ListenStream=[::]:8080
ListenStream=/run/webapp/webapp.sock
IPAddressAllow=any
[Install]
WantedBy=sockets.target
Bu durumda LISTEN_FDS=3 olacak ve servisiniz fd 3, 4, 5’ten soketleri alacak. Hangi soketin hangisi olduğunu bilmek için LISTEN_FDNAMES ortam değişkenini kullanabilirsiniz.
# Socket dosyasına isim ver
# [Socket] bölümüne ekle:
# FileDescriptorName=http
# FileDescriptorName=https
# Uygulamada kontrol et
listen_fdnames = os.environ.get('LISTEN_FDNAMES', '').split(':')
Monitoring ve Metrikler
Soket aktivasyonunun durumunu izlemek için birkaç pratik komut:
# Tüm soket unitlerinin özetini gör
systemctl list-units --type=socket
# Soket istatistikleri
systemctl show myapp.socket
-p ListenStream
-p NAccepted
-p NConnections
-p NRefused
# ss komutu ile soket durumunu gör
ss -tlnp | grep 8080
# Bağlantı sayısını izle
watch -n 1 "systemctl show myapp.socket -p NConnections"
Prometheus ile izleme yapıyorsanız node_exporter‘ın systemd collector’ı bu metrikleri otomatik olarak toplar. node_systemd_socket_accepted_connections_total gibi metrikler doğrudan kullanılabilir.
Sonuç
Systemd socket activation, modern Linux sistem yönetiminin önemli bir parçası. Özellikle mikroservis mimarileri, container ortamları ve kaynak kısıtlı sistemlerde ciddi avantajlar sunuyor. Özetlemek gerekirse:
- Kaynak tasarrufu açısından bakıldığında, servisler sadece ihtiyaç duyulduğunda başlıyor. Onlarca idle servis yerine systemd soketi tutuyor.
- Güvenilirlik konusunda, servis çöktüğünde soket hayatta kalıyor. Yeni bağlantılar gelince servis otomatik yeniden başlıyor, bağlantı kaybı yaşanmıyor.
- Boot performansı için soketler hızla hazır hale geliyor, servisler paralel başlıyor.
- Güvenlik tarafında, privileged port’lar için root yetkisi gereksiz hale geliyor, servisler düşük yetkiyle çalışabiliyor.
Başlangıç için önerim şu: Mevcut bir servisinizi alın, Accept=no ile basit bir .socket dosyası yazın ve systemd-socket-activate ile test edin. Uygulamanızın soket aktivasyonunu desteklemesi için büyük değişikliklere gerek yok, çoğu durumda mevcut kod olduğu gibi çalışır. Özellikle nginx, apache, postgresql gibi yaygın servisler zaten socket activation’ı destekliyor, sadece unit dosyalarını doğru yapılandırmanız yeterli.