Moby: سيقوم Dockerfile COPY with file globs بنسخ الملفات من الدلائل الفرعية إلى الدليل الوجهة

تم إنشاؤها على ٢٦ أغسطس ٢٠١٥  ·  54تعليقات  ·  مصدر: moby/moby

وصف مشكلة:
عند استخدام COPY في Dockerfile واستخدام globs لنسخ الملفات والمجلدات ، سيقوم عامل الإرساء (أحيانًا؟) بنسخ الملفات من المجلدات الفرعية إلى مجلد الوجهة.

$ docker version
Client:
 Version:      1.8.1
 API version:  1.20
 Go version:   go1.4.2
 Git commit:   d12ea79
 Built:        Thu Aug 13 19:47:52 UTC 2015
 OS/Arch:      darwin/amd64

Server:
 Version:      1.8.0
 API version:  1.20
 Go version:   go1.4.2
 Git commit:   0d03096
 Built:        Tue Aug 11 17:17:40 UTC 2015
 OS/Arch:      linux/amd64

$ docker info
Containers: 26
Images: 152
Storage Driver: aufs
 Root Dir: /mnt/sda1/var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 204
 Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 4.0.9-boot2docker
Operating System: Boot2Docker 1.8.0 (TCL 6.3); master : 7f12e95 - Tue Aug 11 17:55:16 UTC 2015
CPUs: 4
Total Memory: 3.858 GiB
Name: dev
ID: 7EON:IEHP:Z5QW:KG4Z:PG5J:DV4W:77S4:MJPX:2C5P:Z5UY:O22A:SYNK
Debug mode (server): true
File Descriptors: 42
Goroutines: 95
System Time: 2015-08-26T17:17:34.772268259Z
EventsListeners: 1
Init SHA1:
Init Path: /usr/local/bin/docker
Docker Root Dir: /mnt/sda1/var/lib/docker
Username: jfchevrette
Registry: https://index.docker.io/v1/
Labels:
 provider=vmwarefusion

$ uname -a
Darwin cerberus.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64

تفاصيل البيئة:
الإعداد المحلي على OSX / w boot2docker تم إنشاؤه باستخدام جهاز الإرساء

كيفية التكاثر:

سياق الكلام

$ tree
.
├── Dockerfile
└── files
    ├── dir
    │   ├── dirfile1
    │   ├── dirfile2
    │   └── dirfile3
    ├── file1
    ├── file2
    └── file3

ملف Dockerfile

FROM busybox

