การใช้ Docker-compose กับ CI - จะจัดการกับรหัสออกและคอนเทนเนอร์ที่เชื่อมต่อกับ daemonized ได้อย่างไร?


90

ตอนนี้ตัวแทน Jenkins ของเราสร้าง docker-compose.yml สำหรับแต่ละโปรเจ็กต์ Rails ของเราแล้วเรียกใช้ docker-compose docker-compose.yml มีคอนเทนเนอร์ "เว็บ" หลักที่มี rbenv และการอ้างอิง Rails อื่น ๆ ทั้งหมดของเราภายใน เชื่อมโยงกับคอนเทนเนอร์ DB ที่มี Postgres DB ทดสอบ

ปัญหาเกิดขึ้นเมื่อเราต้องเรียกใช้การทดสอบและสร้างรหัสทางออก เซิร์ฟเวอร์ CI ของเราจะปรับใช้ก็ต่อเมื่อสคริปต์ทดสอบส่งคืน exit 0 แต่ docker-compose จะคืนค่า 0 เสมอแม้ว่าคำสั่งคอนเทนเนอร์ใดคำสั่งหนึ่งจะล้มเหลวก็ตาม

ปัญหาอื่น ๆ คือคอนเทนเนอร์ DB ทำงานไปเรื่อย ๆ แม้ว่าเว็บคอนเทนเนอร์จะเรียกใช้การทดสอบเสร็จแล้วก็ตามดังนั้นdocker-compose upอย่าส่งคืน

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

คำตอบ:


78

ตั้งแต่เวอร์ชัน1.12.0คุณสามารถใช้--exit-code-fromตัวเลือก

จากเอกสารประกอบ :

- ออกจากรหัสจากบริการ

ส่งคืนรหัสทางออกของคอนเทนเนอร์บริการที่เลือก หมายถึง - ทางเข้า - ออกจากตู้คอนเทนเนอร์


1
นั่นควรเป็นวิธีที่ถูกต้องหากคุณใช้docker-compose1.12.0 ขึ้นไป บางทีมันอาจจะเป็นกรณีของคุณด้วย ตัวอย่างอาจเป็น: docker-compose up --exit-code-from test-unit. โปรดทราบว่ามันไม่ได้ผลสำหรับฉันจนกว่าฉันจะเพิ่มset -eที่ตอนต้นของสคริปต์ของฉัน
Adrian Antunez

--exit-code-fromใช้ไม่ได้กับ-dแม้ว่า มันจะทำให้เกิดข้อผิดพลาดเหล่านี้: using --exit-code-from implies --abort-on-container-exitและ --abort-on-container-exit and -d cannot be combined.
ericat

3
ฉันสามารถทำงานนี้บน Travis CI: travis-ci.org/coyote-team/coyote/builds/274582053 นี่คือ travis.yml: github.com/coyote-team/coyote/blob/master/.travis.yml # L12
subelsky

2
เอกสารดังกล่าวมีความเลวร้าย แฟล็กนี้เข้ากันได้กับอะไร? เป็นบริการเดียวหรือคุณสามารถส่งต่อได้หลายรายการ
worc

42

docker-compose runเป็นวิธีง่ายๆในการรับสถานะทางออกที่คุณต้องการ ตัวอย่างเช่น:

$ 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

หรือคุณมีตัวเลือกในการตรวจสอบตู้คอนเทนเนอร์ที่ตายแล้ว คุณสามารถใช้-fแฟล็กเพื่อรับสถานะการออก

$ 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

สำหรับคอนเทนเนอร์ db ที่ไม่มีวันกลับมาถ้าคุณใช้docker-compose upคุณจะต้อง sigkill คอนเทนเนอร์นั้น นั่นอาจไม่ใช่สิ่งที่คุณต้องการ แต่คุณสามารถใช้docker-compose up -dเพื่อรันคอนเทนเนอร์ของคุณ daemonized และฆ่าคอนเทนเนอร์ด้วยตนเองเมื่อการทดสอบของคุณเสร็จสมบูรณ์ docker-compose run ควรเรียกใช้คอนเทนเนอร์ที่เชื่อมโยงให้คุณ แต่ฉันเคยได้ยินการพูดคุยเกี่ยวกับ SO เกี่ยวกับข้อบกพร่องที่ทำให้ไม่สามารถทำงานได้ตามที่ตั้งใจไว้ในตอนนี้


