Cara memasang volume host ke wadah buruh pelabuhan di Dockerfile selama proses pembuatan

236

Pertanyaan awal: Bagaimana cara menggunakan instruksi VOLUME di Dockerfile?

Pertanyaan sebenarnya yang ingin saya pecahkan adalah - bagaimana cara me-mount volume host ke dalam wadah buruh pelabuhan di Dockerfile selama pembuatan, yaitu, memiliki docker run -v /export:/exportkemampuan selama docker build.

Alasan di baliknya, bagi saya, adalah ketika membangun sesuatu di Docker, saya tidak ingin apt-get installcache ( ) itu terkunci di satu buruh pelabuhan, tetapi untuk membagikan / menggunakannya kembali. Itulah alasan utama saya bertanya tentang pertanyaan ini.

Pembaruan Terbaru:

Sebelum docker v18.09, jawaban yang benar haruslah yang dimulai dengan:

Ada cara untuk memasang volume selama membangun, tetapi tidak melibatkan Dockerfiles.

Namun, itu adalah jawaban yang dinyatakan dengan buruk, terorganisir dan didukung. Ketika saya sedang menginstal ulang buruh pelabuhan saya, saya kebetulan menemukan artikel berikut:

Dockerize layanan apt-cacher-ng
https://docs.docker.com/engine/examples/apt-cacher-ng/

Itulah solusi buruh pelabuhan untuk pertanyaan ini / saya, tidak secara langsung tetapi tidak langsung. Ini adalah cara buruh pelabuhan ortodoks menyarankan kita untuk melakukan. Dan saya akui itu lebih baik daripada yang saya coba tanyakan di sini.

Cara lain adalah, jawaban yang baru diterima , misalnya, Buildkit di v18.09.

Pilih mana yang cocok untuk Anda.


Was: Ada solusi - rocker, yang bukan dari Docker, tetapi sekarang rocker dihentikan, saya mengembalikan jawaban kembali ke "Tidak mungkin" lagi.


Pembaruan Lama: Jadi jawabannya adalah "Tidak mungkin". Saya dapat menerimanya sebagai jawaban karena saya tahu masalah ini telah dibahas secara luas di https://github.com/docker/docker/issues/3156 . Saya bisa mengerti bahwa portabilitas adalah masalah terpenting bagi pengembang buruh pelabuhan; tetapi sebagai pengguna buruh pelabuhan, saya harus mengatakan saya sangat kecewa dengan fitur yang hilang ini. Izinkan saya menutup argumen saya dengan kutipan dari diskusi di atas: " Saya ingin menggunakan Gentoo sebagai gambar dasar tapi jelas tidak ingin> 1GB data portage tree berada di salah satu lapisan setelah gambar dibuat. Anda bisa memiliki wadah kompak yang bagus jika bukan karena pohon portage raksasa harus muncul di gambar selama instalasi."Ya, saya dapat menggunakan wget atau curl untuk mengunduh apa pun yang saya butuhkan, tetapi fakta bahwa hanya pertimbangan portabilitas sekarang memaksa saya untuk mengunduh> 1GB pohon Portage setiap kali saya membangun gambar dasar Gentoo tidak efisien atau ramah pengguna. Lebih lanjut lebih lanjut, repositori paket AKAN SELALU berada di bawah / usr / portage, jadi SELALU PORTABEL di bawah Gentoo. Sekali lagi, saya menghargai keputusan itu, tetapi tolong izinkan saya mengungkapkan kekecewaan saya juga dalam waktu yang bersamaan. Terima kasih.


Pertanyaan asli secara terperinci:

Dari

Bagikan Direktori melalui Volume
http://docker.readthedocs.org/en/v0.7.3/use/working_with_volumes/

dikatakan bahwa fitur Volume data "telah tersedia sejak versi 1 dari Docker Remote API". Docker saya adalah versi 1.2.0, tetapi saya menemukan contoh yang diberikan dalam artikel di atas tidak berfungsi:

# BUILD-USING:        docker build -t data .
# RUN-USING:          docker run -name DATA data
FROM          busybox
VOLUME        ["/var/volume1", "/var/volume2"]
CMD           ["/usr/bin/true"]

Apa cara yang tepat di Dockerfile untuk memasang volume yang dipasang di host ke dalam wadah buruh pelabuhan, melalui perintah VOLUME?

$ apt-cache policy lxc-docker
lxc-docker:
  Installed: 1.2.0
  Candidate: 1.2.0
  Version table:
 *** 1.2.0 0
        500 https://get.docker.io/ubuntu/ docker/main amd64 Packages
        100 /var/lib/dpkg/status

$ cat Dockerfile 
FROM          debian:sid

VOLUME        ["/export"]
RUN ls -l /export
CMD ls -l /export

$ docker build -t data .
Sending build context to Docker daemon  2.56 kB
Sending build context to Docker daemon 
Step 0 : FROM          debian:sid
 ---> 77e97a48ce6a
Step 1 : VOLUME        ["/export"]
 ---> Using cache
 ---> 59b69b65a074
Step 2 : RUN ls -l /export
 ---> Running in df43c78d74be
total 0
 ---> 9d29a6eb263f
Removing intermediate container df43c78d74be
Step 3 : CMD ls -l /export
 ---> Running in 8e4916d3e390
 ---> d6e7e1c52551
Removing intermediate container 8e4916d3e390
Successfully built d6e7e1c52551

$ docker run data
total 0

$ ls -l /export | wc 
     20     162    1131

$ docker -v
Docker version 1.2.0, build fa7b24f
xpt
sumber
Rupanya lebih banyak permintaan fitur saat ini (bukan yang saya harapkan akan diimplementasikan, tetapi untuk berjaga-jaga): buruh pelabuhan / buruh pelabuhan # 14080
Jesse Glick
memang ada diskusi yang luas yang seharusnya tidak diizinkan untuk menautkan direktori host dan direktori kontainer selama pembuatan yaitu sesuatu seperti VOLUME ~/host_dir ~/container_dir. Pembahasannya cukup luas, dan ada cara singkat untuk meringkas apa alasannya?
Charlie Parker

Jawaban:

34

Pertama, untuk menjawab "mengapa tidak VOLUMEberhasil?" Ketika Anda mendefinisikan a VOLUMEdi Dockerfile, Anda hanya bisa menentukan target, bukan sumber volume. Selama membangun, Anda hanya akan mendapatkan volume anonim dari ini. Volume anonim itu akan dipasang di setiap RUNperintah, diisi terlebih dahulu dengan isi gambar, dan kemudian dibuang di akhir RUNperintah. Hanya perubahan pada wadah yang disimpan, bukan perubahan volume.


Karena pertanyaan ini telah diajukan, beberapa fitur telah dirilis yang dapat membantu. Pertama adalah multistage build yang memungkinkan Anda membangun ruang disk tahap pertama yang tidak efisien, dan menyalin hanya output yang dibutuhkan ke tahap akhir yang Anda kirimkan. Dan fitur kedua adalah Buildkit yang secara dramatis mengubah cara gambar dibangun dan kemampuan baru ditambahkan ke build.

Untuk pembuatan multi-tahap, Anda akan memiliki beberapa FROMbaris, masing-masing memulai pembuatan gambar terpisah. Hanya gambar terakhir yang ditandai secara default, tetapi Anda dapat menyalin file dari tahap sebelumnya. Penggunaan standar adalah memiliki lingkungan kompiler untuk membangun biner atau artefak aplikasi lainnya, dan lingkungan runtime sebagai tahap kedua yang menyalin dari artefak itu. Kamu bisa saja:

FROM debian:sid as builder
COPY export /export
RUN compile command here >/result.bin

FROM debian:sid
COPY --from=builder /result.bin /result.bin
CMD ["/result.bin"]