RUN mkdir /test
COPY files/* /test/

النتائج الفعلية

$ docker run -it copybug ls -1 /test/
dirfile1
dirfile2
dirfile3
file1
file2
file3

نتائج متوقعة
يجب أن تحتوي الصورة الناتجة على نفس بنية الدليل من السياق

arebuilder

التعليق الأكثر فائدة

قم بعمل CP أمر جديد واحصل عليه بشكل صحيح هذه المرة من فضلك.

ال 54 كومينتر

تم تحديث الرسالة الأصلية بإخراج من docker info و uname -a وإعادة تنسيقها لتتلاءم مع نموذج الإبلاغ عن المشكلات.

لقد حصلت على هذا في 1.6.2 و 1.8
https://gist.github.com/jrabbit/e4f864ca1664ec0dd288 يتم التعامل مع أدلة المستوى الثاني على أنها أدلة المستوى الأول يجب أن تكون لسبب ما؟

لأولئك الذين يستخدمون googling: إذا كنت تواجه مشكلات مع COPY * / src ، فجرّب COPY / / src

jfchevrette أعتقد أنني أعرف لماذا يحدث هذا.
لديك COPY files/* /test/ الذي يتوسع إلى COPY files/dir files/file1 files/file2 files/file /test/ . إذا قمت بتقسيم هذا إلى أوامر نسخ فردية (على سبيل المثال COPY files/dir /test/ ) فسترى أن (للأفضل أو للأسوأ) COPY سيقوم بنسخ محتويات كل arg dir إلى الوجهة dir. ليس الدير نفسه ، ولكن المحتويات. إذا قمت بإضافة مستوى ثالث من dirs أراهن أن هؤلاء سوف يستمرون.

لست سعيدًا بحقيقة أن COPY لا تحافظ على المستوى الأعلى من dir ، لكنها كانت كذلك لفترة من الوقت الآن.

يمكنك محاولة جعل هذا أقل إيلامًا عن طريق نسخ مستوى أعلى في شجرة src ، إن أمكن.

أنا واثق تمامًا من أن duglin على حق وقد يكون تغيير هذا السلوك محفوفًا بالمخاطر. قد تتعطل العديد من ملفات dockerfiles أو تنسخ ببساطة أشياء غير مقصودة.

ومع ذلك ، أود أن أزعم أنه على المدى الطويل سيكون من الأفضل إذا كانت COPY تتبع الطريقة التي تتعامل بها أدوات مثل cp أو rsync مع globs والشرطات المائلة على المجلدات. بالتأكيد ليس من المتوقع أن يقوم COPY بنسخ الملفات من مجلد فرعي يطابق dir / * إلى الوجهة IMO

jfchevrette نعم - الفرصة الأولى التي نحصل عليها يجب أن "نصلح" هذا.
إغلاقها الآن ...

duglin إذن ، يعني الإغلاق أنه لن يتم إصلاحه؟

tugberkugurlu نعم ، على الأقل في الوقت الحالي. هناك عمل جاري لإعادة البنية التحتية للإنشاء بالكامل وعندما نفعل ذلك ، يمكننا جعل COPY (أو ما يعادله الجديد) يعمل بالطريقة التي ينبغي أن يفعلها.

duglin شكرا. هل من الممكن إبقاء هذه المشكلة مفتوحة وتحديث الحالة هنا؟ أو هل هناك مشكلة أخرى يمكنني الاشتراك فيها؟

tugberkugurlu اعتقدت أن لدينا مشكلة تتعلق "بدعم منشئ جانب العميل" ولكن لا يمكنني العثور عليها على ما يبدو. لذلك كل ما قد يكون لدينا هو ما تقوله خريطة الطريق (https://github.com/docker/docker/blob/master/ROADMAP.md#22-dockerfile-syntax).

بالنسبة لإبقاء القضية مفتوحة ، لا أعتقد أنه يمكننا القيام بذلك. القاعدة العامة التي يتبعها Docker هي إغلاق أي مشكلة غير قابلة للتنفيذ على الفور. عادةً ما يتم إغلاق مشكلات العمل المستقبلي ثم إعادة فتحها بمجرد تغيير حالة الأشياء بحيث يمكن اتخاذ بعض الإجراءات (العلاقات العامة) لهذه المشكلة.

duglin هذه مشكلة خطيرة للغاية ، لا يجب عليك إغلاقها فقط لأن المشكلة

أعتقد أن معظم الناس يستخدمون:

COPY . /app

وقم بإدراج جميع المجلدات الأخرى في القائمة السوداء في .gitignore أو لديك بنية دليل ذات مستوى واحد واستخدم COPY الذي يحتوي بالفعل على دلالات mv :

COPY src /myapp

من الصعب جدًا بالنسبة لي أن أتخيل أن شخصًا ما سيستخدم بالفعل COPY لتسوية بنية الدليل. الحل الآخر لهذا هو استخدام tar -cf .. & ADD tarfile.tar.gz . تغيير هذا على الأقل سيكون مفيدًا حقًا. الشيء الآخر هو احترام الشرطات المائلة في أسماء الدلائل COPY src /src مقابل COPY src/ /src (والتي يتم تجاهلها تمامًا حاليًا).

أغلق duglin هذا في 1 سبتمبر 2015

duglin هذه قضية سخيفة ومثيرة للغضب ولا يجب إغلاقها. يتصرف الأمر COPY بشكل خاص في الاختلاف مع الاستخدام الموثق والأمثلة.

tjwebb لا تزال هناك مشكلة مفتوحة https://github.com/docker/docker/issues/29211. لا يمكن النظر في هذا إلا إذا كانت هناك طريقة لإصلاح ذلك متوافق تمامًا مع الإصدارات السابقة. نحن منفتحون على الاقتراحات إذا كان لديك اقتراح _ كيف_ يمكن تنفيذ ذلك (إذا كنت _ تفعل _ ، فلا تتردد في كتابة هذا ، وفتح اقتراح ، مع ربط هذه المشكلة). لاحظ أن هناك فرقًا بالفعل بين (على سبيل المثال) ، OS X ، و Linux في طريقة التعامل مع cp ؛

mkdir -p repro-15858 \
  && cd repro-15858 \
  && mkdir -p source/dir1 source/dir2 \
  && touch source/file1 source/dir1/dir1-file1 \
  && mkdir -p target1 target2 target3 target4 target5 target6

cp -r source target1 \
&& cp -r source/ target2 \
&& cp -r source/ target3/ \
&& cp -r source/* target4/ \
&& cp -r source/dir* target5/ \
&& cp -r source/dir*/ target6/ \
&& tree

OS X:

.
├── source
│   ├── dir1
│   │   └── dir1-file1
│   ├── dir2
│   └── file1
├── target1
│   └── source
│       ├── dir1
│       │   └── dir1-file1
│       ├── dir2
│       └── file1
├── target2
│   ├── dir1
│   │   └── dir1-file1
│   ├── dir2
│   └── file1
├── target3
│   ├── dir1
│   │   └── dir1-file1
│   ├── dir2
│   └── file1
├── target4
│   ├── dir1
│   │   └── dir1-file1
│   ├── dir2
│   └── file1
├── target5
│   ├── dir1
│   │   └── dir1-file1
│   └── dir2
└── target6
    └── dir1-file1

20 directories, 12 files

على Ubuntu (/ bin / sh)

