Menggunakan buruh pelabuhan-menulis dengan CI - bagaimana menangani kode keluar dan wadah terkait daemonized?

90

Saat ini agen Jenkins kami membuat docker-compose.yml untuk setiap project Rails kami dan kemudian menjalankan docker-compose up. Docker-compose.yml memiliki container "web" utama yang memiliki rbenv dan semua dependensi Rails lainnya di dalamnya. Ini ditautkan ke penampung DB yang berisi tes Postgres DB.

Masalahnya muncul ketika kita benar-benar perlu menjalankan tes dan menghasilkan kode keluar. Server CI kami hanya akan men-deploy jika skrip pengujian menampilkan exit 0, tetapi docker-compose selalu menampilkan 0, bahkan jika salah satu perintah container gagal.

Masalah lainnya adalah penampung DB berjalan tanpa batas, bahkan setelah penampung web selesai menjalankan pengujian, jadi docker-compose uptidak pernah kembali.

Adakah cara agar kami dapat menggunakan docker-compose untuk proses ini? Kita harus dapat menjalankan penampung, tetapi keluar setelah penampung web selesai dan mengembalikan kode keluarnya. Saat ini kami terjebak secara manual menggunakan buruh pelabuhan untuk memutar wadah DB dan menjalankan wadah web dengan opsi --link.

Logan Serman
sumber

Jawaban:

78

Sejak versi 1.12.0, Anda dapat menggunakan --exit-code-fromopsi.

Dari dokumentasi :

--exit-code-from SERVICE

Kembalikan kode keluar dari wadah layanan yang dipilih. Menyiratkan --abort-on-container-exit.

panjan
sumber
1
Itu seharusnya cara yang benar untuk melakukannya jika Anda menggunakan docker-compose1.12.0 dan yang lebih baru. Mungkin itu kasusmu juga. Contoh bisa menjadi: docker-compose up --exit-code-from test-unit. Perhatikan bahwa itu tidak berhasil untuk saya sampai saya menambahkan set -edi awal skrip saya.
Adrian Antunez
--exit-code-fromtidak bekerja dengan baik -d. Ini akan membuang kesalahan ini: using --exit-code-from implies --abort-on-container-exitdan --abort-on-container-exit and -d cannot be combined.
ericat
2
dokumentasinya mengerikan. flag apa yang kompatibel dengan ini? apakah hanya satu layanan atau dapatkah Anda melewatinya beberapa?
ibadah
42

docker-compose runadalah cara sederhana untuk mendapatkan status keluar yang Anda inginkan. Sebagai contoh:

$ cat docker-compose.yml 
roit:
    image: busybox
    command: 'true'
naw:
    image: busybox
    command: 'false'
$ docker-compose run --rm roit; echo $?
Removing test_roit_run_1...
0
$ docker-compose run --rm naw; echo $?
Removing test_naw_run_1...
1

Alternatifnya, Anda memiliki opsi untuk memeriksa wadah yang mati. Anda dapat menggunakan -fbendera untuk mendapatkan status keluar saja.

$ docker-compose up
Creating test_naw_1...
Creating test_roit_1...
Attaching to test_roit_1
test_roit_1 exited with code 0
Gracefully stopping... (press Ctrl+C again to force)
$ docker-compose ps -q | xargs docker inspect -f '{{ .Name }} exited with status {{ .State.ExitCode }}'
/test_naw_1 exited with status 1
/test_roit_1 exited with status 0

Adapun db container yang tidak pernah kembali, jika Anda menggunakan docker-compose upmaka Anda perlu sigkill container itu; itu mungkin bukan yang Anda inginkan. Sebagai gantinya, Anda dapat menggunakan docker-compose up -duntuk menjalankan kontainer Anda dengan daemonized, dan secara manual mematikan kontainer ketika pengujian Anda selesai.docker-compose run harus menjalankan wadah tertaut untuk Anda, tetapi saya telah mendengar obrolan di SO tentang bug yang mencegahnya berfungsi sebagaimana dimaksud saat ini.

kojiro
sumber
Masalah dengan menjalankan buruh pelabuhan adalah bahwa ia tidak memberikan keluaran apapun saat dijalankan dengan -T, dan kita menginginkan keluarannya sehingga kita dapat memeriksa pembangunan yang gagal.
Logan Serman
1
@LoganSerman Anda dapat memeriksa output dengandocker-compose logs
kojiro
Apakah ada cara untuk terus mengirimkan log tersebut ke STDOUT selama proses berjalan sehingga kita dapat melihatnya saat build CI sedang berlangsung?
Logan Serman
Saya kira saya tidak mengerti mengapa Anda lari dengan-T
kojiro
Beberapa perintah yang kami jalankan di dalam container untuk menjalankan pengujian berpotensi meminta masukan, kami ingin menjalankan dengan -T untuk menghindari hal ini. Rbenv misalnya menanyakan apakah Anda ingin menginstal ulang versi Ruby jika sudah ada.
Logan Serman
24

Membangun jawaban kojiro:

docker-compose ps -q | xargs docker inspect -f '{{ .State.ExitCode }}' | grep -v '^0' | wc -l | tr -d ' '

  1. dapatkan ID penampung
  2. dapatkan kode keluar terakhir untuk setiap ID penampung
  3. hanya kode status yang tidak dimulai dengan '0'
  4. hitung jumlah kode status bukan-0
  5. potong ruang putih

Menampilkan berapa banyak kode keluar bukan-0 yang dikembalikan. Akan menjadi 0 jika semuanya keluar dengan kode 0.

