ฉันจะตรวจสอบระบบไฟล์ของ "docker build" ที่ล้มเหลวได้อย่างไร


272

ฉันกำลังพยายามสร้างอิมเมจ Docker ใหม่สำหรับกระบวนการพัฒนาของเราโดยใช้cpanmเพื่อติดตั้งโมดูล Perl จำนวนมากเป็นอิมเมจพื้นฐานสำหรับโครงการต่างๆ

ขณะพัฒนา Dockerfile cpanmส่งคืนรหัสความล้มเหลวเนื่องจากบางโมดูลไม่ได้ติดตั้งอย่างสมบูรณ์

ฉันค่อนข้างแน่ใจว่าฉันต้องaptติดตั้งบางอย่างเพิ่มเติม

คำถามของฉันคือฉันจะหาได/.cpanm/workเร็กตอรี่ที่ยกมาในเอาท์พุทได้จากที่ไหนเพื่อตรวจสอบบันทึก? ในกรณีทั่วไปฉันจะตรวจสอบระบบไฟล์ของdocker buildคำสั่งที่ล้มเหลวได้อย่างไร?

เช้าแก้ไขหลังจากกัดกระสุนและทำงานfindฉันพบ

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

สิ่งนี้น่าเชื่อถือหรือไม่หรือฉันควรสร้างที่เก็บ "เปล่า" และเรียกใช้ข้อมูลด้วยตนเองจนกว่าฉันจะมีทุกสิ่งที่ฉันต้องการ


เกี่ยวกับสิ่ง/var/lib/docker/aufs/diff/3afa404e[...]/.cpanmเหล่านี้คือ internals ของ Docker และฉันจะไม่ยุ่งกับพวกเขา
Thomasleveil

คำตอบ:


355

ทุกครั้งที่นักเทียบท่าเรียกใช้RUNคำสั่งจาก Dockerfile ได้สำเร็จเลเยอร์ใหม่ในระบบไฟล์รูปภาพจะถูกคอมมิต คุณสามารถใช้รหัสเลเยอร์เหล่านั้นเป็นรูปภาพเพื่อเริ่มคอนเทนเนอร์ใหม่ได้อย่างสะดวก

ใช้ 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ฉันคิดว่านี้ไม่ได้ทำงานเพราะมันกล่าวว่า อย่างไรก็ตามฉันสับสนกับรหัสหลายประเภท ปรากฎว่าคุณต้องใช้รหัสที่อยู่หลังลูกศรโดยตรงไม่ใช่รหัสที่ระบุว่า "กำลังทำงานอยู่ใน ... "
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 เนื่องจากช่วยให้คุณสามารถจับภาพและตรวจสอบคอนเทนเนอร์ในสถานะที่ล้มเหลวโดยไม่ต้องรันคำสั่ง failing อีกครั้ง บางครั้งคำสั่งที่ล้มเหลวใช้เวลาในการดำเนินการเป็นนาทีหรือนานกว่านี้จึงเป็นวิธีที่สะดวกในการติดแท็กสถานะที่ล้มเหลว ตัวอย่างเช่นฉันกำลังใช้วิธีนี้เพื่อตรวจสอบบันทึกของการสร้างไลบรารี่ C ++ ที่ล้มเหลวซึ่งใช้เวลาหลายนาที แก้ไข - เพิ่งสังเกตเห็นว่า Drew กล่าวว่าในสถานการณ์ [เขา] คำสั่งที่ล้มเหลวคือบิลด์ที่ใช้เวลาหลายชั่วโมงดังนั้นการกรอกลับใหม่ก่อนที่คำสั่งที่ล้มเหลวและการรันอีกครั้งจะใช้เวลานานและไม่เป็นประโยชน์มาก
Jaime Soto

@ nhh ฉันคิดว่าปัญหากับการพยายามที่จะเริ่มภาชนะที่ล้มเหลวคือคำสั่งเริ่มต้นของภาชนะที่จะต้องมีการเปลี่ยนแปลงจะเป็นประโยชน์ หากคุณพยายามเริ่มต้นคอนเทนเนอร์ที่ล้มเหลวอีกครั้งมันจะเรียกใช้คำสั่งที่ล้มเหลวอีกครั้งและคุณจะกลับมาที่จุดเริ่มต้น ด้วยการสร้างอิมเมจคุณสามารถเริ่มคอนเทนเนอร์ด้วยคำสั่ง start อื่น
Centimane