.
|-- source
|   |-- dir1
|   |   `-- dir1-file1
|   |-- dir2
|   `-- file1
|-- target1
|   `-- source
|       |-- dir1
|       |   `-- dir1-file1
|       |-- dir2
|       `-- file1
|-- target2
|   `-- source
|       |-- dir1
|       |   `-- dir1-file1
|       |-- dir2
|       `-- file1
|-- target3
|   `-- source
|       |-- dir1
|       |   `-- dir1-file1
|       |-- dir2
|       `-- file1
|-- target4
|   |-- dir1
|   |   `-- dir1-file1
|   |-- dir2
|   `-- file1
|-- target5
|   |-- dir1
|   |   `-- dir1-file1
|   `-- dir2
`-- target6
    |-- dir1
    |   `-- dir1-file1
    `-- dir2

24 directories, 12 files
diff --git a/macos.txt b/ubuntu.txt
index 188d2c3..d776f19 100644
--- a/macos.txt
+++ b/ubuntu.txt
@@ -11,15 +11,17 @@
 │       ├── dir2
 │       └── file1
 ├── target2
-│   ├── dir1
-│   │   └── dir1-file1
-│   ├── dir2
-│   └── file1
+│   └── source
+│       ├── dir1
+│       │   └── dir1-file1
+│       ├── dir2
+│       └── file1
 ├── target3
-│   ├── dir1
-│   │   └── dir1-file1
-│   ├── dir2
-│   └── file1
+│   └── source
+│       ├── dir1
+│       │   └── dir1-file1
+│       ├── dir2
+│       └── file1
 ├── target4
 │   ├── dir1
 │   │   └── dir1-file1
@@ -30,6 +32,8 @@
 │   │   └── dir1-file1
 │   └── dir2
 └── target6
-    └── dir1-file1
+    ├── dir1
+    │   └── dir1-file1
+    └── dir2

-20 directories, 12 files
+24 directories, 12 files

قم بعمل CP أمر جديد واحصل عليه بشكل صحيح هذه المرة من فضلك.

أود أن أكرر ما سبق ، يجب أن يكون هذا قد أهدر ساعات تطوير لا حصر لها ، إنه أمر غير بديهي للغاية.

+1 مني. هذا سلوك غبي حقًا ويمكن علاجه بسهولة عن طريق إضافة أمر CP الذي ينفذ الطريقة التي يجب أن يكون بها COPY.

"التوافق مع الإصدارات السابقة" هو خروج شرطي

نسخة TL ؛ DR :

لا تستخدم COPY * /app ، فهو لا يفعل ما تتوقعه.
استخدم COPY . /app بدلاً من ذلك للحفاظ على شجرة الدليل.

نسخ فقط قادر على نسخ المجلد الفرعي الخاص به.

لقد أمضيت للتو ساعات لا حصر لها في هذا ... لماذا يعمل هذا بهذه الطريقة؟

أنا أستخدم Paket وأريد نسخ ما يلي بالبنية الصحيحة:

.
├── .paket/
│   ├── paket.exe
│   ├── paket.bootstrapper.exe
├── paket.dependencies
├── paket.lock
├── projectN/

وبعمل COPY *paket* ./ ينتج هذا داخل الحاوية:

.
├── paket.dependencies
├── paket.lock

ماذا عن إضافة علامة --glob أو --recursive لـ COPY و ADD ؟

ينسخ . / الوجهة تحافظ على المجلدات الفرعية.

ثلاث سنوات ومازالت هذه مشكلة: - /

هل يمكننا الحصول على ETA ، عندما يتم إصلاح ذلك

لا مشكلة ...
من اعلى...
ينسخ .

صحيح ، لم تعد هناك مشكلة بعد أن دخان لمدة نصف يوم وينتهي بك الأمر هنا. بالتأكيد :)
لنكن بناءين ،

image

نحتاج حقًا إلى أمر _CP_ جديد أو علامة --recursive لـ _COPY_ لذلك يتم الحفاظ على التوافق مع الإصدارات السابقة.

أهم النقاط إذا أظهرنا أيضًا تحذيرًا بشأن إنشاء الصورة ، مثل:
Directory structure not preserved with COPY *, use CP or COPY . More here <link>. إذا اكتشفنا إساءة استخدام محتملة.

أبحث عن هذا من أجل النسخ عبر ملفات lerna package.json المتداخلة في الدلائل الفرعية للاستفادة بشكل أفضل من ذاكرة التخزين المؤقت npm install للتشغيل فقط عندما تتغير التبعيات. حاليًا كل الملفات التي تم تغييرها تتسبب في تثبيت التبعيات مرة أخرى.

شيء من هذا القبيل سيكون رائعًا:

COPY ["package.json", "packages/*/package.json", "/app/"]

اذهب تحقق # 29211 يا شباب. تم إغلاق هذا واحد ولا أحد يهتم.

