SSD Nodes Learn 🎉 VPS dari $5.50/bln
Panduan Matt ConnorOleh Matt Connor

Docker Compose build vs image: Mana satu nak guna?

Ketahui perbezaan antara kunci image dan build dalam Docker Compose. Artikel ini menjelaskan mengapa perubahan Dockerfile tidak dikesan pada VPS dan cara membetulkannya.

Perbezaan antara build dan image dalam Docker Compose: jawapan ringkas

Dalam fail Docker Compose, image: menentukan imej yang perlu ditarik (pull) daripada registri, manakala build: mengarahkan Compose untuk membina imej pada mesin ini menggunakan Dockerfile. Jika anda menetapkan image: sahaja, Compose akan menarik tag tersebut dan menjalankannya. Jika anda menetapkan build: sahaja, Compose akan membina imej secara setempat dan memberikan nama berdasarkan nama projek serta nama servis. Jika anda menetapkan kedua-duanya, Compose akan membina imej secara setempat dan kemudian melabelkan hasilnya dengan nama dalam image:; ini adalah cara anda membina imej dan menolaknya (push) menggunakan nama pilihan anda.

Itulah perbezaan keseluruhannya. Segala maklumat di bawah menerangkan maksudnya semasa operasi pada pelayan. Ini mengandaikan Docker Engine dan pemalam Compose telah pun dipasang; menjalankan Docker pada VPS merangkumi bahagian tersebut.

Tiga bentuk sepenuhnya

Tarik tag yang telah diterbitkan dan jalankannya. Tiada Dockerfile terlibat pada mana-mana peringkat.

services:
  web:
    image: nginx:1.27
    restart: unless-stopped
    ports:
      - "80:80"

Bina daripada Dockerfile dalam direktori semasa. Tiada apa-apa yang ditarik kecuali imej asas yang dinamakan dalam FROM.

services:
  web:
    build: .
    restart: unless-stopped
    ports:
      - "80:80"

Bina secara setempat dan tandakan (tag) hasilnya. docker compose push kemudian boleh menghantar tag tepat tersebut ke registry.

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    image: registry.example.com/acme/web:1.4.2
    restart: unless-stopped
    ports:
      - "80:80"

context ialah direktori yang dihantar kepada pembina. dockerfile diselesaikan relatif kepada konteks tersebut, jadi context: . dengan dockerfile: docker/prod.Dockerfile adalah normal dan betul. Jalankan docker compose images untuk melihat nama imej dan ID imej di sebalik setiap bekas servis, yang merupakan cara terpantas untuk mengesahkan bentuk mana antara tiga bentuk ini yang sebenarnya anda tulis.

Mengapa docker compose up tidak membina semula imej selepas saya mengubah Dockerfile?

Kerana up menyemak sama ada imej wujud, bukan sama ada imej tersebut adalah versi terkini.

Apabila Compose memulakan servis yang mempunyai bahagian build:, ia mencari imej tersebut dalam storan imej tempatan. Jika imej dengan nama tersebut sudah ada, Compose akan menggunakannya. Ia tidak membaca Dockerfile anda, membandingkan fail sumber anda, atau melihat sebarang cap masa (timestamp). Spesifikasi Compose menyatakan peraturan ini sebagai atribut pull_policy, dan tingkah laku lalai adalah membina imej hanya apabila imej tersebut tiada. Keadaan imej yang sedia ada dianggap sudah mencukupi.

Jadi, anda menyunting app.py, menjalankan docker compose up -d, dan melihat Compose melaporkan kontena sedang berjalan, namun ia masih menghidangkan kod lama. Tiada apa-apa yang gagal, jadi tiada amaran diberikan kepada anda. Ini adalah laporan "perubahan saya tidak berkesan" yang paling biasa berlaku dengan Compose. Petunjuknya ialah perkataan status yang dicetak oleh Compose di sebelah nama kontena: kontena yang digantikan oleh Compose akan melaporkan sebagai recreated atau started, manakala kontena yang dibiarkan oleh Compose akan melaporkan sebagai running.

Dua semakan dapat menyelesaikan masalah ini. docker compose images mencetak ID imej yang digunakan oleh setiap kontena, jadi catatkan ID tersebut sebelum proses deploy dan bandingkan selepasnya. docker image ls mempunyai lajur CREATED, dan imej yang dicipta sebelum komit terakhir anda adalah imej lama (stale), tidak kira apa yang dicetak oleh skrip deploy.

