Bagaimana saya bisa memeriksa sistem file `buruh pelabuhan` yang gagal?


272

Saya mencoba membuat gambar Docker baru untuk proses pengembangan kami, menggunakan cpanmuntuk menginstal banyak modul Perl sebagai gambar dasar untuk berbagai proyek.

Saat mengembangkan Dockerfile, cpanmmengembalikan kode kegagalan karena beberapa modul tidak menginstal dengan bersih.

Saya cukup yakin saya perlu aptmenginstal beberapa hal lagi.

Pertanyaan saya adalah, di mana saya dapat menemukan /.cpanm/workdirektori yang dikutip dalam output, untuk memeriksa log? Dalam kasus umum, bagaimana saya bisa memeriksa sistem file dari docker buildperintah yang gagal ?

Sunting pagi Setelah menggigit peluru dan menjalankan findsaya menemukan

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

Apakah ini dapat diandalkan, atau lebih baik saya membangun wadah "telanjang" dan menjalankan barang secara manual sampai saya memiliki semua hal yang saya butuhkan?


tentang /var/lib/docker/aufs/diff/3afa404e[...]/.cpanmitu adalah internal Docker dan saya tidak akan mengacaukannya
Thomasleveil

Jawaban:


356

Setiap docker berhasil mengeksekusi RUNperintah dari Dockerfile, layer baru di sistem file gambar dikomit. Anda bisa menggunakan id layer itu sebagai gambar untuk memulai wadah baru.

Ambil Dockerfile berikut:

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

dan membangunnya:

$ 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

Anda sekarang dapat memulai wadah baru dari 00f017a8c2a6, 044e1532c690dan 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

tentu saja Anda mungkin ingin memulai sebuah shell untuk menjelajahi filesystem dan mencoba perintah:

$ 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

Ketika salah satu perintah Dockerfile gagal, yang perlu Anda lakukan adalah mencari id dari lapisan sebelumnya dan menjalankan shell di wadah yang dibuat dari id itu:

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

Sekali dalam wadah:

  • coba perintah yang gagal, dan ulangi masalahnya
  • kemudian perbaiki perintah dan ujilah
  • akhirnya perbarui Dockerfile Anda dengan perintah tetap

Jika Anda benar-benar perlu bereksperimen di lapisan aktual yang gagal alih-alih bekerja dari lapisan kerja terakhir, lihat jawaban Drew .


2
ya benar. Tidak ada gunanya menyimpan kontainer yang hanya dimaksudkan untuk men-debug Dockerfile Anda ketika Anda dapat membuatnya kembali sesuka hati.
Thomasleveil

1
OK ini sebenarnya sangat berguna, tapi saya punya masalah di mana jika kontainer gagal, saya tidak bisa menggunakan trik ini dengan hash dari wadah yang dikatakan berhasil. Tidak ada gambar yang dibuat jika RUN gagal. Dapatkah saya menempel pada wadah perantara yang tidak pernah dibersihkan?
Altreus

6
ketika salah satu perintah Dockerfile gagal, apa yang perlu Anda lakukan adalah mencari id dari lapisan sebelumnya dan menjalankan wadah dengan shell dari id itu: docker run --rm -it <id_last_working_layer> bash -ildan sekali dalam wadah coba perintah yang gagal mereproduksi masalah, lalu memperbaiki perintah dan mengujinya, akhirnya perbarui Dockerfile Anda dengan perintah tetap.
Thomasleveil

2
Juga, Anda bisa docker diff <container>dan mendapatkan daftar menyeluruh dari perubahan sistem file tertentu yang dibuat pada lapisan tertentu (file ditambahkan, dihapus, atau diubah di seluruh sistem file untuk gambar itu).
L0j1k

14
Saya pikir ini tidak berhasil karena katanya Unable to find image 'd5219f1ffda9:latest' locally. Namun, saya bingung dengan berbagai jenis ID. Ternyata Anda harus menggunakan ID yang langsung setelah panah, bukan yang mengatakan "Berlari ...".
rspeer

201

Jawaban teratas berfungsi jika Anda ingin segera memeriksa status sebelum perintah gagal.

Namun, pertanyaannya adalah bagaimana memeriksa kondisi wadah yang gagal itu sendiri. Dalam situasi saya, perintah gagal adalah bangunan yang membutuhkan beberapa jam, jadi memutar ulang sebelum perintah gagal dan menjalankannya lagi membutuhkan waktu lama dan tidak terlalu membantu.

Solusinya di sini adalah menemukan wadah yang gagal:

$ 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

Komit ke gambar:

$ docker commit 6934ada98de6
sha256:7015687976a478e0e94b60fa496d319cdf4ec847bcd612aecf869a72336e6b83

Dan kemudian jalankan gambar [jika perlu, jalankan bash]:

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

Sekarang Anda benar-benar melihat keadaan build pada saat itu gagal, bukan pada saat sebelum menjalankan perintah yang menyebabkan kegagalan.


