Tänk noga innan du använder Docker-in-Docker för CI eller testmiljö

Tänk noga innan du använder Docker-in-Docker för CI eller testmiljö

Docker-in-Docker är en virtualiserad Docker-demonmiljö som körs i själva behållaren för att bygga behållarbilder. Huvudsyftet med att skapa Docker-in-Docker var att hjälpa till att utveckla Docker själv. Många använder den för att köra Jenkins CI. Detta verkar normalt till en början, men sedan uppstår problem som kan undvikas genom att installera Docker i en Jenkins CI-behållare. Den här artikeln berättar hur du gör detta. Om du är intresserad av den slutliga lösningen utan detaljer, läs bara det sista avsnittet i artikeln, "Lösa problemet."

Tänk noga innan du använder Docker-in-Docker för CI eller testmiljö

Docker-in-Docker: "Bra"

För mer än två år sedan satte jag mig i Docker flagga –privilegierade och skrev första versionen av dind. Målet var att hjälpa kärnteamet att utveckla Docker snabbare. Innan Docker-in-Docker såg den typiska utvecklingscykeln ut så här:

  • hackitetshack;
  • bygga;
  • stoppa en pågående Docker-demon;
  • lanserar en ny Docker-demon;
  • testning;
  • upprepa cykeln.

Om du ville göra en vacker, reproducerbar montering (det vill säga i en behållare), blev det mer invecklat:

  • hackitetshack;
  • se till att en fungerande version av Docker körs;
  • bygga ny Docker med gamla Docker;
  • stoppa Docker-demonen;
  • starta en ny Docker-demon;
  • testa;
  • stoppa den nya Docker-demonen;
  • upprepa.

Med tillkomsten av Docker-in-Docker har processen blivit enklare:

  • hackitetshack;
  • montering + lansering i ett steg;
  • upprepa cykeln.

Är det inte mycket bättre på det här sättet?

Tänk noga innan du använder Docker-in-Docker för CI eller testmiljö

Docker-in-Docker: "Bad"

Dock, i motsats till vad många tror är Docker-in-Docker inte 100% stjärnor, ponnyer och enhörningar. Vad jag menar är att det finns flera problem som en utvecklare måste vara medveten om.

En av dem gäller LSM:er (Linux-säkerhetsmoduler) som AppArmor och SELinux: när man kör en container kan den "interna Docker" försöka tillämpa säkerhetsprofiler som kommer att komma i konflikt med eller förvirra den "externa Docker". Detta är det svåraste problemet att lösa när man försöker slå samman den ursprungliga implementeringen av flaggan –privileged. Mina ändringar fungerade och alla tester skulle passera på min Debian-maskin och Ubuntu test-VM, men de kraschade och bränns på Michael Crosbys maskin (han hade Fedora som jag minns). Jag kommer inte ihåg den exakta orsaken till problemet, men det kan ha varit för att Mike är en klok kille som arbetar med SELINUX=enforce (jag använde AppArmor) och mina ändringar tog inte hänsyn till SELinux-profiler.

Docker-in-Docker: "Evil"

Det andra problemet är med Docker-lagringsdrivrutiner. När du kör Docker-in-Docker körs extern Docker ovanpå ett vanligt filsystem (EXT4, BTRFS eller vad du nu har) och intern Docker körs ovanpå ett kopiera-på-skriv-system (AUFS, BTRFS, Device Mapper) , etc.). , beroende på vad som är konfigurerat för att använda extern Docker). Detta skapar många kombinationer som inte kommer att fungera. Till exempel kommer du inte att kunna köra AUFS ovanpå AUFS.

Om du kör BTRFS ovanpå BTRFS bör det fungera först, men när det väl finns kapslade undervolymer kommer det att misslyckas att ta bort den överordnade undervolymen. Device Mapper-modulen har inget namnutrymme, så om flera Docker-instanser kör den på samma maskin kommer de alla att kunna se (och påverka) bilderna på varandra och på behållarens säkerhetskopieringsenheter. Det här är dåligt.

Det finns lösningar för att lösa många av dessa problem. Till exempel, om du vill använda AUFS i intern Docker, gör bara /var/lib/docker-mappen till en volym så kommer du att klara det. Docker har lagt till några basnamnområden till Device Mapper-målnamnen så att om flera Docker-anrop körs på samma maskin kommer de inte att trampa på varandra.

En sådan installation är dock inte alls enkel, vilket framgår av dessa artiklar i dind-förvaret på GitHub.

Docker-in-Docker: Det blir värre

Hur är det med build-cachen? Detta kan också vara ganska svårt. Folk frågar mig ofta "om jag kör Docker-in-Docker, hur kan jag använda bilder som finns på min värd istället för att dra tillbaka allt till min interna Docker"?