zentby Conversation هنا ، يتم تعقب المشكلة هناك (بما أن هذه القضية مغلقة) ... إنها محيرة.

الحل البديل هو ملفات COPY و الأمر RUN cp -R

COPY files /tmp/
RUN cp -R /tmp/etc/* /etc/ && rm -rf /tmp/etc

لن يعمل ذلك instabledesign لأن الأمر COPY يدمر ذاكرة التخزين المؤقت عندما يكون الملف مختلفًا ولا يجب أن يبطل ذاكرة التخزين المؤقت (على سبيل المثال ، أريد فقط نسخ الملفات المتعلقة بتثبيت تبعية npm لأن ذلك لا يتغير غالبًا)

احتجت أيضًا إلى نسخ مجموعة فقط من الملفات (في حالتي ، ملفات * .sln و * .csproj لنواة dotnet) إلى ذاكرة التخزين المؤقت الضارة. حل واحد هو إنشاء كرة القطران من الملفات التي تريدها فقط ثم إضافة كرة القطران في ملف Docker. نعم ، الآن يجب أن يكون لديك برنامج نصي شل بالإضافة إلى ملف Docker ...

build.sh

#!/bin/bash

# unfortunately there's no easy way to copy just the *.sln and *.csproj (see https://github.com/moby/moby/issues/15858)
# so we generate a tar file containing the required files for the layer

find .. -name '*.csproj' -o -name 'Finomial.InternalServicesCore.sln' -o -name 'nuget.config' | sort | tar cf dotnet-restore.tar -T - 2> /dev/null

docker build -t finomial/iscore-build -f Dockerfile ..

ملف عامل ميناء

FROM microsoft/aspnetcore-build:2.0
WORKDIR /src

# set up a layer for dotnet restore 

ADD docker/dotnet-restore.tar ./

RUN dotnet restore

# now copy all the source and do the dotnet buld
COPY . ./

RUN dotnet publish --no-restore -c Release -o bin Finomial.InternalServicesCore.sln

يمكنك استخدام أوامر متعددة COPY للقيام بذلك ، لكن هذا له عيب في إنشاء طبقات صور متعددة وانتفاخ حجم صورتك النهائية.

كما هو مذكور أعلاه kayjtea ، يمكنك أيضًا لف الأمر docker build في برنامج نصي مساعد لإنشاء كرات القطران التي تحافظ على بنية الدليل ، و ADD فيها ، لكن هذا يضيف تعقيدًا ويفكك أشياء مثل docker-compose build و Docker Hub المؤتمتة.

حقًا ، يجب أن يعمل COPY تمامًا مثل الأمر /bin/cp -r المتوافق مع POSIX ، ولكن يبدو أن هذا لن يحدث لـ "التوافق مع الإصدارات السابقة" ، على الرغم من أن السلوك الحالي غير بديهي تمامًا لأي شخص لديه خبرة في * لا شىء الأنظمة.


أفضل حل وسط وجدته هو استخدام بناء متعدد المراحل كاختراق:

FROM scratch as project_root
# Use COPY to move individual directories
# and WORKDIR to change directory
WORKDIR /
COPY ./file1 .
COPY ./dir1/ ./dir1/
COPY ./dir2/ .
WORKDIR /newDir
COPY ./file2 .

# The actual final build you end up using/pushing
# Node.js app as example
FROM node
WORKDIR /opt/app

COPY package.json .
RUN npm install

COPY --from=project_root / .
CMD ["npm", "start"]

هذا مضمّن ذاتيًا في ملف Dockerfile واحد ، وينشئ طبقة واحدة فقط في الصورة النهائية ، تمامًا مثل كيفية عمل ADD project.tar .

من شأن وجود أمر COPY كامل أن يساعد حقًا عند محاولة الحفاظ على ذاكرة التخزين المؤقت لإنشاء عامل الإرساء. يتطور مجتمع ROS باستخدام مساحة عمل متداخلة من الحزم ، مع إعلان كل واحدة عن التبعيات في ملفها package.xml . يتم استخدام هذه الملفات من قبل مدير التبعية لتثبيت أي مكتبات أولية. يتغير ملف package.xml بشكل غير متكرر نسبيًا إلى كتابة التعليمات البرمجية في الحزم نفسها بمجرد تعيين الأساس. إذا تم الحفاظ على بنية شجرة الدليل أثناء النسخ ، فيمكننا ببساطة نسخ مساحة العمل الخاصة بنا أثناء إنشاء عامل الإرساء على مرحلتين لزيادة التخزين المؤقت ، على سبيل المثال:

# copy project dependency metadata
COPY ./**/package.xml /opt/ws/

# install step that fetches unsatisfied dependency
RUN dependency_manager install --workspace /opt/ws/

# copy the rest of the project's code
COPY ./ /opt/ws/

# compile code with cached dependencies
RUN build_tool build --workspace /opt/ws/

