Linux Systemd Detayları ve Sunucuda Servis Yazmak
Linux sunucunuzda sıfırdan bir servis yazmak veya elinizdeki bir scripti servise çevirmek için systemd ana processini kullanacağız. Böylece systemctl ve service komutlarıyla kendi servisinizi özgürce kontrol edebilir, başlangıçta otomatik çalışan programlar hazırlayabilir ve dilediğinizde programınızı açıp kapatabilirsiniz. Bu yazımızda RHEL, Centos 7 kullanarak örnekleri anlatacağız.
Önyüklemede işlemleri yönetme ve başlatma mekanizması değiştirildi. RHEL / CentOS 6.x’e kadar, /etc/init.d/
içinde bir betik oluşturup chkconfig
yardımıyla etkinleştirmiş olurdunuz, ancak RHEL / CentOS 7’de işler farklılaşıyor. Artık init.d yerine systemd ana processini kullanmamız gerekiyor ve systemd ana Linux dağıtımlarında default process manager olduğu için daha faydalı ve verimli bir kullanıma geçmiş oluyoruz.
1. Systemd Nedir?
Systemd programı, Linux ailesinde bulunur ve amacı; bilgisayardaki sistem ve servislerin çalışmasını organize etmektir. Bu yönetimi, systemctl
, journalctl
, notify
, analyze
, cgls
, cgtop
, loginctl
ve nspawn
olarak adlandırılan araçlar sayesinde gerçekleştirir.
Linux’taki her süreç şeffaf bir şekilde görünür olduğundan, systemd
‘nin nerede gizlendiğini görerek başlayalım. Sistemimde ps -ef | grep systemd
komutuyla aşağıdakileri alıyorum:
~$ ps -ef | grep systemd
root 1 0 0 15:29 ? 00:01:02 /lib/systemd/systemd --system --deserialize 22
message+ 768 1 0 15:29 ? 00:05:46 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
root 805 1 0 15:29 ? 00:10:22 /lib/systemd/systemd-logind
ankush 1132 1 0 15:29 ? 00:00:00 /lib/systemd/systemd --user
ankush 1176 1132 0 15:29 ? 00:04:50 /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
ankush 9772 20029 0 21:11 pts/6 00:00:00 grep --color=auto systemd
systemd+ 17298 1 0 15:29 ? 00:00:12 /lib/systemd/systemd-resolved
systemd+ 17303 1 0 15:29 ? 00:00:00 /lib/systemd/systemd-timesyncd
root 17307 1 0 15:29 ? 00:00:02 /lib/systemd/systemd-journald
root 18182 1 0 15:29 ? 00:00:00 /lib/systemd/systemd-udevd
Sizinde farkettiğinizi gibi listedeki ilk işlem root olarak başlatıldı ve pid 1’e sahip. Bu, sistemin önyükleme (boot) sırasında başlattığı ilk işlemdi. Systemd‘ye merhaba deyin 🙂
Dolayısıyla, oldukça basit bir şekilde systemd
, sistemdeki diğer işlemleri başlatan, yöneten ve sonlandıran, günlükler, dosya sistemi durumları vb. hakkında bilgi sağlamanın yanı sıra özetle “ana” işlemdir.
Systemd binary’si /usr/lib/systemd/systemd
dosyası olarak sistemde bulunmakta ve systemd rpm paketi tarafından kurulmaktadır. Systemd binary’si eski yapıdaki /sbin/init
programının yerini alır. Şu an için RHEL 7 ‘de zaten /sbin/init tam manasıyla /usr/lib/systemd/systemd dosyasına bir linktir.
1.1 Systemd Önemli Dizinler
Systemd ile çalışırken aşağıdaki dizin adreslerini bilmek işimizi kolaylaştıracaktır.
Dizin | Açıklama |
---|---|
/usr/lib/systemd/system/ | RPM paketleri systemd servislerinin yapılandırma dosyasını buraya kurmaktadır. |
/run/systemd/system/ | Runtime dediğimiz işletim sistemi çalışırken bu dizin kullanılır. |
/etc/systemd/system/ | Systemd servisleri enable veya disable edildiğinde /usr/lib/systemd/system dizinindeki dosyalar buraya linklenir. |
1.2 Unit
Systemd, unit adı verdiğimiz bileşen ve bunların kendi arasındaki ilişkilerinden oluşmaktadır. Bu yapılandırma dosyaları sadece servisler için değil runlevel’ların yerini alan target’lar, mount edilecek bileşenleri içeren kısımlar ve birçok bileşenden oluşabilmektedir. Bu dosyalar /usr/lib/systemd/system
dizini altında şu uzantılarla yer alabilmektedir.
Unit Tipi | Dosya Uzantısı | Açıklaması |
---|---|---|
Service unit | .service | Sistem servisi |
Target unit | .target | Systemd unit grubu (eski runlevel’lar) |
Automount unit | .automount | Automount edilecekler |
Device unit | .device | Kernel’ın tanıdığı cihaz dosyaları. |
Mount unit | .mount | Mount edilecek dosya sistemleri |
Path unit | .path | Dosya sistemindeki dosya veya dizinler |
Scope unit | .scope | Sonradan çalıştırılan harici programlar. |
Slice unit | .slice | Sistem süreçlerini yöneten belirli bir hiyerarşideki unit dosyaları. |
Snapshot unit | .snapshot | Systemd’nin snapshotları (kendimizin oluşturuduğu runlevel’lar denebilir) |
Socket unit | .socket | IPC için kullanılan dosyaları oluşturan unit dosyaları |
Swap unit | .swap | Swap dosyası veya swapleri aktive eden unit dosysaları |
Timer unit | .timer | Systemd timer |
1.3 Target
Systemd ile birlikte anmamız ve es geçmemiz bir konuda Target‘lardır. Yani eski isimleriyle runlevel‘lar. Sunucularımızın, sunduğu hizmetleri yerine getirilebilmesi için, bu hizmetlerin sunucu önyükleme işlemi sırasında çağırılması ve farklı seviyelere ulaşması gerekir. Bazı hizmetler, bilgisayar örneğin kurtarma düzeyine (run level 0) ulaştığında ve bazıları da multi-user (çok kullanıcı) düzeyine (run level 3) ulaştığında çalıştırılmak üzere çağırılır. Bu seviyeleri, targets (hedefler) olarak düşünebilirsiniz.
Farklı servislerin birleşmesi ile oluşan runlevel’lara artık target ismi verilmektedir. Bu runlevel’ların geriye dönük isimlendirilmeler korunsa da uzun vadede bu takma adlar (aliaslar) kaldırılacaktır. Eskiden 0-6 arası runlevel’lar varken yeni yapıdaki target’lar şu şekildedir.
Runlevel | Target Unit | Açıklama |
---|---|---|
0 | runlevel0.target, poweroff.target | Poweroff target |
1 | runlevel1.target, rescue.target | Rescue yani kurtarma target’ı |
2 | runlevel2.target, multi-user.target | Çoklu kullanıcı target’ı (runlevel 2,runlevel 3 ve 4 aynı) |
3 | runlevel3.target, multi-user.target | Çoklu kullanıcı target’ı (runlevel 2,runlevel 3 ve 4 aynı) |
4 | runlevel4.target, multi-user.target | Çoklu kullanıcı target’ı (runlevel 2,runlevel 3 ve 4 aynı) |
5 | runlevel5.target, graphical.target | Grafik ekranın başlatıldığı target |
6 | runlevel6.target, reboot.target | Reboot target |
Sisteminizin o an hangi target ile çalıştığını görmek için systemctl get-default
komutunu kullanabilirsiniz.
2. Neden RHEL Systemd Kullanmaya Başladı?
Yukarıdaki başlıkta gördüğünüz gibi, systemd bir sistem ve süreç yöneticisidir ve RHEL 7’de UpStart (init.d) programının yerini almaktadır. RHEL bu kararı neden aldı? Bunun için çok iyi nedenler var, o yüzden hızlıca bir göz atalım.
2.1 Paralel hizmet başlatma
SysV init programından farklı olarak systemd hizmetleri paralel olarak başlatma yeteneğine sahiptir. Init programını hizmetleri birer birer başlatırken systemd onları eş zamanlı olarak başlatır. Mobil cihazların bile çok çekirdekli CPU’lara sahip olduğu bir çağda, paralel başlatmanın olmaması init.d açısından büyük bir sorun.
2.2 Dinamik (hot) servis yönetimi
USB sürücülerin Ubuntu ve benzeri dağıtımlarda otomatik olarak açılırken önceki Fedora sistemlerinde mount edilmesi gerektiğini fark ettiyseniz, bunun nedeni systemd’dir. Donanımdaki canlı değişiklikleri algılayabilir ve sonlandırabilir.
2.3 Ertelenmiş hizmet başlatma
Systemd, hizmetin başlamasını gerçekten ihtiyaç duyulduğu zamana erteleyebildiği için önyükleme sürelerini kısaltır. Basit bir örnek olarak ağ dosya sistemi ile ilgili hizmetleri verebiliriz. Kullanılabilir ağa bağlı disk yoksa, çalışır durumda bir hizmetin olması mantıklı değildir.
2.4 Daha hızlı süreç iletişimi
Systemd’nin paralel yetenekleri süreçler arası iletişime geçer. Systemd, soketlere ve sistem veriyoluna paralel erişim sunarak iletişim kaynakları için işlem bekleme sürelerini önemli ölçüde azaltır.
2.5 Otomatik yeniden başlatma
Bir hizmet çökerse, systemd bunu algılayabilir ve yeniden başlatmayı deneyebilir. Çoğu zaman, daha temel sorunlar olmadıkça, bir uygulamanın yeniden çalışmaya başlaması için gereken tek şey basit bir yeniden başlatma işlemidir.
Kısa bir özetle daha kıyaslama yapacak olursak;
- SysV init script’leri ile tam uyumludur. Init.d altındaki scriptleri de çalıştırabilir.
- Servislerin parallel olarak başlatılabilmesi (dolayısıyla daha hızlı sistem açılışı ve kapanışı)
- Servislerin on-deamon aktivasyonu. Yani bir servisi başlatırken onun ihtiyacı olan diğer servislerin de otomatik başlatılması
- Sistem servislerinin snapshot’ının alınabilmesi (Yani basitçe ifade ile kendi runlevel’larınızı oluşturabilme diyebiliriz buna)
- Servis bir şekilde ölürse bunun tesbiti ve ilgili servisin tekrar başlatılması
3. Servis Nasıl Yazılır?
Systemd hakkında detaylı iki başlıktan sonra artık servisimizi yazmak için kolları sıvayalım. Bunun için ilk olarak root kullanıcısına geçiniz ve /etc/systemd/system/
dizinine giderek servis adımızla yeni bir dosyayı nano veya vim sayesinde oluşturarak açanız. Ardından aşağıdaki örnek şablon üzerinden kullanılabilecek parametrelerin neler oldukları konusunda detay verelim.
[Unit]
Description=Live
After=network.target
Requires=syslog.target
[Install]
WantedBy=multi-user.target
Alias=hello_kerteriz.service
[Service]
Type=simple
User=root
Group=wheel
Restart=always
RestartSec=1
ExecStart=/usr/bin/sh /home/kerteriz/sayhello.sh
ExecStop=/bin/kill ${MAINPID}
# Execute pre and post scripts as root
PermissionsStartOnly=true
- Description: Servis açıklaması
- After: Hangi servisten sonra çalışması gerektiğini söyler. Bu örnekte network servisi başlatıldıktan sonra servisimiz başlar.
- Requires: Servisimizin çalışabilmesi için hangi servisin çalışıyor olması gerektiğini belirleyebiliriz.
- WantedBy: Servisimizin hangi seviyede çalışacağını gösterir. Dolayısıyla, hizmetimizi etkinleştirirsek, multi-user.target.wants klasöründe, bu hizmete sembolik bir bağlantı oluşturulacaktır. Disable edildiğinde de silinir.
- Alias: Servisin ismi
- Type: simple ile herhangi bir zamanda yalnızca bir instance çalışacaktır.
- User=root: Hangi kullanıcıda çalışacağı
- Group=wheel: Hangi grupta çalışacağı
- Restart: Servis bir şekilde kapanırsa ne zaman restart edileceği. Biz her clean veya unclean signal bulunduğunda restart etmesi için (
always
) kullanıyoruz. Tabi bunun yanında; unclean exit code veya signal bulunduğunda restart edenon-failure
, unclean signal, watchdog veya timeout olduğunda restart edenon-abnormal
ve yalnızca clean bir signal veya return code ile durdurulduğunda restart edenon-success
parametreleri de mevcuttur. - RestartSec: Eğer yazılmazsa, sistem default 100ms sonra yeniden başlatmayı dener, biz 1 saniye sonra başlat dedik
- ExecStart: Çalıştırmak istediğimiz binary (sh) ve dosya adı (sayhello.sh)
- ExecStop: Servisi stop ettiğimizde çalışacak komut. Burada kill ediyoruz.
- StartLimitIntervalSec=10: Servis kapanırsa, ve yeniden başlattığında başarılı olamıyorsa, systemde 10 saniyelik bir süre ile 5 defa yeniden dener, başlatamazsa bir daha denemez.
Bu bilgiler ışığında oldukça basit ve minimum gereksinimlerle bir service dosyası oluşturalım:
cd /etc/systemd/system/
vim hello_kerteriz.service
[Unit]
Description=Say Hello Program
After=network.target
[Service]
Type=simple
User=root
Restart=always
ExecStart=/usr/bin/sh /home/kerteriz/sayhello.sh
ExecStop=/bin/kill ${MAINPID}
[Install]
WantedBy=multi-user.target
Ardından servis daemonunu yeniden başlatarak yaptığımız değişikliği algılamasını sağlayalım.
systemctl daemon-reload
Şimdi ise hem servisimizin başlangıçta çalışması (enable
) hemde şu anda çalışmaya başlaması (start
) için aşağıdaki komutla servisimizi başlatalım.
systemctl enable --now hello_kerteriz
Servisimizin çalışıp çalışmadığını görmek için status
çıktısına bakabiliriz.
systemctl status hello_kerteriz
Yazdığım basit /home/kerteriz/sayhello.sh
dosyasının içeriği şu şekildeydi ve 3 sn de bir çıktı dosyasına belirttiğim yazıyı yazıyordu;
echo "Merhaba Kerteriz Blog" >> /home/dataserver/say_out.txt
sleep 3
Bu shell komutlarınında çalışıp çalışmadığına bakalım;
Böylece yazdığımız scripti, programı vs. servise çevirerek tam kontrolü ele almış olduk. Takıldığınız yerleri sormak için aşağıdaki yorum kutusunu kullanabilirsiniz.