Bagaimana cara memasukkan file di luar konteks build Docker?

462

Bagaimana saya bisa memasukkan file dari luar konteks pembangunan Docker menggunakan perintah "ADD" di file Docker?

Dari dokumentasi Docker:

Jalan harus berada di dalam konteks bangunan; Anda tidak dapat ADD ../something/something, karena langkah pertama pembuatan buruh pelabuhan adalah mengirim direktori konteks (dan subdirektori) ke daemon buruh pelabuhan.

Saya tidak ingin merestrukturisasi seluruh proyek saya hanya untuk mengakomodasi Docker dalam hal ini. Saya ingin menyimpan semua file Docker saya di sub-direktori yang sama.

Selain itu, tampaknya Docker belum (dan mungkin tidak pernah) mendukung symlink: Perintah ADD Dockerfile tidak mengikuti symlink pada host # 1676.

Satu-satunya hal lain yang dapat saya pikirkan adalah memasukkan langkah pra-bangun untuk menyalin file ke dalam konteks pembangunan Docker (dan mengonfigurasi kontrol versi saya untuk mengabaikan file-file itu). Apakah ada solusi yang lebih baik dari itu?

terus terang
sumber
94
Ini pasti hal terburuk tentang Docker. Dari sudut pandang saya, tidak ada yang namanya "proyek Docker". Docker adalah untuk proyek pengiriman. Itu hanya alat. Saya tidak ingin harus membangun kembali seluruh proyek saya untuk mengakomodasi buruh pelabuhan, menambahkan .dockerignore dll. Pada akhirnya, siapa yang tahu berapa lama Docker akan bertahan? Akan bagus untuk memiliki pemisahan antara kode (yaitu proyek sudut), dan apa pun cara untuk menyebarkannya (yaitu buruh pelabuhan). Lagi pula, benar-benar tidak ada manfaatnya memiliki file buruh pelabuhan di sebelah yang lainnya. Ini hanya perkabelan untuk membuat gambar :(
TigerBear
3
Ya, ini adalah kesalahan besar. Saya menghadapi masalah yang sama dan saya memiliki file biner berukuran lebih besar (sudah dikompresi) yang saya tidak ingin salin ke setiap konteks pembangunan Docker. Saya lebih suka sumber dari lokasi saat ini (di luar konteks pembangunan Docker). Dan saya tidak ingin memetakan volume saat run time, karena saya mencoba untuk COPY / ADD file pada waktu build dan unzip dan melakukan apa yang saya butuhkan sehingga biner tertentu dimasukkan ke dalam gambar. Dengan cara ini, memutar wadah dengan cepat.
kacang jersey
Saya menemukan struktur yang bagus dan saya jelaskan dengan detail di stackoverflow.com/a/53298446/433814
Marcello de Sales
1
masalah dengan docker build adalah konsep "konteks" yang dibuat-buat. Dockerfiles tidak cukup untuk mendefinisikan build, kecuali mereka ditempatkan di bawah direktori strategis (alias konteks), yaitu "/" sebagai ekstrem, sehingga Anda dapat mengakses jalur apa pun (perhatikan bahwa itu bukan hal yang tepat untuk dilakukan dalam proyek waras) baik ..., ditambah itu membuat buruh pelabuhan membangun sangat lambat karena buruh pelabuhan memindai seluruh konteks di awal). Anda dapat mempertimbangkan untuk membuat gambar buruh pelabuhan dengan semua file yang diperlukan, dan mulai FROMmelanjutkan dari sana. Saya tidak akan mengubah struktur proyek untuk mengakomodasi Docker (atau alat membangun apa pun).
Devis L.

Jawaban:

413

Cara terbaik untuk mengatasi ini adalah dengan menentukan Dockerfile secara independen dari konteks build, menggunakan -f.

Misalnya, perintah ini akan memberikan perintah ADD akses ke apa pun di direktori Anda saat ini.

docker build -f docker-files/Dockerfile .

