systemd bağımlılıkları ve koşulları nasıl yapılandırılır?
Requires, Wants, After ve Condition direktiflerinin farklarını öğrenin. Unit dosyalarınızın neden açılışta başarısız olduğunu ve servis sıralama hatalarını nasıl çözeceğinizi inceleyin.
Requires, After anlamına gelmez
systemd bağımlılıkları ve koşulları, çoğu unit dosyasında tek bir mekanizmaymış gibi kullanılan dört ayrı mekanizmadan oluşur. Requires= ve Wants=, hangi diğer unit'lerin sisteme dahil edileceğine karar verir. After= ve Before=, unit'lerin hangi sırada başlayacağını belirler. ExecStartPre=, unit'in başarısız olmasına neden olabilecek bir denetim çalıştırır. Condition ve Assert aileleri ise unit'in hiç çalışıp çalışmayacağına karar verir. Her mekanizma diğerlerinden bağımsızdır; bu nedenle bir unit başka bir unit'i gerektirebilir (require) ve yine de onunla aynı anda başlayabilir.
Bu son cümle, "elle başlattığımda çalışıyor ama açılışta başarısız oluyor" şeklindeki neredeyse tüm hata raporlarının arkasındaki sorundur.
[Unit]
Description=Inventory API
Requires=postgresql.service
[Service]
ExecStartPre=/usr/bin/pg_isready -h 127.0.0.1 -t 5
ExecStart=/usr/local/bin/inventory-apiRequires=postgresql.service, PostgreSQL'i aynı başlatma işlemine dahil eder. Ancak PostgreSQL'in hazır olmasını beklemez. systemd her iki işi de paralel olarak başlatır; bu yüzden pg_isready, PostgreSQL henüz veri dizinini açarken çalışmaya başlar. Henüz dinleyen bir servis olmadığı için 2 hata koduyla çıkar ve unit, ExecStart aşamasına hiç ulaşamadan başarısız olur. Bir saat sonra sudo systemctl start inventory-api komutunu çalıştırmak işe yarar, çünkü o zamana kadar PostgreSQL çoktan ayağa kalkmıştır. Unit dosyasında hiçbir şey değişmediği için dosya kusursuz görünür.
Çözüm tek bir satırdan ibarettir.
[Unit]
Requires=postgresql.service
After=postgresql.serviceAynı noktada daha kritik bir detay gizlidir. Başarısız olan bir Requires= bağımlılığı, yalnızca ona After= ayarını da eklediğinizde unit'inizin başlamasını engeller. Sıralama (ordering) olmadan, systemd diğer unit başarısız olduğunda sizin unit'inizi çoktan başlatmış olur; dolayısıyla iptal edilecek bir şey kalmaz. Requires= tek başına, insanların beklediği korumayı sağlamaz. Özel bir nedeniniz yoksa, her Requires= ve her Wants= ifadesinin yanına mutlaka After= ekleyin.
Requires, Wants, Requisite ve BindsTo neyi taahhüt eder
Bunların hepsi bağımlılık ayarlarıdır. Hiçbiri herhangi bir sıralama yapmaz.
Wants=: diğer birimi sisteme dahil eder. Eğer diğer birim başarısız olursa veya mevcut değilse, bu birim yine de başlatılır.systemctl enable, bir.wants/dizini içinde sembolik bağ (symlink) oluşturduğunda bu gerçekleşir.Requires=: diğer birimi sisteme dahil eder. Eğer diğer birim başarısız olursa ve siz deAfter=ile sıralama yaptıysanız, bu birim başlatılmaz. Eğer diğer birim daha sonra açıkça durdurulursa, bu birim de onunla birlikte durdurulur.Requisite=: diğer birimi sisteme dahil etmez. Eğer diğer birim zaten aktif değilse, bu birim derhal başarısız olur.BindsTo=:Requires=gibidir; ayrıca bu birim, donanımın kaybolması dahil herhangi bir nedenle diğer birim durduğunda kendiliğinden durur.PartOf=: durdurma ve yeniden başlatma komutları diğer birimden bu birime yayılır. Başlatma komutu yayılmaz.Conflicts=: bu birimi başlatmak, diğerini durdurur.
Bir daemon'ın diğeriyle iletişim kurduğu senaryolarda, genellikle Wants= ve After= doğru ikilidir. Requires= yaşam döngülerini birbirine bağlar: veritabanını bakım için durdurduğunuzda uygulamanız da onunla birlikte kapanır ve veritabanı geri geldiğinde uygulama kendiliğinden ayağa kalkmaz. Wants= ve After=, bu bağlama olmadan önyükleme sıralamasını sağlar; bağımlılığın daha sonra ortadan kaybolması durumu ise bir yeniden başlatma politikası (restart policy) ile yönetilir.
Ayrıca yazmadığınız bağımlılıkları da devralırsınız. Varsayılan olan DefaultDependencies=yes ile, standart bir servis ücretsiz olarak Requires=sysinit.target, After=sysinit.target basic.target ve Conflicts=shutdown.target özelliklerini kazanır. Bu nedenle, neredeyse boş bir [Unit] bölümüne sahip bir servis bile önyükleme sırasında geç başlatılır ve kapatma sırasında temiz bir şekilde durdurulur.
After ve Before sadece işlem sırasını belirler, başka bir işlevi yoktur
After= ve Before= tamamen sıralama ile ilgilidir. Herhangi bir bağımlılık şartı taşımazlar. Başka hiçbir birimin Redis'i çekmediği bir birimdeki After=redis.service ifadesi etkisizdir: Eğer redis.service işlemin bir parçası değilse, beklenecek bir şey yoktur ve biriminiz hemen başlatılır.
Bu durumun iki kez vurgulanması gerekir, çünkü aşağıda bahsedilen network-online.target hatasının tam olarak kaynağı budur. Sıralama, yalnızca aynı işlem dahilinde başlatılmakta olan birimleri bekler.
Bu ikili simetriktir. a.service içinde yazılan bir After=b.service, b.service içinde yazılan bir Before=a.service ile aynı anlama gelir; bu nedenle bunlardan birini kullanın ve kendi yönettiğiniz birimin içine yerleştirin. Kapatma sırasında sıralama otomatik olarak tersine çevrilir; dolayısıyla After=b.service, biriminizin b.service durdurulmadan önce durdurulacağı anlamına da gelir.
After= "başlatıldı" durumunu bekler, Type= ise bunun ne anlama geldiğini tanımlar
After=, diğer birimin başlatılma işlemini tamamlamasını bekler. "Başlatılma işleminin tamamlanması" ifadesinin ne anlama geldiği, tamamen ilgili birimin Type= ayarı tarafından belirlenir.
Type=simple: systemd süreci çatalladığı (fork) anda. Program henüz yapılandırma dosyasını ayrıştırmamış, soketini bile açmamış olabilir.Type=exec:execve()başarılı olduğu anda. Bir öncekine göre biraz daha güçlüdür. Yine de hazır olma durumu hakkında hiçbir şey ifade etmez.Type=forking: orijinal ana süreç sonlandığında.Type=oneshot: süreç sonlandığında. Burada "başlatıldı" ifadesi, işin gerçekten bittiği anlamına gelir.Type=notify: servis, bildirim soketi üzerindenREADY=1gönderdiğinde. Gerçek anlamda hazır olma durumunu bildiren tek tür budur.
Bu nedenle, bir Type=simple daemon üzerindeki After= zayıf bir vaattir ve ilk örnekteki yarış durumunun (race condition) ikinci yarısını oluşturur. Bağımlı olduğunuz birim Type=simple olarak yapılandırılmışsa, ondan sonra sıralanmış olması bağlantıları kabul ettiği anlamına gelmez. İki geçerli çözüm yolu vardır. Bunun yerine soket biriminden sonra sıralama yapın; böylece daemon hala başlatılırken çekirdek gelen bağlantıları kuyruğa alır. Veya kendi servisinizi yeniden deneme yapacak şekilde ayarlayın ve yeniden başlatma politikasının süreci yönetmesine izin verin. Bir birimin hangi türü kullandığı systemctl cat içinde görülebilir ve sıralama mekanizmalarına güvenmeden önce Type= ayarı ve her değerin systemd'ye ne anlattığı konusunu okumak faydalıdır.
ExecStartPre, birimi başarısız kılabilecek bir kapıdır
ExecStartPre=, ExecStart= öncesinde çalışır. Eğer sıfırdan farklı bir değerle çıkış yaparsa, aktivasyon iptal edilir ve birim failed durumuna geçer. ExecStart= asla çalıştırılmaz. Programın kendisi hiç başlatılmadığı için gerçek programdan hiçbir mesaj gelmeden başarısız olan birimlerin büyük çoğunluğunun arkasındaki mekanizma budur.
Kullanıcıları yanıltan gerçekler:
- Bu bir kabuk (shell) değildir. Pipe, yönlendirme, glob veya
&&kullanılamaz. İlk belirteç mutlak bir yol olmalıdır. Kabuk sözdizimine ihtiyaç duyduğunuzda satırı/bin/sh -c '...'içine alın. -ön eki, sıfırdan farklı bir çıkışın ölümcül olmamasını sağlar:ExecStartPre=-/usr/bin/optional-check.- Her
ExecStartPre=, bir sonrakisi çalışmadan önce çıkış yapmalıdır. Uzun süreli çalışan bir süreci başlatamaz. - Tüm
ExecStartPre=satırları,ExecStart=ile aynıTimeoutStartSec=değerini paylaşır. Veritabanını bekleyen bir döngüye sahip ön kontrol, başlatma zaman aşımını tüketir ve birim, günlüktestart operation timed out. Terminating.göründükten sonraResult: timeouthatasıyla başarısız olur.
Hata satırı, ana süreci değil, kontrol sürecini belirtir:
inventory-api.service: Control process exited, code=exited, status=2/INVALIDARGUMENT
inventory-api.service: Failed with result 'exit-code'.Bu sembolik ismi dikkatlice okuyun. systemd, küçük çıkış kodlarını sabit bir tablo üzerinden eşler; bu nedenle programın ne kastettiğinden bağımsız olarak 2 her zaman INVALIDARGUMENT olarak yazdırılır. status=203/EXEC, gerçek bilgiyi taşıyan koddur: yol yanlış olduğu veya dosya çalıştırılabilir olmadığı için systemd ikili dosyayı hiçbir şekilde yürütememiştir.
Dizin oluşturmak için ExecStartPre= kullanmayın. RuntimeDirectory=, StateDirectory=, LogsDirectory= ve CacheDirectory= dizinleri doğru sahip ve mod ile oluşturur; ayrıca RuntimeDirectory=, servis durduğunda temizlenir. Bunlar, el ile yazılmış bir mkdir komutunun aksine, DynamicUser= altında da doğru şekilde davranır.
Condition sessizce başarısız olur. Assert ise gürültülü bir şekilde hata verir.
Condition ve Assert aileleri aynı testleri çalıştırır. Aralarındaki tek fark, bir test başarısız olduğunda ne olacağıdır.
Başarısız bir Condition...=, birimi atlar. Başlatma işi başarılı olarak raporlanır. Birim inactive (dead) durumunda kalır, hiçbir şey başarısız olarak işaretlenmez, hiçbir uyarı tetiklenmez ve günlük kaydında tek bir satır oluşur:
Condition check resulted in Inventory API being skipped.systemd 250 ve sonraki sürümlerde, systemctl status nedenini doğrudan yazdırır:
Active: inactive (dead)
Condition: start condition unmet at Thu 2026-08-20 09:14:02 UTC; 2min agoAltındaki girintili satır, başarısız olan yönergeyi tam olarak belirtir; örneğin ConditionPathExists=/etc/inventory/api.conf was not met.
Başarısız bir Assert...=, birimi başarısız duruma düşürür. Günlük kaydında Assertion failed for Inventory API. ifadesi yer alır ve birim failed (Result: assert) durumunda sonlanır; bu durum, izleme sistemlerinin fark etmesi için yeterince belirgindir.
Bu ikisi arasında seçim yaparken, başarısız bir testin ne anlama geldiğini kendinize sorun. Condition, "bu birim bu makine için geçerli değil" anlamına gelir. Assert ise "bu doğru olmalı, değilse birine haber ver" demektir. Çoğu birim Condition kullanmak ister. Assert seçeneğine yalnızca sessizce hiçbir şey yapmamanın, birimin başarısız olmasından daha kötü olduğu durumlarda başvurun.
Condition ailesiyle ilgili iki tuzak vardır.
Birincisi, başarısız bir koşul, kendisine bağımlı olan birimleri başarısız kılmaz. Eğer a.service, Requires=b.service yönergesine sahipse ve b.service bir koşul nedeniyle atlanırsa, b.service için başlatma işi yine de tamamlanmış sayılır; dolayısıyla a.service, b çalışmıyorken normal şekilde başlar. Bir koşul yalnızca yazıldığı birimi korur.
İkincisi, koşullar birim her başladığında, işin çalıştığı anda değerlendirilir. Bir VPS üzerindeki systemd zamanlayıcısı tarafından tetiklenen bir birim, art arda yüzlerce kez atlanabilir ve hiçbir zaman başarısız görünmeyebilir. Bu, çalışan ancak hiçbir şey yapmayan bir cron işi ile aynı türden sessiz bir işlemdir ve onu aynı şekilde bulursunuz: birimin çıkış durumuna güvenmek yerine günlük kayıtlarını okuyun.
Bir sunucuda bilinmesi gereken koşullar şunlardır:
ConditionPathExists=/etc/inventory/api.confve onun olumsuzuConditionPathExists=!/etc/inventory/api.conf.- Bir paketin oluşturduğu ancak boş bıraktığı bir yapılandırma dosyası veya veri dizini için
ConditionFileNotEmpty=veConditionDirectoryNotEmpty=. - Gerçek bir çekirdek arayüzüne ihtiyaç duyan bir birimin
ConditionVirtualization=!containertaşıyabilmesi içinConditionVirtualization=. Sunucunuzun ne raporladığınısystemd-detect-virtile kontrol edin. - Paylaşılan bir birim dosyasının iki farklı sunucuda nasıl farklı davranacağını belirleyen, ana bilgisayar adıyla veya makine kimliğiyle eşleşen
ConditionHost=. - Bir önyükleme parametresine veya minimum çekirdek sürümüne bağlı birimler için
ConditionKernelCommandLine=veConditionKernelVersion=.
Boş bir atama listeyi temizler; bir drop-in dosyasının, paketin sağladığı bir koşulu bu şekilde kaldırmasının yolu budur:
[Unit]
ConditionPathExists=
ConditionPathExists=/srv/inventory/api.confnetwork.target neden ağın hazır olduğu anlamına gelmez
network.target bir durum değil, bir eşitleme noktasıdır. Önyükleme sırasında bu hedeften sonra çalışacak şekilde sıralanmak, ağ yönetimi yazılımının başlatılmış olduğu anlamına gelir. Bu, bir arayüzün IP adresi aldığı veya internete giden bir rotanın var olduğu anlamına gelmez. Bu hedef çoğunlukla diğer yön için mevcuttur: After=network.target ile sıralanan bir birim, sistem kapatılırken ağ bağlantısı kesilmeden önce durdurulur.
network-online.target beklemeyi sağlayan hedeftir. Kullandığınız ağ yöneticisine bağlı olarak bir "wait-online" servisi tarafından desteklenir:
- Eğer ağ bağlantılarını systemd-networkd yönetiyorsa (netplan ile yapılandırılmış Ubuntu sunucularda standart durum budur)
systemd-networkd-wait-online.servicekullanılır. - NetworkManager kullanılıyorsa
NetworkManager-wait-online.servicekullanılır.
Daha eski ifupdown kurulumlarında aynı etki networking.service ile elde edilir. Hangi yöntemi kullanırsanız kullanın, hedefi doğru şekilde kullanmak tek satır değil, iki satır gerektirir.
[Unit]
Wants=network-online.target
After=network-online.targetnetwork-online.target varsayılan önyükleme işlemine dahil değildir ve hiçbir şey onu otomatik olarak çağırmaz. Sadece After= yazarsanız, hiç kuyruğa alınmamış bir birime göre sıralama yapmış olursunuz; dolayısıyla bu sıralama hiçbir işe yaramaz. Bu, daha önce açıklanan, en maliyetli biçimdeki "no-op" durumudur. Wants= satırı, hedefi işlem sırasına dahil eder; böylece After= satırının bekleyebileceği bir birim oluşur.
Bilinmesi gereken ikinci nokta, "çevrimiçi" (online) durumunun systemd tarafından değil, wait-online uygulaması tarafından tanımlandığıdır. systemd-networkd-wait-online, yönettiği bağlantılar yapılandırılmış bir duruma ulaştığında geri döner. DNS çözümlemesini veya herhangi bir uzak ana bilgisayara erişilebilirliği kontrol etmez.
Bu tanım, yaygın bir VPS hatasına yol açar. Netplan içinde tanımlanmış ancak hiçbir zaman adres atanmamış, özel bir ağa ait ikinci bir arayüze sahip bir makinede, bekleme servisi vazgeçene kadar orada bekler:
systemd-networkd-wait-online[612]: Timeout occurred while waiting for network connectivity.
systemd-networkd-wait-online.service: Failed with result 'exit-code'.Varsayılan zaman aşımı süresi 120 saniye olduğu için önyükleme süresi fazladan iki dakika uzar. Bunun iki çözümü vardır. Kullanılmayan arayüzü netplan dosyasında optional: true olarak işaretleyin; böylece networkd onu beklemeyi bırakır. Veya bekleme servisi üzerinde, önemsediğiniz bağlantıyı --interface= ile belirten ya da tek bir bağlantı ayağa kalktığında hemen geri dönmesini sağlayan --any parametresini geçiren bir drop-in dosyası ekleyin.
Daha da iyisi, bu hedefe ihtiyaç duymaktan kaçının. Birçok servis, yalnızca belirli bir adrese bağlandıkları ve önyükleme sırasında aşağıdaki gibi bir hata vererek başarısız oldukları için network-online.target sonrasına sıralanır:
nginx: [emerg] bind() to 203.0.113.10:443 failed (99: Cannot assign requested address)Çekirdek, o adres henüz hazır olmadığı için bağlama işlemini reddeder. net.ipv4.ip_nonlocal_bind=1 ayarını yapmak, bir sürecin henüz sunucuda bulunmayan bir adrese bağlanmasına izin verir; yeniden başlatma politikası ise geri kalan durumu yönetir. Tüm önyükleme sürecini ağın hazır olmasına göre geciktirmek, genellikle tek bir soketle ilgili olan bir sorun için oldukça ağır bir yöntemdir.
Çalışan bir sistemde gerçek systemd bağımlılıklarını görüntüleme
Yalnızca unit dosyasına bakarak çıkarım yapmayın. Drop-in dosyaları, .wants/ sembolik bağları ve örtük varsayılan bağımlılıklar, dosyada görünmeyen ek ilişkiler oluşturur.
systemctl cat inventory-api.serviceBu komut, unit dosyasını ve tüm drop-in dosyalarını, her bloğun üzerinde kaynak yolu olacak şekilde, uygulanma sırasına göre yazdırır. İlk olarak bunu çalıştırın. /etc/systemd/system/inventory-api.service.d/ içindeki beş satırlık bir geçersiz kılma (override), paketlenmiş dosyayı geçersiz kılar ve aksi takdirde görünmez kalır.
systemctl show inventory-api.service -p Requires -p Wants -p After -p Before -p ConditionResult -p AssertResultBu komut, drop-in dosyaları ve systemd tarafından eklenen örtük bağımlılıklar işlendikten sonra çözümlenmiş değerleri yazdırır. ConditionResult=no, "unit başarı raporu verdi ancak hiçbir şey yapmadı" sorusunun doğrudan cevabıdır.
systemctl list-dependencies inventory-api.service
systemctl list-dependencies --reverse inventory-api.service
systemctl list-dependencies --after inventory-api.service
systemctl list-dependencies --before inventory-api.serviceDüz biçim, Requires= ve Wants= dizinlerini aşağı yönlü tarar. --reverse, hangi unit'lerin sizinkini tetiklediğini gösterir; bu, açılışta onu başlatan hedefi bulmanızı sağlar. --after ve --before sıralamayı gösterir; bir şeyin gerçekten beklenip beklenmediğini anlamak istediğinizde okunması gereken ikili budur.
journalctl -b -u inventory-api.service --no-pager
journalctl -b -o short-precise -u inventory-api.service -u postgresql.serviceİkinci komut, iki unit'i milisaniyelik zaman damgalarıyla iç içe geçirir. Tahmin yürütmek yerine bir sıralama çakışmasını (race condition) bu şekilde kanıtlarsınız. pg_isready hatası, PostgreSQL database system is ready to accept connections günlüklerinden önce gerçekleşir ve aralarındaki boşluk doğrudan çıktıda görülür.
systemd-analyze verify /etc/systemd/system/inventory-api.service
systemd-analyze critical-chain inventory-api.serviceverify, unit dosyasını systemd'nin yapacağı şekilde yükler ve bilinmeyen yönergeleri, var olmayan unit'lere olan bağımlılıkları, sıralama döngülerini ve ayrıştıramadığı sözdizimini raporlar. Sistem üzerinde hiçbir şeyi değiştirmez. critical-chain, unit'i geciktiren sıralama zincirini, her adımın aktif hale geldiği süreyle birlikte yazdırır ve yalnızca mevcut açılış sırasında başlatılan bir unit için çalışır.
Herhangi bir unit dosyasını düzenledikten sonra sudo systemctl daemon-reload komutunu çalıştırın. Paketlenmiş bir unit'i değiştirmek için, sizin yerinize bir drop-in dosyası oluşturan sudo systemctl edit inventory-api.service komutunu kullanın. /usr/lib/systemd/system/ altındaki satıcı dosyasını düzenlemek, bir sonraki paket yükseltmesi onu değiştirene kadar çalışır. Aynı drop-in mekanizması, paketin sahip olduğu bir dosyaya dokunmadan bir servise bellek ve CPU limitleri atamanızı sağlar.
Sıralama döngüleri ve günlük kayıtlarında bıraktıkları izler
Sıralamayı her iki yönde de eklerseniz, systemd işlerden birini silerek döngüyü kırar:
systemd[1]: Found ordering cycle on inventory-api.service/start
systemd[1]: Job postgresql.service/start deleted to break ordering cycle starting with inventory-api.service/startsystemd hangi işin silineceğini seçer ve bu seçim sizin tercih ettiğiniz iş olmayabilir. Sonuç, bazı yeniden başlatmalardan sonra eksik olan, bazılarında ise mevcut olan bir servis olarak ortaya çıkar; bu durum dışarıdan bakıldığında hata ayıklaması oldukça zor bir sorundur. Çoğu döngü, DefaultDependencies=no ayarını yapan ve ardından yine de kendisini basic.target birimine göre sıralayan birimlerden veya halihazırda size geri dönen bir After= değerine sahip bir birime Before= eklenmesinden kaynaklanır. systemd-analyze verify, yeniden başlatmaya gerek kalmadan bunları tespit eder.
Sabit birim
[Unit]
Description=Inventory API
Wants=postgresql.service network-online.target
After=postgresql.service network-online.target
ConditionPathExists=/etc/inventory/api.conf
[Service]
Type=notify
StateDirectory=inventory
ExecStart=/usr/local/bin/inventory-api
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.targetHer satır tek bir iş yapar. Wants=, her iki bağımlılığı da birimin ömrünü onlara bağlamadan işleme dahil eder. After= bekleme işlemini gerçekleştirir; bağımlılık ve sıralama ayrı ayarlar olduğu için her iki ismi de tekrar etmesi gerekir. ConditionPathExists=, pakete sahip olan ancak yapılandırması bulunmayan bir makinenin birimi hata vermek yerine sessizce atlaması anlamına gelir; bu, yapılandırma odaklı bir servis için doğru davranıştır. Type=notify, bu birimden sonra sıralanan her şeyin bir fork işleminden ziyade gerçek hazır olma durumunu beklemesi gerektiğini belirtir. Restart=on-failure, sıralama yalnızca ilk başlatma için geçerli olduğundan, veritabanının önyüklemeden çok sonra devre dışı kalması durumunu kapsar. Bu yeniden denemenin ne kadar agresif olması gerektiği, Restart= ve RestartSec= ayarları ile kontrol edilir.
Güvenmeden önce kontrol edin:
sudo systemctl daemon-reload
systemd-analyze verify /etc/systemd/system/inventory-api.service
systemctl list-dependencies --after inventory-api.service
sudo systemctl start inventory-api.service
systemctl show inventory-api.service -p ConditionResult -p ActiveState -p ResultSağlıklı bir birim ConditionResult=yes dosyasını ActiveState=active ile okur ve Result=success son çalıştırmada hiçbir şeyin başarısız olmadığını doğrular. ConditionResult=no ile birlikte ActiveState=inactive, birimin atlandığı anlamına gelir; journal üzerinde koşulu belirten satır, hangi testin başarısız olduğunu size bildirir.
FAQ
Requires= diğer birimin başlamasını bekler mi?
Hayır. Requires= ve After= ayrı ayarlardır. Requires=, diğer birimi aynı işleme dahil eder ve systemd her iki işi de paralel olarak başlatır. Bekleme yapmak için aynı birimi belirten bir After= eklenmelidir. Bunu eklemek için ikinci bir neden daha vardır: Yalnızca After= ayarlandığında başarısız olan bir Requires= bağımlılığı, biriminizin başlamasını engeller; çünkü sıralama belirtilmediğinde, diğer birim başarısız olduğunda sizin biriminiz zaten başlatılmış olur.
network.target mi yoksa network-online.target sonrasında mı sıralama yapmalıyım?
Açılış sırasında network.target, yalnızca ağ yönetim yazılımının başlatıldığı anlamına gelir; bu nedenle adresler veya rotalar hakkında hiçbir garanti vermez. Servisiniz başlangıçta çalışan bir adres gerektiriyorsa network-online.target kullanın ve hem Wants=network-online.target hem de After=network-online.target yazın; çünkü hedef varsayılan açılış işleminde yer almaz ve tek başına After=, kimsenin kuyruğa almadığı bir birimi bekler. Servis yalnızca belirli bir IP'ye bağlandığı için başarısız oluyorsa, Restart=on-failure ile birlikte net.ipv4.ip_nonlocal_bind=1 kullanmak, açılışı geciktirmekten daha hafif bir yöntemdir.
Birimim neden başarılı raporu veriyor ancak hiç çalışmıyor?
Başarısız bir Condition...= testi, birimi atlar ve başlatma işini başarılı olarak raporlar; bu nedenle hiçbir şey başarısız olarak işaretlenmez. systemctl show <unit> -p ConditionResult komutunu çalıştırın, ConditionResult=no bunu doğrulayacaktır. Ardından Condition check resulted in <description> being skipped satırı için journalctl -b -u <unit> dosyasını okuyun. systemd 250 ve daha yeni sürümlerde, systemctl status <unit> karşılanmayan yönergeyi tam olarak belirtir.
Condition ve Assert arasındaki fark nedir?
Aynı testleri çalıştırırlar. Başarısız bir Condition, birimi sessizce atlar ve iş yine de başarılı olur. Başarısız bir Assert ise birimi başarısız duruma düşürür, Assertion failed for <description>. günlüğünü kaydeder ve birimi failed (Result: assert) durumunda bırakır. "Bu birim bu makine için geçerli değil" durumu için Condition kullanın; bu, gerçek vakaların neredeyse tamamını kapsar. Assert'ü yalnızca eksik bir ön koşulun, başarısız birimleri izleyen kişiler tarafından görülmesi gerektiğinde kullanın.
ExecStartPre neden status=203/EXEC ile başarısız oluyor?
203/EXEC, systemd'nin komutu hiçbir şekilde yürütemediği anlamına gelir. Yaygın nedenler; mutlak olmayan bir yol, o makinede bulunmayan bir ikili dosya, çalıştırma izni olmayan bir dosya veya #! satırı eksik bir yorumlayıcıyı işaret eden bir betiktir. systemd'nin diğer küçük kodları sabit bir tablodan gelir, bu nedenle status=2/INVALIDARGUMENT sadece komutun 2 koduyla çıktığını belirtir ve argümanlar hakkında hiçbir şey söylemez. ExecStartPre='nin bir kabuk (shell) üzerinden çalıştırılmadığını unutmayın; bu nedenle pipe ve glob işlemleri için /bin/sh -c '...' gereklidir.