Làm cách nào tôi có thể kiểm tra hệ thống tệp của một `docker build` không thành công?


272

Tôi đang cố gắng xây dựng một hình ảnh Docker mới cho quá trình phát triển của chúng tôi, sử dụng cpanmđể cài đặt một loạt các mô-đun Perl làm hình ảnh cơ bản cho các dự án khác nhau.

Trong khi phát triển Dockerfile, cpanmtrả về mã lỗi vì một số mô-đun không cài đặt sạch.

Tôi khá chắc chắn rằng tôi cần phải aptcài đặt thêm một số thứ.

Câu hỏi của tôi là, tôi có thể tìm thấy /.cpanm/workthư mục được trích dẫn ở đầu ra, để kiểm tra các bản ghi? Trong trường hợp chung, làm thế nào tôi có thể kiểm tra hệ thống tập tin của một docker buildlệnh thất bại ?

Chỉnh sửa buổi sáng Sau khi cắn viên đạn và chạy, findtôi phát hiện ra

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

Điều này có đáng tin cậy không, hay tôi tốt hơn hết là xây dựng một container "trần" và chạy các công cụ thủ công cho đến khi tôi có tất cả những thứ tôi cần?


về /var/lib/docker/aufs/diff/3afa404e[...]/.cpanmnhững thứ đó là nội bộ của Docker và tôi sẽ không gây rối với họ
Thomasleveil

Câu trả lời:


355

Mỗi khi RUNdocker thực hiện thành công một lệnh từ Dockerfile, một lớp mới trong hệ thống tệp hình ảnh được cam kết. Thuận tiện bạn có thể sử dụng các id lớp đó làm hình ảnh để bắt đầu một container mới.

Lấy Dockerfile sau:

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

và xây dựng nó:

$ 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

Bây giờ bạn có thể bắt đầu một container mới từ 00f017a8c2a6, 044e1532c6905bd8172529c1:

$ 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

tất nhiên bạn có thể muốn bắt đầu một shell để khám phá hệ thống tập tin và thử các lệnh:

$ 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

Khi một trong các lệnh Dockerfile thất bại, điều bạn cần làm là tìm id của lớp trước và chạy shell trong một thùng chứa được tạo từ id đó:

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

Khi đã ở trong container:

  • thử lệnh thất bại và tái tạo vấn đề
  • sau đó sửa lệnh và kiểm tra nó
  • cuối cùng cập nhật Dockerfile của bạn với lệnh cố định

Nếu bạn thực sự cần thử nghiệm trong lớp thực tế thất bại thay vì làm việc từ lớp làm việc cuối cùng, hãy xem câu trả lời của Drew .


2
đúng vậy Không có điểm lưu giữ nào chỉ nhằm gỡ lỗi Dockerfile của bạn khi bạn có thể tạo lại chúng theo ý muốn.
Thomasleveil

1
OK điều này thực sự rất hữu ích, nhưng tôi có một vấn đề là nếu việc xây dựng container thất bại, tôi không thể sử dụng thủ thuật này với hàm băm của container mà nó nói nó đang hoạt động. Không có hình ảnh nào được tạo ra nếu RUN thất bại. Tôi có thể gắn vào thùng chứa trung gian không bao giờ được dọn sạch không?
Altreus

6
Khi một trong các lệnh Dockerfile không thành công, điều bạn cần làm là tìm id của lớp trước đó và chạy một thùng chứa có vỏ của id đó: docker run --rm -it <id_last_working_layer> bash -ilvà một lần trong vùng chứa, hãy thử lệnh không thể tái tạo vấn đề, sau đó sửa lỗi và kiểm tra nó, cuối cùng cập nhật Dockerfile của bạn với lệnh cố định.
Thomasleveil

2
Ngoài ra, bạn có thể docker diff <container>và có được một danh sách kỹ lưỡng về các thay đổi hệ thống tệp cụ thể được thực hiện trên lớp cụ thể đó (các tệp được thêm, xóa hoặc thay đổi trên toàn bộ hệ thống tệp cho hình ảnh đó).
L0j1k

14
Tôi nghĩ rằng điều này đã không làm việc bởi vì nó nói Unable to find image 'd5219f1ffda9:latest' locally. Tuy nhiên, tôi đã bị nhầm lẫn bởi nhiều loại ID. Hóa ra bạn phải sử dụng ID trực tiếp sau mũi tên, không phải ID có tên "Chạy trong ...".
rspeer

200

Câu trả lời hàng đầu hoạt động trong trường hợp bạn muốn kiểm tra trạng thái ngay trước lệnh không thành công.

Tuy nhiên, câu hỏi hỏi làm thế nào để kiểm tra trạng thái của chính container bị lỗi. Trong tình huống của tôi, lệnh thất bại là một bản dựng mất vài giờ, do đó, việc tua lại trước lệnh thất bại và chạy lại mất một thời gian dài và không hữu ích lắm.

Giải pháp ở đây là tìm container bị lỗi:

$ 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

Cam kết nó với một hình ảnh:

$ docker commit 6934ada98de6
sha256:7015687976a478e0e94b60fa496d319cdf4ec847bcd612aecf869a72336e6b83

Và sau đó chạy hình ảnh [nếu cần thiết, chạy bash]:

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

Bây giờ bạn đang thực sự xem xét trạng thái của bản dựng tại thời điểm nó bị lỗi, thay vì tại thời điểm trước khi chạy lệnh gây ra lỗi.