menghabiskan waktu
sumber
Anda juga dapat menggunakan output non-quiet from docker-compose ps, misalnya: docker-compose ps | grep -c "Exit 1"akan memberi Anda hitungan di mana "Keluar 1" dicocokkan dalam tampilan dari docker-compose ps(yang menyediakan tabel ringkasan hasil yang dicetak cantik). Kode keluar terdaftar di kolom "Negara".
eharik
Ini sangat mengagumkan. Dalam kasus saya, gagal rangkaian pengujian yang berjalan dalam container tidak membuat container keluar dengan kode 1. Saya tidak dapat menggabungkan jika ada yang keluar dengan kode 1 karena tidak ada yang melakukannya .... Ada ide bagaimana menangani ini kasus?
walkerrandophsmith
9

Jika Anda bersedia menggunakan docker-compose rununtuk memulai pengujian secara manual, menambahkan --rmtanda, anehnya, menyebabkan Tulis secara akurat mencerminkan status keluar perintah Anda.

Inilah contoh saya:

$ docker-compose -v
docker-compose version 1.7.0, build 0d7bf73

$ (docker-compose run bash false) || echo 'Test failed!'  # False negative.

$ (docker-compose run --rm bash false) || echo 'Test failed!'  # True positive.
Test failed!

$ (docker-compose run --rm bash true) || echo 'Test failed!'  # True negative.
esmail
sumber
1
Atau (docker-compose run --rm ...) || exit $?untuk penghentian jika terjadi kesalahan. Berguna dalam skrip bash.
Amirreza Nasiri
8

Gunakan docker waituntuk mendapatkan kode keluar:

$ docker-compose -p foo up -d
$ ret=$(docker wait foo_bar_1)

fooadalah "nama proyek". Dalam contoh di atas, saya menetapkannya secara eksplisit, tetapi jika Anda tidak menyediakannya, itu adalah nama direktori. baradalah nama yang Anda berikan ke sistem yang diuji di docker-compose.yml Anda.

Perhatikan bahwa docker logs -fmelakukan hal yang benar juga, keluar saat container berhenti. Jadi Anda bisa meletakkan

$ docker logs -f foo_bar_1

antara docker-compose updan docker waitsehingga Anda dapat melihat pengujian Anda berjalan.

Bryan Larsen
sumber
8

--exit-code-from SERVICEdan --abort-on-container-exittidak berfungsi dalam skenario di mana Anda perlu menjalankan semua penampung hingga selesai, tetapi gagal jika salah satu penampung ditutup lebih awal. Contohnya mungkin jika menjalankan 2 setelan pengujian secara bersamaan di penampung yang berbeda.

Dengan saran @ spendhil, Anda dapat menggabungkan docker-composeskrip yang akan gagal jika ada container yang gagal.

#!/bin/bash
set -e

# Wrap docker-compose and return a non-zero exit code if any containers failed.

docker-compose "$@"

exit $(docker-compose -f docker-compose.ci.build.yml ps -q | tr -d '[:space:]' |
  xargs docker inspect -f '{{ .State.ExitCode }}' | grep -v 0 | wc -l | tr -d '[:space:]')

Kemudian di server CI Anda cukup ubah docker-compose upke ./docker-compose.sh up.

Matt Cole
sumber
1
skrip ini tidak pernah mencapai bagian keluar karena penampung lain (seperti database, aplikasi web) berjalan terus-menerus. berjalan dalam mode terpisah itu keluar segera setelah kontainernya naik
Baldy
Benar, ini hanya berfungsi jika Anda ingin menjalankan semua container sampai selesai. Mungkin tidak terlalu umum, tetapi berguna bagi saya pada saat penulisan dan saya pikir saya akan membagikannya.
Matt Cole
Beri suara positif pada jawaban Anda karena itu membuat saya hampir selalu berhasil! Menambahkan Docker, tunggu di setiap wadah uji dalam mode terpisah membuatnya berfungsi. Terima kasih telah berbagi :)
Baldy
2

docker-rails memungkinkan Anda untuk menentukan kode kesalahan penampung mana yang dikembalikan ke proses utama, sehingga server CI Anda dapat menentukan hasilnya. Ini adalah solusi yang bagus untuk CI dan pengembangan rel dengan buruh pelabuhan.

Sebagai contoh

exit_code: web

di Anda docker-rails.ymlakan menghasilkan webkode keluar kontainer sebagai hasil dari perintah docker-rails ci test. docker-rails.ymlhanyalah pembungkus meta di sekitar standar docker-compose.ymlyang memberi Anda potensi untuk mewarisi / menggunakan kembali konfigurasi dasar yang sama untuk lingkungan yang berbeda, yaitu pengembangan vs pengujian vs pengujian paralel.

kross
sumber
2

Jika Anda mungkin menjalankan lebih banyak layanan penulisan buruh pelabuhan dengan nama yang sama di satu mesin buruh pelabuhan, dan Anda tidak tahu nama persisnya:

docker-compose up -d
(exit "${$(docker-compose logs -f test-chrome)##* }")

echo %? - mengembalikan kode keluar dari layanan test-chrome

Manfaat:

  • menunggu layanan yang tepat untuk keluar
  • menggunakan nama layanan, bukan nama wadah
cvakiitho
sumber
2

Anda dapat melihat status keluar dengan:

echo $(docker-compose ps | grep "servicename" | awk '{print $4}')
RT Bathula
sumber
Terima kasih telah memulai ini. Ini versi saya yang satu ini (yang bekerja lebih baik untuk saya karena saya pikir format output perintah telah berubah sejak jawaban ini ditulis) -docker-compose ps | grep servicename | grep -v 'Exit 0' && echo "Automation or integration tests failed." && exit 1
DTrejo