Itu akan menghasilkan build yang hanya berisi biner yang dihasilkan, dan bukan direktori penuh / ekspor.


Buildkit keluar dari percobaan di 18.09. Ini adalah desain ulang yang lengkap dari proses build, termasuk kemampuan untuk mengubah parser frontend. Salah satu perubahan parser telah menerapkan RUN --mountopsi yang memungkinkan Anda memasang direktori cache untuk menjalankan perintah Anda. Misalnya, inilah yang memasang beberapa direktori debian (dengan konfigurasi ulang gambar debian, ini dapat mempercepat menginstal ulang paket):

# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/var/lib/apt/lists,type=cache \
    --mount=target=/var/cache/apt,type=cache \
    apt-get update \
 && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
      git

Anda akan menyesuaikan direktori cache untuk cache aplikasi apa pun yang Anda miliki, mis. $ HOME / .m2 untuk maven, atau /root/.cache untuk golang.


TL; DR: Jawaban ada di sini: Dengan RUN --mountsintaks itu, Anda juga dapat mengikat mount direktori read-only dari build-context. Folder harus ada dalam konteks build, dan tidak dipetakan kembali ke host atau klien build:

# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/export,type=bind,source=export \
    process export directory here...

Perhatikan bahwa karena direktori di-mount dari konteks, itu juga di-mount hanya-baca, dan Anda tidak dapat mendorong perubahan kembali ke host atau klien. Saat Anda membangun, Anda ingin memasang 18.09 atau yang lebih baru dan mengaktifkan buildkit export DOCKER_BUILDKIT=1.

Jika Anda mendapatkan kesalahan bahwa flag mount tidak didukung, itu menunjukkan bahwa Anda tidak mengaktifkan buildkit dengan variabel di atas, atau bahwa Anda tidak mengaktifkan sintaksis eksperimental dengan garis sintaksis di bagian atas Dockerfile sebelum baris lain, termasuk komentar. Perhatikan bahwa variabel toggle buildkit hanya akan berfungsi jika instalasi buruh pelabuhan Anda memiliki dukungan buildkit bawaan, yang membutuhkan versi 18.09 atau lebih baru dari Docker, baik pada klien maupun server.

BMitch
sumber
2
Sayangnya, pada Windows Buildkit belum didukung dalam versi 18.09
Wesley
1
Sepertinya "armhf" tidak mendukung "mount".
Mike
2
Saya mendapatkan "Respons kesalahan dari daemon: Dockerfile parse error line xx: Bendera tidak dikenal: mount" di OSX
ChristoKiwi
1
Dukungan untuk komposisi buruh pelabuhan belum ada di sana, tetapi Anda tidak perlu menulis untuk membuat gambar. Masalah untuk dilacak: github.com/moby/buildkit/issues/685
BMitch
116

Tidak mungkin menggunakan VOLUMEinstruksi untuk memberi tahu buruh pelabuhan apa yang harus dipasang. Itu akan sangat merusak portabilitas. Instruksi ini memberi tahu docker bahwa konten dalam direktori tersebut tidak masuk dalam gambar dan dapat diakses dari kontainer lain menggunakan --volumes-fromparameter baris perintah. Anda harus menjalankan wadah menggunakan -v /path/on/host:/path/in/containeruntuk mengakses direktori dari tuan rumah.

Memasang volume host selama pembuatan tidak dimungkinkan. Tidak ada bangunan istimewa dan pemasangan host juga akan secara serius menurunkan portabilitas. Anda mungkin ingin mencoba menggunakan wget atau ikal untuk mengunduh apa pun yang Anda perlukan untuk membangun dan meletakkannya.

