Docker Container
Tini, yani init aklınıza gelebilecek en basitidir. Tini'nin tek yaptığı, tek bir çocuk doğurmak (Tini'nin bir kapta çalıştırılması amaçlanmıştır) ve zombileri biçerken ve sinyal iletme gerçekleştirirken çıkmasını beklemektir.
Daha fazla bilgi için bakınız:
Neden Tini?
Tini'yi kullanmanın çeşitli avantajları vardır:
- Sizi, (zamanla!) tüm sisteminizi PID'ler için aç bırakabilecek (ve onu kullanılmaz hale getirebilecek) yanlışlıkla zombi süreçleri oluşturan yazılımlardan korur.
- Docker görüntünüzde çalıştırdığınız yazılım için varsayılan sinyal işleyicilerin çalışmasını sağlar . Örneğin, Tini ile, SIGTERM açıkça bir sinyal işleyici yüklememiş olsanız bile işleminizi uygun şekilde sonlandırır.
- Bunu tamamen şeffaf bir şekilde yapıyor! Tini olmadan çalışan Docker görüntüleri herhangi bir değişiklik olmadan Tini ile çalışacaktır.
Öncelikle biraz Docker'dan bahsedelim. Bir Docker kapsayıcısını çalıştırdığınızda, Docker onu sistemin geri kalanından yalıtmaya devam eder. Bu izolasyon farklı seviyelerde gerçekleşir (örn. ağ, dosya sistemi, süreçler).
Tini, ağ veya dosya sistemiyle pek ilgilenmez, bu yüzden Tini bağlamında neyin önemli olduğuna odaklanalım.
Processes her Docker kapsayıcısı bir PID ad alanıdır; bu, kapsayıcınızdaki işlemlerin ana bilgisayarınızdaki diğer işlemlerden izole edildiği anlamına gelir. Bir PID ad alanı, PID 1'de başlayan init ve yaygın olarak adlandırılan bir ağaçtır.
Not: Bir Docker kapsayıcısı çalıştırdığınızda, PID 1 sizin olarak ayarladığınız ENTRYPOINTşeydir (veya bir tane yoksa, biçimine bağlı olarak kabuğunuz veya başka bir programdır CMD).
Şimdi, diğer süreçlerin aksine, PID 1'in benzersiz bir sorumluluğu var, o da zombi süreçleri biçmek.
Zombi süreçleri aşağıdakileri yapan süreçlerdir:
- Çıkıldı.
- Üst
waitsüreçleri tarafından düzenlenmediler (waitüst süreçlerin, çocuklarının çıkış kodunu almak için kullandıkları sistem çağrısıdır). - Ebeveynlerini kaybettiler (yani ebeveynleri de ayrıldı), bu da
waitebeveynleri tarafından asla cezalandırılmayacakları anlamına gelir.
Bir zombi yaratıldığında (yani, ebeveyni çıktığında gerçekleşir ve bu nedenle onun tarafından bulunma şansı ortadan kalkar), onu biçmesi beklenen (yani onu çağırmak anlamına gelir wait) reparent to .initwait
Diğer bir deyişle, çocuklarını ortada bırakan "sorumsuz" ebeveynlerin arkasını birilerininwait temizlemesi gerekir ve bu PID 1'in işidir.
Tini'nin yaptığı budur ve JVM'nin (yaptığınızda çalışan budur exec java ...) yapmadığı bir şeydir , bu yüzden Jenkins'i PID 1 olarak çalıştırmak istemezsiniz.
Zombi yaratmanın genellikle hoş karşılanmadığını unutmayın (yani, ideal olarak kodunuzu zombi yaratmaması için düzeltmelisiniz), ancak Jenkins gibi bir şey için kaçınılmazdır: çünkü Jenkins genellikle olmayan bir kod çalıştırır. Jenkins bakıcıları tarafından yazılmışsa (yani derleme komut dosyalarınız), "kodu düzeltemezler".
Bu nedenle Jenkins, zombi oluşturan betikleri oluşturduktan sonra temizlemek için Tini:'yi kullanır.
Şimdi, Bash aslında aynı şeyi yapıyor (zombileri biçiyor), bu yüzden muhtemelen merak ediyorsunuz: neden Bash'i PID 1 olarak kullanmıyorsunuz?
Sorunlardan biri, Bash'i PID 1 olarak çalıştırırsanız, Docker kapsayıcınıza gönderdiğiniz tüm sinyaller (örneğin, docker stopveya kullanarak docker kill) Bash'e gönderilir ve Bash onları hiçbir yere iletmez ( siz kendiniz kodlamadığınız sürece). Başka bir deyişle, Jenkins'i çalıştırmak için Bash'i kullanırsanız ve sonra çalıştırırsanız docker stop, Jenkins stop komutunu asla görmez!
"Sinyalleri ileterek" Tini düzeltmeleri: Tini'ye bir sinyal gönderirseniz, aynı sinyali alt işleminize gönderir (sizin durumunuzda Jenkins).
İkinci bir sorun ise, işleminiz bittiğinde Bash de çıkmaya devam edecek. Dikkatli olmazsanız, Bash çıkış kodu 0 ile çıkabilir, oysa işleminiz gerçekten çökebilir (0, "her şey yolunda" anlamına gelir; bu, Docker yeniden başlatma ilkelerinin beklediğiniz şeyi yapmamasına neden olur). Aslında istediğiniz , Bash'in işleminizin sahip olduğu aynı çıkış kodunu döndürmesidir.
İletmeyi gerçekten yapmak için Bash'te sinyal işleyiciler oluşturarak ve uygun bir çıkış kodu döndürerek bu sorunu giderebileceğinizi unutmayın . Öte yandan, bu daha fazla iştir, oysa Tini'yi eklemek Docker dosyanızda birkaç satırdır.
Şimdi, başka bir çözüm olabilir, örneğin zombileri biçmek için Jenkins'e başka bir iş parçacığı eklemek ve Jenkins'i PID 1 olarak çalıştırmak.
Bu da iki nedenden dolayı ideal değil:
İlk olarak, eğer Jenkins PID 1 olarak çalışıyorsa, o zaman Jenkins'e yeniden ebeveynlik edilen (toplanması gereken) süreç ile Jenkins tarafından ortaya çıkan süreçler (ki olmamalı, çünkü zaten bekleyen başka bir kod var) arasında ayrım yapmak zordur. onlara wait). Eminim bunu kodla çözebilirsin, ama yine de: Tini'yi bırakabilecekken neden yazasın ki?
İkincisi, Jenkins PID 1 olarak çalışıyorsa, gönderdiğiniz sinyalleri alamayabilir!
Bu, PID 1'deki bir inceliktir. Diğer farklı süreçlerin aksine, PID 1'in varsayılan sinyal işleyicileri yoktur; bu, Jenkins'in açıkça bir sinyal işleyici yüklemediği anlamına gelir SIGTERM. varsayılan davranış, süreci sonlandırmak olurdu).
Tini, açık sinyal işleyicileri (tesadüfen iletmek için) kurar, böylece bu sinyaller artık düşmez. Bunun yerine, PID 1 (Tini'dir) olarak çalışmayan ve bu nedenle varsayılan sinyal işleyicilere sahip olan Jenkins'e gönderilirler (not: Jenkins'in Tini'yi kullanmasının nedeni bu değildir , onu sinyal toplama için kullanırlar , ancak Bu nedenle RabbitMQ görüntüsünde kullanılır).
Ayrıca Tini'de, Bash veya Java'da çoğaltılması daha zor olan birkaç ekstra olduğunu unutmayın (örneğin, Tini bir alt biçici olarak kaydolabilir, bu nedenle zombi toplama işini yapmak için aslında PID 1 olarak çalışmasına gerek yoktur), ancak bunlar çoğunlukla uzman kullanım durumları için yararlıdır.
Tini şu özelliklerle farklılık gösterir:
- PID 1'in yapması gereken her şeyi yapıyor, başka bir şey yapmıyor . Ortam dosyalarını okumak, kullanıcıları değiştirmek, süreç denetimi gibi şeyler Tini'nin kapsamı dışındadır (bunlar için başka, daha iyi araçlar vardır)
- İşini düzgün yapmak için sıfır yapılandırma gerektirir (Tini >= 0.6, düzgün çalıştırmazsanız da sizi uyarır).
- Bir sürü testi var.
Tini'yi kullanma
Docker 1.13 veya üstünü kullanıyorsanız Tini, Docker'ın kendisine dahildir. Bu, Docker CE'nin tüm sürümlerini içerir. Tini'yi etkinleştirmek için --init bayrağını docker run komutu ile kullanmanız yeterlidir.
NOT: Tini için önceden oluşturulmuş Docker görüntüleri mevcuttur . Şu anda üssünüz olarak bir Ubuntu veya CentOS görüntüsü kullanıyorsanız, bunlardan birini yedek olarak kullanabilirsiniz.
Şimdi docker üzerinde tini olmadan bir container oluşturup kontrol edelim.
docker run -d --name test2 -p 9998:80 nginx

Şimdi init görevini kimin üstlendiğini kontrol edelim.
pstree aracını kurmak için apt/yum install psmisc komutunu kullanabilirsiniz.

Şimdi docker üzerinde tini ile bir container oluşturup kontrol edelim.
docker run -d --init --name test0 -p 9999:80 nginx

Şimdi init görevini kimin üstlendiğini kontrol edelim.

En basit şekliyle tiniyi bu şekilde kullanabilirsiniz.
Kapsayıcınıza Tini ekleyin ve çalıştırılabilir yapın. Ardından, Tini'yi çağırın ve programınızı ve argümanlarını argüman olarak Tini'ye iletin.
Docker'da, Tini'yi manuel olarak çağırmak zorunda kalmamak için bir giriş noktası kullanmak isteyecek şekilde yapılandırabilirsiniz.
# Add Tini
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]
# Run your program under Tini
CMD ["/your/program", "-and", "-its", "arguments"]
# or docker run your-image /your/program ...