وبالتالي ، فإن ذاكرة التخزين المؤقت لطبقة تثبيت التبعية أعلاه لن تنهار إلا إذا حدث للمطور تغيير تبعية معلنة ، في حين أن التغيير في كود الحزمة لن يؤدي إلا إلى إفساد طبقة الترجمة.

حاليًا ، يتم نسخ جميع ملفات package.xml المتطابقة فوق بعضها البعض إلى جذر دليل الوجهة ، مع كون آخر ملف globed هو package.xml الوحيد الذي ظل موجودًا في الصورة. وهو أمر غير بديهي حقًا للمستخدمين! لماذا يتم الكتابة فوق الملفات المنسوخة فوق بعضها البعض ، بالإضافة إلى السلوك غير المحدد الذي يستمر في النهاية في الصورة.

هذا هو مثل هذا الألم في الأساس في كل حزمة لديها إدارة الحزمة ، لذلك فهي تؤثر على الكثير منا. هل يمكن إصلاحه؟ شيش. لقد كانت مشكلة منذ عام 2015! اقتراح استخدام أمر جديد CP هو اقتراح جيد.

هل يمكننا إعادة فتح هذا؟ إنه سلوك ممل للغاية أن يستخدم الأمر COPY وظيفة golang الداخلية لمطابقة المسار ، بدلاً من معيار حقيقي واسع الانتشار ، مثل glob

بالنسبة لأولئك الذين يرغبون في النسخ عبر الكرة الأرضية باستخدام حل بديل باستخدام بنية buildkit تجريبية ، حتى إذا لم يكن التخزين المؤقت دقيقًا أو قويًا ، فيمكنهم إلقاء نظرة على التعليقات هنا: https://github.com/moby/moby/issues / 39530 # issuecomment -530606189

ما زلت أرغب في رؤية هذه المشكلة قد أعيد فتحها حتى نتمكن من التخزين المؤقت على نسخ نمط glob الانتقائية.

لقد أدركت حلاً بسيطًا نسبيًا لمثالي في https://github.com/moby/moby/issues/15858#issuecomment -462017830 عبر عمليات إنشاء متعددة المراحل ، وأعتقد أن الكثير منكم هنا ممن لديهم احتياجات مماثلة قد يقدرون التخزين المؤقت التعسفي عند النسخ القطع الأثرية من سياق البناء. باستخدام إصدارات متعددة المراحل ، من الممكن تصفية / معالجة الدليل للتخزين المؤقت:

# Add prior stage to cache/copy from
FROM ubuntu AS package_cache

# Copy from build context
WORKDIR /tmp
COPY ./ ./src

# Filter or glob files to cache upon
RUN mkdir ./cache && cd ./src && \
    find ./ -name "package.xml" | \
      xargs cp --parents -t ../cache

# Continue with primary stage
FROM ubuntu

# copy project dependency metadata
COPY --from=package_cache /tmp/cache /opt/ws/

# install step that fetches unsatisfied dependency
RUN dependency_manager install --workspace /opt/ws/

# copy the rest of the project's code
COPY ./ /opt/ws/

# compile code with cached dependencies
RUN build_tool build --workspace /opt/ws/

للحصول على مثال عملي في العالم الحقيقي ، يمكنك أيضًا إلقاء نظرة هنا: https://github.com/ros-planning/navigation2/pull/1122

أبحث عن هذا من أجل النسخ عبر ملفات lerna package.json المتداخلة في الدلائل الفرعية للاستفادة بشكل أفضل من ذاكرة التخزين المؤقت npm install للتشغيل فقط عندما تتغير التبعيات. حاليًا كل الملفات التي تم تغييرها تتسبب في تثبيت التبعيات مرة أخرى.

شيء من هذا القبيل سيكون رائعًا:

COPY ["package.json", "packages/*/package.json", "/app/"]

لدي نفس حالة الاستخدام بالضبط.

أبحث عن هذا من أجل النسخ عبر ملفات lerna package.json المتداخلة في الدلائل الفرعية للاستفادة بشكل أفضل من ذاكرة التخزين المؤقت npm install للتشغيل فقط عندما تتغير التبعيات. حاليًا كل الملفات التي تم تغييرها تتسبب في تثبيت التبعيات مرة أخرى.

شيء من هذا القبيل سيكون رائعًا:

COPY ["package.json", "packages/*/package.json", "/app/"]

هذه الحالة ولكن لمساحات عمل الغزل.

إنه عام 2020 وما زال هذا غير ثابت.

إذا كان أي شخص يكافح مع هذا في إعداد dotnet ، فقد قمت بحلها لنا عن طريق كتابة أداة عالمية أساسية dotnet تستعيد بنية الدليل لملفات * .csproj ، مما يسمح باستعادة المتابعة. انظر الوثائق حول كيفية القيام بذلك هنا .