Andreas Steffan
sumber
2
Terima kasih. Pertanyaan direvisi. Pertanyaan aktual yang ingin saya pecahkan adalah - bagaimana cara me-mount volume host ke dalam kontainer buruh pelabuhan di Dockerfile selama pembuatan. Terima kasih.
xpt
2
Tidak memungkinkan. Lihat jawaban yang direvisi.
Andreas Steffan
3
Saya bisa menghargai "potensi" efek samping buruk terhadap portabilitas, tetapi ada juga kasus penggunaan yang valid untuk memiliki opsi ini. Dalam kasus saya, saya ingin memberi tahu pengguna untuk "Pindah ke direktori dan jalankan perintah 'buruh pelabuhan'" setelah $ (PWD) dipasang ke beberapa direktori kontainer. $ (PWD) memastikan portabilitas terjaga. Walaupun ini mungkin kasus sudut, ini akan sangat membantu saya ketika saya mendistribusikan lingkungan runtime untuk skrip yang disediakan pengguna.
ntwrkguru
64

PEMBARUAN: Seseorang tidak akan menerima jawaban sebagai tidak, dan saya sangat menyukainya, terutama untuk pertanyaan khusus ini.

BERITA BAIK, Ada jalan sekarang -

Solusinya adalah Rocker: https://github.com/grammarly/rocker

John Yani berkata , "IMO, ini menyelesaikan semua titik lemah Dockerfile, membuatnya cocok untuk pengembangan."

Kursi goyang

https://github.com/grammarly/rocker

Dengan memperkenalkan perintah baru, Rocker bertujuan untuk menyelesaikan kasus penggunaan berikut, yang menyakitkan dengan Docker biasa:

  1. Pasang volume yang dapat digunakan kembali pada tahap pembuatan, jadi alat manajemen dependensi dapat menggunakan cache antara build.
  2. Bagikan kunci ssh dengan build (untuk menarik repo pribadi, dll.), Tanpa meninggalkannya di gambar yang dihasilkan.
  3. Membangun dan menjalankan aplikasi dalam gambar yang berbeda, dapat dengan mudah melewatkan artefak dari satu gambar ke gambar lainnya, idealnya memiliki logika ini dalam satu Dockerfile tunggal.
  4. Tandai / Push gambar langsung dari Dockerfiles.
  5. Pass variabel dari perintah shell build sehingga bisa diganti dengan Dockerfile.

Dan lagi. Ini adalah masalah paling kritis yang menghalangi kami mengadopsi Docker di Grammarly.

Pembaruan: Rocker telah dihentikan, sesuai dengan repo proyek resmi di Github

Pada awal 2018, ekosistem wadah jauh lebih matang daripada tiga tahun lalu ketika proyek ini dimulai. Sekarang, beberapa fitur penting dan luar biasa dari rocker dapat dengan mudah ditutupi oleh buruh pelabuhan atau alat yang didukung lainnya, meskipun beberapa fitur tetap unik untuk rocker. Lihat https://github.com/grammarly/rocker/issues/199 untuk rincian lebih lanjut.

xpt
sumber
Saya mencoba menggunakan Rocker untuk menyelesaikan masalah nomor 1 tetapi perintah mount tidak akan berfungsi, dan gambar yang dibuat tidak mengandung folder host. Perintah mount Dockerfile saya terlihat seperti ini - MOUNT ~/code/docker-app-dev/new-editor/:/src/dan perintah build Rocker saya adalah ini - rocker build -f Dockerfile .. Apa yang saya lakukan salah?
Yaron Idan
Mungkin mencoba menggunakan jalur host nyata? ~adalah metacharacter shell Bourne.
Jesse Glick
Rocker buildtidak mengizinkan docker runopsi baris perintah, jadi saat ini tidak mengizinkan hal-hal seperti --privileged.
Monty Wild
Hai @xpt, bisakah kita mendapatkan pembaruan lain karena rocker sekarang dihentikan
Shardj
Sekarang rocker dihentikan, saya mengembalikan jawaban kembali ke "Tidak mungkin" lagi. Lihat OP dan jawaban yang dipilih.
xpt
14

Ada cara untuk memasang volume selama membangun, tetapi tidak melibatkan Dockerfiles.