Flag yang memaksa binaan semula

  • docker compose up -d --build membina dahulu, kemudian mencipta semula mana-mana kontena yang imejnya telah berubah. Ini adalah flag yang dicari oleh kebanyakan pengguna.
  • docker compose build web membina satu servis dan tidak memulakan apa-apa. Ikuti dengan docker compose up --no-deps -d web untuk menggantikan hanya kontena tersebut dan membiarkan selebihnya dalam stack terus berjalan.
  • docker compose build --no-cache web membuang setiap lapisan cache dan membina semula daripada arahan pertama.
  • docker compose build --pull cuba menarik versi imej asas yang lebih baharu dalam FROM, supaya tag yang berubah-ubah seperti node:22 mengambil kandungan semasanya dan bukannya salinan yang anda muat turun pada bulan Mac.
  • docker compose up -d --force-recreate mencipta semula kontena daripada imej yang sedia ada. Ia tidak pernah membina. Menggunakan flag ini apabila anda sebenarnya mahukan --build adalah kesilapan biasa yang membawa kepada jalan buntu.

Anda juga boleh menetapkan keputusan tersebut di dalam fail. Mengikut spesifikasi Compose, pull_policy: build bermaksud Compose membina imej tersebut, dan membina semula jika ia sudah wujud. Setiap up kemudiannya akan memakan masa untuk proses binaan, yang mungkin sesuai pada komputer riba tetapi jarang diperlukan pada pelayan.

services:
  web:
    build: .
    image: registry.example.com/acme/web:dev
    pull_policy: build

Satu lagi interaksi yang perlu diketahui. docker compose pull cuba menarik imej untuk servis yang mempunyai bahagian build juga, dan jika penarikan itu gagal, ia memberitahu anda bahawa imej tersebut mesti dibina sebaliknya. Gunakan --ignore-buildable untuk melangkau servis tersebut secara senyap.

Bagaimana cache binaan menentukan masa deployment anda

Setiap arahan dalam Dockerfile menghasilkan satu layer, dan pembina (builder) akan menggunakan semula layer yang di-cache apabila arahan serta inputnya tidak berubah. Bagi COPY, inputnya ialah kandungan fail yang disalin. Sebaik sahaja satu layer gagal menggunakan cache, setiap layer selepasnya akan dibina semula, kerana setiap layer dibina di atas sistem fail yang dihasilkan oleh layer sebelumnya.

Satu peraturan itu menentukan sama ada deployment anda mengambil masa beberapa saat atau beberapa minit. Susun Dockerfile daripada perkara yang jarang berubah sehinggalah kepada perkara yang berubah pada setiap commit.

FROM node:22-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["node", "server.js"]

npm ci terletak di atas COPY . ., jadi menyunting fail sumber membolehkan layer pemasangan kekal di-cache dan proses binaan disambung semula pada langkah penyalinan. Jika anda menukar kedudukan kedua-dua baris tersebut, perubahan satu aksara akan menyebabkan pemasangan semula setiap dependency, kerana COPY . . membatalkan layer yang menjadi asas kepada npm ci. Bentuk yang sama terpakai pada pip install -r requirements.txt dan go mod download.

--no-cache ialah alat yang tepat apabila anda mengesyaki layer lama yang basi menyembunyikan pembaikan anda. Ia adalah tetapan lalai yang tidak baik, kerana ia membuang kelebihan penggunaan semula cache yang sepatutnya diperoleh melalui penyusunan Dockerfile yang betul.

Satu perkara yang ditetapkan oleh imej dan boleh diatasi (override) oleh Compose: CMD dalam Dockerfile ialah arahan yang dijalankan oleh imej secara lalai, dan kunci command: dalam servis akan menggantikannya. Bagaimana command dan entrypoint berinteraksi adalah penting di sini, kerana override dalam Compose boleh menyebabkan imej yang baru dibina berkelakuan sama seperti imej lama.

Konteks binaan dan .dockerignore

context: . bermaksud Compose membungkus direktori tersebut dan menghantarnya kepada pembina sebelum arahan pertama dijalankan. Segala-galanya di bawahnya akan disertakan, termasuk .git dan sebarang direktori data yang anda simpan di sebelah kod sumber anda. Binaan yang terhenti pada langkah memindahkan konteks bagi projek yang tidak berubah memberitahu anda bahawa konteks tersebut terlalu besar.

Fail .dockerignore pada akar konteks mengecualikan laluan daripada pemindahan tersebut. Sintaksnya hampir sama dengan .gitignore.

.git
node_modules
*.log
data/
.env

Terdapat dua manfaat. Pemindahan menjadi lebih kecil, jadi setiap binaan bermula dengan lebih pantas. Selain itu, COPY . . tidak lagi boleh menyalin .env ke dalam imej, di mana sesiapa yang menarik (pull) imej tersebut boleh membacanya semula.

Kes binaan perlahan yang meningkat dari semasa ke semasa ialah bind mount. Volume bernama wujud di luar direktori projek anda, tetapi bind mount seperti ./data:/var/lib/postgresql/data berada di dalam konteks binaan, jadi binaan anda menjadi lebih perlahan setiap minggu apabila pangkalan data membesar. Satu baris dalam .dockerignore membetulkannya. Bind mount berbanding volume bernama merangkumi pertukaran yang lebih luas.