2
สิ่งนี้ใช้ไม่ได้หากคุณกำลังใช้DOCKER_BUILDKIT=1ในการสร้างของคุณDockerfile
Clintm

ถึงจุดของ @ nmh - คุณไม่จำเป็นต้องส่งรูปภาพหากคุณอยู่หลังเอาต์พุต build คุณสามารถใช้docker container cpเพื่อแยกผลลัพธ์ไฟล์จากคอนเทนเนอร์บิลด์ที่ล้มเหลว
whoisthemachine

7

นักเทียบท่าแคชสถานะระบบไฟล์ทั้งหมดหลังจากแต่ละRUNบรรทัดที่สำเร็จ

รู้ว่า:

  • เพื่อตรวจสอบสถานะล่าสุดก่อนที่RUNคำสั่งที่ล้มเหลวของคุณให้ใส่เครื่องหมายคอมเม้นต์ใน Dockerfile (รวมถึงRUNคำสั่งใด ๆ และคำสั่งถัดไปทั้งหมด) จากนั้นเรียกใช้docker buildและdocker runอีกครั้ง
  • หากต้องการตรวจสอบสถานะหลังจากRUNคำสั่งที่ล้มเหลวเพียงเพิ่มคำสั่ง|| trueเพื่อบังคับให้สำเร็จ จากนั้นดำเนินการตามด้านบน (เก็บRUNคำสั่งใด ๆ และทั้งหมดที่ตามมาไว้ในคอมเม้นต์, รันdocker buildและdocker run)

ธ นา, ไม่จำเป็นต้องยุ่งกับ Docker internals หรือ ID เลเยอร์, ​​และในฐานะโบนัส Docker จะลดปริมาณงานที่ต้องทำใหม่ให้โดยอัตโนมัติ


1
นี่เป็นคำตอบที่เป็นประโยชน์อย่างยิ่งเมื่อใช้ DOCKER_BUILDKIT เนื่องจาก buildkit ดูเหมือนจะไม่สนับสนุนโซลูชันเดียวกับที่ระบุไว้ข้างต้น
M. Anthony Aiello

3

การดีบักการสร้างความล้มเหลวในขั้นตอนเป็นสิ่งที่น่ารำคาญอย่างมาก

ทางออกที่ดีที่สุดที่ฉันพบคือการทำให้แน่ใจว่าแต่ละขั้นตอนที่ทำงานได้สำเร็จจริงและเพิ่มการตรวจสอบหลังจากที่ล้มเหลว ด้วยวิธีนี้คุณจะได้รับเลเยอร์ที่คอมมิตซึ่งมีเอาต์พุตของขั้นตอนที่ล้มเหลวซึ่งคุณสามารถตรวจสอบได้

Dockerfile พร้อมตัวอย่างหลัง# Run DB2 silent installerบรรทัด:

#
# 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 ด้านล่างและรวมถึงบรรทัดที่ละเมิด จากนั้นคุณสามารถเรียกใช้คอนเทนเนอร์และเรียกใช้คำสั่งนักเทียบท่าด้วยมือและดูบันทึกในวิธีปกติ เช่นถ้า 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

@Aaron ขอบคุณที่เตือนฉันถึงคำตอบนี้ ฉันไม่ได้ดูเป็นเวลานาน คุณช่วยอธิบายได้ไหมว่าทำไมคำตอบที่ได้รับการยอมรับดีกว่าคำตอบนี้ ฉันเข้าใจว่าทำไมคำตอบของ Drew จึงดีกว่า ดูเหมือนว่าคำตอบที่ยอมรับยังคงต้องการการเรียกใช้ซ้ำ
seanmcl

จริง ๆ แล้วฉันโหวตให้คำตอบของ Drew และไม่ยอมรับ พวกเขาทั้งสองทำงานโดยไม่ต้องเรียกใช้งานโครงสร้าง ในคำตอบที่ยอมรับคุณสามารถข้ามไปยังเชลล์ก่อนคำสั่งที่ล้มเหลว (คุณอาจเรียกใช้อีกครั้งเพื่อดูข้อผิดพลาดถ้ามันรวดเร็ว) หรือด้วยคำตอบของ Drew คุณสามารถรับเชลล์ได้หลังจากที่คำสั่งล้มเหลวรันแล้ว (ในกรณีของเขาคำสั่งที่ล้มเหลวนั้นรันได้นานและสถานะที่เหลืออยู่ข้างหลังนั้นสามารถตรวจสอบได้)
Aaron McMillin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.