6-bo‘lim

CMD, ENTRYPOINT va PID 1

Exec va shell shakllari, ENTRYPOINT bilan CMD farqi, konteynerdagi 1-jarayon va signallar.

🕑 20 daqiqa o‘qish 📄 830 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Ikki yozuv shakli
  2. Birinchi farq: o'zgaruvchilar
  3. Ikkinchi farq: kim PID 1 bo'ladi?
  4. PID 1 nega muhim?
  5. ENTRYPOINT va CMD birgalikda
  6. Zombi jarayonlar va --init
  7. STOPSIGNAL
  8. Xulosa

2-bobda docker stop 137 kodini qaytarganini ko'rgan edik. Endi sababini oxirigacha tushunamiz - va bu yo'lda ko'p tarqalgan bitta noto'g'ri tushunchani ham to'g'rilaymiz.

Ikki yozuv shakli #

CMD va ENTRYPOINT ni ikki xil yozish mumkin:

DOCKERFILE
CMD ["sleep", "300"]     # exec shakl - JSON massiv
CMD sleep 300            # shell shakl - oddiy matn

Shell shaklda Docker buyruqni /bin/sh -c ga uzatadi. Exec shaklda esa dasturni to'g'ridan-to'g'ri ishga tushiradi.

Birinchi farq: o'zgaruvchilar #

Shell shakl qobiq orqali o'tgani uchun o'zgaruvchi kengayadi:

Terminal
docker run --rm kengay-shell
Natija
shell shakl: dokon

Exec shaklda qobiq yo'q - demak kengaytiradigan ham yo'q:

Terminal
docker run --rm kengay-exec
Natija
exec shakl: $NOM

$NOM matn bo'lib chiqdi. Bu juda ko'p uchraydigan xato.

O'zgaruvchi kerak bo'lsa, qobiqni ataylab chaqiring
Natija
CMD ["sh", "-c", "echo $NOM"]

Bu exec shakl, lekin ichida qobiq bor - demak kengayish ishlaydi va nima bo'layotgani ko'rinib turadi.

Ikkinchi farq: kim PID 1 bo'ladi? #

Ko'p qo'llanmada "shell shakl sh ni PID 1 qiladi" deyiladi. Buni tekshiramiz.

Exec shakl:

Terminal
docker run -d --name ka exec-shakl >/dev/null
sleep 1
docker exec ka ps -o pid,args | sed -n 2p
Natija
    1 sleep 300

Shell shakl, bitta buyruq:

Terminal
docker run -d --name kb shell-bitta >/dev/null
sleep 1
docker exec kb ps -o pid,args | sed -n 2p
Natija
    1 sleep 300

Bir xil. Qobiq PID 1 bo'lib qolmadi.

Sababi: sh ning bajaradigan boshqa ishi qolmaganda, u yangi jarayon yaratmasdan o'z o'rnini buyruqqa beradi (exec optimizatsiyasi).

Endi qobiqning ishi qolsa nima bo'lishini ko'ramiz:

Terminal
docker run -d --name kc shell-ikki >/dev/null
sleep 1
docker exec kc ps -o pid,args | sed -n 2p
docker exec kc ps -o args | sed -n 3p
Natija
    1 /bin/sh -c sleep 300 && echo tugadi
sleep 300

Mana endi qobiq PID 1, sleep esa uning bolasi - alohida jarayon. Chunki sleep tugagach echo ni bajarish kerak, demak qobiq ketolmaydi va kutib turishga majbur.

(Bolaning PID raqami har safar boshqacha bo'lishi mumkin, shuning uchun ikkinchi buyruqda uni ko'rsatmadik.)

"Shell shakl doim sh ni PID 1 qiladi" - noto'g'ri

Bu qobiqqa va buyruqqa bog'liq. Alpine dagi busybox sh oddiy holatda o'z o'rnini beradi, murakkab holatda esa qolib ketadi.

Shuning uchun "shell shakl ishlatmang" degan qoidaga emas, natijani tekshirishga tayaning:

Natija
docker exec <konteyner> ps -o pid,args

PID 1 kimligini bir marta ko'rish yuzta maslahatdan foydaliroq.

PID 1 nega muhim? #