Argumen binaan membawa risiko yang sama dalam versi yang lebih kecil. Nilai yang dihantar melalui args: boleh dilihat dalam sejarah imej oleh sesiapa sahaja yang memegang imej tersebut, jadi letakkan nombor versi di sana dan jangan sekali-kali meletakkan token. Fail Env dan rahsia dalam Compose merangkumi tempat yang sepatutnya bagi kelayakan (credentials).

Patutkah anda membina (build) pada VPS atau membina di tempat lain dan menarik (pull) imej?

Membina pada pelayan yang melayani trafik anda adalah laluan lalai kerana ia merupakan laluan paling singkat: git pull, kemudian docker compose up -d --build. Ini memadai pada pelayan kecil yang belum mempunyai pengguna. Ia menjadi masalah atas dua sebab yang boleh diukur dan satu sebab yang hanya muncul pada waktu kritikal.

Memori. Proses binaan menjalankan pengkompil (compiler) dan pengikat (bundler) di samping aplikasi langsung anda, dan proses ini biasanya menggunakan banyak memori dalam kebanyakan tindanan (stack). Pada VPS 1 GB, pengikat JavaScript atau pengkompil Rust lazimnya menjadi proses terbesar dalam sistem. Apabila kernel kehabisan memori, ia akan mematikan proses terbesar: sama ada binaan terhenti dengan Killed dan status keluar 137, atau pangkalan data anda dimatikan dan tapak web tergendala di tengah-tengah proses penempatan (deploy). dmesg -T | grep -i oom mencetak baris penamatan dengan nama proses, supaya anda boleh mengenal pasti punca sebenar tanpa perlu meneka.

Cakera. Setiap binaan meninggalkan lapisan (layer) di belakang, dan pembina menyimpan cache sendiri secara berasingan daripada imej anda. docker system df menunjukkan kedua-duanya, dan baris cache binaan akan terus berkembang. Tuntut semula ruang dengan docker image prune untuk imej yang tergantung (dangling) dan docker builder prune untuk lapisan cache. Cakera yang penuh akan menghentikan lebih daripada sekadar proses binaan. Pangkalan data juga akan berhenti menulis, dan kegagalan itu menelan kos yang jauh lebih tinggi daripada proses penempatan yang perlahan.

Kebolehulangan (Reproducibility). Imej yang dibina pada pelayan hanya wujud pada pelayan tersebut. Melakukan rollback bermakna menyemak keluar commit lama dan membina semula, dan binaan tersebut tidak dijamin menghasilkan output yang sama seperti sebelumnya, kerana tag asas telah berubah dan cermin pakej (package mirrors) juga telah berubah. Membina di tempat lain dan menolak (push) tag menjadikan proses rollback sebagai satu suntingan: halakan image: kepada tag sebelumnya dan jalankan docker compose up -d.

Susunan yang paling stabil adalah jelas. Integrasi berterusan (CI) anda menjalankan binaan dan menolak registry.example.com/acme/web:<git-sha>, dan fail Compose pada VPS membawa image: tanpa sebarang kunci build:. Penempatan kemudiannya hanya memerlukan dua arahan yang hampir tidak menggunakan memori.

docker compose pull
docker compose up -d

Jalankan docker login registry.example.com sekali pada pelayan dan Compose boleh menarik tag peribadi selepas itu.

Kekalkan bahagian binaan untuk pembangunan dan bukannya memadamnya, dalam fail yang anda namakan sendiri.

# compose.dev.yaml
services:
  web:
    build:
      context: .
    pull_policy: build
docker compose -f compose.yaml -f compose.dev.yaml up -d --build

Namakan fail tersebut compose.dev.yaml dan bukannya compose.override.yaml. Compose memuatkan fail override secara automatik apabila ia hadir, jadi fail override yang tersilap salin ke pelayan akan menyebabkan proses binaan berjalan di sana secara senyap. Melapis beberapa fail Compose menjelaskan cara penggabungan menyelesaikan setiap kunci.

Perangkap seni bina apabila anda membina di tempat lain

Imej membawa seni bina CPU yang ia dibina untuknya. Jika anda membina pada komputer riba Apple Silicon, menolak (push), kemudian menarik (pull) tag tersebut ke VPS x86_64, Docker akan memberi amaran bahawa platform imej yang diminta tidak sepadan dengan platform hos yang dikesan. Proses tersebut kemudian terhenti dengan exec format error, yang kelihatan seperti binari rosak sedangkan ia sebenarnya tidak. Bina untuk sasaran secara eksplisit:

docker buildx build --platform linux/amd64 \
  -t registry.example.com/acme/web:1.4.2 --push .