Tekniknya adalah membuat wadah dari basis apa pun yang ingin Anda gunakan (memasang volume Anda di wadah dengan -vopsi), menjalankan skrip shell untuk melakukan pekerjaan pembangunan gambar Anda, kemudian komit wadah sebagai gambar ketika selesai .

Ini tidak hanya akan menghilangkan file berlebih yang tidak Anda inginkan (ini juga baik untuk file yang aman, seperti file SSH), ini juga membuat satu gambar. Itu memiliki kelemahan: perintah komit tidak mendukung semua instruksi Dockerfile, dan itu tidak membiarkan Anda mengambil ketika Anda tinggalkan jika Anda perlu mengedit skrip pembangunan Anda.

MEMPERBARUI:

Sebagai contoh,

CONTAINER_ID=$(docker run -dit ubuntu:16.04)
docker cp build.sh $CONTAINER_ID:/build.sh
docker exec -t $CONTAINER_ID /bin/sh -c '/bin/sh /build.sh'
docker commit $CONTAINER_ID $REPO:$TAG
docker stop $CONTAINER_ID
Keith Mason
sumber
6
+1 Bisakah Anda jelaskan sedikit lebih banyak tentang instruksi dalam paragraf ke-2. Misalnya, jika basisnya debian:wheezydan skrip shell build.sh, instruksi spesifik apa yang akan digunakan seseorang?
Drux
6

Saat Anda menjalankan wadah, direktori pada host Anda dibuat dan dipasang ke wadah. Anda dapat mengetahui direktori apa ini

$ docker inspect --format "{{ .Volumes }}" <ID>
map[/export:/var/lib/docker/vfs/dir/<VOLUME ID...>]

Jika Anda ingin memasang direktori dari host Anda di dalam wadah Anda, Anda harus menggunakan -vparameter dan menentukan direktori. Dalam kasus Anda ini adalah:

docker run -v /export:/export data

JADI, Anda akan menggunakan folder hosts di dalam wadah Anda.

Behe
sumber
1
Terima kasih. Pertanyaan direvisi. Pertanyaan aktual yang ingin saya pecahkan adalah - bagaimana cara me-mount volume host ke dalam kontainer buruh pelabuhan di Dockerfile selama pembuatan. Terima kasih.
xpt
Tolong jangan merevisi pertanyaan Anda sedemikian drastis . Ini membuat pertanyaan saya tidak valid meskipun itu benar-benar valid sebelum suntingan Anda. Pertimbangkan untuk mengajukan pertanyaan baru.
Behe
11
Pertanyaan awal : Bagaimana cara menggunakan instruksi VOLUME di Dockerfile? Itu masih di awal pertanyaan bahkan sampai hari ini. Jawaban Anda adalah untuk menjalankan waktu , dan pertanyaan saya selalu tentang waktu membangun , untuk apa Dockerfile.
xpt
4

Saya pikir Anda dapat melakukan apa yang ingin Anda lakukan dengan menjalankan build melalui perintah buruh pelabuhan yang sendiri dijalankan di dalam wadah buruh pelabuhan. Lihat Docker sekarang dapat berjalan dalam Docker | Blog Docker . Sebuah teknik seperti ini, tetapi yang benar-benar mengakses buruh pelabuhan luar dengan wadah, digunakan, misalnya, sambil menjelajahi cara Membuat wadah Docker sekecil mungkin | Blog Xebia .

Artikel lain yang relevan adalah Mengoptimalkan Gambar Docker | CenturyLink Labs , yang menjelaskan bahwa jika Anda akhirnya mengunduh hal-hal selama membangun, Anda dapat menghindari ruang yang terbuang dengannya di gambar akhir dengan mengunduh, membangun, dan menghapus unduhan semua dalam satu langkah RUN.

nealmcb
sumber
3

Ini jelek, tapi saya mencapai kemiripan seperti ini:

Dockerfile:

FROM foo
COPY ./m2/ /root/.m2
RUN stuff

