
Fungsi pembangunan multi-tahap di Dockerfiles memungkinkan Anda membuat gambar wadah kecil dengan tingkat caching yang lebih tinggi dan perlindungan yang lebih sedikit. Pada artikel ini, saya akan menunjukkan kepada Anda beberapa templat tingkat lanjut - sesuatu yang lebih dari menyalin file antara build dan run. Mereka memungkinkan Anda untuk mencapai efisiensi maksimum fungsi. Namun, jika Anda seorang pemula di bidang perakitan multi-tahap, maka pertama-tama, mungkin, tidak akan salah untuk membaca panduan pengguna.
Kompatibilitas versi
Dukungan pembangunan bertingkat ditambahkan ke Docker dalam versi v17.05. Semua templat bekerja dengan versi berikutnya, tetapi beberapa lebih efisien, berkat tautan yang menggunakan sisi server BuildKit . Katakanlah BuildKit secara efektif melewatkan tahapan yang tidak digunakan dan, jika mungkin, membuat tahapan pada saat yang sama (saya menyoroti contoh-contoh ini secara terpisah). BuildKit saat ini sedang ditambahkan ke Moby sebagai backend eksperimental untuk build dan harus tersedia di Docker CE v18.06. Ini juga dapat digunakan secara mandiri atau sebagai bagian dari proyek img .
Pewarisan Tahap
Build multi-stage menambahkan beberapa konsep sintaksis baru. Pertama-tama, Anda dapat memberikan tahap dimulai dengan perintah FROM
nama AS stagename
dan menggunakan --from=stagename
dalam COPY
untuk menyalin file dari tahap ini. Bahkan, perintah FROM
dan label --from
memiliki banyak kesamaan, bukan karena mereka tidak memiliki nama yang sama. Keduanya mengambil argumen yang sama, mengenalinya, dan memulai tahap baru dari titik ini, atau menggunakannya sebagai sumber untuk menyalin file. Artinya, untuk menggunakan tahap sebelumnya dalam kualitas gambar asli untuk tahap saat ini, Anda dapat mengambil tidak hanya --from=stagename
, tetapi juga nama panggung FROM stagename
. Berguna jika Anda menggunakan bagian umum yang sama dalam beberapa perintah di Dockerfile: ini mengurangi kode umum dan menyederhanakan pemeliharaannya, menjaga langkah anak terpisah. Jadi, membangun kembali satu tahap tidak memengaruhi cache perakitan untuk orang lain. Dengan demikian, setiap tahap dapat dirakit secara terpisah menggunakan label --target
saat memanggil docker build
.
FROM ubuntu AS base RUN apt-get update && apt-get install git FROM base AS src1 RUN git clone β¦ FROM base as src2 RUN git clone β¦
Dalam contoh ini, fase kedua dan ketiga di BuildKit dibangun secara bersamaan.
Penggunaan gambar secara langsung
Alih-alih menggunakan nama tahap perakitan dalam perintah FROM
yang sebelumnya hanya mendukung referensi gambar, Anda dapat langsung menggunakan gambar menggunakan label --from
. Ternyata untuk menyalin file langsung dari gambar-gambar ini. Sebagai contoh, linuxkit/ca-certificatesimage
dalam kode berikut secara langsung menyalin akar TLS CA ke langkah saat ini.
FROM alpine COPY --from=linuxkit/ca-certificates / /
Alias ββCitra Umum
Fase build tidak harus mencakup perintah apa pun; mungkin terdiri dari satu baris FROM
. Jika Anda menggunakan gambar di beberapa tempat, ini akan menyederhanakan pembacaan dan membuatnya sehingga jika Anda perlu memperbarui gambar keseluruhan, Anda hanya perlu mengubah satu baris.
FROM alpine:3.6 AS alpine FROM alpine RUN β¦ FROM alpine RUN β¦
Dalam contoh ini, setiap tempat yang menggunakan gambar alpine sebenarnya berkomitmen untuk alpine:3.6
, bukan alpine:latest
. Ketika tiba saatnya untuk meningkatkan ke alpine:3.7
, Anda harus mengubah satu baris, dan tidak ada keraguan: sekarang semua elemen perakitan menggunakan versi yang diperbarui.
Ini bahkan lebih penting ketika argumen build digunakan dalam alias. Contoh berikut ini mirip dengan yang sebelumnya, tetapi memungkinkan pengguna untuk mendefinisikan ulang semua instance perakitan di mana gambar alpine terlibat dengan mengatur --build-arg ALPINE_VERSION=value
opsi --build-arg ALPINE_VERSION=value
. Ingat: argumen apa pun yang digunakan dalam perintah FROM
harus ditentukan sebelum tahap pembuatan pertama .
ARG ALPINE_VERSION=3.6 FROM alpine:${ALPINE_VERSION} AS alpine FROM alpine RUN β¦
Menggunakan argumen build di "- from"
Nilai yang ditentukan dalam label COPY
tidak boleh berisi argumen assembly. Misalnya, contoh berikut tidak valid.
// THIS EXAMPLE IS INTENTIONALLY INVALID FROM alpine AS build-stage0 RUN β¦ FROM alpine ARG src=stage0 COPY --from=build-${src} . .
Hal ini disebabkan oleh fakta bahwa ketergantungan antara tahapan harus ditentukan sebelum perakitan dimulai. Maka evaluasi konstan dari semua tim tidak diperlukan. Misalnya, variabel lingkungan yang didefinisikan dalam gambar alpine
dapat memengaruhi evaluasi nilai --from
. Alasan kita dapat mengevaluasi argumen dari perintah FROM
adalah karena argumen ini didefinisikan secara global sebelum tahap apa pun dimulai. Untungnya, seperti yang kami ketahui sebelumnya, itu sudah cukup untuk menentukan tahap alias menggunakan satu perintah FROM
dan merujuknya.
ARG src=stage0 FROM alpine AS build-stage0 RUN β¦ FROM build-${src} AS copy-src FROM alpine COPY --from=copy-src . .
Sekarang, jika Anda menimpa argumen assembly src
, langkah awal untuk elemen COPY
akhir akan beralih. Harap perhatikan: jika beberapa langkah tidak lagi digunakan, maka hanya penghubung berbasis BuildKit yang dapat melompati mereka secara efektif.
Ketentuan Menggunakan Argumen Build
Kami diminta untuk menambahkan dukungan untuk kondisi gaya IF/ELSE
ke Dockerfile. Kami belum tahu apakah kami akan menambahkan sesuatu yang serupa, tetapi di masa mendatang kami akan mencoba menggunakan dukungan klien di BuildKit. Sementara itu, untuk mencapai perilaku serupa, Anda dapat menggunakan konsep multi-tahap saat ini (dengan beberapa perencanaan).
// THIS EXAMPLE IS INTENTIONALLY INVALID FROM alpine RUN β¦ ARG BUILD_VERSION=1 IF $BUILD_VERSION==1 RUN touch version1 ELSE IF $BUILD_VERSION==2 RUN touch version2 DONE RUN β¦
Contoh sebelumnya menunjukkan kode semu untuk kondisi perekaman menggunakan IF/ELSE
. Untuk mencapai perilaku serupa dengan pembangunan multi-tahap saat ini, Anda mungkin perlu mendefinisikan berbagai kondisi cabang sebagai langkah terpisah dan menggunakan argumen untuk memilih jalur ketergantungan yang benar.
ARG BUILD_VERSION=1 FROM alpine AS base RUN β¦ FROM base AS branch-version-1 RUN touch version1 FROM base AS branch-version-2 RUN touch version2 FROM branch-version-${BUILD_VERSION} AS after-condition FROM after-condition RUN β¦
Langkah terakhir di Dockerfile didasarkan pada langkah after-condition
, yaitu alias gambar (dikenali berdasarkan BUILD_VERSION
build BUILD_VERSION
). Bergantung pada nilai BUILD_VERSION
, tahap bagian tengah ini atau itu dipilih.
Harap dicatat: hanya penghubung berbasis BuildKit yang dapat melewati cabang yang tidak digunakan. Di versi sebelumnya dari tautan, semua tahapan akan dibangun, tetapi sebelum membuat gambar akhir, hasilnya akan dibuang.
Asisten Pengembangan / Pengujian untuk Fase Produksi Minimum
Sebagai kesimpulan, mari kita lihat contoh menggabungkan templat sebelumnya untuk menunjukkan cara membuat Dockerfile yang menciptakan gambar produksi minimal dan kemudian dapat menggunakan kontennya untuk menguji dan membuat gambar pengembangan. Mari kita mulai dengan contoh Dockerfile dasar:
FROM golang:alpine AS stage0 β¦ FROM golang:alpine AS stage1 β¦ FROM scratch COPY --from=stage0 /binary0 /bin COPY --from=stage1 /binary1 /bin
Ketika gambar produksi minimal dibuat, ini adalah opsi yang cukup umum. Tetapi bagaimana jika Anda juga perlu mendapatkan gambar pengembang alternatif atau menjalankan tes dengan binari ini pada tahap akhir? Hal pertama yang terlintas dalam pikiran adalah menyalin binari serupa pada tahap pengujian dan pengembangan. Masalahnya adalah ini: tidak ada jaminan bahwa Anda akan menguji semua binari produksi dalam kombinasi yang sama. Pada tahap akhir, sesuatu mungkin berubah, tetapi Anda akan lupa untuk membuat perubahan serupa di tahap lain atau membuat kesalahan dengan cara menyalin file biner. Pada akhirnya, kami tidak menguji file biner yang terpisah, tetapi gambar terakhir.
Alternatifnya adalah menentukan fase pengembangan dan pengujian setelah fase produksi dan menyalin seluruh konten dari fase produksi. Kemudian gunakan satu perintah FROM
untuk langkah produksi untuk menjadikan langkah produksi default sebagai langkah terakhir lagi.
FROM golang:alpine AS stage0 β¦ FROM scratch AS release COPY --from=stage0 /binary0 /bin COPY --from=stage1 /binary1 /bin FROM golang:alpine AS dev-env COPY --from=release / / ENTRYPOINT ["ash"] FROM golang:alpine AS test COPY --from=release / / RUN go test β¦ FROM release
Secara default, Dockerfile ini akan terus membuat gambar default minimum, sementara, misalnya, sebuah perakitan dengan opsi --target=dev-env
akan membuat gambar dengan shell yang berisi semua binari dari versi final.
Semoga ini bermanfaat dan menyarankan cara membuat Dockerfiles multi-tahap yang lebih efisien. Jika Anda berpartisipasi dalam DockerCon2018 dan ingin mempelajari lebih lanjut tentang pembuatan multi-tahap, Dockerfiles, BuildKit, atau topik terkait lainnya, daftar untuk penghubung trek Hallway atau ikuti pertemuan internal platform Docker pada lagu Contribute and Collaborate atau Black Belt .