Docker의 빌드 컨텍스트 외부에 파일을 포함시키는 방법은 무엇입니까?


462

Docker 파일에서 "ADD"명령을 사용하여 Docker의 빌드 컨텍스트 외부의 파일을 어떻게 포함시킬 수 있습니까?

Docker 설명서에서 :

경로는 빌드 컨텍스트 내에 있어야합니다. 도커 빌드의 첫 번째 단계는 컨텍스트 디렉토리 (및 서브 디렉토리)를 도커 디먼에 보내는 것이므로 ../something/something을 추가 할 수 없습니다.

이 문제에 Docker를 수용하기 위해 전체 프로젝트를 재구성하고 싶지 않습니다. 모든 Docker 파일을 동일한 하위 디렉토리에 유지하고 싶습니다.

또한 Docker가 아직 심볼릭 링크를 지원하지 않는 것으로 보입니다. Dockerfile ADD 명령은 호스트 # 1676의 심볼릭 링크를 따르지 않습니다.

내가 생각할 수있는 유일한 다른 것은 파일을 Docker 빌드 컨텍스트에 복사하는 사전 빌드 단계를 포함시키는 것입니다 (그리고 해당 파일을 무시하도록 버전 컨트롤을 구성하십시오). 그보다 더 나은 해결 방법이 있습니까?


