Dockerfileでの条件付きのCOPY / ADD?


103

私のDockerfiles内で、ファイルが存在する場合、それをイメージにコピーしたいのですが、pipのrequirements.txtファイルは適切な候補のようですが、これをどのように実現しますか?

COPY (requirements.txt if test -e requirements.txt; fi) /destination
...
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi

または

if test -e requirements.txt; then
    COPY requiements.txt /destination;
fi
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi

こちらをご覧ください:docs.docker.com/reference/builder
Tuan

4
@Tuan-そのリンクで具体的に何がこれを助けるのですか?
ToolmakerSteve

回答:


24

これは現在サポートされていません(存在に応じて、同じDockerfileがファイルをコピーするかしないかにより、イメージが再現不能になる可能性があると思われます)。

これは、ワイルドカードを使用して、問題13045で引き続き要求されます: " COPY foo/* bar/" not work if no file in foo"(2015年5月)。
今のところ(2015年7月)Dockerには実装されませんが、bockerなどの別のビルドツールでこれをサポートできます。


32
良い答えですが、DockerロジックのIMOには欠陥があります。同じdockerfileを異なるビルドコンテキストで実行すると、異なるイメージが取得されます。それは予想されることです。同じビルドコンテキストを使用すると、同じイメージが得られます。また、同じビルドコンテキストに条件付きのCOPY / ADD命令を挿入すると、同じイメージが取得されます。チェックアウトします。それはちょうど私の2セントです。
nathan g

Dockerは不変のインフラストラクチャーです。dev、staging、およびprodの環境は、同一ではないにせよ99.99%である必要があります。環境変数を使用します。
AndrewMcLagan

3
@AndrewMcLaganたとえば、フロントエンドdev環境がwebpack開発サーバーで実行され、同等のprod環境が/dist静的フォルダーで機能する場合はどうでしょうか。これは、ほとんどのフロントエンドのセットアップ、今日の場合である、と明らかにdevし、prodここでは同じにすることはできません。それで、どう対処するのですか?
Jivan 2016年

ノードフロントエンドの開発にdockerを使用していません。通常のwebpack localhost:3000など...ローカルのdocker dev環境を起動しますが、node / react / angularフロントエンドは、通常のdockerコンテナー環境で実行されているものと通信します。たとえば、API、redis、MySQL、mongo、Elastic Searchなどのマイクロサービス。コンテナでwebpack開発環境を実行できます。しかし、それは遠すぎると感じています...
AndrewMcLagan

@Jivan一般的な手順を定義するためにonbuildイメージを使用してから、devとprodの特定のイメージをビルドしてみませんか。Docker Hubノードリポジトリには、各ノードバージョンのonbuildイメージが含まれているようです:hub.docker.com / _ / node。または、自分でロールすることもできます。
david_i_smith 2016年

83

簡単な回避策は次のとおりです。

COPY foo file-which-may-exist* /target

少なくとも1つの有効なソースが必要なので、foo存在することを確認してくださいCOPY

存在する場合file-which-may-existは、それもコピーされます。

注:ワイルドカードが、コピーするつもりのない他のファイルを取得しないように注意する必要があります。より注意するために、file-which-may-exist?代わりに使用することができます(?1文字だけに一致します)。

または、さらに良いのは、次のような文字クラスを使用して、1つのファイルのみが一致するようにすることです。

COPY foo file-which-may-exis[t] /target

1
フォルダーでも同じことができますか?
Benjamin Toueg 2018年

1
@BenjaminToueg:はい、ドキュメントによると、ファイルだけでなくフォルダもコピーできます。
jdhildeb

2
これはうまくいきます。複数の宛先を持つファイルの場合、一時ディレクトリにコピーしてから、必要な場所に移動しました。COPY --from=docker /usr/bin/docker /usr/lib/libltdl.so* /tmp/docker/ RUN mv /tmp/docker/docker /usr/bin/docker RUN mv /tmp/docker/libltdl.so.7 /usr/lib/libltdl.so.7 || true(共有ライブラリが不明なエンティティである場合)
Adam K Dean

