SSD Nodes Learn RAM 8GB — $66/tahun
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-01

Docker Compose Auto-Mula Semasa Boot: Panduan

Ketahui cara perkhidmatan Docker Compose hidup semula selepas reboot, mengapa on-failure tidak mencukupi, dan bila unit systemd diperlukan.

Jawapan ringkas

Perkhidmatan Docker Compose bermula semasa but apabila dua syarat dipenuhi serentak. Daemon Docker mesti didayakan sebagai perkhidmatan sistem, dan setiap perkhidmatan dalam fail mesti mempunyai dasar mula semula unless-stopped atau always. Tambahkan restart: unless-stopped pada setiap perkhidmatan, jalankan docker compose up -d sekali, dan kontena akan bermula semula secara automatik selepas but semula. Tiada perkara lain diperlukan untuk kes biasa.

Anda hanya memerlukan unit systemd apabila susunan permulaan penting: contohnya, tindanan yang bergantung pada cakera yang dipasang, antara muka VPN atau perkongsian rangkaian yang belum bersedia ketika daemon Docker bermula. Keadaan ini memang berlaku, dan separuh kedua panduan ini menerangkannya. Jika anda masih mempelajari takrifan perkhidmatan dan volume, mulakan dengan asas Docker Compose pada VPS dan kembali ke sini.

Tetapkan dasar mula semula dalam compose.yaml

Dasar ini ditetapkan satu baris bagi setiap perkhidmatan. Tiada suis global. Oleh itu, perkhidmatan yang terlupa ditetapkan akan kekal berhenti selepas but semula, manakala perkhidmatan lain dalam tindanan bermula.

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 itu, kemudian baca semula dasar daripada kontena yang sedang berjalan:

docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)

Perintah itu memaparkan unless-stopped. Jika memaparkan no, fail telah diedit tetapi kontena tidak pernah dicipta semula.

Ini ialah punca kegagalan yang paling biasa. Dasar mula semula disimpan pada kontena, bukan dalam fail YAML. Mengedit compose.yaml tidak mengubah apa-apa pada kontena yang telah wujud. docker compose restart juga tidak membantu kerana perintah itu menghentikan dan memulakan objek kontena yang sama tanpa mengubah konfigurasinya. Hanya docker compose up -d membandingkan fail dengan kontena yang sedang berjalan, mengesan perubahan dasar dan mencipta semula kontena tersebut.

Untuk kontena yang tidak mahu dicipta semula sekarang, ubah dasar secara terus:

docker update --restart unless-stopped my-container

Edit fail YAML itu juga. docker update mengubah kontena yang sedang berjalan, manakala docker compose up -d seterusnya akan membaca fail tersebut dan mengembalikan nilai lama.

Fungsi sebenar setiap nilai restart

Docker mentakrifkan empat nilai. Perbezaannya hanya dapat dilihat apabila mesin but semula atau daemon dimulakan semula.

  • no ialah nilai lalai. Container tidak akan dimulakan semula secara automatik dalam apa-apa keadaan.
  • always memulakan semula container setiap kali container berhenti. Jika anda menghentikannya secara manual, container itu akan berjalan semula apabila Docker daemon dimulakan pada kali berikutnya. Keadaan ini sering mengejutkan: container yang sengaja anda hentikan minggu lalu berjalan semula selepas but semula.
  • unless-stopped berfungsi seperti always, kecuali container yang dihentikan secara manual kekal berhenti selepas daemon dimulakan semula. Gunakan nilai ini untuk perkhidmatan yang kadangkala dihentikan bagi tujuan penyelenggaraan.
  • on-failure memulakan semula container hanya apabila container keluar dengan kod keluar bukan sifar. Anda boleh mengehadkan bilangan percubaan, seperti dalam restart: on-failure:3.

Untuk stack yang sepatutnya sentiasa berjalan apabila server berjalan, unless-stopped ialah nilai lalai yang sesuai. Pilih always hanya jika anda mahu container yang tidak mudah dibiarkan dalam keadaan berhenti.

Mengapa dimulakan semula: on-failure tidak kekal selepas but semula

Ramai orang memilih on-failure kerana ia kelihatan lebih berhati-hati, kemudian mendapati semua kontena berhenti selepas but semula pertama. Sebabnya terdapat dalam definisinya. on-failure hanya bertindak balas terhadap satu perkara: proses kontena keluar dengan kod ralat.

But semula bukan ralat. Apabila hos dimatikan, systemd menghentikan docker.service, dan daemon menghentikan setiap kontena dengan sengaja. Kontena itu tidak gagal, jadi dasar tersebut tiada apa-apa untuk ditindak balas. Semasa sistem dimulakan semula, daemon menyemak kontena yang perlu disambung semula, dan kontena on-failure yang dihentikan dengan bersih bukan salah satunya. Kontena itu kekal dalam keadaan exited.

Anda boleh melihatnya secara langsung. Tetapkan restart: on-failure pada suatu perkhidmatan, jalankan docker compose up -d, but semula, kemudian jalankan:

docker compose ps -a