Ketidakpadanan yang sama berlaku secara terbalik jika komputer riba anda adalah x86 dan anda menjalankan VPS ARM dan bukannya VPS x86. Membiarkan CI membina pada seni bina yang anda gunakan untuk penempatan (deploy) akan menyelesaikan persoalan ini.

Perkara yang perlu disemak selepas proses deploy

  • docker compose images mencetak imej dan tag di sebalik setiap container yang sedang berjalan. ID imej yang berubah adalah bukti bahawa binaan baharu sedang beroperasi.
  • docker compose config mencetak fail yang telah digabungkan selepas penggantian pemboleh ubah, supaya anda boleh membaca nama imej akhir yang akan digunakan oleh Compose sebelum anda menjalankan sebarang arahan.
  • docker compose logs -f web untuk setengah minit pertama selepas pertukaran. Container yang bermula dan terus keluar akan memulakan semula dalam gelung (loop) dan bukannya kekal aktif, dan gelung ini tidak akan mengeluarkan bunyi kecuali anda memerhatikannya.
  • docker image ls menunjukkan lajur CREATED. Imej yang lebih lama daripada commit terakhir anda bermakna ia tidak dibina semula.

Jika anda masih menyusun fail yang digunakan untuk semakan ini, asas fail Compose pada VPS merangkumi kunci-kunci yang berkaitan, dan helaian nota arahan Compose menyenaraikan sub-arahan yang lain.

FAQ

Bolehkah saya menggunakan build dan image dalam servis yang sama?

Ya, dan ini merupakan tetapan biasa bagi projek yang anda bina sendiri. Compose membina imej daripada bahagian build: dan melabelkan hasilnya dengan nilai image:. Label tersebut ialah apa yang dihantar oleh docker compose push ke registry dan apa yang ditarik oleh mesin lain. Tanpa kunci image:, Compose masih akan membina imej, tetapi ia menamakan imej tersebut mengikut nama projek dan servis, serta memberi amaran bahawa atribut yang tiada itu menghalang imej daripada dihantar (push).

Mengapa docker compose up tidak mengesan perubahan Dockerfile saya?

Kerana up hanya menyemak sama ada imej dengan nama tersebut wujud. Apabila imej sudah ada, Compose akan memulakannya dan tidak akan membandingkannya dengan Dockerfile atau fail sumber anda. Jalankan docker compose up -d --build, atau jalankan docker compose build web diikuti dengan docker compose up --no-deps -d web untuk menggantikan servis tunggal. Menetapkan pull_policy: build pada servis akan menyebabkan setiap up dibina semula, yang sesuai untuk mesin pembangunan.

Apakah perbezaan antara --build dan --force-recreate?

--build membina semula imej, kemudian mencipta semula kontena yang imejnya telah berubah. --force-recreate mencipta semula kontena daripada imej yang sedia ada, jadi ia tidak akan mengesan perubahan kod. Jika perubahan anda adalah pada sumber atau Dockerfile, --build ialah flag yang anda perlukan. --force-recreate adalah untuk menetapkan semula kontena itu sendiri, contohnya untuk mengosongkan lapisan boleh tulis (writable layer) sambil mengekalkan imej yang sama.

Patutkah saya membina imej Docker pada VPS atau di tempat lain?

Bina di tempat lain dan tarik (pull) label imej sebaik sahaja pelayan mula melayani trafik. Proses binaan bersaing dengan aplikasi anda untuk mendapatkan memori, dan VPS yang kecil akan menyelesaikan persaingan itu dengan membiarkan kernel mematikan proses yang paling besar, yang mungkin merupakan proses binaan atau pangkalan data anda. Binaan juga meninggalkan cache pada cakera yang tidak dibersihkan secara automatik. Membina pada pelayan adalah memadai untuk projek kecil tanpa pengguna, dan proses pemindahan kemudiannya tidak memakan kos jika anda mengekalkan bahagian build: dalam fail Compose khusus untuk pembangunan.

Bagaimanakah cara untuk menghentikan cache binaan Docker daripada memenuhi cakera saya?

Jalankan docker system df untuk melihat berapa banyak ruang yang digunakan oleh imej dan cache binaan anda. docker builder prune membuang lapisan cache dan docker image prune membuang imej tergantung (dangling images) yang ditinggalkan oleh binaan terdahulu. Menambah -a pada mana-mana arahan tersebut adalah lebih agresif dan memaksa binaan seterusnya bermula dari awal (cold build). Jangan jadualkan docker system prune -af --volumes pada pelayan, kerana --volumes akan memadamkan sebarang volum yang tidak digunakan oleh mana-mana kontena, sedangkan stack yang anda hentikan untuk penyelenggaraan menyimpan pangkalan data anda dalam volum sedemikian.