Cara Auto-start Docker Compose Semasa Boot
Ketahui cara menetapkan polisi restart: always atau restart: unless-stopped untuk servis Docker anda. Elakkan ralat on-failure dan guna systemd jika perlu urutan but.
Jawapan ringkas
Servis Docker Compose bermula semasa but apabila dua syarat dipenuhi serentak. Daemon Docker perlu didayakan sebagai servis sistem, dan setiap servis dalam fail tersebut mesti mempunyai polisi mula semula unless-stopped atau always. Tambahkan restart: unless-stopped pada setiap servis, jalankan docker compose up -d sekali, dan kontena akan kembali berjalan sendiri selepas but semula. Tiada perkara lain yang diperlukan untuk kes biasa.
Anda hanya memerlukan unit systemd apabila urutan adalah penting: tindanan (stack) yang bergantung pada cakera yang dilekapkan (mounted disk), antara muka VPN, atau perkongsian rangkaian yang belum sedia pada saat daemon Docker bermula. Kes tersebut adalah benar, dan separuh kedua panduan ini akan membincangkannya. Jika anda masih membiasakan diri dengan definisi servis dan volum, mulakan dengan asas Docker Compose pada VPS dan kembali semula ke sini.
Tetapkan polisi mula semula dalam compose.yaml
Polisi ini ditetapkan satu baris bagi setiap servis. Tiada suis global, jadi servis yang terlupa akan kekal mati selepas but semula sementara servis lain dalam stack tersebut berjalan.
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:Gunakan perubahan tersebut dan kemudian semak polisi daripada container yang sedang berjalan:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)Arahan itu akan memaparkan unless-stopped. Jika ia memaparkan no, fail tersebut telah disunting tetapi container tidak dicipta semula.
Ini adalah kegagalan yang paling kerap berlaku. Polisi mula semula disimpan pada container, bukan dalam fail YAML. Menyunting compose.yaml tidak mengubah apa-apa pada container yang sedia ada. docker compose restart juga tidak membantu, kerana ia hanya menghenti dan memulakan objek container yang sama tanpa menyentuh konfigurasinya. Hanya docker compose up -d yang membandingkan fail dengan container yang sedang berjalan, mengesan perubahan polisi, dan mencipta semula container tersebut.
Bagi container yang anda tidak mahu cipta semula buat masa ini, tukar polisi secara terus:
docker update --restart unless-stopped my-containerWalau bagaimanapun, anda tetap perlu menyunting fail YAML tersebut. docker update mengubah container yang sedang berjalan, dan docker compose up -d seterusnya akan membaca fail tersebut lalu menetapkan semula nilai lama.
Fungsi sebenar setiap nilai restart
Docker mentakrifkan empat nilai, dan perbezaan antara nilai tersebut hanya kelihatan apabila mesin but semula atau daemon dimulakan semula.
noialah nilai lalai. Kontena tidak akan dimulakan semula secara automatik dalam apa jua keadaan.alwaysmemulakan semula kontena setiap kali ia berhenti. Jika anda menghentikannya secara manual, ia tetap akan berjalan semula apabila daemon Docker bermula. Ini sering kali tidak dijangka: kontena yang anda hentikan dengan sengaja minggu lepas akan berjalan semula selepas but semula.unless-stoppedberkelakuan sepertialways, kecuali kontena yang dihentikan secara manual akan kekal berhenti merentasi permulaan semula daemon. Ini ialah nilai yang anda perlukan untuk servis yang anda hentikan sekali-sekala untuk penyelenggaraan.on-failurememulakan semula kontena hanya apabila ia keluar dengan kod keluar bukan sifar. Anda boleh mengehadkan bilangan percubaan, seperti dalamrestart: on-failure:3.
Untuk tindanan (stack) yang sepatutnya sentiasa berjalan apabila pelayan berjalan, unless-stopped ialah nilai lalai yang tepat. Pilih always hanya apabila anda mahukan kontena yang tidak akan kekal dalam keadaan berhenti.
Mengapa restart: on-failure tidak berfungsi selepas but semula
Ramai orang memilih on-failure kerana ia kedengaran lebih selamat, namun mendapati semua kontena terhenti selepas but semula yang pertama. Sebabnya terletak pada takrifan polisi tersebut. on-failure hanya bertindak balas terhadap satu perkara sahaja: proses kontena yang keluar dengan kod ralat.
But semula bukanlah satu ralat. Apabila hos dimatikan, systemd menghentikan docker.service, dan daemon tersebut menghentikan setiap kontena secara sengaja. Kontena tersebut tidak gagal, jadi polisi itu tidak mempunyai apa-apa untuk ditindakbalas. Semasa sistem dihidupkan semula, daemon menyemak kontena yang perlu disambung semula, dan kontena on-failure yang dihentikan secara bersih bukanlah salah satu daripadanya. Ia kekal dalam keadaan exited.
Anda boleh melihatnya secara terus. Tetapkan restart: on-failure pada sesuatu servis, jalankan docker compose up -d, but semula, kemudian jalankan:
docker compose ps -aServis tersebut disenaraikan dengan keadaan Exited dan status seperti Exited (0) 2 minutes ago. Tiada apa-apa yang rosak dan tiada apa-apa yang direkodkan sebagai ralat, itulah sebabnya perkara ini sukar untuk didiagnosis. Polisi tersebut melakukan tepat seperti yang dinyatakan.
on-failure masih berguna. Ia sesuai untuk kontena yang menjalankan tugasan dan mungkin terhempas, di mana anda mahukan bilangan percubaan semula yang terhad dan tiada gelung mula semula. Ia adalah alat yang salah untuk memastikan servis yang berjalan lama kekal aktif merentasi but semula.
Dasar mulakan semula hanya berfungsi jika servis Docker bermula semasa but
Dasar mulakan semula dikuatkuasakan oleh daemon Docker. Jika daemon tidak bermula, tiada apa yang akan dikuatkuasakan. Semaknya:
systemctl is-enabled docker
systemctl is-enabled containerdKedua-duanya sepatutnya memaparkan enabled. Pakej daripada repositori rasmi Docker mendayakannya semasa pemasangan, jadi pada pelayan baharu perkara ini biasanya sudah tersedia. Jika mana-mana daripadanya memaparkan disabled, betulkannya:
sudo systemctl enable --now docker containerdTerdapat satu perangkap yang perlu difahami di sini. Ubuntu juga membekalkan docker.socket, yang memulakan daemon atas permintaan pada kali pertama sesuatu berkomunikasi dengan API Docker. Pengguna melihat docker.socket didayakan, menganggap daemon sudah dilindungi, lalu menyahdayakan docker.service untuk menjimatkan memori. Semasa but, tiada apa yang memanggil API, jadi soket tidak disentuh, daemon tidak bermula, dan tiada kontena yang naik sehingga anda menaip arahan docker pertama anda. Pengaktifan soket bukanlah pengganti kepada docker.service yang didayakan.
Apabila unit systemd menjadi pilihan yang lebih baik
Polisi mulakan semula (restart policies) tidak mempunyai konsep penyusunan berbanding bahagian sistem yang lain. Daemon bermula, dan ia akan menaikkan kontena anda secepat mungkin. Jika stack anda melakukan bind-mount pada direktori daripada volum berasingan, perkongsian NFS (network file system), atau cakera yang disulitkan, kontena mungkin bermula sebelum laluan tersebut wujud. Docker akan mencipta direktori kosong pada titik lekap (mount point) dan memulakan kontena tersebut, menyebabkan pangkalan data anda naik tanpa data.
Tulis unit systemd apabila mana-mana keadaan ini berlaku. Stack tersebut memerlukan lekap (mount), antara muka VPN, atau unit lain untuk bersedia terlebih dahulu. Anda mahu systemctl stop myapp dan systemctl start myapp berfungsi seperti perkhidmatan lain pada mesin tersebut. Atau anda mahu stack tersebut ditutup dengan sempurna semasa proses shutdown dan bukannya dimatikan secara paksa bersama-sama daemon. Jika unit systemd adalah perkara baharu bagi anda, menulis perkhidmatan dan pemasa systemd membincangkan format fail tersebut dengan lebih terperinci.
Menulis unit systemd
Letakkan stack tersebut di laluan tetap di luar direktori home. /srv/myapp ialah pilihan yang baik, kerana unit yang berjalan sebelum sesiapa log masuk tidak sepatutnya membaca /home.
Cipta /etc/systemd/system/myapp.service:
[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.targetDayakan dan mulakannya:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.serviceUnit yang sihat akan memaparkan Active: active (exited). Ini kelihatan salah pada kali pertama anda melihatnya. Ia sebenarnya betul: Type=oneshot dengan RemainAfterExit=yes bermaksud unit tersebut telah menjalankan arahannya, arahan itu telah selesai, dan systemd mengekalkan unit tersebut sebagai aktif supaya ExecStop berjalan semasa penutupan sistem.
Setiap baris mempunyai fungsinya. Requires=docker.service bermaksud unit akan gagal dengan pantas dan bukannya menjalankan docker compose pada soket yang tidak berfungsi. After= menetapkan urutan, kerana Requires= secara sendirinya tidak melakukan perkara tersebut. RequiresMountsFor= menyebabkan systemd menarik unit mount untuk laluan tersebut dan menunggunya, yang merupakan sebab utama menggunakan unit berbanding polisi restart. TimeoutStartSec=0 menghalang systemd daripada mematikan tugas permulaan semasa imej yang besar masih sedang dimuat turun.
Nota tentang menggabungkan kedua-dua mekanisme. Dokumentasi Docker menasihatkan agar tidak mencampurkan polisi restart dengan pengurus proses hos. Amaran tersebut adalah mengenai pengurus proses yang menyelia proses kontena itu sendiri dan memulakannya semula semasa daemon cuba melakukan perkara yang sama. Unit Type=oneshot tidak menyelia apa-apa, jadi mengekalkan restart: unless-stopped dalam fail compose di samping unit ini adalah wajar, dan itulah yang anda perlukan. systemd mengendalikan urutan semasa but, dan daemon mengendalikan kontena yang terhenti pada pukul tiga pagi.
Unit tersebut kelihatan berbeza apabila perkara yang anda kekalkan adalah proses jangka panjang biasa dan bukannya stack, kerana dalam keadaan itu tiada daemon di bawahnya dan Restart= milik systemd sendiri perlu melakukan penyeliaan; menjalankan dsh tanpa kepala di sebalik systemd ialah contoh kerja bagi bentuk tersebut, sehingga ke pengguna khusus dan jurnal.
Sahkan dengan but semula sebenar
Tiada pengganti bagi ujian sebenar. systemctl restart docker tidak menguji urutan lekap (mount ordering), dan docker compose down diikuti dengan docker compose up -d tidak menguji apa-apa tentang proses but.
sudo rebootTunggu, sambung semula, dan semak mengikut urutan ini:
uptime
systemctl is-active docker
docker compose psuptime mengesahkan anda sedang melihat mesin yang benar-benar telah but semula. docker compose ps, yang dijalankan dari direktori stack, sepatutnya menyenaraikan setiap servis sebagai running dengan masa hidup (uptime) yang hampir sama dengan mesin tersebut. Servis yang menunjukkan Exited adalah servis yang perlu diperiksa.
Jika sesuatu tidak bermula, log daemon merangkumi tetingkap masa but:
journalctl -u docker.service -b --no-pager | tail -50Bagi stack yang diuruskan oleh unit, journalctl -u myapp.service -b --no-pager menunjukkan output docker compose yang tepat dari proses but, termasuk kegagalan menarik imej (image pull) atau fail .env yang hilang. But semula yang anda jadualkan adalah but semula yang anda pantau, jadi biarkan unit memberitahu anda tentang but semula yang tidak anda pantau: baris OnFailure= yang dihalakan ke pelayan ntfy yang dihoskan sendiri menukarkan stack yang gagal bermula semula menjadi pemberitahuan tolak (push notification) dan bukannya sesuatu yang anda sedari beberapa hari kemudian.
Perkara yang menyebabkan auto-start gagal secara senyap
Kontena yang dicipta dengan docker compose run tidak akan menerima polisi restart daripada fail tersebut. Compose menganggapnya sebagai kontena sekali guna. Jika sesuatu servis kelihatan mengabaikan polisinya, semak sama ada ia dimulakan dengan run dan bukannya up.
Laluan relatif dalam volume atau dalam entri env_file diselesaikan berdasarkan direktori fail compose. Ini berfungsi daripada shell anda, dan ia berfungsi daripada unit yang menetapkan WorkingDirectory. Ia gagal daripada unit yang tidak mempunyainya, kerana direktori kerja kemudiannya menjadi /.
Rootless Docker adalah kes yang berasingan. Daemon berjalan sebagai servis pengguna, dan servis pengguna akan berhenti apabila sesi terakhir untuk pengguna tersebut berakhir. Aktifkan ia untuk pengguna dan benarkan ia terus berjalan walaupun tiada sesi yang log masuk:
systemctl --user enable docker
sudo loginctl enable-linger $USERTanpa enable-linger, daemon rootless akan ditutup apabila anda log keluar dan kontena akan turut terhenti, yang kelihatan sama seperti polisi restart yang rosak.
Satu perkara terakhir. Kemas kini keselamatan automatik boleh melakukan reboot pada pelayan pada jam yang ditetapkan, yang hanya menjadi perkara baik jika stack anda kembali berjalan dengan sendirinya. Menetapkan perkara itu pada mesin baharu adalah sebahagian daripada kerja jam pertama dalam sepuluh minit pertama pada VPS baharu.
FAQ
Apakah perbezaan antara restart: always dan restart: unless-stopped?
Kedua-duanya memulakan semula kontena apabila ia berhenti dengan sendiri. Perbezaannya timbul apabila anda menghentikan kontena secara manual. Dengan always, kontena akan bermula semula pada kali seterusnya Docker daemon dimulakan, jadi but semula akan membatalkan tindakan henti manual anda. Dengan unless-stopped, daemon akan mengingati bahawa kontena tersebut dihentikan secara sengaja dan membiarkannya. Gunakan unless-stopped kecuali anda secara khusus mahukan kontena yang tidak akan kekal berhenti.
Saya telah menambah restart: unless-stopped tetapi kontena masih tidak bermula selepas but semula. Mengapa?
Polisi tersebut wujud pada kontena, bukan dalam fail, dan kontena yang sedia ada tidak dikemas kini dengan menyunting YAML. Jalankan docker compose up -d supaya Compose menciptanya semula, kemudian sahkan dengan docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). Jika ia memaparkan no, bermakna kontena tersebut dicipta sebelum suntingan anda. Punca biasa yang lain ialah docker.service tidak diaktifkan, yang boleh anda semak dengan systemctl is-enabled docker.
Adakah saya memerlukan unit systemd jika saya sudah menggunakan polisi restart?
Biasanya tidak. Polisi restart sudah memadai untuk stack yang hanya memerlukan rangkaian, iaitu kebanyakan stack. Tambahkan unit apabila kontena bergantung pada sesuatu yang belum sedia apabila Docker daemon bermula, seperti cakera luaran, volum yang disulitkan, perkongsian NFS atau antara muka VPN. Unit tersebut memberikan anda kawalan susunan melalui After= dan RequiresMountsFor=, yang tidak dapat dilakukan oleh polisi restart.
Bagaimanakah cara untuk menghentikan stack secara kekal supaya ia tidak kembali pada but semula yang seterusnya?
Dengan unless-stopped, docker compose stop sudah memadai, kerana kontena yang dihentikan secara manual tidak akan disambung semula apabila daemon dimulakan semula. Dengan always, tindakan henti tidak mencukupi dan kontena akan kembali selepas but semula. Sama ada jalankan docker compose down, yang membuang kontena tersebut, atau tukar polisi terlebih dahulu dengan docker update --restart no my-container. Jika unit systemd menguruskan stack tersebut, jalankan sudo systemctl disable myapp.service juga, jika tidak unit tersebut akan memulakannya semula.