لمعلوماتك ، من الناحية النظرية ، يمكن استخدام نهج مشابه في إعدادات أخرى ، ولكن الأداة بشكل أساسي تقوم بهندسة عكسية لهيكل المجلد ، لذلك لست متأكدًا من مدى سهولة أو حتى إمكانية إعداد مساحات عمل lerna أو الغزل. يسعدني التحقيق في الأمر إذا كان هناك اهتمام. يمكن أن يكون ممكنًا في نفس الأداة إذا كان الناس سعداء بتثبيت وقت تشغيل dotnet الأساسي حتى يعمل ، وإلا فسيتعين بناء نفس الأسلوب الذي قمت به بلغة لا تتطلب تبعية جديدة ، مثل العقدة اعتقد.

إنه لأمر مدهش أن يكون تنفيذ أمر النسخ مهمة بالنسبة للطلاب في السنة الأولى ، وهو الآن معقد للغاية بالنسبة للمبرمجين المهرة الذين لديهم سنوات عديدة من الخبرة ...

ربما لا يكون هذا الخطأ الأكثر إحراجًا على الإطلاق ، ولكن مع الأخذ في الاعتبار أنه يتبعه سنوات عديدة من المناقشة دون أي نتيجة ، فإنه بالتأكيد يصل إلى القمة.

تضمين التغريدة
لمعلوماتك ، من الناحية النظرية ، يمكن استخدام نهج مماثل في إعدادات أخرى ، ولكن الأداة أساسًا هي الهندسة العكسية لهيكل المجلد ،

أليس من السهل بالنسبة لمعظم المناسبات أن تفعل ما اقترحه https://github.com/moby/moby/issues/15858#issuecomment -532016362 واستخدام بناء متعدد المراحل للتصفية المسبقة؟

أيضًا بالنسبة للحالة dotnet restore فهي نمط سهل نسبيًا:

# Prefiltering stage using find -exec and cp --parents to copy out
# the project files in their proper directory structure.
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS dotnet-prep
COPY . ./src/
RUN mkdir ./proj && cd ./src && \
  find . -type f -a \( -iname "*.sln" -o -iname "*.csproj" \) \
    -exec cp --parents "{}" ../proj/ \;

# New build stage, independent cache
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS dotnet-build

# Copy only the project files with correct directory structure
# then restore packages
COPY --from=dotnet-prep ./proj ./src/
RUN dotnet restore

# Copy everything else
COPY --from=dotnet-prep ./src ./src/
# etc.

لا يزال لا يقدم عذرًا مناسبًا لـ Docker لعدم تنفيذ متغير لائق على الأمر COPY الذي يتبع دلالات المزامنة العادية والعقلانية.
أعني؛ تعال!

rjgotten ، أنا أحب ذلك! بالتأكيد أسهل بكثير مما فعلته ولا أستطيع أن أرى سبب عدم نجاح ذلك في تلبية احتياجاتي. سأجربها غدًا ، وإذا نجح ذلك ، فسأغير مستنداتي لأوصي بهذا كأسلوب أفضل.

أعتقد أن مشكلتي الأولية كانت أنني كنت على نظام Windows ، لذا ربما رفضت هذا الاقتراح. لم أعد بعد الآن ، ولكن هل لديك نسخة Windows مكافئة للاكتمال؟ أتساءل عما إذا كان PowerShell مثبتًا مسبقًا في صور dotnet الأساسية ...

هل هناك حقًا حاجة إلى الإضافي / المتكرر من ... رغم ذلك؟ في كل مرة تقوم فيها بتشغيل RUN فإنها تنشئ طبقة جديدة للتخزين المؤقت ، أليس كذلك؟ ربما أفتقد شيئًا ما ، لقد مرت فترة منذ أن كان علي التفكير في هذا الأمر!

أتساءل عما إذا كان PowerShell مثبتًا مسبقًا في صور dotnet الأساسية ...

أعتقد أنه هو في الواقع. مما يجعل القيام بذلك أسهل قليلاً بطريقة مشتركة بين الأنظمة الأساسية.

هل هناك حقًا حاجة إلى الإضافي / المتكرر من ... رغم ذلك؟

تحصل مراحل البناء المعزولة على تخزين مؤقت للطبقة المستقلة.
المرحلة الأولى تقوم بأعمال التحضير. نظرًا لأنه يتعين عليه في البداية نسخ كل شيء فيه ، فإنه دائمًا ما يبطل ذاكرة التخزين المؤقت للطبقة الأولى ، وبالتالي الطبقات التي تليها ، عندما يتغير أي ملف. ولكن هذا ينطبق فقط على الطبقات _ ضمن تلك المرحلة للبناء_.

تبدأ المرحلة الثانية بنسخ الملفات المتعلقة بالمشروع _ فقط_ وطالما أن هذه الملفات هي نفسها - أي أسماء الملفات نفسها ؛ نفس المحتوى وما إلى ذلك - عبر يبني تلك الطبقة _لا تبطل. مما يعني أن طبقة dotnet restore _أيضًا_ لن يتم إبطالها ما لم يتم تغيير ملفات المشروع هذه بالفعل.