ปัญหาเกี่ยวกับการรันนักเทียบท่าคือมันไม่ได้ให้เอาต์พุตใด ๆ เมื่อรันด้วย -T และเราต้องการเอาต์พุตเพื่อให้เราสามารถตรวจสอบบิลด์ที่ล้มเหลวได้
Logan Serman

1
@LoganSerman คุณสามารถตรวจสอบผลลัพธ์ด้วยdocker-compose logs
kojiro

มีวิธีที่จะไพพ์บันทึกเหล่านั้นไปยัง STDOUT อย่างต่อเนื่องในระหว่างการรันเพื่อให้เราเห็นมันในขณะที่กำลังสร้าง CI อยู่หรือไม่?
Logan Serman

ฉันเดาว่าฉันไม่เข้าใจว่าทำไมคุณถึงวิ่งด้วย-T
kojiro

คำสั่งบางคำสั่งที่เราเรียกใช้ภายในคอนเทนเนอร์เพื่อเรียกใช้การทดสอบมีศักยภาพในการขออินพุตเราต้องการรันด้วย -T เพื่อหลีกเลี่ยงสิ่งนี้ ตัวอย่างเช่น Rbenv ถามว่าคุณต้องการติดตั้งเวอร์ชัน Ruby ใหม่หรือไม่หากมีอยู่แล้ว
Logan Serman

24

สร้างจากคำตอบของ kojiro:

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

  1. รับรหัสคอนเทนเนอร์
  2. รับรหัสทางออกสุดท้ายสำหรับแต่ละรหัสคอนเทนเนอร์
  3. เฉพาะรหัสสถานะที่ไม่ได้ขึ้นต้นด้วย '0'
  4. นับจำนวนรหัสสถานะที่ไม่ใช่ 0
  5. ตัดพื้นที่สีขาวออก

แสดงจำนวนรหัสทางออกที่ไม่ใช่ 0 ที่ส่งคืน จะเป็น 0 ถ้าทุกอย่างออกด้วยรหัส 0


คุณยังสามารถใช้เอาท์พุตแบบไม่เงียบได้docker-compose psเช่น: docker-compose ps | grep -c "Exit 1"จะให้การนับที่ "ทางออก 1" ตรงกับในจอแสดงผลจากdocker-compose ps(ซึ่งมีตารางสรุปผลลัพธ์ที่ค่อนข้างพิมพ์ออกมา) รหัสทางออกแสดงอยู่ในคอลัมน์ "สถานะ"
eharik

นี่มันสุดยอดจริงๆ ในกรณีของฉันชุดทดสอบล้มเหลวที่ทำงานในคอนเทนเนอร์ไม่ได้ทำให้คอนเทนเนอร์ออกด้วยรหัส 1 ฉันไม่สามารถรวมได้หากมีการออกด้วยรหัส 1 เนื่องจากไม่มีชุดใดทำ .... ความคิดวิธีจัดการสิ่งนี้ กรณี?
walkerrandophsmith

9

หากคุณยินดีที่จะใช้docker-compose runในการเริ่มต้นการทดสอบของคุณด้วยตนเองการเพิ่ม--rmแฟล็กซึ่งผิดปกติมากพอที่จะทำให้การเขียนแสดงสถานะการออกคำสั่งของคุณได้อย่างถูกต้อง

นี่คือตัวอย่างของฉัน:

$ 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.

1
หรือ(docker-compose run --rm ...) || exit $?สำหรับการยุติในกรณีที่เกิดข้อผิดพลาด มีประโยชน์ในสคริปต์ทุบตี
Amirreza Nasiri

8

ใช้docker waitเพื่อรับรหัสทางออก:

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

fooคือ "ชื่อโครงการ" ในตัวอย่างด้านบนฉันระบุไว้อย่างชัดเจน แต่ถ้าคุณไม่ได้ระบุชื่อไดเรกทอรีนั้น barเป็นชื่อที่คุณตั้งให้กับระบบภายใต้การทดสอบใน docker-compose.yml ของคุณ

โปรดทราบว่าdocker logs -fสิ่งที่ถูกต้องเช่นกันออกเมื่อคอนเทนเนอร์หยุด ดังนั้นคุณสามารถใส่