Perkhidmatan itu disenaraikan dengan keadaan Exited dan status seperti Exited (0) 2 minutes ago. Tiada apa-apa yang rosak dan tiada apa-apa direkodkan sebagai ralat, sebab itu masalah ini sukar didiagnosis. Dasar tersebut bertindak tepat seperti yang dinyatakan.

on-failure masih berguna. Dasar ini sesuai untuk kontena yang menjalankan tugas dan mungkin ranap, apabila anda mahukan bilangan percubaan semula yang terhad tanpa gelung mulakan semula. Namun, dasar ini bukan alat yang sesuai untuk memastikan perkhidmatan yang berjalan berterusan kekal aktif merentasi but semula.

Dasar mula semula hanya berfungsi jika perkhidmatan Docker bermula semasa but

Dasar mula semula dikuatkuasakan oleh daemon Docker. Jika daemon tidak bermula, tiada apa-apa yang menguatkuasakannya. Semak perkara ini:

systemctl is-enabled docker
systemctl is-enabled containerd

Kedua-duanya sepatutnya mencetak enabled. Pakej daripada repositori rasmi Docker mendayakannya semasa pemasangan, jadi pada pelayan baharu, semakan ini biasanya berjaya. Jika salah satu mencetak disabled, baikinya:

sudo systemctl enable --now docker containerd

Terdapat perangkap yang perlu difahami. Ubuntu turut menyediakan docker.socket, yang memulakan daemon atas permintaan apabila sesuatu mula-mula berkomunikasi dengan API Docker. Pengguna melihat docker.socket didayakan, menganggap daemon telah dikonfigurasikan, lalu melumpuhkan docker.service untuk menjimatkan memori. Semasa but, tiada apa-apa memanggil API, jadi soket tidak pernah diakses, daemon tidak pernah bermula dan tiada kontena dimulakan sehingga anda menaip perintah docker yang pertama. Pengaktifan soket bukan pengganti untuk mendayakan docker.service.

Apabila unit systemd ialah pilihan yang lebih baik

Dasar mula semula tidak mempunyai konsep susunan berbanding bahagian lain dalam sistem. Daemon bermula dan menghidupkan kontena anda sebaik sahaja boleh. Jika tindanan anda memasang bind direktori daripada volum berasingan, perkongsian NFS (sistem fail rangkaian) atau cakera yang disulitkan, kontena mungkin bermula sebelum laluan tersebut wujud. Docker akan mencipta direktori kosong pada titik pelekap dan memulakan kontena menggunakannya, lalu pangkalan data anda bermula tanpa data.

Tulis unit systemd jika mana-mana keadaan ini terpakai. Tindanan memerlukan pelekap, antara muka VPN atau unit lain tersedia terlebih dahulu. Anda mahu systemctl stop myapp dan systemctl start myapp berfungsi seperti unit tersebut untuk setiap perkhidmatan lain pada pelayan. Atau anda mahu tindanan dihentikan dengan teratur semasa penutupan, bukannya dimatikan bersama daemon. Jika unit systemd masih baharu bagi anda, menulis perkhidmatan dan pemasa systemd menerangkan format fail dengan lebih terperinci.

Letakkan tindanan dalam laluan tetap di luar direktori rumah. /srv/myapp ialah pilihan yang baik kerana unit yang dijalankan sebelum sesi log masuk bermula tidak perlu 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.target

Dayakan dan mulakannya:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.service

Unit yang sihat memaparkan Active: active (exited). Paparan ini kelihatan salah pada kali pertama anda melihatnya. Paparan ini betul: Type=oneshot dengan RemainAfterExit=yes bermaksud unit telah menjalankan perintahnya, perintah itu telah selesai, dan systemd mengekalkan unit sebagai aktif supaya ExecStop dijalankan semasa penutupan.

Setiap baris mempunyai tujuan. Requires=docker.service bermaksud unit gagal dengan segera dan bukannya menjalankan docker compose terhadap soket yang tidak aktif. After= menetapkan susunan kerana Requires= sahaja tidak berbuat demikian. RequiresMountsFor= menyebabkan systemd memuatkan unit lekap untuk laluan tersebut dan menunggunya. Inilah sebab utama menggunakan unit dan bukannya dasar mula semula. TimeoutStartSec=0 menghalang systemd daripada menghentikan tugas permulaan ketika imej besar masih sedang ditarik.

Perhatikan penggabungan kedua-dua mekanisme ini. Dokumentasi Docker menasihatkan supaya dasar mula semula tidak digabungkan dengan pengurus proses hos. Amaran itu merujuk kepada pengurus proses yang menyelia proses kontena itu sendiri dan memulakannya semula ketika daemon cuba melakukan perkara yang sama. Unit Type=oneshot tidak menyelia apa-apa, jadi mengekalkan restart: unless-stopped dalam fail compose bersama unit ini adalah selamat dan memang itulah yang diperlukan. systemd mengendalikan susunan semasa but, manakala daemon mengendalikan kontena yang ranap pada pukul tiga pagi.

Sahkan dengan but semula sebenar

Tiada pengganti bagi ujian sebenar. systemctl restart docker tidak menguji susunan pemasangan, manakala docker compose down diikuti docker compose up -d tidak menguji apa-apa berkaitan proses but.

