Docker Compose servislerini sistem açılışında başlatma
Docker Compose servislerini yeniden başlatma sonrası otomatik çalıştırmak için restart policy ayarlarını ve systemd birimi kullanımını adım adım öğrenin.
Kısa cevap
Docker Compose servisleri, iki koşul aynı anda sağlandığında sistem açılışında otomatik olarak başlar. Docker daemon'ın bir systemd servisi olarak etkinleştirilmiş olması ve dosyadaki her servisin unless-stopped veya always yeniden başlatma politikasına sahip olması gerekir. Her servise restart: unless-stopped ekleyin, docker compose up -d komutunu bir kez çalıştırın; konteynerler yeniden başlatma sonrasında kendiliğinden ayağa kalkacaktır. Genel durumlar için başka bir işleme gerek yoktur.
Yalnızca sıralamanın önemli olduğu durumlarda bir systemd birimine ihtiyaç duyarsınız: Docker daemon başladığı sırada henüz hazır olmayan mount edilmiş bir disk, VPN arayüzü veya ağ paylaşımına bağımlı bir yığın gibi. Bu durum gerçek bir gereksinimdir ve bu kılavuzun ikinci yarısı bunu ele almaktadır. Servis tanımları ve volume yapılandırmaları konusunda henüz yeniyseniz, VPS üzerinde Docker Compose temelleri ile başlayıp buraya geri dönün.
compose.yaml dosyasında yeniden başlatma politikasını ayarlama
Politika, her servis için tek satırlık bir tanımlamadır. Küresel bir anahtar bulunmadığından, yapılandırmayı unuttuğunuz bir servis yeniden başlatma sonrasında kapalı kalırken yığındaki diğer servisler çalışmaya devam eder.
services:
app:
image: nginx:1.27
restart: unless-stopped
ports:
- "8080:80"
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD: change-me
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:Politikayı uygulayın ve ardından çalışan container üzerinden mevcut politikayı okuyun:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)Bu komut unless-stopped çıktısını verir. Eğer no çıktısı alıyorsanız, dosya düzenlenmiş ancak container yeniden oluşturulmamış demektir.
Bu, en sık karşılaşılan hata türüdür. Yeniden başlatma politikası YAML dosyasında değil, container üzerinde saklanır. compose.yaml dosyasını düzenlemek, halihazırda var olan bir container üzerinde hiçbir değişikliğe yol açmaz. docker compose restart komutu da işe yaramaz; çünkü bu komut, yapılandırmasına dokunmadan aynı container nesnesini durdurup başlatır. Yalnızca docker compose up -d komutu dosyayı çalışan container'lar ile karşılaştırır, politikanın değiştiğini fark eder ve container'ları yeniden oluşturur.
Şu an yeniden oluşturmak istemediğiniz bir container için politikayı yerinde değiştirin:
docker update --restart unless-stopped my-containerYine de YAML dosyasını düzenlemeyi ihmal etmeyin. docker update canlı container'ı değiştirir ancak bir sonraki docker compose up -d komutu dosyayı okuyacak ve eski değeri geri yükleyecektir.
Her bir restart değerinin işlevi
Docker dört farklı değer tanımlar; bu değerler arasındaki farklar yalnızca makine yeniden başlatıldığında veya daemon yeniden başlatıldığında ortaya çıkar.
novarsayılan değerdir. Container hiçbir koşulda otomatik olarak yeniden başlatılmaz.always, container durduğu her durumda onu yeniden başlatır. Eğer container'ı manuel olarak durdurduysanız, Docker daemon bir sonraki sefer başladığında container yine çalışmaya başlar. Bu durum genellikle şaşırtıcıdır: geçen hafta bilerek durdurduğunuz bir container, sistem yeniden başlatıldıktan sonra tekrar çalışır durumda olur.unless-stopped,alwaysile benzer şekilde davranır; ancak manuel olarak durdurulan bir container, daemon yeniden başlatılsa bile durdurulmuş olarak kalır. Bakım amacıyla ara sıra kapattığınız servisler için tercih etmeniz gereken değer budur.on-failure, container yalnızca sıfır olmayan bir çıkış koduyla kapandığında onu yeniden başlatır. Yeniden başlatma denemelerinirestart: on-failure:3örneğinde olduğu gibi sınırlandırabilirsiniz.
Sunucu açık olduğu sürece çalışması gereken bir yığın (stack) için unless-stopped doğru varsayılan değerdir. always değerini ise yalnızca kapalı kalmasına izin vermek istemediğiniz container'lar için seçin.
Neden yeniden başlatma: on-failure bir sistem yeniden başlatmasından sonra çalışmaya devam etmez
Birçok kişi kulağa tedbirli geldiği için on-failure seçeneğini tercih eder, ancak ilk yeniden başlatmanın ardından tüm container'ların durduğunu fark eder. Bunun nedeni tanımın kendisinde gizlidir. on-failure yalnızca tek bir duruma tepki verir: container sürecinin bir hata koduyla sonlanması.
Yeniden başlatma bir hata değildir. Ana makine kapandığında systemd docker.service servisini durdurur ve daemon her bir container'ı kasıtlı olarak sonlandırır. Container başarısız olmadığı için politikanın tepki vereceği bir durum oluşmaz. Sistem tekrar açıldığında daemon devam ettirmesi gereken container'lara bakar; temiz bir şekilde durdurulmuş bir on-failure container'ı bunlar arasında yer almaz. Container, exited durumunda kalır.
Bunu doğrudan gözlemleyebilirsiniz. Bir servis üzerinde restart: on-failure ayarını yapın, docker compose up -d komutunu çalıştırın, sistemi yeniden başlatın ve ardından şunu çalıştırın:
docker compose ps -aServis, Exited durumunda ve Exited (0) 2 minutes ago benzeri bir statü ile listelenir. Hiçbir şey bozulmamıştır ve hata olarak kaydedilen bir durum yoktur; bu da durumu teşhis etmeyi zorlaştıran şeydir. Politika tam olarak tanımlandığı şekilde çalışmıştır.
on-failure hala kullanışlıdır. Bir işi çalıştıran ve çökebilecek, sınırlı sayıda yeniden deneme istediğiniz ve sonsuz yeniden başlatma döngüsüne girmesini istemediğiniz container'lar için uygundur. Uzun süre çalışan bir servisi yeniden başlatmalar boyunca ayakta tutmak için yanlış araçtır.
Yeniden başlatma politikaları yalnızca Docker servisi açılışta başlarsa çalışır
Yeniden başlatma politikaları Docker daemon tarafından uygulanır. Daemon başlamazsa hiçbir kural geçerli olmaz. Durumu kontrol edin:
systemctl is-enabled docker
systemctl is-enabled containerdHer iki komut da enabled çıktısını vermelidir. Docker'ın resmi deposundan alınan paketler kurulum sırasında bu ayarları otomatik etkinleştirir, bu nedenle yeni bir sunucuda genellikle sorun yaşanmaz. Eğer herhangi biri disabled çıktısı verirse, şu şekilde düzeltin:
sudo systemctl enable --now docker containerdBurada anlaşılması gereken önemli bir detay vardır. Ubuntu ayrıca, Docker API'sine ilk istek geldiğinde daemon'ı başlatan docker.socket servisini de içerir. Kullanıcılar docker.socket durumunun etkin olduğunu görünce daemon'ın koruma altında olduğunu varsayar ve bellek tasarrufu için docker.service servisini devre dışı bırakır. Açılış sırasında API'ye hiçbir çağrı yapılmadığı için socket tetiklenmez, daemon başlamaz ve siz ilk docker komutunu girene kadar hiçbir container ayağa kalkmaz. Socket aktivasyonu, docker.service servisinin etkinleştirilmesinin bir alternatifi değildir.
Hangi durumlarda systemd unit daha iyi bir çözümdür
Yeniden başlatma politikalarının (restart policies), sistemin geri kalanına göre bir sıralama mantığı yoktur. Daemon başlar ve container'larınızı mümkün olan en kısa sürede ayağa kaldırır. Eğer yığınınız (stack) ayrı bir birimden, bir NFS (network file system) paylaşımından veya şifreli bir diskten dizin bind-mount ediyorsa, container'lar ilgili yol henüz mevcut değilken başlayabilir. Docker, mount noktasında boş bir dizin oluşturup container'ı bunun üzerinde başlatmaktan çekinmez; bu durumda veritabanınız veri olmadan ayağa kalkar.
Aşağıdaki durumlardan herhangi biri geçerliyse bir systemd unit yazın. Yığının bir mount noktasına, bir VPN arayüzüne veya başka bir unit'in hazır olmasına ihtiyaç duyması. systemctl stop myapp ve systemctl start myapp komutlarının sunucudaki diğer tüm servislerde olduğu gibi çalışmasını istemeniz. Veya yığının kapatılma sırasında daemon ile birlikte aniden sonlandırılmak yerine düzgün bir şekilde durdurulmasını istemeniz. Eğer systemd unit yapıları sizin için yeniyse, systemd servisi ve zamanlayıcısı yazma konusu dosya formatını daha ayrıntılı olarak ele almaktadır.
systemd biriminin yazılması
Stack'i ev dizini dışında sabit bir yola yerleştirin. /srv/myapp iyi bir tercihtir; çünkü kimse oturum açmadan önce çalışan bir birimin /home dizinini okumasına gerek yoktur.
/etc/systemd/system/myapp.service dosyasını oluşturun:
[Unit]
Description=myapp docker compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
RequiresMountsFor=/srv/myapp/data
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.targetBunu etkinleştirin ve başlatın:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.serviceSağlıklı bir birim Active: active (exited) durumunu gösterir. İlk gördüğünüzde bu yanlış görünebilir ancak doğrudur: Type=oneshot ve RemainAfterExit=yes ifadesi, birimin komutunu çalıştırdığını, komutun tamamlandığını ve systemd'nin birimi aktif olarak işaretleyerek ExecStop komutunun kapanışta çalışmasını sağladığını belirtir.
Her satırın bir amacı vardır. Requires=docker.service, birimin ölü bir soket üzerinde docker compose çalıştırmak yerine hızlıca hata vermesini sağlar. After= sıralamayı belirler, çünkü Requires= tek başına bunu yapmaz. RequiresMountsFor=, systemd'nin bu yol için mount birimini çekmesini ve beklemesini sağlar; bir restart politikası yerine birim kullanmanın temel nedeni budur. TimeoutStartSec=0, büyük bir imaj çekilirken systemd'nin başlatma işini sonlandırmasını engeller.
İki mekanizmanın birleştirilmesi hakkında bir not: Docker dokümantasyonu, restart politikaları ile bir ana makine süreç yöneticisinin karıştırılmasını önermez. Bu uyarı, konteyner sürecini doğrudan denetleyen ve daemon aynı şeyi yapmaya çalışırken onu yeniden başlatan süreç yöneticileri içindir. Bir Type=oneshot birimi hiçbir şeyi denetlemez, bu nedenle restart: unless-stopped ayarını bu birimle birlikte compose dosyasında tutmakta bir sakınca yoktur ve yapılması gereken de budur. systemd önyükleme sırasındaki sıralamayı yönetir, daemon ise gece yarısı çöken bir konteyneri yönetir.
Canlı tuttuğunuz şey bir stack değil de uzun süre çalışan basit bir süreç olduğunda birim farklı görünür; çünkü bu durumda altında bir daemon yoktur ve denetleme işini systemd'nin kendi Restart= mekanizması yapmalıdır; dsh'yi systemd arkasında headless çalıştırma rehberi, özel kullanıcı ve journal kullanımı dahil olmak üzere bu yapı için işlenmiş bir örnektir.
Gerçek bir yeniden başlatma ile doğrulama
Gerçek testin yerini hiçbir şey tutamaz. systemctl restart docker bağlama (mount) sıralamasını test etmez; docker compose down komutunu takiben docker compose up -d komutunun çalıştırılması ise önyükleme süreciyle ilgili hiçbir şeyi sınamaz.
sudo rebootBekleyin, yeniden bağlanın ve şu sırayla kontrol edin:
uptime
systemctl is-active docker
docker compose psuptime, gerçekten yeniden başlatılmış bir makineye baktığınızı doğrular. Yığın dizininden çalıştırılan docker compose ps, her servisi running durumunda ve makinenin çalışma süresine yakın bir süreyle listelemelidir. Exited durumunu gösteren bir servis, incelenmesi gereken servistir.
Eğer bir servis ayağa kalkmadıysa, daemon günlüğü önyükleme aralığını kapsar:
journalctl -u docker.service -b --no-pager | tail -50Bir unit tarafından yönetilen yığınlar için journalctl -u myapp.service -b --no-pager, başarısız bir imaj çekme işlemi veya eksik bir .env dosyası dahil olmak üzere önyüklemeden gelen tam docker compose çıktısını gösterir. Planladığınız yeniden başlatma, izlediğiniz yeniden başlatmadır; bu nedenle, izlemedikleriniz hakkında sizi bilgilendirmesi için unit'ten yararlanın: kendi kendine barındırılan bir ntfy sunucusuna yönlendirilmiş bir OnFailure= satırı, ayağa kalkamayan bir yığını günler sonra fark edeceğiniz bir sorun olmaktan çıkarıp anlık bir bildirime dönüştürür.
Otomatik başlatmayı sessizce bozan durumlar
docker compose run ile oluşturulan container'lar, dosyadaki yeniden başlatma politikasını asla almaz. Compose bunları tek seferlik container'lar olarak ele alır. Bir servis politikasını yok sayıyor gibi görünüyorsa, up yerine run ile başlatılıp başlatılmadığını kontrol edin.
Bir volume veya env_file girdisindeki göreli yol, compose dosyasının dizinine göre çözümlenir. Bu, shell üzerinden çalışır ve WorkingDirectory ayarını yapan bir unit ile de çalışır. Ancak bu ayarın olmadığı bir unit'te başarısız olur, çünkü çalışma dizini bu durumda / olur.
Rootless Docker ayrı bir durumdur. Daemon bir kullanıcı servisi olarak çalışır ve kullanıcıya ait son oturum kapandığında kullanıcı servisi de durur. Servisi kullanıcı için etkinleştirin ve kimse oturum açmamışken çalışmaya devam etmesine izin verin:
systemctl --user enable docker
sudo loginctl enable-linger $USERenable-linger olmadan, rootless daemon oturumu kapattığınızda kapanır ve container'lar da onunla birlikte gider; bu durum tam olarak bozuk bir yeniden başlatma politikası gibi görünür.
Son bir husus. Otomatik güvenlik güncellemeleri sunucuyu belirli bir saatte yeniden başlatabilir; bu durum yalnızca stack'iniz kendi kendine ayağa kalkıyorsa iyi bir şeydir. Yeni bir makinede bunun ayarlanması, yeni bir VPS üzerindeki ilk on dakika içinde yapılması gereken diğer işlemlerle birlikte ele alınmalıdır.
FAQ
restart: always ve restart: unless-stopped arasındaki fark nedir?
Her ikisi de container kendi kendine durduğunda onu yeniden başlatır. Fark, container'ı elle durdurduğunuzda ortaya çıkar. always ile container, Docker daemon bir sonraki sefer başladığında tekrar çalışır; yani yeniden başlatma, manuel durdurma işleminizi geçersiz kılar. unless-stopped ile daemon, container'ın kasıtlı olarak durdurulduğunu hatırlar ve ona müdahale etmez. Container'ın kapalı kalmasını özellikle istemediğiniz durumlar haricinde unless-stopped kullanın.
restart: unless-stopped ekledim ancak container yeniden başlatma sonrasında hala çalışmıyor. Neden?
Yeniden başlatma politikası dosya içinde değil, container üzerinde tanımlıdır; mevcut bir container, YAML dosyası düzenlenerek güncellenmez. Compose'un container'ı yeniden oluşturması için docker compose up -d komutunu çalıştırın, ardından docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app) ile doğrulayın. Eğer çıktı no değerini döndürüyorsa, container düzenlemenizden önce oluşturulmuş demektir. Diğer yaygın bir neden ise docker.service özelliğinin etkin olmamasıdır; bunu systemctl is-enabled docker ile kontrol edebilirsiniz.
Yeniden başlatma politikaları kullanıyorsam systemd birimine ihtiyacım var mı?
Genellikle hayır. Yeniden başlatma politikası, yalnızca ağa ihtiyaç duyan yığınlar için yeterlidir; çoğu yığın bu kapsamdadır. Container'lar, Docker daemon başladığında henüz hazır olmayan harici bir disk, şifreli bir birim, NFS paylaşımı veya VPN arayüzü gibi bir kaynağa bağımlıysa bir birim ekleyin. Birim, yeniden başlatma politikasının ifade edemediği After= ve RequiresMountsFor= üzerinden sıralama yapmanıza olanak tanır.
Bir yığını, bir sonraki yeniden başlatmada geri gelmeyecek şekilde kalıcı olarak nasıl durdururum?
unless-stopped ile docker compose stop yeterlidir, çünkü elle durdurulan bir container daemon yeniden başladığında tekrar çalıştırılmaz. always ile durdurma işlemi yeterli değildir ve container yeniden başlatma sonrasında geri döner. Ya container'ları kaldıran docker compose down komutunu çalıştırın ya da önce politikayı docker update --restart no my-container ile değiştirin. Eğer yığını bir systemd birimi yönetiyorsa, sudo systemctl disable myapp.service komutunu da çalıştırın; aksi takdirde birim onu tekrar başlatacaktır.