실패한`docker build`의 파일 시스템을 어떻게 검사 할 수 있습니까?


272

cpanm다양한 프로젝트의 기본 이미지로 Perl 모듈을 설치하는 데 사용하여 개발 프로세스를 위해 새로운 Docker 이미지를 작성하려고합니다 .

Dockerfile을 개발하는 동안 cpanm일부 모듈이 제대로 설치되지 않았기 때문에 실패 코드를 리턴합니다.

apt좀 더 설치 해야한다고 확신합니다 .

내 질문은 /.cpanm/work로그를 검사하기 위해 출력에서 ​​인용 된 디렉토리를 어디에서 찾을 수 있습니까? 일반적인 경우 실패한 docker build명령 의 파일 시스템을 어떻게 검사 할 수 있습니까?

아침 편집 글 머리 기호를 물고 실행 한 후 find발견

/var/lib/docker/aufs/diff/3afa404e[...]/.cpanm

이것이 신뢰할 수 있습니까, 아니면 "베어"컨테이너를 빌드하고 필요한 것을 모두 얻을 때까지 수동으로 실행하는 것이 더 낫습니까?


/var/lib/docker/aufs/diff/3afa404e[...]/.cpanm이것들은 Docker의 내부에 관한 것입니다. 나는 그들을 엉망으로 만들지 않을 것입니다
Thomasleveil

답변:


355

docker RUN가 Dockerfile에서 명령을 성공적으로 실행할 때마다 이미지 파일 시스템의 새 레이어 가 커밋됩니다. 편리하게 해당 레이어 ID를 이미지로 사용하여 새 컨테이너를 시작할 수 있습니다.

다음 Dockerfile을 가져옵니다.

FROM busybox
RUN echo 'foo' > /tmp/foo.txt
RUN echo 'bar' >> /tmp/foo.txt

그리고 그것을 빌드하십시오 :

$ docker build -t so-2622957 .
Sending build context to Docker daemon 47.62 kB
Step 1/3 : FROM busybox
 ---> 00f017a8c2a6
Step 2/3 : RUN echo 'foo' > /tmp/foo.txt
 ---> Running in 4dbd01ebf27f
 ---> 044e1532c690
Removing intermediate container 4dbd01ebf27f
Step 3/3 : RUN echo 'bar' >> /tmp/foo.txt
 ---> Running in 74d81cb9d2b1
 ---> 5bd8172529c1
Removing intermediate container 74d81cb9d2b1
Successfully built 5bd8172529c1

당신은 지금부터 새 컨테이너를 시작할 수 있습니다 00f017a8c2a6, 044e1532c690그리고 5bd8172529c1:

$ docker run --rm 00f017a8c2a6 cat /tmp/foo.txt
cat: /tmp/foo.txt: No such file or directory

$ docker run --rm 044e1532c690 cat /tmp/foo.txt
foo

$ docker run --rm 5bd8172529c1 cat /tmp/foo.txt
foo
bar

물론 파일 시스템을 탐색하고 명령을 시도하기 위해 쉘을 시작하려고 할 수 있습니다.

$ docker run --rm -it 044e1532c690 sh      
/ # ls -l /tmp
total 4
-rw-r--r--    1 root     root             4 Mar  9 19:09 foo.txt
/ # cat /tmp/foo.txt 
foo

Dockerfile 명령 중 하나가 실패하면 이전 계층ID 를 찾아 해당 ID에서 작성된 컨테이너에서 쉘을 실행해야합니다.

docker run --rm -it <id_last_working_layer> bash -il

컨테이너에 들어가면 :

  • 실패한 명령을 시도하고 문제를 재현하십시오.
  • 그런 다음 명령을 수정하고 테스트하십시오.
  • 마지막으로 고정 명령으로 Dockerfile을 업데이트하십시오.

마지막 작업 계층에서 작업하는 대신 실패한 실제 계층에서 실제로 실험해야하는 경우 Drew의 답변을 참조하십시오 .


2
그렇습니다. Dockerfile을 마음대로 다시 만들 수있을 때 디버깅하기위한 컨테이너를 유지하는 데 아무런 요점이 없습니다.
Thomasleveil

1
이것은 실제로 매우 유용했지만 컨테이너 빌드가 실패하면 작업중 인 컨테이너의 해시와 함께이 트릭을 사용할 수없는 문제가 있습니다. RUN이 실패하면 이미지가 생성되지 않습니다. 청소하지 않은 중간 용기에 부착 할 수 있습니까?
Altreus