sudo reboot

Tunggu, sambung semula, kemudian semak mengikut urutan ini:

uptime
systemctl is-active docker
docker compose ps

uptime mengesahkan bahawa anda sedang melihat mesin yang benar-benar telah but semula. docker compose ps, apabila dijalankan dari direktori tindanan, sepatutnya menyenaraikan setiap perkhidmatan sebagai running dengan masa aktif yang hampir sama dengan mesin tersebut. Perkhidmatan yang menunjukkan Exited ialah perkhidmatan yang perlu diperiksa.

Jika sesuatu tidak dimulakan, log daemon merangkumi tempoh but:

journalctl -u docker.service -b --no-pager | tail -50

Untuk tindanan yang diurus oleh unit, journalctl -u myapp.service -b --no-pager memaparkan output docker compose yang tepat sejak proses but, termasuk penarikan imej yang gagal atau fail .env yang tiada.

Perkara yang menyebabkan auto-start gagal secara senyap

Container yang dicipta dengan docker compose run tidak pernah menerima dasar restart daripada fail tersebut. Compose menganggapnya sebagai container sekali jalan. Jika sesuatu service kelihatan mengabaikan dasarnya, periksa sama ada service itu dimulakan dengan run dan bukannya up.

Laluan relatif dalam volume atau dalam entri env_file diselesaikan berdasarkan direktori fail compose. Ini berfungsi dari shell anda dan juga dari unit yang menetapkan WorkingDirectory. Ia gagal dari unit yang tidak menetapkannya kerana direktori kerja ketika itu ialah /.

Rootless Docker ialah kes yang berasingan. Daemon berjalan sebagai user service, dan user service berhenti apabila sesi terakhir bagi pengguna tersebut berakhir. Dayakan service itu untuk pengguna berkenaan dan benarkan ia terus berjalan apabila tiada sesi pengguna yang aktif:

systemctl --user enable docker
sudo loginctl enable-linger $USER

Tanpa enable-linger, daemon rootless ditutup apabila anda log keluar dan container turut berhenti. Keadaan ini kelihatan sama seperti dasar restart yang rosak.

Satu perkara terakhir. Kemas kini keselamatan automatik boleh reboot server pada waktu yang ditetapkan. Ini hanya bermanfaat jika stack anda boleh bermula semula secara automatik. Menetapkan perkara ini pada mesin baharu merupakan sebahagian daripada kerja jam pertama yang lain dalam sepuluh minit pertama pada VPS baharu.

FAQ

Apakah perbezaan antara restart: always dengan restart: unless-stopped?

Kedua-duanya memulakan semula bekas apabila bekas berhenti sendiri. Perbezaannya berlaku selepas anda menghentikan bekas secara manual. Dengan always, bekas dimulakan semula apabila daemon Docker dimulakan pada kali seterusnya, jadi but semula sistem membatalkan penghentian manual anda. Dengan unless-stopped, daemon mengingati bahawa bekas itu dihentikan dengan sengaja dan membiarkannya berhenti. Gunakan unless-stopped melainkan anda benar-benar memerlukan bekas yang kekal berhenti.

Saya menambahkan restart: unless-stopped, tetapi bekas masih tidak dimulakan selepas but semula. Mengapa?

Dasar itu disimpan pada bekas, bukan dalam fail, dan bekas yang telah wujud tidak dikemas kini dengan mengedit YAML. Jalankan docker compose up -d supaya Compose mencipta semula bekas itu, kemudian sahkan dengan docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). Jika perintah itu memaparkan no, bekas tersebut telah wujud sebelum anda membuat pengeditan. Punca lazim yang lain ialah docker.service tidak didayakan, yang boleh anda semak dengan systemctl is-enabled docker.

Adakah saya memerlukan unit systemd jika saya sudah menggunakan dasar mulakan semula?

Biasanya tidak. Dasar mulakan semula mencukupi untuk tindanan yang hanya memerlukan rangkaian, dan kebanyakan tindanan adalah sedemikian. Tambahkan unit apabila bekas bergantung pada sesuatu yang belum tersedia semasa daemon Docker dimulakan, seperti cakera luaran, volum yang disulitkan, perkongsian NFS atau antara muka VPN. Unit tersebut menyediakan pengurutan melalui After= dan RequiresMountsFor=, yang tidak dapat dinyatakan oleh dasar mulakan semula.

Bagaimanakah saya menghentikan tindanan secara kekal supaya ia tidak dimulakan semula pada but semula seterusnya?

Dengan unless-stopped, docker compose stop sudah memadai kerana bekas yang dihentikan secara manual tidak disambung semula apabila daemon dimulakan semula. Dengan always, penghentian sahaja tidak mencukupi dan bekas akan kembali selepas but semula. Jalankan docker compose down, yang membuang bekas, atau ubah dasar itu terlebih dahulu dengan docker update --restart no my-container. Jika unit systemd mengurus tindanan tersebut, jalankan sudo systemctl disable myapp.service juga; jika tidak, unit itu akan memulakannya semula.