$ docker logs -f foo_bar_1

ระหว่างdocker-compose upและdocker waitเพื่อให้คุณสามารถชมการทดสอบของคุณได้


8

--exit-code-from SERVICEและ--abort-on-container-exitอย่าทำงานในสถานการณ์ที่คุณต้องเรียกใช้คอนเทนเนอร์ทั้งหมดจนเสร็จสิ้น แต่จะล้มเหลวหากหนึ่งในนั้นออกก่อนเวลา ตัวอย่างเช่นหากเรียกใช้ชุดทดสอบ 2 ชุดพร้อมกันในคอนเทนเนอร์ที่แตกต่างกัน

ด้วยคำแนะนำของ @edededhil คุณสามารถรวมdocker-composeสคริปต์ที่จะล้มเหลวหากมีคอนเทนเนอร์ใด ๆ

#!/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:]')

จากนั้นบนเซิร์ฟเวอร์ CI ของคุณเพียงแค่เปลี่ยนdocker-compose upเป็น./docker-compose.sh up.


1
สคริปต์นี้จะไม่ไปถึงส่วนออกเนื่องจากคอนเทนเนอร์อื่น ๆ (เช่นฐานข้อมูลเว็บแอป) ทำงานตลอดเวลา ทำงานในโหมดแยกออกทันทีที่คอนเทนเนอร์ขึ้น
Baldy

ถูกต้องสิ่งนี้ใช้ได้เฉพาะเมื่อคุณต้องการเรียกใช้คอนเทนเนอร์ทั้งหมดจนเสร็จสิ้น อาจจะไม่ธรรมดาเป็นพิเศษ แต่มันมีประโยชน์สำหรับฉันในขณะที่เขียนและฉันคิดว่าจะแบ่งปัน
Matt Cole

เพิ่มคะแนนให้คำตอบของคุณอยู่ดีเพราะทำให้ฉันได้รับคะแนนมากที่สุด! การเพิ่ม Docker รอในแต่ละคอนเทนเนอร์ทดสอบในโหมดแยกออกทำให้มันใช้งานได้ ขอบคุณสำหรับการแบ่งปัน :)
Baldy

2

นักเทียบท่ารางช่วยให้คุณระบุรหัสข้อผิดพลาดของคอนเทนเนอร์ที่ส่งกลับไปยังกระบวนการหลักดังนั้นเซิร์ฟเวอร์ CI ของคุณจึงสามารถกำหนดผลลัพธ์ได้ เป็นโซลูชันที่ยอดเยี่ยมสำหรับ CI และการพัฒนารางพร้อมนักเทียบท่า

ตัวอย่างเช่น

exit_code: web

ในของคุณdocker-rails.ymlจะให้รหัสภาชนะทางออกเป็นผลมาจากคำสั่ง web เป็นเพียง meta wrapper รอบ ๆ มาตรฐานที่ช่วยให้คุณมีศักยภาพในการสืบทอด / ใช้การกำหนดค่าพื้นฐานเดียวกันสำหรับสภาพแวดล้อมที่แตกต่างกันเช่น development vs test vs parallel_testsdocker-rails ci testdocker-rails.ymldocker-compose.yml


2

ในกรณีที่คุณอาจเรียกใช้บริการ Docker-compose ที่มีชื่อเดียวกันใน Docker engine หนึ่งเครื่องและคุณไม่ทราบชื่อที่แน่นอน:

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

echo %? - ส่งคืนรหัสออกจากบริการทดสอบโครเมี่ยม

สิทธิประโยชน์:

  • รอให้บริการที่แน่นอนออก
  • ใช้ชื่อบริการไม่ใช่ชื่อคอนเทนเนอร์

2

คุณสามารถดูสถานะการออกได้ด้วย:

echo $(docker-compose ps | grep "servicename" | awk '{print $4}')

ขอบคุณสำหรับการเริ่มต้น นี่คือเวอร์ชันของฉัน (ซึ่งทำงานได้ดีขึ้นสำหรับฉัน b / c ฉันคิดว่ารูปแบบเอาต์พุตคำสั่งเปลี่ยนไปตั้งแต่เขียนคำตอบนี้) -docker-compose ps | grep servicename | grep -v 'Exit 0' && echo "Automation or integration tests failed." && exit 1
DTrejo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.