複数の既存のファイルをコピーする場合、宛先はディレクトリでなければなりません。fooと存在する可能性のあるファイルの両方が存在する場合、これはどのように機能しますか?
melchoir55

1
したがって、答えは「ファイルがあることを確認する」であり、次にCOPY演算子の使用方法のデモンストレーションですか?これが元の質問とどのように関連しているかはわかりません。
18

27

このコメントで述べたように、Santhosh Hirekerurの回答ではファイルがまだコピーされています。真の条件付きコピーをアーカイブするには、この方法を使用できます。

ARG BUILD_ENV=copy

FROM alpine as build_copy
ONBUILD COPY file /file

FROM alpine as build_no_copy
ONBUILD RUN echo "I don't copy"

FROM build_${BUILD_ENV}
# other stuff

このONBUILD手順では、によって「ブランチ」が選択されてBUILD_ENVいる場合にのみファイルがコピーされるようにします。呼び出す前に小さなスクリプトを使用してこの変数を設定しますdocker build


2
この回答が気に入ったのは、非常に便利なONBUILDだけでなく、渡された他の変数と統合するのが最も簡単だと思われるためです。たとえば、BUILD_ENVに基づいてタグを設定したり、 ENV。
DeusXMachina

私はそのようなものを試してみました:デーモンからのエラー応答:Dockerfile解析エラー行52:ビルドステージの無効な名前: "site_builder _ $ {host_env}"、名前は数字で始めることも記号を含めることもできません
paulecoyote

9

ソリューションの回避策

ENV変数に基づいてサーバーにFOLDERをコピーする必要がありました。空のサーバーイメージを取得しました。ローカルフォルダーに必要な展開フォルダー構造を作成しました。次に、DockerFileの下の行に追加して、フォルダーをコンテナーにコピーします。I nは最後の行は、ドッキングウィンドウには、サーバを起動する前に初期化file.shを実行するためのエントリポイントを追加しました。

#below lines added to integrate testing framework
RUN mkdir /mnt/conf_folder
ADD install /mnt/conf_folder/install
ADD install_test /mnt/conf_folder/install_test
ADD custom-init.sh /usr/local/bin/custom-init.sh
ENTRYPOINT ["/usr/local/bin/custom-init.sh"]

次に、以下のようなスクリプトを使用して、ローカルにcustom-init.shファイルを作成し ます