imageBuild.sh:

docker build . -t barImage
container="$(docker run -d barImage)"
rm -rf ./m2
docker cp "$container:/root/.m2" ./m2
docker rm -f "$container"

Saya memiliki java build yang mengunduh semesta ke /root/.m2, dan melakukannya setiap saat . imageBuild.shmenyalin isi folder itu ke host setelah membangun, dan Dockerfilemenyalinnya kembali ke gambar untuk membangun selanjutnya.

Ini adalah sesuatu seperti bagaimana sebuah volume akan berfungsi (yaitu tetap ada di antara build).

MatrixManAtYrService
sumber
Ini adalah solusi yang layak untuk integrasi berkelanjutan berbasis Docker alias CI. Siapkan pustaka dan kompiler dan jalankan make via perintah Dockerfile, luncurkan gambar sepele hanya untuk membuat wadah, dan akhirnya salin artefak yang diinginkan seperti .deb. Tampaknya berhasil, terima kasih telah memposting ini.
chrisinmtown
Solusi ini membuat Anda gambar dengan SEMUA file di ./m2/ - yang Anda butuhkan dan yang tidak Anda butuhkan - dan ini dapat menyebabkan gambar produksi BESAR, yang tidak diinginkan! Dengan pemasangan ke direktori dependensi eksternal hanya file yang diperlukan yang akan disalin ke gambar.
Marko Krajnc
Jika Anda berniat untuk mempublikasikan gambar, mungkin sebaiknya tunggu saja dan biarkan pakar unduh dependensinya sendiri setiap kali. Peretasan ini hanya masuk akal jika Anda melakukan staging untuk pengujian - sebuah gambar yang tidak akan pernah dihubungi oleh pengguna akhir.
MatrixManAtYrService
1

Berikut adalah versi sederhana dari pendekatan 2 langkah menggunakan build dan commit, tanpa skrip shell. Ini melibatkan:

  1. Membangun gambar secara parsial, tanpa volume
  2. Menjalankan wadah dengan volume , membuat perubahan, lalu melakukan hasilnya, mengganti nama gambar asli.

Dengan perubahan yang relatif kecil, langkah tambahan hanya menambahkan beberapa detik ke waktu pembuatan.

Pada dasarnya:

docker build -t image-name . # your normal docker build

# Now run a command in a throwaway container that uses volumes and makes changes:
docker run -v /some:/volume --name temp-container image-name /some/post-configure/command

# Replace the original image with the result:
# (reverting CMD to whatever it was, otherwise it will be set to /some/post-configure/command)   
docker commit --change="CMD bash" temp-container image-name 

# Delete the temporary container:
docker rm temp-container

Dalam kasus penggunaan saya, saya ingin membuat file maven toolchains.xml, tetapi banyak instalasi JDK saya ada di volume yang tidak tersedia sampai runtime. Beberapa gambar saya tidak kompatibel dengan semua JDKS, jadi saya perlu menguji kompatibilitas pada waktu pembuatan dan mengisi toolchains.xml secara kondisional. Perhatikan bahwa saya tidak perlu gambar menjadi portabel, saya tidak menerbitkannya ke Docker Hub.

Akom
sumber
1

Seperti yang sudah banyak dijawab, pemasangan volume host selama pembangunan tidak dimungkinkan. Saya hanya ingin menambahkan docker-composecara, saya pikir akan menyenangkan untuk dimiliki, sebagian besar untuk pengembangan / penggunaan pengujian

Dockerfile

FROM node:10
WORKDIR /app
COPY . .
RUN npm ci
CMD sleep 999999999

docker-compose.yml

version: '3'
services:
  test-service:
    image: test/image
    build:
      context: .
      dockerfile: Dockerfile
    container_name: test
    volumes:
      - ./export:/app/export
      - ./build:/app/build

Dan jalankan wadah Anda dengan docker-compose up -d --build

Yegor Zaremba
sumber