Chunki docker stop signalni faqat PID 1 ga yuboradi.

SIGTERM faqat PID 1 ga boradi Yaxshi PID 1: ilova SIGTERM ni ushlaydi ishni tugatadi, ulanishni yopadi chiqish kodi 0 Yomon PID 1: sh yoki sleep signalni e'tiborsiz qoldiradi 10 soniya kutish, so'ng SIGKILL chiqish kodi 137 Ilova PID 1 dan pastda tursa, signal unga umuman yetib bormaydi - qobiq uni bolalariga uzatmaydi.
Muhimi shakl emas - PID 1 signalni ushlaydimi yoki yo'qmi

Uchala konteynerni to'xtatamiz:

Terminal
for n in ka kb kc; do
    docker stop $n >/dev/null
    echo "$n: kod=$(docker inspect -f '{{.State.ExitCode}}' $n)"
done
Natija
ka: kod=137
kb: kod=137
kc: kod=137

Uchalasi ham 137 - va har biri 10 soniya kutdi. sleep SIGTERM ni ushlamaydi, qobiq ham uni bolasiga uzatmaydi.

Endi signalni to'g'ri ushlaydigan skriptni ko'ramiz:

Natija
#!/bin/sh
trap 'echo "SIGTERM keldi, toza chiqamiz"; exit 0' TERM
echo "xizmat ishlayapti"
sleep 300 &
wait
DOCKERFILE
FROM alpine:3.21
COPY boshla.sh /boshla.sh
CMD ["/boshla.sh"]
Terminal
docker run -d --name toza toza-chiqish >/dev/null
sleep 1
docker stop toza >/dev/null
echo "kod: $(docker inspect -f '{{.State.ExitCode}}' toza)"
docker logs toza
Natija
kod: 0
xizmat ishlayapti
SIGTERM keldi, toza chiqamiz

Chiqish kodi 0, va bu deyarli bir zumda sodir bo'ldi - 10 soniya kutish bo'lmadi.

E'tibor bering: sleep 300 & fondan ishga tushirilgan va wait kutayapti. Shunday yozilmasa, trap ishlamas edi - qobiq sleep tugashini kutib turgan bo'lardi va signalni faqat undan keyin ko'rar edi.

ENTRYPOINT va CMD birgalikda #

Ikkalasi ham bo'lsa, ular qo'shiladi: ENTRYPOINT - buyruq, CMD - unga standart argument.

DOCKERFILE
FROM alpine:3.21
ENTRYPOINT ["echo", "Salom,"]
CMD ["dunyo"]
Terminal
docker run --rm salomla
Natija
Salom, dunyo

Buyruq satrida bergan argumentingiz CMD ni almashtiradi:

Terminal
docker run --rm salomla Husanboy
Natija
Salom, Husanboy

ENTRYPOINT esa joyida qoldi. Uni almashtirish uchun alohida bayroq kerak:

Terminal
docker run --rm --entrypoint echo salomla "butunlay boshqa"
Natija
butunlay boshqa

Faqat CMD bo'lsa, u to'liq almashtiriladi:

Terminal
docker run --rm exec-shakl echo "almashtirildi"
Natija
almashtirildi
Nima kerakQanday yoziladi
Foydalanuvchi buyruqni almashtira olsinFaqat CMD
Buyruq qat'iy, argument o'zgaruvchanENTRYPOINT + CMD
Obraz bitta vazifa uchun vositaFaqat ENTRYPOINT
Qaysi birini tanlash kerak?

Obraz xizmat bo'lsa (veb server, ishchi jarayon) - ENTRYPOINT ga ishga tushirish skriptini, CMD ga esa standart argumentlarni qo'ying.

Obraz vosita bo'lsa (masalan konvertor) - ENTRYPOINT ga vositani qo'ying, shunda foydalanuvchi uni oddiy buyruqdek ishlatadi:

Natija
docker run --rm mening-vositam --help

Zombi jarayonlar va --init #

PID 1 ning yana bir vazifasi bor: egasiz qolgan bolalarni yig'ish. Oddiy ilovalar buni qilmaydi.