#!/bin/bash
if [ "${BUILD_EVN}" = "TEST" ]; then
    cp -avr /mnt/conf_folder/install_test/* /mnt/wso2das-3.1.0/
else
    cp -avr /mnt/conf_folder/install/* /mnt/wso2das-3.1.0/
fi;

ドッキングウィンドウ・コンライン下のファイル。

環境:-BUILD_EVN = TEST

これらの変更により、Dockerのビルド中にフォルダーがコンテナーにコピーされます。docker-compose upを実行 すると、サーバーが起動する前に実際に必要なフォルダーをサーバーにコピーまたはデプロイします。


8
しかし、Dockerイメージは階層化されています。ADDは、ifステートメントに関係なく、これらをイメージにコピーします...
MyUserInStackOverflow 2017年

@MyUserInStackOverflow-この「回避策」のアイデアは、installとinstall_testの両方がイメージにコピーされることですが、イメージを実行すると、これらのフォルダーの1つだけが最終的な場所にコピーされます。両方が画像のどこかにあることが問題ない場合、これは妥当なテクニックになる可能性があります。
ToolmakerSteve

4

すべてのファイルを使い捨てディレクトリにコピーし、必要なファイルを手で選択して、残りを破棄します。

COPY . /throwaway
RUN cp /throwaway/requirements.txt . || echo 'requirements.txt does not exist'
RUN rm -rf /throwaway

同じソリューションに依存するビルドステージを使用cpして、条件付きでコピーすることで、同様のことを実現できます。ビルドステージを使用することにより、最終的なイメージには最初のからのすべてのコンテンツが含まれなくなりますCOPY

FROM alpine as copy_stage
COPY . .
RUN mkdir /dir_for_maybe_requirements_file
RUN cp requirements.txt /dir_for_maybe_requirements_file &>- || true

FROM alpine
# Must copy a file which exists, so copy a directory with maybe one file
COPY --from=copy_stage /dir_for_maybe_requirements_file /
RUN cp /dir_for_maybe_requirements_file/* . &>- || true
CMD sh

これは技術的に問題を解決しますが、画像のサイズを縮小するものではありません。巨大なもの(ディープネットワークモデルなど)を条件付きでコピーしようとしている場合でも、オーバーレイfsが機能する方法が原因で、画像のサイズが大きくなります。
DeusXMachina

@DeusXMachina、使用しているdockerのバージョンは?ドキュメントは、docs.docker.com/develop/develop-images/multistage-build/…の言っていることと矛盾します。層は、非最終ビルドステージから保持されるべきではありません。
cdosborn

@cdosburn-18.09にこれを観察しました。私は主に最初の例について話していましたが、段階的ビルドはその問題を回避する必要があります。そして、今ではすべてのFROMステージがコンパクト化されていると思いますが、私の思い出を2番目に推測してもらいます。いくつか実験してみる必要があります。
DeusXMachina

@DeusXMachina、右の2番目のソリューションだけが画像サイズを縮小します。
cdosborn

それは私の場合の良い回避策です。私はコピーし、cacheそれが何であるかに応じて、スクリプトファイルで実行するキャッシュを選択します!
Paschalis

1

他のアイデアを試してみましたが、要件を満たしていませんでした。アイデアは、子静的Webアプリケーション用のベースnginxイメージを作成することです。セキュリティ、最適化、および標準化の理由から、ベースイメージはRUN、子イメージによって追加されたディレクトリに対してコマンドを実行できる必要があります。ベースイメージは、子イメージによって追加されるディレクトリを制御しません。子画像であると仮定すると、のCOPYどこかにリソースがありCOMMON_DEST_ROOTます。

このアプローチはハックですが、基本イメージはCOPY、子イメージによって追加された1からNのディレクトリの指示をサポートするという考えです。 ARG PLACEHOLDER_FILEそしてENV UNPROVIDED_DEST満たすために使用されている<src><dest>すべてのための要件COPYは必要ない命令を。

#
# base-image:01
#
FROM nginx:1.17.3-alpine
ENV UNPROVIDED_DEST=/unprovided
ENV COMMON_DEST_ROOT=/usr/share/nginx/html
ONBUILD ARG PLACEHOLDER_FILE
ONBUILD ARG SRC_1
ONBUILD ARG DEST_1
ONBUILD ARG SRC_2
ONBUILD ARG DEST_2
ONBUILD ENV SRC_1=${SRC_1:-PLACEHOLDER_FILE}
ONBUILD ENV DEST_1=${DEST_1:-${UNPROVIDED_DEST}}
ONBUILD ENV SRC_2=${SRC_2:-PLACEHOLDER_FILE}
ONBUILD ENV DEST_2=${DEST_2:-${UNPROVIDED_DEST}}

ONBUILD COPY ${SRC_1} ${DEST_1}
ONBUILD COPY ${SRC_2} ${DEST_2}

ONBUILD RUN sh -x \
    #
    # perform operations on COMMON_DEST_ROOT
    #
    && chown -R limited:limited ${COMMON_DEST_ROOT} \
    #
    # remove the unprovided dest
    #
    && rm -rf ${UNPROVIDED_DEST}

#
# child image
#
ARG PLACEHOLDER_FILE=dummy_placeholder.txt
ARG SRC_1=app/html
ARG DEST_1=/usr/share/nginx/html/myapp
FROM base-image:01

このソリューションには、PLACEHOLDER_FILEサポートされているCOPY命令のダミーおよびハードコーディングされた数などの明らかな欠点があります。また、COPY命令で使用されるENV変数を取り除く方法もありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.