Karena ketertarikan, mengapa Anda perlu membuat gambar baru dari wadah? Mengapa tidak memulai wadah saja? Jika sebuah gambar yang dibuat dari wadah yang gagal dapat dijalankan, maka pasti wadah yang dihentikan / gagal juga dapat dijalankan? Atau apakah saya melewatkan sesuatu?
nmh

@nmh Karena memungkinkan Anda untuk menangkap dan memeriksa wadah dalam kondisi gagal tanpa harus menjalankan perintah gagal lagi. Terkadang perintah gagal membutuhkan beberapa menit atau lebih lama untuk dieksekusi sehingga ini adalah cara yang mudah untuk menandai kondisi gagal. Sebagai contoh, saya saat ini menggunakan pendekatan ini untuk memeriksa log dari pustaka C ++ gagal yang membutuhkan beberapa menit. Sunting - Hanya memperhatikan bahwa Drew mengatakan bahwa dalam situasi [nya], perintah yang gagal adalah perintah yang membutuhkan beberapa jam, jadi memutar ulang sebelum perintah yang gagal dan menjalankannya lagi membutuhkan waktu yang lama dan tidak terlalu membantu.
Jaime Soto

@nmh Saya pikir masalah dengan mencoba memulai wadah yang gagal adalah bahwa perintah mulai wadah biasanya perlu diubah agar bermanfaat. Jika Anda mencoba memulai wadah yang gagal lagi itu akan menjalankan perintah yang gagal lagi, dan Anda akan kembali ke tempat Anda mulai. Dengan membuat gambar, Anda dapat memulai sebuah wadah dengan perintah mulai yang berbeda.
Centimane

2
Ini tidak berfungsi jika Anda menggunakannya DOCKER_BUILDKIT=1untuk membangunDockerfile
Clintm

Untuk titik @ nmh - Anda tidak perlu mengkomit gambar jika Anda hanya setelah output build. Anda dapat menggunakan buruh pelabuhan kontainer cp untuk mengekstrak hasil file dari wadah gagal dibangun.
whoisthemachine

7

Docker melakukan cache seluruh status sistem file setelah setiap RUNbaris yang berhasil .

Mengetahui bahwa:

  • untuk memeriksa status terakhir sebelum RUNperintah gagal Anda , komentari di Dockerfile (dan juga RUNperintah lainnya), kemudian jalankan docker builddan docker runkembali.
  • untuk memeriksa keadaan setelahRUN perintah yang gagal , cukup tambahkan || truepadanya untuk memaksanya agar berhasil; kemudian lanjutkan seperti di atas (pertahankan setiap dan semua RUNperintah selanjutnya dikomentari, jalankan docker builddandocker run )

Namun, tidak perlu dipusingkan dengan internal Docker atau lapisan ID, dan sebagai bonus Docker secara otomatis meminimalkan jumlah pekerjaan yang perlu dilakukan kembali.


1
Ini adalah jawaban yang sangat membantu ketika menggunakan DOCKER_BUILDKIT, karena buildkit tampaknya tidak mendukung solusi yang sama dengan yang tercantum di atas.
M. Anthony Aiello

3

Kegagalan langkah pembangunan debugging memang sangat menjengkelkan.

Solusi terbaik yang saya temukan adalah memastikan bahwa setiap langkah yang berhasil benar-benar berhasil, dan menambahkan tanda centang setelah yang gagal. Dengan begitu Anda mendapatkan lapisan yang berisi output dari langkah gagal yang dapat Anda periksa.

Dockerfile, dengan contoh setelah # Run DB2 silent installerbaris:

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

Yang akan saya lakukan adalah mengomentari Dockerfile di bawah ini dan termasuk baris yang menyinggung. Kemudian Anda dapat menjalankan kontainer dan menjalankan perintah buruh pelabuhan dengan tangan, dan melihat log dengan cara biasa. Misal jika Dockerfile adalah

RUN foo
RUN bar
RUN baz

dan sekarat di bar saya akan lakukan

RUN foo
# RUN bar
# RUN baz

Kemudian

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

Itulah yang akan saya lakukan juga sebelum menemukan utas ini. Ada beberapa cara yang lebih baik yang tidak memerlukan menjalankan ulang build.
Aaron McMillin

@ Harun. Terima kasih telah mengingatkan saya pada jawaban ini. Saya sudah lama tidak melihatnya. Bisakah Anda jelaskan mengapa jawaban yang diterima lebih baik daripada yang ini dari sudut pandang praktis. Saya pasti mengerti mengapa jawaban Drew lebih baik. Tampaknya jawaban yang diterima masih perlu dijalankan kembali.
seanmcl

Saya benar-benar memilih jawaban Drew dan bukan yang diterima. Keduanya bekerja tanpa menjalankan ulang build. Di jawaban yang diterima Anda dapat melompat ke shell tepat sebelum perintah gagal (Anda mungkin menjalankannya lagi untuk melihat kesalahan jika cepat). Atau dengan jawaban Drew Anda bisa mendapatkan shell setelah perintah gagal dijalankan (Dalam kasusnya perintah gagal sudah berjalan lama dan meninggalkan keadaan di belakang yang bisa diperiksa).
Aaron McMillin
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.