6
Dockerfile 명령 중 하나가 실패하면 이전 레이어의 ID를 찾고 해당 ID의 쉘로 컨테이너를 실행해야합니다. 컨테이너 docker run --rm -it <id_last_working_layer> bash -il에서 한 번 문제를 재현하지 못한 명령을 시도한 다음 명령을 수정하고 테스트하고 마지막으로 고정 명령으로 Dockerfile을 업데이트하십시오.
Thomasleveil

2
또한 docker diff <container>특정 계층에서 수행 된 특정 파일 시스템 변경 사항 (해당 이미지의 전체 파일 시스템에서 추가, 삭제 또는 변경된 파일)의 전체 목록을 얻을 수 있습니다 .
L0j1k

14
나는 그것이 말했기 때문에 이것이 작동하지 않는다고 생각했다 Unable to find image 'd5219f1ffda9:latest' locally. 그러나 여러 종류의 ID로 인해 혼란 스러웠습니다. "Running in ..."이 아닌 화살표 바로 뒤에있는 ID를 사용해야한다는 것이 밝혀졌습니다.
rspeer

201

최상위 답변은 실패한 명령 직전에 상태를 검사하려는 경우에 작동합니다.

그러나 질문은 실패한 컨테이너 자체의 상태를 검사하는 방법을 묻습니다. 내 상황에서 실패한 명령은 몇 시간이 걸리는 빌드이므로 실패한 명령 전에 되감기하고 다시 실행하는 데 시간이 오래 걸리고별로 도움이되지 않습니다.

여기서 해결책은 실패한 컨테이너를 찾는 것입니다.

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                          PORTS               NAMES
6934ada98de6        42e0228751b3        "/bin/sh -c './utils/"   24 minutes ago      Exited (1) About a minute ago                       sleepy_bell

이미지로 커밋 :

$ docker commit 6934ada98de6
sha256:7015687976a478e0e94b60fa496d319cdf4ec847bcd612aecf869a72336e6b83

그런 다음 이미지를 실행하십시오 (필요한 경우 bash 실행).

$ docker run -it 7015687976a4 [bash -il]

이제 실제로는 실패를 일으킨 명령을 실행하기 전이 아닌 실패한 시점의 빌드 상태를 실제로보고 있습니다.


관심이 없다면 컨테이너에서 새 이미지를 만들어야하는 이유는 무엇입니까? 왜 컨테이너를 시작하지 않습니까? 실패한 컨테이너에서 생성 된 이미지를 실행할 수 있다면 중지 / 실패한 컨테이너도 실행할 수 있습니까? 아니면 뭔가 빠졌습니까?
nmh

@nmh 실패한 명령을 다시 실행할 필요없이 실패한 상태의 컨테이너를 캡처하고 검사 할 수 있기 때문입니다. 때때로 실패한 명령을 실행하는 데 몇 분 이상이 걸리므로 실패한 상태를 태그하는 편리한 방법입니다. 예를 들어 현재이 방법을 사용하여 몇 분 정도 걸리는 실패한 C ++ 라이브러리 빌드의 로그를 검사하고 있습니다. 편집-드류는 [그의] 상황에서 실패한 명령은 몇 시간이 걸리는 빌드이므로 실패한 명령 이전에 되감기하고 다시 실행하는 데 시간이 오래 걸리고 그다지 도움이되지 않는다고 말했습니다.
Jaime Soto

@ nmh 실패한 컨테이너를 시작하려고 할 때의 문제는 일반적으로 컨테이너의 시작 명령을 유용하게 변경해야한다는 것입니다. 실패한 컨테이너를 다시 시작하려고 시도하면 다시 실패한 명령이 실행되고 시작한 곳으로 돌아갑니다. 이미지를 작성하면 다른 시작 명령으로 컨테이너를 시작할 수 있습니다.
Centimane

2
당신이 사용하는 경우이 작동하지 않습니다 DOCKER_BUILDKIT=1귀하의 구축Dockerfile
Clintm

@nmh의 요점으로-빌드 출력 직후에 이미지를 커밋 할 필요가 없습니다. docker container cp 를 사용하여 실패한 빌드 컨테이너에서 파일 결과를 추출 할 수 있습니다 .
whoisthemachine

7

Docker 각 성공적인 RUN라인 후에 전체 파일 시스템 상태를 캐시합니다 .

그것을 아는 것은:

  • 당신의 실패하기 전에 최신 상태 검사 RUN명령을의 Dockerfile (뿐만 아니라 이후의 모든 그것을 주석 RUN, 다음 실행 명령) docker builddocker run다시.
  • 실패한 명령 상태를 검사하려면 RUN단순히 추가 || true하여 성공하도록하십시오. 그런 다음 위와 같이 진행하십시오 (모든 후속 RUN명령은 주석 처리하고 실행 docker build하고 docker run)