Pembaruan : Docker sekarang memungkinkan Dockerfile berada di luar konteks build (diperbaiki pada 18.03.0-ce, https://github.com/docker/cli/pull/886 ). Jadi Anda juga bisa melakukan sesuatu seperti

docker build -f ../Dockerfile .
Cyberwiz
sumber
8
@Ro. Anda menggunakan dockerfile:properti di build:bagian dalam Compose file docs.docker.com/compose/compose-file/#/compose-file-reference
Emerson Farrugia
3
Saya mendapatkan "Dockerfile harus berada dalam konteks build" - Saya benar-benar ingin memiliki satu Dockerfile yang dapat berada di bawah konteks build saat ini. Dalam contoh Anda, Anda memiliki Dockerfile di dalam / di bawah konteks build saat ini, yang tentu saja berfungsi.
Alexander Mills
3
Ya saya hanya ingin Dockerfile bersama yang sesuai dengan beberapa subdirektori, yang semuanya adalah "membangun konteks"
Alexander Mills
51
Apakah ini menyelesaikan masalah OP yang menginginkan ADDfile yang berada di luar direktori konteks? Itulah yang saya coba lakukan tetapi saya tidak berpikir untuk menggunakannya-f membuat file eksternal dapat ditambahkan.
Sridhar Sarnobat
18
Tidak bisa upvote cukup ini .. di saya buruh pelabuhan-compose.yml saya: build: context: .., dockerfile: dir/Dockerfile. Sekarang konteks build saya adalah direktori induk!
Mike Gleason jr Couturier
51

Saya sering mendapati diri saya menggunakan --build-argopsi untuk tujuan ini. Misalnya setelah meletakkan yang berikut di Dockerfile:

ARG SSH_KEY
RUN echo "$SSH_KEY" > /root/.ssh/id_rsa

Anda bisa melakukannya:

docker build -t some-app --build-arg SSH_KEY="$(cat ~/file/outside/build/context/id_rsa)" .

Tetapi perhatikan peringatan berikut dari dokumentasi Docker :

Peringatan: Tidak disarankan untuk menggunakan variabel build-time untuk meneruskan rahasia seperti kunci github, kredensial pengguna, dll. Nilai variabel build-time terlihat oleh setiap pengguna gambar dengan perintah history docker.

pengguna2658308
sumber
6
Ini saran yang buruk tanpa peringatan besar. Dari dokumentasi Docker: "Peringatan: Tidak disarankan untuk menggunakan variabel build-time untuk meneruskan rahasia seperti kunci github, kredensial pengguna, dll. Nilai variabel build-time terlihat oleh pengguna gambar mana pun dengan perintah history docker." [1] Dengan kata lain, contoh yang diberikan dalam contoh ini mengungkapkan kunci SSH pribadi pada gambar buruh pelabuhan. Dalam beberapa konteks, itu mungkin baik-baik saja. docs.docker.com/engine/reference/builder/#arg
sheldonh
3
Akhirnya, untuk mengatasi masalah keamanan ini, Anda dapat menggunakan teknik seperti squashing atau multistage-builds: vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo
46

Di Linux Anda dapat memasang direktori lain alih-alih menghubungkannya

mount --bind olddir newdir

Lihat /superuser/842642 untuk lebih jelasnya.

Saya tidak tahu apakah sesuatu yang serupa tersedia untuk OS lain. Saya juga mencoba menggunakan Samba untuk berbagi folder dan mengirimnya kembali ke dalam konteks Docker yang berfungsi juga.

Günter Zöchbauer
sumber
2
Hanya root yang dapat mengikat direktori
jjcf89
28

Saya menghabiskan waktu yang baik untuk mencari tahu pola yang baik dan bagaimana menjelaskan dengan lebih baik apa yang terjadi dengan dukungan fitur ini. Saya menyadari bahwa cara terbaik untuk menjelaskannya adalah sebagai berikut ...

  • Dockerfile: Hanya akan melihat file di bawah jalur relatifnya sendiri
  • Konteks: tempat di "spasi" tempat file yang ingin Anda bagikan dan Dockerfile Anda akan disalin

Jadi, dengan itu, inilah contoh Dockerfile yang perlu menggunakan kembali file yang disebut start.sh

Dockerfile

Ini ALWAYSakan memuat dari jalur relatifnya, memiliki dir saat ini sendiri sebagai localreferensi ke jalur yang Anda tentukan.

COPY start.sh /runtime/start.sh

File

Mempertimbangkan gagasan ini, kita dapat berpikir memiliki banyak salinan untuk Dockerfiles membangun hal-hal tertentu, tetapi mereka semua memerlukan akses ke start.sh.

./all-services/
   /start.sh
   /service-X/Dockerfile
   /service-Y/Dockerfile
   /service-Z/Dockerfile
./docker-compose.yaml

Mempertimbangkan struktur ini dan file-file di atas, inilah docker-compose.yml

docker-compose.yaml

  • Dalam contoh ini, shareddir konteks Anda adalahruntime dir.
    • Model mental yang sama di sini, berpikir bahwa semua file di bawah dir ini dipindahkan ke yang disebut context .
    • Demikian pula, tentukan saja Dockerfile yang ingin Anda salin ke dir yang sama. Anda dapat menentukan itu menggunakan dockerfile.
  • direktori tempat konten utama Anda berada adalah konteks aktual yang akan ditetapkan.

The docker-compose.ymladalah sebagai berikut

version: "3.3"
services:

  service-A
    build:
      context: ./all-service
      dockerfile: ./service-A/Dockerfile

  service-B
    build:
      context: ./all-service
      dockerfile: ./service-B/Dockerfile

  service-C
    build:
      context: ./all-service
      dockerfile: ./service-C/Dockerfile
  • all-servicediatur sebagai konteks, file bersama start.shdisalin di sana serta Dockerfile ditentukan oleh masing-masingdockerfile .
  • Masing-masing dapat dibangun dengan caranya sendiri, berbagi file awal!

Bersulang!

Marcello de Sales
sumber
1
Titik Anda pada Dockerfile tidak completly benar, seperti menunjuk dengan jawaban yang diterima, jika Anda berada di hirarki folder a/b/c, maka ya berjalan docker build .di ctidak akan memungkinkan Anda untuk mengakses ../file-in-b. Tapi, saya pikir kesalahpahaman umum dalam hal ini (atau setidaknya milik saya) adalah bahwa konteksnya ditentukan oleh lokasi yang dinyatakan oleh argumen pertama dari perintah build, bukan oleh lokasi Dockerfile. Jadi seperti yang dinyatakan dalam jawaban yang diterima: from a: docker build -f a/b/c/Dockerfile . berarti bahwa di Dockerfile .sekarang foldera
β.εηοιτ.βε
1
Mengutip dari dokumen Dockerfile: jalur file dan direktori akan ditafsirkan sebagai relatif terhadap sumber konteks pembangunan.
Nishant George Agrwal
18

Jika Anda membaca diskusi dalam masalah 2745 tidak hanya buruh pelabuhan mungkin tidak pernah mendukung symlinks mereka mungkin tidak pernah mendukung penambahan file di luar konteks Anda. Tampaknya menjadi filosofi desain bahwa file yang masuk ke bangunan buruh pelabuhan harus secara eksplisit menjadi bagian dari konteksnya atau berasal dari URL yang mungkin juga digunakan dengan versi tetap sehingga pembuatannya dapat diulang dengan URL atau file terkenal yang dikirimkan bersama wadah buruh pelabuhan.

Saya lebih suka membangun dari sumber versi terkontrol - yaitu buruh pelabuhan -t hal-hal http://my.git.org/repo - kalau tidak saya membangun dari beberapa tempat acak dengan file acak.

pada dasarnya, tidak .... - SvenDowideit, Docker Inc

Hanya pendapat saya tapi saya pikir Anda harus merestrukturisasi untuk memisahkan kode dan repositori buruh pelabuhan. Dengan cara itu wadah bisa menjadi generik dan menarik versi kode apa pun pada saat dijalankan daripada membangun waktu.

Atau, gunakan buruh pelabuhan sebagai artefak penyebaran kode dasar Anda dan kemudian Anda letakkan buruh pelabuhan di root repositori kode. jika Anda menggunakan rute ini mungkin masuk akal untuk memiliki container docker induk untuk detail level sistem yang lebih umum dan container anak untuk pengaturan khusus untuk kode Anda.

Usman Ismail
sumber
Mengapa menggunakan buruh pelabuhan sama sekali?
lscoughlin
11

Saya percaya solusi yang lebih sederhana adalah mengubah 'konteks' itu sendiri.

Jadi, misalnya, alih-alih memberi:

docker build -t hello-demo-app .

yang menetapkan direktori saat ini sebagai konteks, katakanlah Anda menginginkan direktori induk sebagai konteks, cukup gunakan:

docker build -t hello-demo-app ..
Anshuman Manral
sumber
6
Saya pikir ini istirahat .dockerignore: - \
NullVoxPopuli
Saya menyerah pada .dockerignore dan membuat folder buruh pelabuhan Makefile yang dikelola yang hanya berisi file yang diperlukan untuk membangun konteks ... Saya hanya perlu memanggil make builddan menarik semua file yang diperlukan jika mereka diperbarui dan kemudian memanggil build docker yang sesuai ... Saya perlu melakukan pekerjaan ekstra, tetapi bekerja dengan sempurna karena saya dalam kendali penuh.
Sahsahae
5

Anda juga dapat membuat tarball dari apa yang dibutuhkan gambar terlebih dahulu dan menggunakannya sebagai konteks Anda.

https://docs.docker.com/engine/reference/commandline/build/#/tarball-contexts

Laurent Picquet
sumber
Tip yang bagus! Saya menemukan Anda bahkan dapat memberi makan buruh pelabuhan membangun tarball sebagai konteks pada stdin: tar zc /dir1 /dir2 |docker build -. Ini sangat membantu dalam kasus saya.
Tore Olsen
3

Menggunakan docker-compose, saya menyelesaikan ini dengan membuat layanan yang me-mount volume yang saya butuhkan dan melakukan gambar kontainer. Kemudian, dalam layanan selanjutnya, saya mengandalkan gambar yang dilakukan sebelumnya, yang memiliki semua data yang disimpan di lokasi yang dipasang. Anda kemudian harus menyalin file-file ini ke tujuan akhir mereka, karena direktori yang di-host di host tidak mendapatkan komitmen ketika menjalankan docker commitperintah

Anda tidak harus menggunakan komposisi buruh pelabuhan untuk melakukan ini, tetapi itu membuat hidup sedikit lebih mudah

# docker-compose.yml

version: '3'
  services:
    stage:
      image: alpine
      volumes:
        - /host/machine/path:/tmp/container/path
      command: bash -c "cp -r /tmp/container/path /final/container/path"
    setup:
      image: stage
# setup.sh

# Start "stage" service
docker-compose up stage

# Commit changes to an image named "stage"
docker commit $(docker-compose ps -q stage) stage

# Start setup service off of stage image
docker-compose up setup
Kris Rivera
sumber
1

Saya memiliki masalah yang sama dengan proyek dan beberapa file data yang saya tidak dapat pindah ke dalam konteks repo karena alasan HIPPA. Saya akhirnya menggunakan 2 Dockerfiles. Seseorang membangun aplikasi utama tanpa barang-barang yang saya butuhkan di luar wadah dan menerbitkannya ke repo internal. Kemudian dockerfile kedua menarik gambar itu dan menambahkan data dan membuat gambar baru yang kemudian digunakan dan tidak pernah disimpan di mana pun. Tidak ideal, tetapi berfungsi untuk tujuan saya menjaga informasi sensitif dari repo.

Annulet Consulting
sumber
1

Solusi mudahnya adalah dengan memasang volume (menggunakan flag -v atau --mount) ke kontainer saat Anda menjalankannya dan mengakses file dengan cara itu.

contoh:

docker run -v /path/to/file/on/host:/desired/path/to/file/in/container/ image_name

untuk lebih lanjut lihat: https://docs.docker.com/storage/volumes/

Ben Wex
sumber
Perhatikan bahwa ini hanya berfungsi jika volume adalah ketergantungan runtime. Untuk dependensi waktu build, docker runsudah terlambat.
user3735633
1

Seperti yang dijelaskan dalam masalah GitHub ini , build benar-benar terjadi /tmp/docker-12345, jadi path relatif seperti ../relative-add/some-filerelatif terhadap /tmp/docker-12345. Maka akan dicari /tmp/relative-add/some-file, yang juga ditampilkan dalam pesan kesalahan. *

Tidak diperbolehkan memasukkan file dari luar direktori build, jadi ini menghasilkan pesan "Jalur terlarang". "

Rocksn17
sumber
0

Salah satu cara cepat dan kotor adalah mengatur konteks build sebanyak level yang Anda butuhkan - tetapi ini dapat memiliki konsekuensi. Jika Anda bekerja dalam arsitektur layanan microser yang terlihat seperti ini:

./Code/Repo1
./Code/Repo2
...

Anda dapat mengatur konteks build ke Codedirektori induk dan kemudian mengakses semuanya, tetapi ternyata dengan sejumlah besar repositori, ini dapat menyebabkan build membutuhkan waktu lama.

Contoh situasi bisa jadi tim lain mengelola skema basis data Repo1dan kode tim AndaRepo2 bergantung pada ini. Anda ingin mengurangi ketergantungan ini dengan beberapa data seed Anda sendiri tanpa khawatir tentang perubahan skema atau mengotori repositori tim lain (tergantung pada apa perubahannya Anda mungkin masih harus mengubah skrip data seed Anda tentu saja) Pendekatan kedua adalah hacky tetapi mengatasi masalah long build:

Buat skrip sh (atau ps1) ./Code/Repo2untuk menyalin file yang Anda butuhkan dan aktifkan perintah buruh pelabuhan yang Anda inginkan, misalnya:

#!/bin/bash
rm -r ./db/schema
mkdir ./db/schema

cp  -r ../Repo1/db/schema ./db/schema

docker-compose -f docker-compose.yml down
docker container prune -f
docker-compose -f docker-compose.yml up --build

Dalam file pembuatan docker, cukup atur konteksnya sebagai Repo2root dan gunakan konten ./db/schemadirektori di dockerfile Anda tanpa khawatir tentang path. Ingatlah bahwa Anda akan menjalankan risiko tidak sengaja melakukan direktori ini ke kontrol sumber, tetapi tindakan pembersihan skrip harus cukup mudah.

pengguna1007074
sumber
0

Dalam kasus saya, Dockerfile saya ditulis seperti templat yang berisi placeholder yang saya ganti dengan nilai nyata menggunakan file konfigurasi saya.

Jadi saya tidak bisa menentukan file ini secara langsung tetapi pipa ke bangunan buruh pelabuhan seperti ini:

sed "s/%email_address%/$EMAIL_ADDRESS/;" ./Dockerfile | docker build -t katzda/bookings:latest . -f -;

Tetapi karena pipa itu, COPYperintahnya tidak bekerja. Tetapi cara di atas menyelesaikannya dengan -f -(secara eksplisit mengatakan file tidak disediakan). Melakukan hanya -tanpa -fbendera, konteks DAN Dockerfile tidak disediakan yang merupakan peringatan.

Daniel Katz
sumber
0

Triknya adalah mengenali bahwa Anda dapat menentukan konteks dalam perintah build untuk memasukkan file dari direktori induk jika Anda menentukan jalur Docker, saya akan mengubah Dockerfile saya menjadi seperti ini:

...
COPY ./ /dest/
...

Maka perintah build saya dapat terlihat seperti ini:

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

Dari direktori proyek

docker built -t username/project[:tag] -f ./docker/Dockerfile .

Dari proyek / buruh pelabuhan

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
daniekpo
sumber