{"id":32526,"date":"2019-10-31T21:47:32","date_gmt":"2019-10-31T18:47:32","guid":{"rendered":"https:\/\/prohoster.info\/blog\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\/"},"modified":"2019-10-31T21:47:32","modified_gmt":"2019-10-31T18:47:32","slug":"protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","status":"publish","type":"post","link":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","title":{"rendered":"Udviklings- og testproces med Docker og Gitlab CI","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Jeg vil gerne pr\u00e6sentere transkriptet af Alexander Sigachevs rapport fra Inventos \"Udviklings- og testprocessen med Docker + Gitlab CI\"<\/strong><\/p>\n<p><\/p>\n<p>De, der lige er begyndt at implementere udviklings- og testprocessen baseret p\u00e5 Docker + Gitlab CI, stiller ofte grundl\u00e6ggende sp\u00f8rgsm\u00e5l. Hvor skal man starte? Hvordan organiserer man det? Hvordan tester man?<\/p>\n<p><\/p>\n<p>Dette foredrag er v\u00e6rdifuldt, da det struktureret fort\u00e6ller om udvikling og test med brug af Docker og Gitlab CI. Selve foredraget er fra 2017. Jeg tror, at man kan hente grundl\u00e6ggende viden, metodologi, id\u00e9 og erfaring fra dette foredrag. <\/p>\n<p>\n<center><div class=\"youtube-placeholder\" data-id=\"lJsqRwULRVA\" onclick=\"loadVideo(this)\">\r\n        <img decoding=\"async\" src=\"https:\/\/img.youtube.com\/vi\/lJsqRwULRVA\/hqdefault.jpg\" alt=\"Afspil video\" loading=\"lazy\" width=\"480\" height=\"360\" style=\"width:100%;height:auto;\">\r\n        <div class=\"play-button\"><\/div>\r\n    <\/div><\/center><\/p>\n<p>Hvis nogen er interesseret, s\u00e5 kom ind under kat. <noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p><\/p>\n<p>Mit navn er Alexander Sigachev. Jeg arbejder i virksomheden Inventos. Jeg vil fort\u00e6lle om min erfaring med Docker og hvordan vi langsomt implementerer det i projekter i virksomheden.<\/p>\n<p><\/p>\n<p>Emnet for foredraget: Udviklingsprocessen med brug af Docker og Gitlab CI. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/e30c4757e4bda1bcc3e20b16e8e2709e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dette er mit andet foredrag om Docker. P\u00e5 tidspunktet for det f\u00f8rste foredrag brugte vi Docker kun i udviklingen p\u00e5 udviklernes maskiner. Antallet af medarbejdere, der brugte Docker, var omkring 2-3 personer. Gradvis har vi opn\u00e5et erfaring, og vi er kommet lidt videre. Link til vores <noindex><a rel=\"nofollow\" href=\"https:\/\/www.slideshare.net\/gled\/docker-development-70411088\">f\u00f8rste foredrag<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Hvad vil der v\u00e6re i dette foredrag? Vi vil dele vores erfaring omkring hvilke udfordringer vi har samlet, hvilke problemer vi har l\u00f8st. Det har ikke altid v\u00e6ret smukt, men det har gjort det muligt at komme videre.<\/p>\n<p><\/p>\n<p>Vores motto: dockeriser alt, hvad vores h\u00e6nder r\u00e6kker ud efter.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/25ae4f97f1c502384abfe697bac502b1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hvilke problemer l\u00f8ser vi?<\/p>\n<p><\/p>\n<p>N\u00e5r der er flere teams i virksomheden, er programm\u00f8ren en delt ressource. Der er tidspunkter, hvor programm\u00f8ren tr\u00e6kkes ud af et projekt og gives til et andet projekt i en periode.<\/p>\n<p><\/p>\n<p>For at programm\u00f8ren hurtigt kan s\u00e6tte sig ind i det, er det n\u00f8dvendigt at downloade koden fra projektet og s\u00e5 hurtigt som muligt starte et milj\u00f8, der g\u00f8r det muligt for ham at forts\u00e6tte med at l\u00f8se opgaverne i dette projekt.<\/p>\n<p><\/p>\n<p>Normalt, hvis man starter fra bunden, er der ikke meget dokumentation i projektet. Oplysninger om, hvordan man konfigurerer, findes kun hos de erfarne. Medarbejdere ops\u00e6tter selv deres arbejdsplads p\u00e5 en til to dage. For at fremskynde dette har vi anvendt Docker.<\/p>\n<p><\/p>\n<p>Den n\u00e6ste \u00e5rsag er standardiseringen af indstillinger i udviklingen. Efter min erfaring tager udviklerne altid initiativ. I hver femte sag introduceres et brugerdefineret dom\u00e6ne, for eksempel vasya.dev. Ved siden af sidder naboen Petja, der har dom\u00e6net petya.dev. De udvikler en hjemmeside eller et systemkomponent ved at bruge dette dom\u00e6nenavn.<\/p>\n<p><\/p>\n<p>N\u00e5r systemet vokser, og disse dom\u00e6nenavne begynder at komme ind i konfigurationerne, opst\u00e5r der konflikter i udviklingsmilj\u00f8erne, og stien til hjemmesiden bliver overskrevet.<\/p>\n<p><\/p>\n<p>Det samme sker med databaseindstillingerne. Nogen t\u00e6nker ikke over sikkerheden og arbejder med root-adgangskoden som tom. For nogen, da MySQL blev installeret, blev der kr\u00e6vet en adgangskode, og den viste sig at v\u00e6re 123. Det sker ofte, at databasekonfigurationen konstant bliver \u00e6ndret afh\u00e6ngigt af udviklerens commit. Nogen har rettet det, nogen har ikke rettet konfigureringen. Der var tricks, hvor vi tog en testkonfiguration og <code>.gitignore<\/code> hver udvikler skulle installere databasen. Det gjorde opstartsprocessen mere kompliceret. Udover alt andet skal man huske p\u00e5 databasen. Databasen skal initialiseres, adgangskoden skal angives, brugeren skal angives, tabellen skal oprettes og s\u00e5 videre.<\/p>\n<p><\/p>\n<p>En anden af problemerne er de forskellige versioner af bibliotekerne. Det sker ofte, at udvikleren arbejder med forskellige projekter. Der er et Legacy-projekt, som blev p\u00e5begyndt for fem \u00e5r siden (fra 2017 \u2014 bem\u00e6rk red.). P\u00e5 tidspunktet for starten, blev der startet med MySQL 5.5. Der er ogs\u00e5 moderne projekter, hvor vi fors\u00f8ger at implementere mere moderne versioner af MySQL, for eksempel 5.7 eller nyere (i 2017 \u2014 bem\u00e6rk red.).<\/p>\n<p><\/p>\n<p>Den, der arbejder med MySQL, ved, at disse biblioteker medf\u00f8rer afh\u00e6ngigheder. Det er ret problematisk at k\u00f8re to databaser sammen. I det mindste er det problematisk at forbinde gamle klienter til den nye database. Dette skaber i sin tur flere problemer.<\/p>\n<p><\/p>\n<p>Det n\u00e6ste problem er, at n\u00e5r udvikleren arbejder p\u00e5 sin lokale maskine, bruger han lokale ressourcer, lokale filer, lokal RAM. Al interaktion under udviklingen af l\u00f8sningen foreg\u00e5r inden for rammerne af, at det k\u00f8rer p\u00e5 \u00e9n maskine. Et eksempel kunne v\u00e6re, n\u00e5r vi har 3 backend-servere i produktion, og udvikleren gemmer filer i roden, som nginx henter for at svare p\u00e5 foresp\u00f8rgsler. N\u00e5r s\u00e5dan en kode g\u00e5r i produktion, f\u00e5r vi, at filen kun er tilg\u00e6ngelig p\u00e5 en af de 3 servere.<\/p>\n<p><\/p>\n<p>I \u00f8jeblikket udvikles retningen mod mikrotjenester. N\u00e5r vi opdeler vores store applikationer i mindre komponenter, der interagerer med hinanden. Dette muligg\u00f8r at v\u00e6lge teknologier specifikt til den givne opgave. Det g\u00f8r ogs\u00e5, at arbejdet og ansvarsomr\u00e5derne kan opdeles blandt udviklerne.<\/p>\n<p><\/p>\n<p>Frontend-udvikleren, der udvikler i JS, p\u00e5virker praktisk talt ikke Backend. Backend-udvikleren udvikler i vores tilf\u00e6lde Ruby on Rails og forstyrrer ikke Frontend. Interaktionen sker via API.<\/p>\n<p><\/p>\n<p>Som en bonus har vi ved hj\u00e6lp af Docker v\u00e6ret i stand til at optimere ressourcerne p\u00e5 Staging. Hver projekt kr\u00e6ver bestemte indstillinger p\u00e5 grund af sin specifik. Fysisk skulle der allokeres enten en virtuel server, der skulle konfigureres hver for sig, eller man skulle dele et n\u00e6rt milj\u00f8, og projekterne kunne p\u00e5virke hinanden afh\u00e6ngigt af versionerne af bibliotekerne.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/fa5c99f90faef84a356d4791fd915830.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>V\u00e6rkt\u00f8jer. Hvad bruger vi? <\/p>\n<p><\/p>\n<ul>\n<li>Selve Docker. I Dockerfile beskrives afh\u00e6ngighederne for \u00e9n applikation. <\/li>\n<li>Docker-compose er forbindelsen, der forener flere af vores Docker-applikationer.<\/li>\n<li>GitLab bruger vi til opbevaring af kildekoden.<\/li>\n<li>GitLab-CI bruger vi til systemintegration.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/b6041827a6ba928e0d33add6b79c581c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Pr\u00e6sentationen best\u00e5r af to dele.<\/p>\n<p><\/p>\n<p>Den f\u00f8rste del vil fort\u00e6lle om, hvordan vi k\u00f8rte Docker p\u00e5 udviklernes maskiner.<\/p>\n<p><\/p>\n<p>Den anden del vil fort\u00e6lle om, hvordan man interagerer med GitLab, hvordan vi k\u00f8rer tests og hvordan vi implementerer p\u00e5 Staging.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/45746d3323d35e0d18f066c8bfcb2214.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Docker er denne teknologi, der g\u00f8r det muligt (ved at bruge en deklarativ tilgang) at beskrive de n\u00f8dvendige komponenter. Dette er et eksempel p\u00e5 en Dockerfile. Her erkl\u00e6rer vi, at vi arver fra det officielle Docker-billede Ruby:2.3.0. Det indeholder den installerede Ruby version 2.3. Vi installerer de n\u00f8dvendige biblioteker til opbygning og NodeJS. Vi beskriver, at vi opretter et bibliotek. <code>\/app<\/code>Vi udpeger app-katalogen som arbejdsmappen. I denne mappe placerer vi den n\u00f8dvendige minimale Gemfile og Gemfile.lock. Derefter bygger vi projekterne, der installerer denne afh\u00e6ngighed. Vi angiver, at containeren skal v\u00e6re klar til at lytte p\u00e5 den eksterne port 3000. Den sidste kommando er den, der direkte starter vores applikation. Hvis vi udf\u00f8rer kommandoen for at starte projektet, vil applikationen fors\u00f8ge at k\u00f8re og udf\u00f8re den angivne kommando.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/ba503c305ae90f49b39f4ae2cbda1668.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dette er et minimalt eksempel p\u00e5 en docker-compose-fil. I dette tilf\u00e6lde viser vi, hvad der sker ved forbindelsen mellem to containere. Dette er direkte service til databasen og webtjenesten. Vores webapplikationer kr\u00e6ver i de fleste tilf\u00e6lde en form for backend-database til opbevaring af data. Da vi bruger MySQL, er eksemplet med MySQL \u2014 men intet forhindrer os i at bruge en anden database (PostgreSQL, Redis).<\/p>\n<p><\/p>\n<p>Vi henter en u\u00e6ndret MySQL 5.7.14-billede fra den officielle kilde fra Docker Hub. Billedet, der er ansvarligt for vores webapplikation, opbygges fra den nuv\u00e6rende mappe. Ved f\u00f8rste opstart bygger den billedet for os. Derefter k\u00f8rer den den kommando, vi her udf\u00f8rer. Hvis vi ser tilbage, vil vi se, at der blev fastlagt en opstartskommando via Puma. Puma er en service skrevet i Ruby. I det andet tilf\u00e6lde \u00e6ndrer vi. Denne kommando kan v\u00e6re vilk\u00e5rlig alt efter vores behov eller opgaver.<\/p>\n<p><\/p>\n<p>Vi beskriver ogs\u00e5, at vi skal videregive porten fra vores udviklerhjemmesystem fra port 3000 til containerens port 3000. Dette udf\u00f8res automatisk ved hj\u00e6lp af iptables og sin mekanisme, der er forankret i Docker. <\/p>\n<p><\/p>\n<p>Udvikleren kan som f\u00f8r tilg\u00e5 enhver tilg\u00e6ngelig IP-adresse, for eksempel den lokale 127.0.0.1 eller maskinens eksterne IP-adresse.<\/p>\n<p><\/p>\n<p>Den sidste linje siger, at webcontaineren afh\u00e6nger af db-containeren. N\u00e5r vi kalder p\u00e5 at starte webcontaineren, vil docker-compose f\u00f8rst starte databasen for os. N\u00e5r databasen er startet (faktisk \u2014 efter containeren er startet! Dette garanterer ikke databasens klarhed) vil applikationen, vores backend, starte.<\/p>\n<p><\/p>\n<p>Dette hj\u00e6lper med at undg\u00e5 fejl, n\u00e5r databasen ikke er startet, og g\u00f8r det muligt at spare ressourcer, n\u00e5r vi stopper databasecontaineren og dermed frig\u00f8r ressourcer til andre projekter. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4acbcf5de89b98db055e8abc419202de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hvad giver brugen af database-dockerisering os i projektet. Vi angiver versionen af MySQL for alle udviklere. Dette hj\u00e6lper med at undg\u00e5 visse fejl, der kan opst\u00e5 ved versionsforskelle, n\u00e5r syntaks, konfiguration og standardindstillinger \u00e6ndres. Det muligg\u00f8r angivelse af f\u00e6lles hostname for databasen, login og password. Vi fjerner os fra det kaos af navne og konflikter i konfigurationsfiler, som vi havde tidligere. <\/p>\n<p><\/p>\n<p>Vi har mulighed for at anvende en mere optimal konfiguration til udviklingsmilj\u00f8et, der adskiller sig fra standardkonfigurationen. MySQL er som standard indstillet til svage maskiner, og dens ydeevne er meget lav lige fra starten.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/417d77a06b18e2df9b71440c0f2a1653.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Docker g\u00f8r det muligt at bruge den \u00f8nskede version af Python, Ruby, NodeJS, PHP. Vi slipper for behovet for at bruge en version manager. Tidligere brugte vi rpm-pakker til Ruby, som gjorde det muligt at \u00e6ndre versionen afh\u00e6ngigt af projektet. Det muligg\u00f8r ogs\u00e5, takket v\u00e6re Docker-containeren, en glidende migrering af kode og versionering sammen med afh\u00e6ngigheder. Vi har ikke problemer med at forst\u00e5 versionen af b\u00e5de interpreter og kode. For at opdatere versionen skal vi stoppe den gamle container og starte den nye container. Hvis noget g\u00e5r galt, kan vi stoppe den nye container og starte den gamle container.<\/p>\n<p><\/p>\n<p>Efter opbygningen af billedet vil containerne b\u00e5de i udvikling og produktion v\u00e6re identiske. Dette er s\u00e6rligt vigtigt for store installationer.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/185a1bfe257f7e3e0eecdc6e0e0d330f.jpg\" style=\"display:block;margin: 0 auto;\" \/> P\u00e5 frontend bruger vi JavaScript og NodeJS.<\/p>\n<p><\/p>\n<p>Vores seneste projekt er lavet med ReactJS. Udvikleren k\u00f8rte alle containere og udviklede ved hj\u00e6lp af hot-reload.<\/p>\n<p><\/p>\n<p>Derefter startes opgaven med at kompilere JavaScript, og den kompilerede kode leveres som statisk gennem nginx for at spare ressourcer.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4c2e28319d56d7fc81845b4133a1bfe6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Her pr\u00e6senterer jeg skemaet for vores seneste projekt.<\/p>\n<p><\/p>\n<p>Hvilke opgaver l\u00f8ste vi? Vi havde behov for at opbygge et system, som mobile enheder interagerer med. De modtager data. En af mulighederne er at sende push-notifikationer til enheden. <\/p>\n<p><\/p>\n<p>Hvilke skridt tog vi for dette?<\/p>\n<p><\/p>\n<p>Vi delte applikationen i komponenter som: administrationsdelen i JS, backend, der fungerer gennem REST-interface under Ruby on Rails. Backend interagerer med databasen. Resultatet, der genereres, leveres til klienten. Admin-delen interagerer med backend og databasen gennem REST-interface.<\/p>\n<p><\/p>\n<p>Vi havde ogs\u00e5 behov for at sende Push-notifikationer. Tidligere havde vi et projekt, hvor der blev implementeret en mekanisme til at levere meddelelser til mobile platforme. <\/p>\n<p><\/p>\n<p>Vi udviklede en s\u00e5dan ordning: operat\u00f8ren interagerer med adminpanelet fra browseren, adminpanelet interagerer med backend, og der s\u00e6ttes en opgave til at sende Push-notifikationer.<\/p>\n<p><\/p>\n<p>Push-notifikationer interagerer med en anden komponent, der er implementeret p\u00e5 NodeJS.<\/p>\n<p><\/p>\n<p>K\u00f8er opbygges, og derefter sendes notifikationerne i henhold til deres mekanisme.<\/p>\n<p><\/p>\n<p>Her er to databaser tegnet. I \u00f8jeblikket bruger vi med Docker to uafh\u00e6ngige databaser, der ikke er forbundet med hinanden, bortset fra at de har et f\u00e6lles virtuelt netv\u00e6rk, og de fysiske data gemmes i forskellige mapper p\u00e5 udviklerens maskine.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/dfde59db7e6886d2d851dcf884049318.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Det samme, men i tal. Her er genbrug af kode vigtigt.<\/p>\n<p><\/p>\n<p>Hvis vi tidligere talte om genbrug af kode i form af biblioteker, s\u00e5 genbruges vores service, der h\u00e5ndterer Push-notifikationer, her som en komplet server. Den tilbyder et API, og vores nye udvikling interagerer med det.<\/p>\n<p><\/p>\n<p>P\u00e5 det tidspunkt brugte vi version 4 af NodeJS. I dag (i 2017 \u2014 red. anm.) bruger vi version 7 af NodeJS i nye projekter. Der er ingen problemer med at tr\u00e6kke nye versioner af biblioteker ind i nye komponenter. <\/p>\n<p><\/p>\n<p>Ved behov kan vi refaktorere og opgradere NodeJS-versionen for Push-notifikationsservicen. <\/p>\n<p><\/p>\n<p>Og hvis vi kan bevare API-kompatibilitet, kan den erstattes i andre projekter, der blev brugt tidligere.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/5c58c65be078a5a1b9fc17ee2e7b4ad4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hvad er n\u00f8dvendigt for at tilf\u00f8je Docker? Vi tilf\u00f8jer en Dockerfile til vores repository, som beskriver de n\u00f8dvendige afh\u00e6ngigheder. I dette eksempel er komponenterne opdelt efter logik. Dette er et minimumss\u00e6t for backend-udvikleren.<\/p>\n<p><\/p>\n<p>N\u00e5r vi opretter et nyt projekt, opretter vi en Dockerfile, der beskriver den n\u00f8dvendige \u00f8kosystem (Python, Ruby, NodeJS). I docker-compose beskriver vi den n\u00f8dvendige afh\u00e6ngighed \u2014 databasen. Vi beskriver, at vi har brug for en database af en bestemt version og at gemme data der.<\/p>\n<p><\/p>\n<p>Vi bruger en separat tredje container med nginx til at servere statiske filer. Der er mulighed for at uploade billeder. Backend'en placerer dem i et forberedt volumen, som ogs\u00e5 er monteret i nginx containeren, som serverer statiske filer.<\/p>\n<p><\/p>\n<p>For storing the configuration of nginx and mysql, we added a Docker folder where we keep the necessary configs. When a developer clones the repository to their machine, they receive a project that is ready for local development. There\u2019s no question about which port or which settings to apply.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/e7a6b308a75cacf8ce60687a9e6081dd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Next, we have several components: admin, inform-API, push notifications.<\/p>\n<p><\/p>\n<p>To launch all of this, we created another repository called dockerized-app. Currently, we are using several repositories for each component. They simply differ logically \u2014 in GitLab, it appears as a folder, and on the developer's machine, it is a folder for a specific project. At a lower level are the components that will be unified.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/f4e2c45c8ec20fca0bac74b3a3892543.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>This is an example of the contents of dockerized-app. We also take out the Docker directory here, where we fill in the configurations required for interactions of all components. There is a README.md that gives a brief description of how to run the project.<\/p>\n<p><\/p>\n<p>Here, we applied two docker-compose files. This is done to have the option to launch stepwise. When a developer works with the core, they do not need Push notifications, so they just launch the docker-compose file, saving resources accordingly.<\/p>\n<p><\/p>\n<p>If there is a need for integration with Push notifications, then docker-compose.yaml and docker-compose-push.yaml are started.<\/p>\n<p><\/p>\n<p>Since docker-compose.yaml and docker-compose-push.yaml are in the folder, a single virtual network is automatically created.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/535019c82c72c1602176fbaf4aa5640f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Description of the components. This is a more detailed file that is responsible for gathering the components. What\u2019s notable here? We are introducing the load balancer component.<\/p>\n<p><\/p>\n<p>This is a ready-made Docker image that runs nginx and the application that listens to the Docker socket. It dynamically regenerates the nginx config as containers are started and stopped. We distribute interactions with components across third-level domain names.<\/p>\n<p><\/p>\n<p>For the Development environment, we use the domain .dev \u2014 api.informer.dev. Applications with the .dev domain are accessible on the developer's local machine.<\/p>\n<p><\/p>\n<p>Next, the configs are transferred to each project and all the projects are started simultaneously.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/bc1e11e6f1fd1c837d2c8087d04145c4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>If we were to visualize it, the client would be our browser or some tool through which we make requests to the load balancer.<\/p>\n<p><\/p>\n<p>The load balancer determines which container to contact based on the domain name.<\/p>\n<p><\/p>\n<p>Dette kan v\u00e6re nginx, der leverer JS-administrationsgr\u00e6nseflade. Det kan v\u00e6re nginx, der leverer API'er eller statiske filer, som nginx leverer som billeddownloads.<\/p>\n<p><\/p>\n<p>P\u00e5 diagrammet kan man se, at containerne er forbundet til et virtuelt netv\u00e6rk og skjult bag en proxy.<\/p>\n<p><\/p>\n<p>P\u00e5 udviklerens maskine kan man f\u00e5 adgang til containeren, hvis man kender IP-adressen, men vi bruger det grundl\u00e6ggende ikke. Behovet for direkte adgang opst\u00e5r n\u00e6sten aldrig.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/2908c31fd9d3c79f6189bdbeeb6d028f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hvilket eksempel skal man se for at dockerisere sin applikation? Efter min mening er det officielle Docker-billede til MySQL et godt eksempel.<\/p>\n<p><\/p>\n<p>Det er ret komplekst. Der er mange versioner. Men dens funktionalitet kan d\u00e6kke mange behov, der kan opst\u00e5 under den videre udvikling. Hvis du bruger tid p\u00e5 at forst\u00e5, hvordan alt det her interagerer, s\u00e5 tror jeg, at du ikke vil have problemer med at implementere det selv.<\/p>\n<p><\/p>\n<p>P\u00e5 hub.docker.com er der normalt links til github.com, hvor de r\u00e5 data er angivet, som man kan samle billedet fra selv.<\/p>\n<p><\/p>\n<p>Derudover findes der i dette repository et script kaldet docker-endpoint.sh, som er ansvarligt for den f\u00f8rste initialization og for den videre behandling ved opstart af applikationen.<\/p>\n<p><\/p>\n<p>I dette eksempel er der ogs\u00e5 mulighed for konfiguration ved hj\u00e6lp af milj\u00f8variabler. Ved at angive milj\u00f8variabler ved opstart af en enkelt container eller gennem docker-compose kan man sige, at vi skal angive en tom adgangskode for docker for root-brugeren i MySQL eller en hvilken som helst, som vi \u00f8nsker.<\/p>\n<p><\/p>\n<p>Der er mulighed for at oprette en tilf\u00e6ldig adgangskode. Vi siger, at vi har brug for en bruger, at vi skal installere en adgangskode for brugeren, og at vi skal oprette en database.<\/p>\n<p><\/p>\n<p>I vores projekter har vi lidt standardiseret Dockerfile, som er ansvarlig for initialization. Der har vi tilpasset det til vores behov og lavet en simpel udvidelse af brugerens rettigheder, som applikationen bruger. Dette gjorde det muligt senere at oprette databasen fra applikationens konsol. I Ruby-applikationer er der en kommando til at oprette, \u00e6ndre og slette databaser.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4a9b56e21819d28dd39753134d23fee4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dette er et eksempel p\u00e5, hvordan en specifik version af MySQL ser ud p\u00e5 github.com. Dockerfile kan \u00e5bnes, og man kan se, hvordan installationen foreg\u00e5r.<\/p>\n<p><\/p>\n<p>docker-endpoint.sh er et script, der fungerer som indgangspunkt. Ved den indledende initiering kr\u00e6ves der nogle forberedende skridt, som alle er inkluderet i scriptet til initiering.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4153fa46526f3d802931dd1db48b08cd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vi g\u00e5r videre til den anden del.<\/p>\n<p><\/p>\n<p>Til opbevaring af kildekoder er vi skiftet til gitlab. Det er et kraftfuldt system, der har et visuelt interface.<\/p>\n<p><\/p>\n<p>En af komponenterne i Gitlab er Gitlab CI. Det tillader at beskrive en sekvens af kommandoer, som senere vil blive brugt til at organisere systemet for kodetransport eller k\u00f8rsel af automatiserede tests.<\/p>\n<p><\/p>\n<p>Pr\u00e6sentation om Gitlab CI 2 <noindex><a rel=\"nofollow\" href=\"https:\/\/goo.gl\/uohKjI\">https:\/\/goo.gl\/uohKjI<\/a><\/noindex> \u2014 en pr\u00e6sentation fra Ruby Russia club \u2014 den er temmelig detaljeret, og m\u00e5ske vil den interessere dig.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/5d26d20500f34eeb9768e6c3d91bbefa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lad os nu se p\u00e5, hvad der kr\u00e6ves for at aktivere Gitlab CI. For at starte Gitlab CI er det tilstr\u00e6kkeligt at placere filen .gitlab-ci.yml i roden af projektet.<\/p>\n<p><\/p>\n<p>Her beskriver vi, hvad vi \u00f8nsker at udf\u00f8re i sekventielle tilstande som test og deployment.<\/p>\n<p><\/p>\n<p>Vi k\u00f8rer scripts, der direkte kalder docker-compose til at bygge vores applikation. Dette er et eksempel fra backend.<\/p>\n<p><\/p>\n<p>Derefter angiver vi, at det er n\u00f8dvendigt at k\u00f8re migrationer til \u00e6ndring af databasen og udf\u00f8re tests.<\/p>\n<p><\/p>\n<p>Hvis scriptsene k\u00f8rer korrekt og ikke returnerer en fejlkode, vil systemet derfor g\u00e5 videre til den anden fase af deployment.<\/p>\n<p><\/p>\n<p>Deploymentfasen er i \u00f8jeblikket implementeret for staging. Vi har ikke organiseret en nulventet genstart.<\/p>\n<p><\/p>\n<p>Vi slukker alle containere tvangsm\u00e6ssigt og derefter starter vi alle containere op igen, samlet i det f\u00f8rste trin under testen.<\/p>\n<p><\/p>\n<p>Vi k\u00f8rer allerede migrationer til databaserne for det aktuelle milj\u00f8, som udviklerne har skrevet.<\/p>\n<p><\/p>\n<p>Der er en anm\u00e6rkning om, at dette kun skal anvendes til master branch.<\/p>\n<p><\/p>\n<p>Ved \u00e6ndring af andre grene vil det ikke blive udf\u00f8rt.<\/p>\n<p><\/p>\n<p>Der er mulighed for at organisere udrulninger pr. branch.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/8a5b880bd9a0d4bab0b276568e2ba413.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>For at g\u00f8re dette, skal vi installere Gitlab Runner.<\/p>\n<p><\/p>\n<p>Dette er et v\u00e6rkt\u00f8j skrevet i Golang. Det er en enkelt fil, som det er s\u00e6dvanligt i Golang-verdenen, og det kr\u00e6ver ingen afh\u00e6ngigheder.<\/p>\n<p><\/p>\n<p>Ved opstart registrerer vi Gitlab Runner.<\/p>\n<p><\/p>\n<p>Vi f\u00e5r en n\u00f8gle i Gitlab's web-interface.<\/p>\n<p><\/p>\n<p>Derefter kalder vi initialiseringskommandoen i kommandoprompten.<\/p>\n<p><\/p>\n<p>Vi konfigurerer Gitlab Runner i dialogtilstand (Shell, Docker, VirtualBox, SSH)<\/p>\n<p><\/p>\n<p>Koden p\u00e5 Gitlab Runner vil blive udf\u00f8rt ved hver commit afh\u00e6ngigt af indstillingerne i .gitlab-ci.yml.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/37c8ac7499f0cfba2eb07a84083e44e1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hvordan det visuelt ser ud i Gitlab i webinterfacet. Efter at vi har tilsluttet Gitlab CI, f\u00e5r vi et flag, der viser, hvilken status bygget er i p\u00e5 nuv\u00e6rende tidspunkt.<\/p>\n<p><\/p>\n<p>Vi kan se, at der for 4 minutter siden blev lavet et commit, som har best\u00e5et alle tests uden problemer.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/33ac79c45f0651a5f8bde75092c4ac44.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vi kan se n\u00e6rmere p\u00e5 bygningerne. Her ser vi, at der allerede er g\u00e5et igennem to tilstande. Testtilstand og deployment til staging.<\/p>\n<p><\/p>\n<p>Hvis vi klikker p\u00e5 et specifikt byg, vil der v\u00e6re en konsoludgang af de kommandoer, der blev k\u00f8rt i processen if\u00f8lge .gitlab-ci.yml.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/d26dd28708c7fc8a409af4be4d48f502.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>S\u00e5dan ser historien for vores produkt ud. Vi kan se, at der har v\u00e6ret vellykkede fors\u00f8g. N\u00e5r testene fejler, g\u00e5r det ikke videre til n\u00e6ste trin, og koden p\u00e5 staging opdateres ikke.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/ee7bd81dc283c7af0c194779d4230780.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hvilke opgaver l\u00f8ste vi p\u00e5 staging, da vi implementerede docker? Vores system best\u00e5r af komponenter, og vi havde behov for kun at genstarte de opdaterede komponenter i repository i stedet for hele systemet.<\/p>\n<p><\/p>\n<p>For at g\u00f8re dette m\u00e5tte vi opdele alt i separate mapper.<\/p>\n<p><\/p>\n<p>Efter vi havde gjort dette, opstod der et problem med, at Docker-compose skaber sit eget netv\u00e6rk for hver mappe og ikke ser naboens komponenter.<\/p>\n<p><\/p>\n<p>For at omg\u00e5 dette, oprettede vi manuelt et netv\u00e6rk i Docker. I Docker-compose definerede vi, at dette projekt skulle bruge det p\u00e5g\u00e6ldende netv\u00e6rk.<\/p>\n<p><\/p>\n<p>Dermed ser hver komponent, der starter med dette netv\u00e6rk, komponenter i andre dele af systemet.<\/p>\n<p><\/p>\n<p>N\u00e6ste problem er opdelingen af staging mellem flere projekter.<\/p>\n<p><\/p>\n<p>For at det hele ser p\u00e6nt og s\u00e5 t\u00e6t p\u00e5 production som muligt, er det godt at bruge port 80 eller 443, som er almindeligt anvendt i WEB.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/f863cf1833271bf132455a70ad7d2706.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hvordan l\u00f8ste vi dette? Vi tildelte \u00e9n Gitlab Runner til alle store projekter.<\/p>\n<p><\/p>\n<p>Gitlab tillader at starte flere distribuerede Gitlab Runners, som simpelthen i vilk\u00e5rlig r\u00e6kkef\u00f8lge henter alle opgaver og k\u00f8rer dem.<\/p>\n<p><\/p>\n<p>For at undg\u00e5 kaos begr\u00e6nsede vi gruppen af vores projekter til \u00e9n Gitlab Runner, som fungerer problemfrit med vores volumen.<\/p>\n<p><\/p>\n<p>Vi har flyttet nginx-proxy til et separat opstartsscript, hvor vi har angivet netv\u00e6rkene for alle projekter. <\/p>\n<p><\/p>\n<p>Vores projekt har \u00e9t netv\u00e6rk, mens balancereren har flere netv\u00e6rk navngivet efter projekterne. Den kan proxy videre baseret p\u00e5 dom\u00e6nenavne.<\/p>\n<p><\/p>\n<p>Vi modtager anmodninger p\u00e5 dom\u00e6net p\u00e5 port 80, som ledes til en gruppe containere, der h\u00e5ndterer dette dom\u00e6ne.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/25c769f029b272a17fd06bbe60cf0ea0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hvilke andre problemer har der v\u00e6ret? Det er s\u00e5dan, at alle containere som standard k\u00f8rer som bruger root. Denne root er ikke den samme som systemets root.<\/p>\n<p><\/p>\n<p>Men hvis man logger ind i containeren, vil det v\u00e6re root, og den fil, vi opretter i denne container, f\u00e5r root-rettigheder.<\/p>\n<p><\/p>\n<p>Hvis udvikleren logger ind i containeren og udf\u00f8rer nogle kommandoer, der genererer filer, og derefter logger ud af containeren, vil han i sin arbejdsmappe have en fil, som han ikke har adgang til.<\/p>\n<p><\/p>\n<p>Hvordan kan vi l\u00f8se dette? Man kan tilf\u00f8je brugere, som vil v\u00e6re i containeren.<\/p>\n<p><\/p>\n<p>Hvilke problemer opstod, da vi tilf\u00f8jede brugeren?<\/p>\n<p><\/p>\n<p>N\u00e5r vi opretter en bruger, har vi ofte ikke matchende gruppe-ID (UID) og bruger-ID (GID).<\/p>\n<p><\/p>\n<p>For at l\u00f8se dette problem bruger vi brugere med ID 1000 i containeren.<\/p>\n<p><\/p>\n<p>I vores tilf\u00e6lde stemte det overens med, at de fleste udviklere bruger Ubuntu OS. Og i Ubuntu OS har den f\u00f8rste bruger ID 1000.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/d4911374e4953fdcd807c387efeafec3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hvad er vores planer?<\/p>\n<p><\/p>\n<p>At l\u00e6se dokumentationen om Docker igen. Projektet udvikler sig aktivt, og dokumentationen \u00e6ndrer sig. Oplysningerne, der blev f\u00e5et for to-tre m\u00e5neder siden, er allerede langsomt for\u00e6ldede. <\/p>\n<p><\/p>\n<p>Nogle af de problemer, vi har l\u00f8st, er sandsynligvis allerede l\u00f8st med standardv\u00e6rkt\u00f8jer.<\/p>\n<p><\/p>\n<p>Det ville v\u00e6re rart at komme videre og direkte g\u00e5 til orkestrering.<\/p>\n<p><\/p>\n<p>Et eksempel er den indbyggede mekanisme i Docker kaldet Docker Swarm, som leveres out-of-the-box. Man \u00f8nsker at k\u00f8re noget produktion baseret p\u00e5 Docker Swarm-teknologi.<\/p>\n<p><\/p>\n<p>At generere containere g\u00f8r arbejdet med logs besv\u00e6rligt. Lige nu er logs isolerede. De er spredt over containere. En opgave er at g\u00f8re adgangen til logs mere bekvem gennem et webinterface.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Udviklings- og testproces med Docker og Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/cff8c72dd8af7de1fa0227601bd1d69c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Kilde: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/449742\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &#171;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&#187; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u0431\u0430\u0437\u0435 Docker + Gitlab CI \u0447\u0430\u0441\u0442\u043e \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044e\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b. \u0421 \u0447\u0435\u0433\u043e \u043d\u0430\u0447\u0430\u0442\u044c? \u041a\u0430\u043a \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c? \u041a\u0430\u043a \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c? \u042d\u0442\u043e\u0442 \u0434\u043e\u043a\u043b\u0430\u0434 \u0445\u043e\u0440\u043e\u0448 \u0442\u0435\u043c, \u0447\u0442\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":24326,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-32526","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &quot;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&quot; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"da_DK\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker \u0438 Gitlab CI | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &quot;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&quot; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:47:32+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:47:32+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Udviklings- og testprocedure med Docker og Gitlab CI | ProHoster","description":"Jeg foresl\u00e5r at tage et kig p\u00e5 redeg\u00f8relsen af Alexander Siga\u010devs foredrag fra Inventos \"Udviklings- og testprocedure med Docker + Gitlab CI\". Dem der lige er begyndt at implementere udviklingsprocessen.","canonical_url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"da_DK","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker \u0438 Gitlab CI | ProHoster","og:description":"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &quot;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&quot; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438.","og:url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:47:32+00:00","article:modified_time":"2019-10-31T18:47:32+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"32526","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 11:18:24","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 02:57:22","updated":"2026-01-21 11:18:24","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/32526","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/comments?post=32526"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/32526\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media\/24326"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=32526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=32526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=32526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}