Vissa företagsamma människor har försökt binda /var/lib/docker från värden till en Docker-in-Docker-behållare. Ibland delar de /var/lib/docker med flera behållare.

Tänk noga innan du använder Docker-in-Docker för CI eller testmiljö
Vill du korrumpera din data? För det är just detta som kommer att skada din data!

Docker-demonen var tydligt designad för att ha exklusiv tillgång till /var/lib/docker. Inget annat ska "röra, peta eller prod" några Docker-filer som finns i den här mappen.

Varför är det så? Eftersom det här är resultatet av en av de svåraste lärdomarna under utvecklingen av dotCloud. DotCloud-behållarmotorn kördes genom att flera processer fick åtkomst till /var/lib/dotcloud samtidigt. Listiga knep som atomär filbyte (istället för redigering på plats), peppande kod med rådgivande och obligatoriska lås och andra experiment med säkra system som SQLite och BDB fungerade inte alltid. När vi designade om vår containermotor, som så småningom blev Docker, var ett av de stora designbesluten att konsolidera alla containeroperationer under en enda demon för att göra sig av med alla samtidiga nonsens.

Missförstå mig rätt: det är fullt möjligt att göra något bra, pålitligt och snabbt som involverar flera processer och modern parallellstyrning. Men vi tycker att det är enklare och lättare att skriva och underhålla kod med Docker som enda spelare.

Detta betyder att om du delar katalogen /var/lib/docker mellan flera Docker-instanser kommer du att få problem. Naturligtvis kan detta fungera, särskilt i de tidiga stadierna av testning. "Hör du, mamma, jag kan köra ubuntu som hamnarbetare!" Men prova något mer komplext, som att dra samma bild från två olika instanser, så ser du världen brinna.

Detta innebär att om ditt CI-system utför byggnationer och ombyggnader, varje gång du startar om din Docker-in-Docker-behållare, riskerar du att släppa en kärnvapen i dess cache. Det här är inte alls coolt!

Lösning

Låt oss ta ett steg tillbaka. Behöver du verkligen Docker-in-Docker eller vill du bara kunna köra Docker och bygga och köra containrar och bilder från ditt CI-system medan själva CI-systemet är i en container?

Jag slår vad om att de flesta vill ha det senare alternativet, vilket betyder att de vill ha ett CI-system som Jenkins för att kunna köra containrar. Och det enklaste sättet att göra detta är att helt enkelt sätta in en Docker-socket i din CI-behållare och associera den med flaggan -v.

Enkelt uttryckt, när du kör din CI-behållare (Jenkins eller annat), istället för att hacka något tillsammans med Docker-in-Docker, starta den med raden:

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

Den här behållaren kommer nu att ha tillgång till Docker-socket och kan därför köra behållare. Förutom att istället för att köra "barn"-behållare, kommer den att starta "syskon"-behållare.

Prova detta med den officiella docker-bilden (som innehåller Docker-binären):

docker run -v /var/run/docker.sock:/var/run/docker.sock 
           -ti docker

Det ser ut och fungerar som Docker-in-Docker, men det är inte Docker-in-Docker: när den här behållaren skapar ytterligare behållare kommer de att skapas i Docker på toppnivå. Du kommer inte att uppleva bieffekterna av kapsling och sammansättningscachen kommer att delas över flera samtal.

Obs! Tidigare versioner av den här artikeln rekommenderade att länka Docker-binären från värden till behållaren. Detta har nu blivit opålitligt eftersom Docker-motorn inte längre täcker statiska eller nästan statiska bibliotek.

Så om du vill använda Docker från Jenkins CI har du två alternativ:
installera Docker CLI med det grundläggande bildpaketeringssystemet (dvs. om din bild är baserad på Debian, använd .deb-paket), med Docker API.

Några annonser 🙂

Tack för att du stannar hos oss. Gillar du våra artiklar? Vill du se mer intressant innehåll? Stöd oss ​​genom att lägga en beställning eller rekommendera till vänner, moln VPS för utvecklare från $4.99, en unik analog av ingångsservrar, som uppfanns av oss för dig: Hela sanningen om VPS (KVM) E5-2697 v3 (6 kärnor) 10GB DDR4 480GB SSD 1Gbps från $19 eller hur delar man en server? (tillgänglig med RAID1 och RAID10, upp till 24 kärnor och upp till 40 GB DDR4).

Dell R730xd 2 gånger billigare i Equinix Tier IV datacenter i Amsterdam? Bara här 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV från $199 i Nederländerna! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - från $99! Läs om Hur man bygger infrastructure corp. klass med användning av Dell R730xd E5-2650 v4-servrar värda 9000 XNUMX euro för en slant?

Källa: will.com

Lägg en kommentar