94
Docker의 경우 최악의 상황입니다. 내 관점에서 볼 때 "Docker 프로젝트"와 같은 것은 없습니다. Docker는 프로젝트 배송 용입니다. 도구 일뿐입니다. 나는 전체 프로젝트를 accomadte docker로 다시 빌드하고 .dockerignore 등을 추가하고 싶지 않습니다. 하루가 끝나면 Docker의 지속 시간을 누가 알 수 있습니까? 코드 (예 : 각도 프로젝트)와 코드를 배포하는 모든 수단 (예 : 도커)을 분리하는 것이 좋습니다. 결국, 도커 파일을 다른 모든 것 옆에두면 아무런 이점이 없습니다. 그냥 이미지를 만들기 위해 물건을 배선 :(
TigerBear

3
예, 이것은 큰 문제입니다. 나는 같은 문제에 직면하고 있으며 각 Docker 빌드 컨텍스트에 복사하고 싶지 않은 더 큰 크기의 이진 파일 (이미 압축되어 있음)이 있습니다. 오히려 현재 위치 (Docker 빌드 컨텍스트 외부)에서 소스를 만들고 싶습니다. 그리고 빌드 타임에 파일을 복사 / 압축하고 압축을 풀고 특정 바이너리가 이미지에 구워 ​​지도록하기 때문에 런타임에 볼륨을 매핑하고 싶지 않습니다. 이렇게하면 컨테이너를 빠르게 회전시킬 수 있습니다.
저지 빈


1
도커 빌드의 문제점은 "컨텍스트"의 구성 개념입니다. Dockerfile은 전략적 디렉토리 (일명 컨텍스트) (예 : "/")에 극단적으로 배치되지 않는 한 빌드를 정의하기에 충분하지 않으므로 모든 경로에 액세스 할 수 있습니다. 도 커가 시작시 전체 컨텍스트를 스캔하기 때문에 도커 빌드가 매우 느려집니다. 필요한 모든 파일로 도커 이미지를 작성 FROM하고 계속 사용할 수 있습니다. Docker (또는 빌드 도구)를 수용하기 위해 프로젝트 구조를 변경하지는 않습니다.
Devis L.

답변:


412

이 문제를 해결하는 가장 좋은 방법은 -f를 사용하여 빌드 컨텍스트와 독립적으로 Dockerfile을 지정하는 것입니다.

예를 들어이 명령은 ADD 명령에 현재 디렉토리의 모든 항목에 대한 액세스 권한을 부여합니다.

docker build -f docker-files/Dockerfile .

업데이트 : Docker는 이제 Dockerfile을 빌드 컨텍스트 외부에 허용합니다 (18.03.0-ce, https://github.com/docker/cli/pull/886에 고정 ). 그래서 당신은 또한 같은 것을 할 수 있습니다

docker build -f ../Dockerfile .

8
@로 작성 파일 docs.docker.com/compose/compose-file/#/compose-file-referencedockerfile:build:섹션에 있는 특성 을 사용하십시오.
Emerson Farrugia

3
"Dockerfile이 빌드 컨텍스트 내에 있어야합니다"라는 메시지가 나타납니다. 현재 빌드 컨텍스트 아래에있을 수있는 하나의 Dockerfile을 갖고 싶습니다. 귀하의 예에서 현재 빌드 컨텍스트 내에 / 아래에 Dockerfile이 있습니다. 물론 작동합니다.
Alexander Mills

3
그래, 난 그냥 공유 Dockerfile에게 "빌드 상황"모든있는 여러 하위 디렉토리에있는 대응, 원하는
알렉산더 밀스

50
이것이 ADD컨텍스트 디렉토리 외부 에 있는 파일을 원하는 OP의 문제점을 해결합니까 ? 그것이 내가하려는 일이지만 -f외부 파일을 추가 할 수 있다고 생각하지 않습니다 .
Sridhar Sarnobat

18
내에서 ..이 충분히 찬성 투표 할 수 없습니다 내가 가지고있는 고정 표시기-compose.yml : build: context: .., dockerfile: dir/Dockerfile. 이제 내 빌드 컨텍스트는 상위 디렉토리입니다!
Mike Gleason jr Couturier

51

나는 종종 --build-arg이 목적을 위해 옵션을 사용하고 있다고 생각합니다 . 예를 들어 Dockerfile에 다음을 넣은 후 :

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

당신은 할 수 있습니다 :

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

그러나 Docker 설명서 에서 다음 경고에 유의하십시오 .

경고 : github 키, 사용자 자격 증명 등과 같은 비밀을 전달하기 위해 빌드 시간 변수를 사용하지 않는 것이 좋습니다. 빌드 시간 변수 값은 docker history 명령으로 이미지의 모든 사용자에게 표시됩니다.


6
이것은 큰 경고가없는 나쁜 조언입니다. Docker 문서에서 : "경고 : github 키, 사용자 자격 증명 등과 같은 비밀을 전달하는 데 빌드 시간 변수를 사용하지 않는 것이 좋습니다. 빌드 시간 변수 값은 docker history 명령으로 이미지의 모든 사용자에게 표시됩니다." 다시 말해서,이 예에서 주어진 예는 도커 이미지에서 개인 SSH 키를 개시한다. 어떤 상황에서는 괜찮을 수도 있습니다. docs.docker.com/engine/reference/builder/#arg
sheldonh

3
마지막으로이 보안 문제를 극복하기 위해 스쿼시 또는 다단계 빌드와 같은 기술을 사용할 수 있습니다. vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo

46

Linux에서는 다른 디렉토리를 심볼릭 링크하는 대신 마운트 할 수 있습니다

mount --bind olddir newdir

자세한 내용은 /superuser/842642 를 참조하십시오.

다른 OS에서 비슷한 것을 사용할 수 있는지 모르겠습니다. 또한 Samba를 사용하여 폴더를 공유하고 폴더를 Docker 컨텍스트에 다시 마운트하려고 시도했습니다.


2
오직 루트 만이 디렉토리를 묶을 수있다
jjcf89

28

나는 좋은 패턴을 알아 내기 위해이 시간을 보냈다. 설명하는 가장 좋은 방법은 다음과 같습니다.

  • Dockerfile : 자체 상대 경로에서 파일 만 볼 수 있습니다
  • 컨텍스트 : 공유하려는 파일과 Dockerfile이 복사 될 "공간"의 위치

그래서, 말했듯이, 여기에 파일을 재사용 해야하는 Dockerfile의 예가 있습니다. start.sh

도커 파일

그것은 ALWAYS로 자신의 현재 디렉토리를 가진, 상대 경로에서로드됩니다 local사용자가 지정한 경로에 대한 참조입니다.

COPY start.sh /runtime/start.sh

파일

이 아이디어를 고려하면 Dockerfiles에 특정 항목을 빌드하기 위해 여러 개의 사본이 있다고 생각할 수 있지만 모두에 액세스해야합니다 start.sh.

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

이 구조와 위의 파일을 고려하면 docker-compose.yml이 있습니다.

docker-compose.yaml

  • 이 예에서 shared컨텍스트 디렉토리는 runtime디렉토리입니다.
    • 여기 동일한 정신 모델로,이 디렉토리 아래의 모든 파일이 소위로 이동한다고 생각하십시오 context.
    • 마찬가지로 동일한 디렉토리에 복사하려는 Dockerfile을 지정하십시오. 을 사용하여 지정할 수 있습니다 dockerfile.
  • 기본 컨텐츠가있는 디렉토리는 설정할 실제 컨텍스트입니다.

docker-compose.yml다음과 같습니다

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-service는 컨텍스트로 설정되고 공유 파일 start.sh은 각로 지정된 Dockerfile과 함께 복사됩니다 dockerfile.
  • 시작 파일을 공유하여 각자 고유의 방식으로 빌드됩니다!

건배!


1
Dockerfile에 요점은 폴더 계층에있는 경우 허용 대답에 의해 지적, completly 사실이 아니다 a/b/c, 다음 예를 실행 docker build .c액세스를 허용하지 않습니다 ../file-in-b. 그러나 이것에 대한 일반적인 오해는 (또는 적어도 내 것이 었습니다) 컨텍스트는 Dockerfile의 위치가 아니라 build 명령의 첫 번째 인수에 의해 지정된 위치에 의해 정의된다는 것입니다. 에서 : 그래서으로 허용 대답에 명시된 a: docker build -f a/b/c/Dockerfile . Dockerfile에 있음을 의미 .폴더 지금a
β.εηοιτ.βε

1
Dockerfile 문서에서 인용 : 파일 및 디렉토리의 경로는 빌드 컨텍스트의 소스를 기준으로 해석됩니다.
Nishant George Agrwal '18

18

문제 2745 의 토론을 읽으면 docker는 심볼릭 링크를 지원하지 않을뿐만 아니라 컨텍스트 외부의 파일 추가를 지원하지 않을 수도 있습니다. docker 빌드에 들어가는 파일은 명시 적으로 컨텍스트의 일부이거나 고정 버전으로도 배포 된 URL에서 가져와야하므로 잘 알려진 URL 또는 파일과 함께 제공되는 파일로 빌드를 반복 할 수있는 디자인 철학 인 것 같습니다. 도커 컨테이너.

나는 버전 제어 소스 (예 : docker build -t stuff http://my.git.org/repo) 에서 빌드하는 것을 선호합니다. 그렇지 않으면 임의의 파일로 임의의 장소에서 빌드하고 있습니다.

근본적으로, .....-SvenDowideit, Docker Inc

내 의견이지만 코드와 도커 리포지토리를 분리하려면 재구성해야한다고 생각합니다. 이렇게하면 컨테이너가 일반적이며 빌드 타임이 아닌 런타임에 모든 버전의 코드를 가져올 수 있습니다.

또는 docker를 기본 코드 배포 아티팩트로 사용하고 dockerfile을 코드 리포지토리의 루트에 넣습니다. 이 경로를 사용하는 경우 더 일반적인 시스템 수준 세부 정보를위한 부모 도커 컨테이너와 코드와 관련된 설정을위한 자식 컨테이너를 갖는 것이 좋습니다.


왜 도커를 사용합니까?
lscoughlin

11

더 간단한 해결 방법은 '컨텍스트'자체를 변경하는 것입니다.

예를 들어, 대신 다음을 제공하십시오.

docker build -t hello-demo-app .

현재 디렉토리를 컨텍스트로 설정합니다. 부모 디렉토리를 컨텍스트로 원한다고 가정 해 봅시다.

docker build -t hello-demo-app ..

6
나는 이것이 .dockerignore를 깨뜨린다고 생각한다 :-\
NullVoxPopuli 23.25의

.dockerignore를 포기하고 대신 빌드 컨텍스트에 필요한 파일 만 포함하는 Makefile 관리 된 docker 폴더를 만들었습니다 ... 만 호출하면 make build되고 업데이트 된 경우 필요한 모든 파일을 가져온 다음 적절한 docker 빌드를 호출합니다 ... 추가 작업을 수행해야하지만 완전히 제어하기 때문에 완벽하게 작동합니다.
Sahsahae


3

docker-compose를 사용하여 필요한 볼륨을 마운트하는 서비스를 만들고 컨테이너 이미지를 커밋 함으로써이 작업을 수행했습니다. 그런 다음 후속 서비스에서 이전에 커밋 된 이미지에 의존합니다.이 이미지에는 마운트 된 위치에 모든 데이터가 저장되어 있습니다. 그런 다음 docker commit명령을 실행할 때 호스트 마운트 디렉토리가 커밋되지 않으므로 이러한 파일을 최종 대상으로 복사해야합니다.

이 작업을 수행하기 위해 docker-compose를 사용할 필요는 없지만 인생을 조금 더 쉽게 만듭니다.

# 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

1

HIPPA 이유로 인해 repo 컨텍스트 내부로 이동할 수 없었던 프로젝트 및 일부 데이터 파일에서 이와 동일한 문제가 발생했습니다. 나는 2 개의 Dockerfile을 사용했습니다. 하나는 컨테이너 외부에서 필요한 것들없이 주 응용 프로그램을 빌드하고 내부 저장소에 게시합니다. 그런 다음 두 번째 dockerfile은 해당 이미지를 가져 와서 데이터를 추가하고 새 이미지를 생성 한 다음 배포하고 어디에도 저장하지 않습니다. 이상적이지는 않지만 민감한 정보를 저장소에 보관하지 않는 것이 목적이었습니다.


1

쉬운 해결 방법은 볼륨을 실행할 때 볼륨을 컨테이너에 마운트하고 (-v 또는 --mount 플래그를 사용하여) 파일에 액세스하는 것입니다.

예:

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

자세한 내용은 https://docs.docker.com/storage/volumes/ 를 참조하십시오.


이것은 볼륨이 런타임 종속성 인 경우에만 작동합니다. 빌드 시간 종속성의 경우 docker run너무 늦습니다.
user3735633

1

GitHub 문제 에서 설명했듯이 빌드는 실제로 발생 /tmp/docker-12345하므로 같은 상대 경로 ../relative-add/some-file는에 상대적 /tmp/docker-12345입니다. 따라서 /tmp/relative-add/some-file오류 메시지에도 표시되는 을 검색 합니다. *

빌드 디렉토리 외부에서 파일을 포함 할 수 없으므로 "금지 경로"메시지가 나타납니다.


0

빠르고 더러운 방법 중 하나는 빌드 컨텍스트를 필요한 수준으로 설정하는 것입니다. 그러나 결과가 발생할 수 있습니다. 다음과 같은 마이크로 서비스 아키텍처에서 작업중인 경우 :

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

빌드 컨텍스트를 부모 Code디렉토리로 설정 한 다음 모든 것에 액세스 할 수 있지만 많은 저장소가 있으면 빌드 시간이 오래 걸릴 수 있습니다.

상황에 따라 다른 팀이 데이터베이스 스키마를 유지 관리하고 Repo1팀 코드를 관리하는 경우가 있습니다 Repo2. 스키마 변경에 대한 걱정이나 다른 팀의 저장소를 오염시키지 않고 자신의 일부 시드 데이터 로이 종속성을 도킹하려고합니다 (변경 사항에 따라 시드 데이터 스크립트를 변경해야 할 수도 있음) 두 번째 방법은 해킹이지만 긴 빌드 문제를 해결합니다.

sh (또는 ps1) 스크립트를 작성하여 ./Code/Repo2필요한 파일을 복사하고 원하는 docker 명령을 호출하십시오.

#!/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

docker-compose 파일에서 컨텍스트를 Repo2루트 로 설정 ./db/schema하고 경로에 대해 걱정하지 않고 dockerfile 의 디렉토리 내용을 사용하십시오 . 실수로이 디렉토리를 소스 제어에 커밋 할 위험이 있지만 스크립트 정리 조치는 충분히 쉬워야합니다.


0

필자의 경우 Dockerfile은 구성 파일을 사용하여 실제 값으로 바꾸는 자리 표시자를 포함하는 템플릿처럼 작성됩니다.

따라서이 파일을 직접 지정할 수는 없지만 다음과 같이 docker 빌드에 파이프하십시오.

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

그러나 파이프 때문에 COPY명령이 작동하지 않았습니다. 그러나 위의 방법은 -f -(명시 적으로 파일이 제공되지 않음) 으로 해결합니다 . 플래그 -없이 만 -f, 컨텍스트와 Dockerfile은 제공되지 않습니다.


0

트릭은 Docker 경로를 지정하면 부모 디렉토리의 파일을 포함하도록 build 명령에 컨텍스트를 지정할 수 있다는 것을 인식하는 것입니다. Dockerfile을 다음과 같이 변경합니다.

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

그런 다음 내 빌드 명령은 다음과 같습니다.

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

프로젝트 디렉토리에서

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

프로젝트 / 도커에서

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.