Không quan tâm, tại sao bạn cần tạo một hình ảnh mới từ container? Tại sao không chỉ bắt đầu container? Nếu một hình ảnh được tạo từ container bị lỗi có thể chạy, thì chắc chắn container bị dừng / bị lỗi cũng có thể chạy? Hay tôi đang thiếu một cái gì đó?
nmh

@nmh Bởi vì nó cho phép bạn chụp và kiểm tra một container ở trạng thái không thành công mà không phải chạy lại lệnh bị lỗi. Đôi khi lệnh fail mất vài phút hoặc lâu hơn để thực thi, vì vậy đây là cách thuận tiện để gắn thẻ trạng thái không thành công. Ví dụ, tôi hiện đang sử dụng phương pháp này để kiểm tra nhật ký của việc xây dựng thư viện C ++ không thành công mất vài phút. Chỉnh sửa - Chỉ cần lưu ý rằng Drew nói rằng trong tình huống [của mình], lệnh thất bại là một bản dựng mất vài giờ, do đó, việc tua lại trước lệnh thất bại và chạy lại mất một thời gian dài và không hữu ích lắm.
Jaime Soto

@nmh Tôi nghĩ rằng vấn đề với việc cố gắng khởi động bộ chứa bị lỗi là lệnh khởi động của bộ chứa thông thường cần phải được thay đổi để có ích. Nếu bạn cố gắng khởi động lại container bị lỗi, nó sẽ chạy lại lệnh bị lỗi và bạn sẽ quay lại nơi bạn đã bắt đầu. Bằng cách tạo một hình ảnh, bạn có thể bắt đầu một container với một lệnh bắt đầu khác.
Centimane

2
Điều này không hoạt động nếu bạn đang sử dụng DOCKER_BUILDKIT=1để xây dựngDockerfile
Clintm

Đến điểm của @ nmh - bạn không cần phải cam kết hình ảnh nếu bạn chỉ sau đầu ra bản dựng. Bạn có thể sử dụng cp container container docker để trích xuất kết quả tệp từ thùng chứa bản dựng không thành công.
whoisthemachine

7

Docker lưu trữ toàn bộ trạng thái hệ thống tập tin sau mỗi RUNdòng thành công .

Biết rằng:

  • để kiểm tra trạng thái mới nhất trước RUNlệnh thất bại của bạn , hãy nhận xét nó trong Dockerfile (cũng như bất kỳ và tất cả các RUNlệnh tiếp theo ), sau đó chạy docker buildvà chạy docker runlại.
  • để kiểm tra trạng thái sau khiRUN lệnh thất bại , chỉ cần thêm || truevào nó để buộc nó thành công; sau đó tiến hành như trên (giữ bất kỳ và tất cả các RUNlệnh tiếp theo nhận xét, chạy docker builddocker run)

Tada, không cần phải gây rối với nội bộ Docker hoặc ID lớp và vì Docker thưởng sẽ tự động giảm thiểu số lượng công việc cần phải thực hiện lại.


1
Đây là một câu trả lời đặc biệt hữu ích khi sử dụng DOCKER_BUILDKIT, vì buildkit dường như không hỗ trợ các giải pháp tương tự như các giải pháp được liệt kê ở trên.
M. Anthony Aiello

3

Gỡ lỗi bước xây dựng thất bại thực sự rất khó chịu.

Giải pháp tốt nhất tôi đã tìm thấy là đảm bảo rằng mỗi bước thực hiện công việc thực sự thành công và thêm một kiểm tra sau những bước không thành công. Bằng cách đó, bạn có được một lớp cam kết có chứa các đầu ra của bước không thành công mà bạn có thể kiểm tra.

Dockerfile, với một ví dụ sau # Run DB2 silent installerdòng:

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

Những gì tôi sẽ làm là bình luận về Dockerfile bên dưới và bao gồm cả dòng vi phạm. Sau đó, bạn có thể chạy container và chạy các lệnh docker bằng tay, và xem các bản ghi theo cách thông thường. Ví dụ: nếu Dockerfile là

RUN foo
RUN bar
RUN baz

và nó sẽ chết ở quán bar tôi sẽ làm

RUN foo
# RUN bar
# RUN baz

Sau đó

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

Đó là những gì tôi đã làm quá trước khi tìm thấy chủ đề này. Có nhiều cách tốt hơn mặc dù không yêu cầu chạy lại bản dựng.
Aaron McMillin

@Aaron. Cảm ơn đã nhắc nhở tôi về câu trả lời này. Tôi đã không nhìn nó một lúc lâu. Bạn có thể giải thích tại sao câu trả lời được chấp nhận tốt hơn câu trả lời này theo quan điểm thực tế. Tôi chắc chắn hiểu lý do tại sao câu trả lời của Drew là tốt hơn. Có vẻ như câu trả lời được chấp nhận vẫn yêu cầu chạy lại.
seanmcl

Tôi thực sự đã bỏ phiếu cho câu trả lời của Drew và không được chấp nhận. Cả hai đều làm việc mà không chạy lại bản dựng. Trong câu trả lời được chấp nhận, bạn có thể nhảy vào shell ngay trước lệnh thất bại (Bạn có thể chạy lại để xem lỗi nếu nó nhanh). Hoặc với câu trả lời của Drew, bạn có thể nhận được một vỏ sau khi lệnh thất bại đã chạy (Trong trường hợp của anh ta, lệnh thất bại đã chạy lâu và để lại trạng thái phía sau có thể được kiểm tra).
Aaron McMillin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.