Terminal
docker run -d --name z1 zombi >/dev/null
sleep 1
docker exec z1 ps -o pid,args | sed -n 2p
Natija
    1 sh -c sleep 300 & sleep 300 & wait

Endi --init bilan:

Terminal
docker run -d --init --name z2 zombi >/dev/null
sleep 1
docker exec z2 ps -o pid,args | sed -n 2p
Natija
    1 /sbin/docker-init -- sh -c sleep 300 & sleep 300 & wait

PID 1 endi docker-init - kichik, faqat shu ish uchun yozilgan dastur. U signallarni uzatadi va zombi jarayonlarni yig'adi.

--init qachon kerak?

Ilovangiz ko'p bola jarayon yaratsa va ularni o'zi kutmasa - konteynerda zombi jarayonlar to'planadi.

Belgisi: ps da <defunct> yoki Z holatidagi jarayonlar.

Yechim ikkita: --init ishlatish yoki ilovaga to'g'ri PID 1 kutubxonasini qo'shish (tini, dumb-init).

STOPSIGNAL #

Ba'zi dasturlar SIGTERM emas, boshqa signalni kutadi. Nginx masalan SIGQUIT ni "muloyim to'xtash" deb tushunadi.

DOCKERFILE
FROM alpine:3.21
STOPSIGNAL SIGINT
CMD ["sh", "-c", "trap 'echo INT keldi; exit 0' INT; sleep 300 & wait"]
Terminal
docker run -d --name sig signalli >/dev/null
sleep 1
docker stop -t 5 sig >/dev/null
docker logs sig
echo "kod: $(docker inspect -f '{{.State.ExitCode}}' sig)"
Natija
INT keldi
kod: 0

docker stop endi SIGTERM emas, SIGINT yubordi.

Xulosa #

  • Exec shakl (["sleep","300"]) dasturni to'g'ridan-to'g'ri ishga tushiradi; shell shakl (sleep 300) uni /bin/sh -c ga uzatadi.
  • Shell shaklda muhit o'zgaruvchisi kengayadi, exec shaklda esa $NOM oddiy matn bo'lib qoladi.
  • "Shell shakl doim sh ni PID 1 qiladi" - noto'g'ri: qobiq boshqa ishi qolmasa o'z o'rnini beradi.
  • Qobiq PID 1 bo'lib qoladigan holat - buyruqda &&, ; yoki quvur bo'lganda.
  • Muhimi shakl emas, PID 1 signalni ushlaydimi: uni docker exec <konteyner> ps -o pid,args bilan tekshiring.
  • SIGTERM ni ushlamaydigan PID 1 - 137 va 10 soniya kutish; trap bilan to'g'ri yozilgani - 0 va bir zumda.
  • Fon jarayonini & bilan ishga tushirib wait qilish kerak, aks holda trap signalni o'z vaqtida ko'rmaydi.
  • ENTRYPOINT + CMD qo'shiladi: birinchisi buyruq, ikkinchisi standart argument; buyruq satri CMD ni almashtiradi.
  • ENTRYPOINT ni faqat --entrypoint bayrog'i almashtiradi.
  • --init konteynerga to'g'ri PID 1 qo'shadi - zombi jarayonlar to'planganda kerak bo'ladi.
Amaliy topshiriq
  1. Bir xil buyruqni exec va shell shaklda yozib, ps bilan PID 1 ni solishtiring.
  2. Shell shaklga && echo tugadi qo'shib, PID 1 o'zgarishini kuzating.
  3. ENV o'zgaruvchisini ikkala shaklda chop etib, farqni ko'ring.
  4. SIGTERM ni ushlamaydigan konteynerni to'xtatib, vaqtni o'lchang.
  5. trap bilan to'g'ri yozilgan skript yarating va vaqtni yana o'lchang.
  6. ENTRYPOINT va CMD ni birga ishlatib, argument almashtiring.
  7. --entrypoint bilan obraz buyrug'ini butunlay almashtiring.
  8. --init bilan va usiz PID 1 ni solishtiring.
  9. STOPSIGNAL ni SIGINT ga o'zgartirib, natijani tekshiring.
  10. O'z ilovangizda SIGTERM ishlovchisi bormi - tekshiring va bo'lmasa qo'shing.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.