كان لدي بعض الوقت للجلوس مع هذا وأنا أفهم الآن! يعد Docker ممتعًا لأنه ما لم تقضي دائمًا وقتًا معه ، فإنك تنسى كيفية عمل جميع الأوامر. بشكل حاسم ، لقد نسيت أن RUN cmd يمكن أن يعمل فقط على نظام ملفات صورة عامل الإرساء ، وليس ملفات سياق البناء. لذا فأنت مجبر على نسخ كل شيء قبل أن تتمكن من إجراء RUN cmd المعقد الذي يحافظ على dirs. وهذا هو السبب في أننا في أمس الحاجة إلى النسخ اللائق!

هذا النهج
النسخة الأولية cmd هي ، كما ذكرت ، نسخ _ كل شيء_ ثم يتم سحب ملفات .sln و .csproj إلى مجلد منفصل / proj. أي تغيير رمز سوف يبطل هذه الخطوات. بشكل حاسم ، فإن القيد الذي يعمل حوله هو أن RUN linux cmd الرائع يمكن أن يعمل فقط على الملفات _على صورة عامل الإرساء بالفعل_ ، التي جلبتها النسخة الجشعة مسبقًا.

ثم تبدأ مرحلة جديدة ، ونسخ محتويات المجلد / proj والتي يمكن استخدامها مقابل dotnet restore . نظرًا لأن "المفتاح" في ذاكرة التخزين المؤقت هو تجزئة الملف بشكل أساسي ، نادرًا ما يؤدي ذلك إلى تعطيل طبقة ذاكرة التخزين المؤقت هذه ، ولا الطبقة اللاحقة dotnet restore ، لذلك تتجنب الاستعادة باهظة الثمن. لطيف!

مقاربتي
يستخدم فقط مرحلة بناء واحدة لهذا على حساب عدد قليل من أوامر COPY لجلب الملفات التي تؤثر على استعادة dotnet. قمت على وجه التحديد بتسوية جميع ملفات .csproj في dir واحد ، ثم استخدم أداتي العامة لإعادة بناء بنية الدليل الصحيحة من ملف الإدخال .sln. بعد ذلك فقط أنسخ جميع ملفات src ، حتى أتمكن من تخزين الطبقات بشكل مؤقت حتى هنا بانتظام ، بدلاً من الاضطرار دائمًا إلى نسخ جميع ملفات src في المقدمة.

الوجبات الجاهزة
أعتقد أن الأمر سيعتمد على قاعدة بيانات الأشخاص فيما يتعلق بمدى فعالية كل نهج. بالنسبة لنا ، في الريبو الأحادي ، لدينا الكثير من الأكواد المشتركة التي يتم نسخها عبر نسخة "all src over". .dockerignore يساعد هنا ، ولكن من الصعب الحفاظ عليه ، لذلك نحن "جشعون" جدًا في تلك النسخة ؛ لذلك فهي بطيئة للغاية. على هذا النحو ، فإن أسلوبي ، على الرغم من أنه أكثر تعقيدًا بعض الشيء ، ربما يكون أسرع بالنسبة لنا من هذا البديل.

شكرا على الشرح. ما زلت لا أصدق أنه يتعين علينا حتى إجراء هذه المحادثة هاها. ما زلت بحاجة إلى التحقيق في عناصر BuildKit الأحدث لمعرفة ما إذا كان ذلك أسهل الآن. هل قام أي شخص آخر بذلك؟

تحقيق BuildKit
RUN --mount=type=bind - يبدو أن هذا سيسمح لنا بعمل تنسيق linux الرائع مقابل سياق الإنشاء (بدلاً من اقتصار RUN فقط على نظام ملفات الصورة). في الواقع يبدو أنه يتخلف عن السداد الافتراضي لسياق البناء.

RUN --mount=type=cache - يبدو وكأنه نوع من دليل ذاكرة التخزين المؤقت القابل لإعادة الاستخدام (بين بنى عامل الإرساء؟) المحفوظ؟ لذلك ، في جوهرها ، لا داعي للقلق كثيرًا بشأن طبقة ذاكرة التخزين المؤقت لاستعادة الحزمة ، لأنه مع وجود ذاكرة تخزين مؤقت مُعاد استخدامها للحزم التي تمت استعادتها سابقًا ، ستكون بالفعل أسرع كثيرًا !؟

أعتقد أن المشكلة لم يتم حلها بعد لأنها "مغلقة" والناس معتادون على الحلول البديلة.

عندما لا تفهم لماذا يميل الناس إلى الانتقال إلى نوع حاوية أخرى.

هل هناك نوع حاوية أخرى يمكنني استخدامها. لا أصدق أن هذا لم يتم دعمه بعد سنوات عديدة؟ هل docker مشروع مفتوح المصدر ، هل يمكن لأي شخص إصلاحه؟

لدينا خيار COPY --dir في Earthly لجعل نسخة تتصرف مثل cp -r . ربما يمكن نقل هذا إلى Dockerfile أيضًا؟