Tada는 Docker 내부 또는 레이어 ID를 망칠 필요가 없으며 보너스로 Docker는 다시 수행 해야하는 작업량을 자동으로 최소화합니다.


1
buildkit은 위에 나열된 것과 동일한 솔루션을 지원하지 않는 것 같으므로 DOCKER_BUILDKIT를 사용할 때 특히 유용한 답변입니다.
M. Anthony Aiello

3

빌드 단계 실패 디버깅은 실제로 매우 성가신 일입니다.

내가 찾은 최선의 해결책은 실제 작업을 수행하는 각 단계가 성공했는지 확인하고 실패한 단계 후에 검사를 추가하는 것입니다. 이렇게하면 검사 할 수없는 실패한 단계의 출력이 포함 된 커밋 된 레이어를 얻을 수 있습니다.

# Run DB2 silent installer행 뒤에 예제가있는 Dockerfile :

#
# DB2 10.5 Client Dockerfile (Part 1)
#
# Requires
#   - DB2 10.5 Client for 64bit Linux ibm_data_server_runtime_client_linuxx64_v10.5.tar.gz
#   - Response file for DB2 10.5 Client for 64bit Linux db2rtcl_nr.rsp 
#
#
# Using Ubuntu 14.04 base image as the starting point.
FROM ubuntu:14.04

MAINTAINER David Carew <carew@us.ibm.com>

# DB2 prereqs (also installing sharutils package as we use the utility uuencode to generate password - all others are required for the DB2 Client) 
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y sharutils binutils libstdc++6:i386 libpam0g:i386 && ln -s /lib/i386-linux-gnu/libpam.so.0 /lib/libpam.so.0
RUN apt-get install -y libxml2


# Create user db2clnt
# Generate strong random password and allow sudo to root w/o password
#
RUN  \
   adduser --quiet --disabled-password -shell /bin/bash -home /home/db2clnt --gecos "DB2 Client" db2clnt && \
   echo db2clnt:`dd if=/dev/urandom bs=16 count=1 2>/dev/null | uuencode -| head -n 2 | grep -v begin | cut -b 2-10` | chgpasswd && \
   adduser db2clnt sudo && \
   echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

# Install DB2
RUN mkdir /install
# Copy DB2 tarball - ADD command will expand it automatically
ADD v10.5fp9_linuxx64_rtcl.tar.gz /install/
# Copy response file
COPY  db2rtcl_nr.rsp /install/
# Run  DB2 silent installer
RUN mkdir /logs
RUN (/install/rtcl/db2setup -t /logs/trace -l /logs/log -u /install/db2rtcl_nr.rsp && touch /install/done) || /bin/true
RUN test -f /install/done || (echo ERROR-------; echo install failed, see files in container /logs directory of the last container layer; echo run docker run '<last image id>' /bin/cat /logs/trace; echo ----------)
RUN test -f /install/done

# Clean up unwanted files
RUN rm -fr /install/rtcl

# Login as db2clnt user
CMD su - db2clnt

0

내가 할 일은 아래 Dockerfile을 주석 처리하고 문제를 일으키는 행을 포함시키는 것입니다. 그런 다음 컨테이너를 실행하고 수동으로 docker 명령을 실행하고 일반적인 방식으로 로그를 볼 수 있습니다. 예를 들어 Dockerfile이

RUN foo
RUN bar
RUN baz

그리고 내가 할 바에서 죽어 가고있다.

RUN foo
# RUN bar
# RUN baz

그때

$ docker build -t foo .
$ docker run -it foo bash
container# bar
...grep logs...

이 스레드를 찾기 전에 내가 한 일입니다. 빌드를 다시 실행하지 않아도되는 더 좋은 방법이 있습니다.
Aaron McMillin 2016 년

@ 아론. 이 답변을 상기시켜 주셔서 감사합니다. 나는 그것을 오랫동안 보지 않았다. 실제적인 관점에서 허용되는 답변이이 것보다 더 나은 이유를 설명해 주시겠습니까? 드류의 대답이 더 나은 이유를 분명히 알 수 있습니다. 허용 된 답변을 여전히 다시 실행해야합니다.
seanmcl 2016 년

나는 실제로 드류의 대답에 투표했지만 받아들이지 않았습니다. 둘 다 빌드를 다시 실행하지 않고 작동합니다. 허용 된 답변에서 실패한 명령 직전에 쉘로 이동할 수 있습니다 (빠른 경우 오류를보기 위해 다시 실행할 수 있습니다). 또는 Drew의 대답으로 실패한 명령이 실행 된 후 쉘을 얻을 수 있습니다 (그의 경우 실패한 명령이 오래 실행되고 그 뒤에 남겨진 상태를 검사 할 수 있음).
Aaron McMillin 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.