لتسريع بناء الصور للتطبيقات الأساسية. net ، يجب علينا إنشاء حاوية وسيطة تحتوي على جميع حزم nuget المستعادة. بالنسبة لحل متعدد المشاريع ، استخدمت هذا الحل البديل. لقد قمت للتو بنسخ جميع ملفات المشروع إلى مجلد واحد وتشغيل استعادة dotnet لكل منها. هناك بعض التحذيرات حول المشاريع الفائتة لأننا لا نستطيع الحفاظ على التسلسل الهرمي للمجلدات ، ولكن لا يزال هذا الحل يعمل.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build

# Nugets restore
WORKDIR /src/allprojects          # just temporary storage for .csproj files
COPY */*.csproj ./
RUN for file in $(ls *.csproj); do dotnet restore ${file}; done

# Build/Publish
WORKDIR /src/solution             # actual folder with source code and full hierarchy 
COPY . .
RUN dotnet publish "MyProject/MyProject.csproj" -c Release -o /bublish/myproject

# Run Application
FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS base
WORKDIR /app
COPY --from=build /bublish/myproject .
ENTRYPOINT ["dotnet", "MyProject.dll"]

تضمين التغريدة
هناك بعض التحذيرات حول المشاريع الفائتة لأننا لا نستطيع الحفاظ على التسلسل الهرمي للمجلدات ، ولكن لا يزال هذا الحل يعمل.

رقم؛ هذا لا يعمل. وها هو السبب:

يقوم .NET Core بتخزين المعلومات الوصفية للحزمة في الدليل الفرعي ./obj المرتبط بكل مشروع. بدون هذه المعلومات ، لن يتم اعتبار الحزمة مثبتة وجاهزة للاستخدام. (لا تصدقني؟ ثم تخلص من مجلد ./obj الخاص بك ، ثم افتح المشروع على سبيل المثال في VSCode وشاهده يطلب منك إعادة تشغيل استعادة الحزمة. انطلق ؛ جربه.)

إذا كانت ملفات المشروع التي تنفذ عليها استعادة الحزمة موجودة في هيكل دليل مختلف عن التالي dotnet build أو dotnet publish ، فلن ترى هذه الأوامر الحزمة على أنها مستعادة.

سبب عدم فشل حلك تمامًا هو أن dotnet publish و dotnet build كلاهما يشيران dotnet restore . إنهم يبحثون بنشاط عن الحزم غير المستعادة ويستعيدونها أثناء التنقل. لتجنب القيام بذلك ، عليك أن تمرر العلم --no-restore ، وهو ما لا تفعله.

حسنًا ، ما يفعله الحل الخاص بك هو استعادة الحزم _TWICE_. تعد المرة الأولى بشكل أساسي مضيعة كبيرة للوقت والمكان ، لأنها لا تدخل في بنية الدليل الصحيحة لإعادة استخدامها. المرة الثانية ، ضمنًا كجزء من الأمر publish ، تعمل ؛ ولكن نظرًا لأنها جزء من نفس الطبقة مثل عملية الإنشاء والنشر ، لا يتم فعليًا تخزين الحزم مؤقتًا بشكل منفصل عن تغييرات التعليمات البرمجية الخاصة بك على الإطلاق.

rjgotten ،
شكرا لك على ردك وتوضيحاتك.
في الواقع ، يتم تخزين جميع حزم nuget مؤقتًا في مجلد global-packages في حاوية عامل بناء 'build'. في حالتي ، يكون المجلد $ # $ /root/.nuget/packages/ $ # $ ، ويحتوي المجلد obj فقط على ملفات صغيرة ذات مراجع إلى وحدة التخزين العالمية هذه ، لذلك لا يوجد هدر للتخزين (كما ذكرت).
الاستعادة الثانية أثناء النشر أسرع بـ 10 مرات على الأقل (في حالتي) لأن جميع القطع الصغيرة مخزنة مؤقتًا في الحاوية.

zatuliveterrjgotten شكرا للمعلومات في النهاية هنا. كنت أواجه مشكلات مماثلة وتوصلت إلى الجزء التالي من ملف dockerfile لتحسين الأمثلة التي قدمتها. باش بالتأكيد ليست بدلتي القوية لذا تهاون علي! هيكلنا هو Project/Project.csproj لجميع مشاريعنا. يؤدي هذا إلى نسخ جميع ملفات proj الموجودة ، ونقلها إلى المكان الصحيح ، ثم القيام باستعادة / نسخ الكل / النشر.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ./*/*.csproj ./proj/
RUN for file in $(ls ./proj); do mkdir /src/${file%.*} && mv ./proj/${file} /src/${file%.*}/${file}; done
RUN dotnet restore "MyProject/MyProject.csproj"
COPY . .
WORKDIR "/src/MyProject"
RUN dotnet publish "MyProject.csproj" -c Release -o /app --no-restore
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات