{"id":69351,"date":"2020-02-18T14:44:06","date_gmt":"2020-02-18T11:44:06","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker"},"modified":"2020-03-03T16:15:00","modified_gmt":"2020-03-03T13:15:00","slug":"sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","status":"publish","type":"post","link":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","title":{"rendered":"Het opzetten van een CI\/CD-pijplijn en automatisering van het werk met Docker","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ik heb mijn eerste websites aan het eind van de jaren 90 geschreven. Toen was het heel eenvoudig om ze operationeel te krijgen. Er was een Apache-server op een of andere gedeelde hosting, en je kon deze server bereiken via FTP door iets in de browserbalk in te voeren zoals <code>ftp:\/\/ftp.example.com<\/code>. Vervolgens moest je de naam en het wachtwoord invoeren en de bestanden naar de server uploaden. Het waren andere tijden, alles was toen eenvoudiger dan nu.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\"><img decoding=\"async\" alt=\"Het opzetten van een CI\/CD-pijplijn en automatisering van het werk met Docker\" src=\"\/wp-content\/uploads\/2020\/02\/0861cdd82770ef4695ffac7a09777c52.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><\/p>\n<p>In de twee decennia die sindsdien zijn verstreken, is er veel veranderd. Websites zijn ingewikkelder geworden, ze moeten voor productie worden samengesteld. E\u00e9n enkele server is veranderd in meerdere servers die achter load balancers werken, en het gebruik van versiebeheersystemen is de norm geworden.<\/p>\n<p>Voor mijn persoonlijke project had ik een speciale configuratie. En ik wist dat ik de mogelijkheid nodig had om de website in productie te brengen met slechts \u00e9\u00e9n actie: het publiceren van de code naar de branch <code>master<\/code> op GitHub. Ik wist ook dat ik, om mijn kleine webapplicatie te laten draaien, geen enorm Kubernetes-cluster wilde beheren, of Docker Swarm wilde gebruiken, of een serverpark met pods, agenten en allerlei andere complicaties wilde onderhouden. Om mijn doel van maximale vereenvoudiging te bereiken, moest ik me verdiepen in CI\/CD.<\/p>\n<p>Als je een klein project hebt (in ons geval een Node.js-project) en je wilt weten hoe je de implementatie van dit project kunt automatiseren, zodat hetgene wat in de repository staat exact overeenkomt met hetgeen dat in productie draait, dan denk ik dat je ge\u00efnteresseerd kunt zijn in dit artikel.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Vereisten<\/h2>\n<p>\nHet wordt verondersteld dat de lezer van dit artikel basiskennis heeft van het gebruik van de opdrachtregel en het schrijven van Bash-scripts. Bovendien heeft hij accounts nodig <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex> en <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/p>\n<h2>Doelen<\/h2>\n<p>\nIk zal niet zeggen dat dit artikel zonder meer als een 'handleiding' kan worden bestempeld. Het is meer een document waarin ik beschrijf wat ik heb geleerd en het proces van het testen en implementeren van code in productie, dat in \u00e9\u00e9n geautomatiseerde stap wordt uitgevoerd.<\/p>\n<p>Dit is uiteindelijk mijn werkproces geworden.<\/p>\n<p>Voor code die naar een andere branch van de repository wordt gestuurd dan <code>master<\/code>, worden de volgende acties uitgevoerd:<\/p>\n<ul>\n<li>De bouw van het project wordt gestart op Travis CI.<\/li>\n<li>Alle modulaire, integratie- en end-to-end tests worden uitgevoerd.<\/li>\n<\/ul>\n<p>\nAlleen voor code die komt in <code>master<\/code>, wordt het volgende uitgevoerd:<\/p>\n<ul>\n<li>Alles wat hierboven is gezegd, plus...<\/li>\n<li>Het opbouwen van een Docker-image op basis van de huidige code, instellingen en omgeving.<\/li>\n<li>Het plaatsen van het image op Docker Hub.<\/li>\n<li>Verbinding maken met de productie-server.<\/li>\n<li>Het ophalen van het image van Docker Hub naar de server.<\/li>\n<li>De huidige container stoppen en een nieuwe starten, gebaseerd op het nieuwe image.<\/li>\n<\/ul>\n<p>\nAls je helemaal niets weet van Docker, images en containers \u2013 maak je geen zorgen. Ik zal je er alles over vertellen.<\/p>\n<h2>Wat is CI\/CD?<\/h2>\n<p>\nDe afkorting CI\/CD staat voor \"continuous integration\/continuous deployment\" \u2013 \"continue integratie\/continue implementatie\".<\/p>\n<h3>\u258dContinue integratie<\/h3>\n<p>\nContinue integratie is het proces waarbij ontwikkelaars commits maken naar de hoofdrepository van de broncode van het project (meestal naar de branch <code>master<\/code>). Hierbij wordt de kwaliteit van de code gewaarborgd door middel van geautomatiseerde tests.<\/p>\n<h3>\u258dContinue implementatie<\/h3>\n<p>\nContinue implementatie is het frequent geautomatiseerd implementeren van code in de productie. Het tweede deel van de afkorting CI\/CD wordt soms uitgelegd als \"continuous delivery\" (\"continue levering\"). Dit is in wezen hetzelfde als \"continue implementatie\", maar \"continue levering\" betekent dat handmatige bevestiging van wijzigingen vereist is voordat het implementatieproces van het project kan starten.<\/p>\n<h2>Aan de slag<\/h2>\n<p>\nDe applicatie waarin ik dit allemaal heb geleerd, heet <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/taniarascia\/takenote\">TakeNote<\/a><\/noindex>. Dit is een webproject waaraan ik werk, bedoeld voor het maken van notities. Eerst probeerde ik een <noindex><a rel=\"nofollow\" href=\"https:\/\/jamstack.org\/\">JAMStack<\/a><\/noindex>-project of alleen een frontend-applicatie zonder server te maken, om gebruik te maken van de standaardmogelijkheden voor hosting en implementatie van projecten die worden aangeboden door <noindex><a rel=\"nofollow\" href=\"https:\/\/www.netlify.com\/\">Netlify<\/a><\/noindex>. Naarmate de complexiteit van de applicatie toenam, moest ik ook een servergedeelte maken, wat betekende dat ik mijn eigen strategie voor geautomatiseerde integratie en geautomatiseerde implementatie van het project moest ontwikkelen.<\/p>\n<p>In mijn geval is de applicatie een Express-server die draait in een Node.js-omgeving, die een single-page React-applicatie bedient en een beveiligde server-API ondersteunt. Deze architectuur volgt een strategie die je kunt vinden in <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/full-stack-cookies-localstorage-react-express\/\">dit<\/a><\/noindex> handleiding voor full-stack-authenticatie.<\/p>\n<p>Ik heb overlegd met <noindex><a rel=\"nofollow\" href=\"http:\/\/qualitytesting.tech\/\">een vriend<\/a><\/noindex>, die een expert is op het gebied van automatisering, en vroeg hem wat ik moest doen zodat alles werkte zoals ik wilde. Hij gaf me het idee van hoe de geautomatiseerde workflow eruit zou moeten zien, zoals uiteengezet in de sectie 'Doelen' van dit artikel. Het stellen van dergelijke doelen betekende dat ik moest begrijpen hoe ik Docker moest gebruiken.<\/p>\n<h2>Docker<\/h2>\n<p>\nDocker is een tool die, dankzij containerisatietechnologie, het gemakkelijk maakt om applicaties te verspreiden, evenals om ze te implementeren en uit te voeren in dezelfde omgeving, zelfs als de Docker-platform zelf in verschillende omgevingen draait. Om te beginnen had ik toegang nodig tot de Command Line Interface (CLI) tools van Docker. <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/install\/\">De instructie<\/a><\/noindex> voor het installeren van Docker is niet per se heel duidelijk of begrijpelijk, maar je kunt eruit opmaken dat om de eerste stap van de installatie te maken, je Docker Desktop (voor Mac of Windows) moet downloaden.<\/p>\n<p>Docker Hub is ongeveer hetzelfde als <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/\">GitHub<\/a><\/noindex> voor git-repositories, of een registry <noindex><a rel=\"nofollow\" href=\"https:\/\/www.npmjs.com\/\">npm<\/a><\/noindex> voor JavaScript-pakketten. Dit is een online repository voor Docker-images. Dit is waar Docker Desktop verbinding mee maakt.<\/p>\n<p>Dus om aan de slag te gaan met Docker, moet je twee dingen doen:<\/p>\n<ul>\n<li>Installeer <noindex><a rel=\"nofollow\" href=\"https:\/\/www.docker.com\/get-started\">Docker Desktop<\/a><\/noindex>.<\/li>\n<li>Registreer je op <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nDaarna kun je de werking van de Docker CLI controleren door de volgende opdracht uit te voeren om de versie van Docker te controleren:<\/p>\n<pre><code class=\"plaintext\">docker -v<\/code><\/pre>\n<p>\nVervolgens log je in op Docker Hub door, wanneer daarom gevraagd, je gebruikersnaam en wachtwoord in te voeren:<\/p>\n<pre><code class=\"plaintext\">docker login<\/code><\/pre>\n<p>\nOm Docker te gebruiken, moet je de concepten van images en containers begrijpen.<\/p>\n<h3>\u258dImages<\/h3>\n<p>\nEen image is te vergelijken met een plan dat instructies bevat voor het bouwen van een container. Het is een onveranderlijke snapshot van het bestandssysteem en de applicatie-instellingen. Ontwikkelaars kunnen gemakkelijk images uitwisselen.<\/p>\n<pre><code class=\"plaintext\"># \u0412\u044b\u0432\u043e\u0434 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u0439 \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u043e\u0431\u0440\u0430\u0437\u0430\u0445\ndocker images<\/code><\/pre>\n<p>\nDit commando zal een tabel weergeven met de volgende kop:<\/p>\n<pre><code class=\"plaintext\">REPOSITORY \u00a0 \u00a0 TAG     IMAGE ID \u00a0 \u00a0 CREATED     SIZE\n---<\/code><\/pre>\n<p>\nVervolgens bekijken we enkele voorbeelden van commando's in dezelfde indeling - eerst de opdracht met commentaar, gevolgd door een voorbeeld van de uitvoer.<\/p>\n<h3>\u258dContainers<\/h3>\n<p>\nEen container is een uitvoerbaar pakket dat alles bevat wat nodig is om een applicatie uit te voeren. Bij deze aanpak werkt de applicatie altijd op dezelfde manier, ongeacht de infrastructuur: in een ge\u00efsoleerde omgeving en in dezelfde omgeving. Het gaat erom dat in verschillende omgevingen exemplaren van dezelfde afbeelding worden uitgevoerd.<\/p>\n<pre><code class=\"plaintext\"># \u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432\ndocker ps -a\nCONTAINER ID \u00a0 \u00a0 IMAGE     COMMAND \u00a0 \u00a0 CREATED     STATUS     PORTS \u00a0 \u00a0 NAMES\n---<\/code><\/pre>\n<p><\/p>\n<h3>\u258dTags<\/h3>\n<p>\nEen tag is een aanwijzing voor een specifieke versie van een afbeelding.<\/p>\n<h3>\u258dKorte handleiding voor Docker-commando's<\/h3>\n<p>\nHier is een overzicht van enkele vaak gebruikte Docker-commando's.<\/p>\n<p>Opdracht<\/p>\n<p>Context<\/p>\n<p>Actie<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">docker build<\/a><\/noindex><\/p>\n<p>Afbeelding<\/p>\n<p>Een afbeelding bouwen vanuit een Dockerfile<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/tag\/\">docker tag<\/a><\/noindex><\/p>\n<p>Afbeelding<\/p>\n<p>Afbeelding taggen<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/images\/\">docker images<\/a><\/noindex><\/p>\n<p>Afbeelding<\/p>\n<p>Een lijst van afbeeldingen weergeven<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/run\/\">docker run<\/a><\/noindex><\/p>\n<p>Container<\/p>\n<p>Een container op basis van een afbeelding starten<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/push\/\">docker push<\/a><\/noindex><\/p>\n<p>Afbeelding<\/p>\n<p>Een afbeelding naar de containerregistry verzenden<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/about:blank\">docker pull<\/a><\/noindex><\/p>\n<p>Afbeelding<\/p>\n<p>Een afbeelding uit de containerregistry downloaden<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/ps\/\">docker ps<\/a><\/noindex><\/p>\n<p>Container<\/p>\n<p>Een lijst van containers weergeven<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/system_prune\/\">docker system prune<\/a><\/noindex><\/p>\n<p>Afbeelding\/Container<\/p>\n<p>Verwijderen van ongebruikte containers en afbeeldingen<\/p>\n<h3>\u258dDockerfile<\/h3>\n<p>\nIk weet hoe ik een applicatie lokaal kan starten voor productie. Ik heb een Webpack-configuratie die bedoeld is voor het bouwen van een kant-en-klaar React-app. Daarnaast heb ik een commando dat een server start, gebaseerd op Node.js, op poort <code>5000<\/code>. Dit ziet er als volgt uit:<\/p>\n<pre><code class=\"plaintext\">npm i \u00a0 \u00a0 \u00a0 \u00a0 # afhankelijkheden installeren\nnpm run build # React-app bouwen\nnpm run start # Node-server starten<\/code><\/pre>\n<p>\nIk moet opmerken dat ik geen voorbeeldapplicatie heb voor dit materiaal. Maar voor experimenten zal iedere eenvoudige Node-applicatie voldoen.<\/p>\n<p>Om gebruik te maken van de container moet je instructies aan Docker geven. Dit doe je met behulp van een bestand genaamd <code>Dockerfile<\/code>, dat zich in de hoofdmap van het project bevindt. Dit bestand lijkt in het begin behoorlijk verwarrend.<\/p>\n<p>Maar wat erin staat, beschrijft alleen met speciale commando's iets als het instellen van de werkomgeving. Hier zijn enkele van deze commando's:<\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#from\">FROM<\/a><\/noindex> \u2014 Dit commando start het bestand. Het geeft de basisafbeelding aan waar de container op is gebaseerd.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#copy\">COPY<\/a><\/noindex> \u2014 Bestanden kopi\u00ebren vanuit een lokale bron naar de container.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#workdir\">WORKDIR<\/a><\/noindex> \u2014 De werkdirectory instellen voor de volgende commando's.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#run\">RUN<\/a><\/noindex> \u2014 Commando's uitvoeren.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#expose\">EXPOSE<\/a><\/noindex> \u2014 Het poortnummer instellen.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#entrypoint\">ENTRYPOINT<\/a><\/noindex> \u2014 Het uitvoerende commando aangeven.<\/li>\n<\/ul>\n<p>\n<code>Dockerfile<\/code> kan er ongeveer zo uitzien:<\/p>\n<pre><code class=\"plaintext\"># \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043e\u0431\u0440\u0430\u0437\nFROM node:12-alpine\n\n# \u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e app\/\nCOPY . app\/\n\n# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c app\/ \u0432 \u0440\u043e\u043b\u0438 \u0440\u0430\u0431\u043e\u0447\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438\nWORKDIR app\/\n\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 (\u043a\u043e\u043c\u0430\u043d\u0434\u0430 npm ci \u043f\u043e\u0445\u043e\u0436\u0430 npm i, \u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0441\u0431\u043e\u0440\u043e\u043a)\nRUN npm ci --only-production\n\n# \u0421\u043e\u0431\u0440\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0435 React-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u0430\nRUN npm run build\n\n# \u041f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442\nEXPOSE 5000\n\n# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c Node-\u0441\u0435\u0440\u0432\u0435\u0440\nENTRYPOINT npm run start<\/code><\/pre>\n<p>\nAfhankelijk van de gekozen basisafbeelding moet u mogelijk extra afhankelijkheden installeren. Sommige basisafbeeldingen (zoals Node Alpine Linux) zijn ontworpen om zo compact mogelijk te zijn. Hierdoor kunnen sommige programma's ontbreken die u verwacht.<\/p>\n<h3>\u258dBouwen, taggen en uitvoeren van de container<\/h3>\n<p>\nLokale bouw en uitvoering van de container zijn, zodra we hebben <code>Dockerfile<\/code>, vrij eenvoudig. Voordat u de afbeelding naar Docker Hub verzendt, moet u deze lokaal testen.<\/p>\n<h3>\u258dBouwen<\/h3>\n<p>\nEerst moet u <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">afbeelding<\/a><\/noindex>, met een naam, en optioneel een tag (als er geen tag wordt opgegeven, wijst het systeem automatisch een tag toe aan de afbeelding <code>latest<\/code>).<\/p>\n<pre><code class=\"plaintext\"># \u0421\u0431\u043e\u0440\u043a\u0430 \u043e\u0431\u0440\u0430\u0437\u0430\ndocker build -t &lt;image&gt;:&lt;tag&gt; .<\/code><\/pre>\n<p>\nNa het uitvoeren van deze opdracht kunt u zien hoe Docker de afbeelding bouwt.<\/p>\n<pre><code class=\"plaintext\">Sending build context to Docker daemon \u00a0 2.88MB\nStep 1\/9 : FROM node:12-alpine\n\u00a0---&gt; ...uitvoering van bouwstappen...\nSuccesvol gebouwd 123456789123\nSuccesvol getagd &lt;image&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nHet bouwen kan een paar minuten duren \u2014 dit hangt ervan af hoeveel afhankelijkheden u heeft. Na voltooiing van de bouw kunt u de opdracht uitvoeren <code>docker images<\/code> en kijken naar de beschrijving van uw nieuwe afbeelding.<\/p>\n<pre><code class=\"plaintext\">REPOSITORY\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 TAG               IMAGE ID\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 CREATED              SIZE\n&lt;image&gt; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 latest            123456789123\u00a0 \u00a0 \u00a0 \u00a0 Ongeveer een minuut geleden   x.xxGB<\/code><\/pre>\n<p><\/p>\n<h3>\u258dUitvoeren<\/h3>\n<p>\nDe afbeelding is aangemaakt. Dit betekent dat u een container kunt starten op basis hiervan. Aangezien ik wil dat ik toegang heb tot de applicatie die in de container draait, op het adres <code>localhost:5000<\/code>, heb ik in de linkerhelft van de paar <code>5000:5000<\/code> in de volgende opdracht ingesteld <code>5000<\/code>. In de rechterhelft bevindt zich de poort van de container.<\/p>\n<pre><code class=\"plaintext\"># \u0417\u0430\u043f\u0443\u0441\u043a \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0440\u0442\u0430 5000 \u0438 \u043f\u043e\u0440\u0442\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 5000\ndocker run -p 5000:5000 &lt;image&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nNu de container is aangemaakt en gestart, kunt u de opdracht gebruiken <code>docker ps<\/code> om informatie over deze container te bekijken (of u kunt de opdracht gebruiken <code>docker ps -a<\/code>, die informatie over alle containers toont, niet alleen de actieve).<\/p>\n<pre><code class=\"plaintext\">CONTAINER ID        IMAGE               COMMAND                   CREATED              STATUS                     PORTS                    NAMES\n987654321234                    \"\/bin\/sh -c 'npm run\u2026\"   6 seconden geleden     Up 6 seconden             0.0.0.0:5000-&gt;5000\/tcp   stoic_darwin<\/code><\/pre>\n<p>\nAls u nu naar het adres gaat <code>localhost:5000<\/code> \u2014 kunt u de webpagina van de draaiende applicatie zien, die er precies zo uitziet als de webpagina van de applicatie die in de productieomgeving draait.<\/p>\n<h3>\u258dTag toekennen en publiceren<\/h3>\n<p>\nOm gebruik te maken van een van de gemaakte afbeeldingen op de productie-server, moeten we in staat zijn om deze afbeelding van Docker Hub te downloaden. Dit betekent dat we eerst een repository voor het project op Docker Hub moeten aanmaken. Daarna hebben we een plek waar we de afbeelding naartoe kunnen sturen. De afbeelding moet een naam krijgen die begint met onze gebruikersnaam op Docker Hub. Daarna volgt de naam van de repository. Aan het einde van de naam kan een willekeurige tag staan. Hieronder is een voorbeeld van hoe afbeeldingennamen volgens deze schema moeten worden gegeven.<\/p>\n<p>Nu kunnen we de afbeelding samenstellen met een nieuwe naam en het commando uitvoeren <code>docker push<\/code> om deze naar de Docker Hub-repository te sturen.<\/p>\n<pre><code class=\"plaintext\">docker build -t  \/: .\ndocker tag  \/:  \/:latest\ndocker push  \/:\n\n# In de praktijk kan dit er bijvoorbeeld zo uitzien:\ndocker build -t user \/app:v1.0.0 .\ndocker tag user \/app:v1.0.0 user \/app:latest\ndocker push user \/app:v1.0.0<\/code><\/pre>\n<p>\nAls alles goed gaat, is de afbeelding beschikbaar op Docker Hub en kan deze eenvoudig op de server worden gedownload of aan andere ontwikkelaars worden overgedragen.<\/p>\n<h2>Volgende stappen<\/h2>\n<p>\nTot nu toe hebben we bevestigd dat de applicatie, in de vorm van een Docker-container, lokaal werkt. We hebben de container naar Docker Hub ge\u00fcpload. Dit betekent dat we al een eind op weg zijn naar ons doel. Nu moeten we nog twee vragen oplossen:<\/p>\n<ul>\n<li>De CI-tool instellen voor het testen en uitrollen van de code.<\/li>\n<li>De productie-server instellen zodat deze onze code kan downloaden en uitvoeren.<\/li>\n<\/ul>\n<p>\nIn ons geval gebruiken we als CI\/CD-oplossing <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex>. Als server gebruiken we \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex>.<\/p>\n<p>Het is vermeldenswaard dat hier ook andere combinatie van diensten kan worden gebruikt. In plaats van Travis CI kan bijvoorbeeld CircleCI of Github Actions worden gebruikt. En in plaats van DigitalOcean \u2014 AWS of Linode.<\/p>\n<p>We hebben besloten om met Travis CI te werken, en ik heb al iets ingesteld in deze service. Daarom zal ik nu kort uitleggen hoe je het voor gebruik kunt voorbereiden.<\/p>\n<h2>Travis CI<\/h2>\n<p>\nTravis CI is een tool voor het testen en uitrollen van code. Ik wil niet in detail treden over de instellingen van Travis CI, omdat elk project uniek is en dat niet veel nut zal hebben. Maar ik zal de basisprincipes uitleggen die u in staat stellen om te beginnen als u besluit Travis CI te gebruiken. Wat u ook kiest \u2014 Travis CI, CircleCI, Jenkins, of iets anders, de methoden voor instellingen zullen vergelijkbaar zijn.<\/p>\n<p>Om te beginnen met Travis CI, ga naar <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">de projectwebsite<\/a><\/noindex> en maak een account aan. Integreer vervolgens Travis CI met uw GitHub-account. Tijdens het instellen van het systeem moet u de repository opgeven waarmee u automatisering wilt toepassen en toegang daartoe inschakelen. (Ik gebruik GitHub, maar ik ben er zeker van dat Travis CI ook kan integreren met BitBucket, GitLab en andere soortgelijke diensten).<\/p>\n<p>Elke keer wanneer Travis CI aan de slag gaat, wordt er een server gestart die de in het configuratiebestand aangegeven commando's uitvoert, inclusief het implementeren van de bijbehorende takken van de repository.<\/p>\n<h3>\u258dLevenscyclus van een taak<\/h3>\n<p>\nHet configuratiebestand van Travis CI, dat wordt genoemd <code>.travis.yml<\/code> en dat zich in de hoofdmap van het project bevindt, ondersteunt het concept van gebeurtenissen <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.travis-ci.com\/user\/job-lifecycle\/\">levenscyclus<\/a><\/noindex> van de taak. Dit zijn de gebeurtenissen, in de volgorde waarin ze plaatsvinden:<\/p>\n<ul>\n<li><code>apt addons<\/code><\/li>\n<li><code>cache componenten<\/code><\/li>\n<li><code>before_install<\/code><\/li>\n<li><code>install<\/code><\/li>\n<li><code>before_script<\/code><\/li>\n<li><code>script<\/code><\/li>\n<li><code>before_cache<\/code><\/li>\n<li><code>after_success of after_failure<\/code><\/li>\n<li><code>before_deploy<\/code><\/li>\n<li><code>deploy<\/code><\/li>\n<li><code>after_deploy<\/code><\/li>\n<li><code>after_script<\/code><\/li>\n<\/ul>\n<p><\/p>\n<h3>\u258dTesten<\/h3>\n<p>\nIn het configuratiebestand ga ik een lokale Travis CI-server instellen. Voor de taal heb ik gekozen voor Node 12 en heb ik het systeem aangegeven dat het de benodigde afhankelijkheden voor Docker moet installeren.<\/p>\n<p>Alles wat vermeld staat in <code>.travis.yml<\/code>, zal worden uitgevoerd bij het verwerken van alle pull-verzoeken naar alle takken van de repository, tenzij anders aangegeven. Dit is een nuttige functie, omdat het betekent dat we de volledige code die naar de repository komt kunnen testen. Dit helpt te weten of de code klaar is om in de branch te worden geschreven <code>master<\/code>, en of het de buildprocessen van het project zal verstoren. In deze globale configuratie installeer ik alles lokaal, start de Webpack-ontwikkelaarsserver op de achtergrond (dit is een kenmerk van mijn workflow) en voer ik de tests uit.<\/p>\n<p>Als u wilt dat er insignia's met informatie over de testcodecoverage in uw repository worden weergegeven, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/display-build-status-and-test-coverage\/\">here<\/a><\/noindex> vindt u een korte handleiding voor het gebruik van Jest, Travis CI en Coveralls voor het verzamelen en weergeven van deze informatie.<\/p>\n<p>Dus, hier is de inhoud van het bestand <code>.travis.yml<\/code>:<\/p>\n<pre><code class=\"plaintext\"># \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u044f\u0437\u044b\u043a\nlanguage: node_js\n\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e Node.js\nnode_js:\n\u00a0\u00a0- '12'\n\nservices:\n\u00a0\u00a0# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 Docker\n\u00a0\u00a0- docker\n\ninstall:\n\u00a0\u00a0# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\n\u00a0\u00a0- npm ci\n\nbefore_script:\n\u00a0\u00a0# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\n\u00a0\u00a0- npm run dev &amp;\n\nscript:\n\u00a0\u00a0# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b\n\u00a0\u00a0- npm run test<\/code><\/pre>\n<p>\nHier eindigen de handelingen die worden uitgevoerd voor alle takken van de repository en voor pull-verzoeken.<\/p>\n<h3>\u258dImplementatie<\/h3>\n<p>\nUitgaande van de veronderstelling dat alle geautomatiseerde tests succesvol zijn afgerond, kunnen we, wat niet verplicht is, de code op de productie-server implementeren. Aangezien we dit alleen willen doen voor de code uit de branch <code>master<\/code>, geven we het systeem de juiste instructies in de uitrolinstellingen. Voordat u de code die we hierna bespreken in uw project probeert te gebruiken, wil ik u waarschuwen dat u een echt script moet hebben dat wordt aangeroepen voor de uitrol.<\/p>\n<pre><code class=\"plaintext\">deploy:\n\u00a0\u00a0# Bouw de Docker-container en upload deze naar Docker Hub\n\u00a0\u00a0provider: script\n\u00a0\u00a0script: bash deploy.sh\n\u00a0\u00a0on:\n\u00a0\u00a0\u00a0\u00a0branch: master<\/code><\/pre>\n<p>\nHet uitrolscript vervult twee taken:<\/p>\n<ul>\n<li>Het bouwen, taggen en verzenden van het image naar Docker Hub met behulp van het CI-tool (in ons geval is dat Travis CI).<\/li>\n<li>Het uploaden van het image naar de server, stoppen van de oude container en starten van de nieuwe (in ons geval draait de server op het DigitalOcean-platform).<\/li>\n<\/ul>\n<p>\nEerst moet het automatische proces voor het bouwen, taggen en verzenden van het image naar Docker Hub worden ingesteld. Dit lijkt erg op wat we al handmatig hebben gedaan, met uitzondering van het feit dat we hier een strategie voor het toekennen van unieke tags aan de images en automatisering van het inloggen nodig hebben. Ik had moeite met enkele details van het uitrolscript, zoals de tagstrategie, inloggen, codering van SSH-sleutels en het tot stand brengen van de SSH-verbinding. Maar gelukkig kan mijn vriend heel goed overweg met bash, net als met veel andere dingen. Hij hielp me dit script te schrijven.<\/p>\n<p>Dus, het eerste deel van het script is het uploaden van het image naar Docker Hub. Dit is vrij eenvoudig te doen. De taggingstrategie die ik heb gebruikt, houdt in dat de git-hash en de git-tag worden gecombineerd, als deze bestaat. Dit zorgt ervoor dat er een unieke tag wordt aangemaakt en het vergemakkelijkt de identificatie van de build waarop deze is gebaseerd. <code>DOCKER_USERNAME<\/code> en <code>DOCKER_PASSWORD<\/code> \u2014 dit zijn gebruikersvariabelen die kunnen worden ingesteld via de interface van Travis CI. Travis CI verwerkt de geheimen automatisch zodat ze niet in verkeerde handen vallen.<\/p>\n<p>Dit is het eerste deel van het script. <code>deploy.sh<\/code>.<\/p>\n<pre><code class=\"plaintext\">#!\/bin\/sh\nset -e # \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u043e\u0448\u0438\u0431\u043e\u043a\n\nIMAGE=&quot;&lt;username&gt;\/&lt;repository&gt;&quot; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 # \u041e\u0431\u0440\u0430\u0437 Docker\nGIT_VERSION=$(git describe --always --abbrev --tags --long) # Git-\u0445\u044d\u0448 \u0438 \u0442\u0435\u0433\u0438\n\n# \u0421\u0431\u043e\u0440\u043a\u0430 \u0438 \u0442\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0437\u0430\ndocker build -t ${IMAGE}:${GIT_VERSION} .\ndocker tag ${IMAGE}:${GIT_VERSION} ${IMAGE}:latest\n\n# \u0412\u0445\u043e\u0434 \u0432 Docker Hub \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u043a\u0430 \u043e\u0431\u0440\u0430\u0437\u0430\necho &quot;${DOCKER_PASSWORD}&quot; | docker login -u &quot;${DOCKER_USERNAME}&quot; --password-stdin\ndocker push ${IMAGE}:${GIT_VERSION}<\/code><\/pre>\n<p>\nHoe het tweede deel van het script eruit zal zien, hangt volledig af van welke host u gebruikt en hoe de verbinding ermee is georganiseerd. In mijn geval, aangezien ik gebruik maak van Digital Ocean, worden de commando's gebruikt om verbinding te maken met de server <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/digitalocean\/doctl\">doctl<\/a><\/noindex>. Bij gebruik van Aws zal de utility <code>aws<\/code>, enzovoort, worden gebruikt.<\/p>\n<p>Het configureren van de server was niet bijzonder moeilijk. Zo heb ik een droplet ingesteld op basis van een basisafbeelding. Het is vermeldenswaard dat het door mij gekozen systeem een eenmalige handmatige installatie van Docker en een eenmalige handmatige opstart van Docker vereist. Voor de installatie van Docker gebruikte ik Ubuntu 18.04, dus als jij ook Ubuntu gebruikt, kun je gewoon volgen <noindex><a rel=\"nofollow\" href=\"https:\/\/phoenixnap.com\/kb\/how-to-install-docker-on-ubuntu-18-04\">deze<\/a><\/noindex> eenvoudige handleiding.<\/p>\n<p>Ik heb het hier niet over specifieke opdrachten voor de service, aangezien dit aspect in verschillende gevallen sterk kan vari\u00ebren. Ik geef alleen een algemeen actieplan dat wordt uitgevoerd na het inloggen via SSH op de server waarop het project zal worden uitgerold:<\/p>\n<ul>\n<li>Je moet de container vinden die momenteel draait en deze stoppen.<\/li>\n<li>Daarna moet je, op de achtergrond, een nieuwe container starten.<\/li>\n<li>Je moet de lokale serverpoort instellen op <code>80<\/code> \u2014 dit maakt het mogelijk om de site te bereiken via een adres zoals <code>example.com<\/code>, zonder de poort op te geven, in plaats van een adres zoals <code>example.com:5000<\/code>.<\/li>\n<li>En tot slot moet je alle oude containers en afbeeldingen verwijderen.<\/li>\n<\/ul>\n<p>\nHier is het vervolg van het script.<\/p>\n<pre><code class=\"plaintext\"># \u041d\u0430\u0439\u0442\u0438 ID \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\nCONTAINER_ID=$(docker ps | grep takenote | cut -d&quot; &quot; -f1)\n\n# \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0442\u0430\u0440\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439, \u043e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u0443\ndocker stop ${CONTAINER_ID}\ndocker run --restart unless-stopped -d -p 80:5000 ${IMAGE}:${GIT_VERSION}\ndocker system prune -a -f<\/code><\/pre>\n<p><\/p>\n<h2>Enkele dingen om op te letten<\/h2>\n<p>\nMisschien zie je een waarschuwing wanneer je verbinding maakt met de server via SSH vanuit Travis CI, die de installatie zal stoppen omdat het systeem op een reactie van de gebruiker wacht.<\/p>\n<pre><code class=\"plaintext\">De authenticiteit van de host ' ()' kan niet worden vastgesteld.\nRSA-sleutelvingerafdruk is .\nWeet je zeker dat je wilt doorgaan met verbinden (ja\/nee)?<\/code><\/pre>\n<p>\nIk heb ontdekt dat de string sleutel kan worden gecodeerd in base64, zodat deze in een vorm wordt opgeslagen waarmee je er gemakkelijk en veilig mee kunt werken. Tijdens de installatie kan de publieke sleutel worden gedecodeerd en in een bestand worden opgeslagen <code>known_hosts<\/code> om de hierboven beschreven fout te vermijden.<\/p>\n<pre><code class=\"plaintext\">echo  | base64 # geeft  weer<\/code><\/pre>\n<p>\nIn de praktijk kan dit commando er zo uitzien:<\/p>\n<pre><code class=\"plaintext\">echo \"123.45.67.89 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom\/BWDSU\nGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3\nPbv7kOdJ\/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK\/7XA\nt3FaoJoAsncM1Q9x5+3V0Ww68\/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw\/Pb0rwert\/En\nmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z\/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx\nNrRFi9wrf+M7Q== you@example.com\" | base64<\/code><\/pre>\n<p>\nEn zo ziet het eruit \u2014 een string in base64-codering:<\/p>\n<pre><code class=\"plaintext\">123.45.67.89 ssh-rsa AAAAB3Nza...user@example.com<\/code><\/pre>\n<p>\nDit is de hierboven genoemde opdracht<\/p>\n<pre><code class=\"plaintext\">install:\n  - echo  | base64 -d &gt;&gt; $HOME\/.ssh\/known_hosts<\/code><\/pre>\n<p>\nDezelfde benadering kan worden gebruikt met de priv\u00e9sleutel bij het tot stand brengen van de verbinding, aangezien je mogelijk de priv\u00e9sleutel nodig hebt om toegang te krijgen tot de server. Zorg ervoor dat je de sleutel veilig opslaat in de omgevingsvariabele Travis CI, zodat deze nergens wordt weergegeven.<\/p>\n<p>Een ander punt om op te letten is dat je mogelijk het hele implementatiescript als \u00e9\u00e9n regel moet uitvoeren, bijvoorbeeld door middel van <code>doctl<\/code>. Dit kan wat extra inspanning vergen.<\/p>\n<pre><code class=\"plaintext\">doctl compute ssh  --ssh-command \"alle commando's komen hier &amp;&amp;&amp; hier\"<\/code><\/pre>\n<p><\/p>\n<h2>TLS\/SSL en load balancing<\/h2>\n<p>\nNadat ik alles had gedaan wat hierboven is genoemd, was het laatste probleem dat ik tegenkwam dat de server geen SSL had. Aangezien ik een Node.js-server gebruik, om ervoor te zorgen dat <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/how-to-secure-a-containerized-node-js-application-with-nginx-let-s-encrypt-and-docker-compose\">met werken<\/a><\/noindex> reverse proxy Nginx en Let's Encrypt, is er behoorlijk wat werk mee gemoeid.<\/p>\n<p>Ik wilde deze SSL-instellingen helemaal niet handmatig uitvoeren, dus heb ik gewoon een load balancer gemaakt en de gegevens ervan in de DNS vastgelegd. In het geval van DigitalOcean bijvoorbeeld, is het cre\u00ebren van een automatisch vernieuwend zelfondertekend certificaat op de load balancer een eenvoudige, gratis en snelle procedure. Dit heeft ook het extra voordeel dat het, indien nodig, heel eenvoudig is om SSL in te stellen op meerdere servers die achter de load balancer draaien. Dit stelt de servers in staat om helemaal niet over SSL na te denken, maar het kan nog steeds normaal poort gebruiken. <code>80<\/code>Het instellen van SSL op de load balancer is dus veel eenvoudiger en handiger dan alternatieve methoden voor het instellen van SSL.<\/p>\n<p>Nu kan ik alle poorten op de server sluiten die inkomende verbindingen accepteren - behalve de poort <code>80<\/code>, die wordt gebruikt voor communicatie met de load balancer, en de poort <code>22<\/code> voor SSH. Als gevolg hiervan zal de poging om rechtstreeks verbinding te maken met de server via andere poorten dan deze twee mislukken.<\/p>\n<h2>Conclusies<\/h2>\n<p>\nNadat ik alles had gedaan waarover ik in dit artikel heb verteld, was ik niet langer bang voor het Docker-platform of de concepten van geautomatiseerde CI\/CD-ketens. Ik kon een continue integratieketen instellen, waarin de code wordt getest voordat deze in productie wordt genomen en de code automatisch op de server wordt uitgerold. Dit alles is voor mij nog relatief nieuw, en ik ben ervan overtuigd dat er manieren zijn om mijn geautomatiseerde werkproces te verbeteren en effici\u00ebnter te maken. Dus als je idee\u00ebn hierover hebt - laat het me weten. <noindex><a rel=\"nofollow\" href=\"mailto:hello@taniarascia.com\">mij<\/a><\/noindex> Ik hoop dat dit artikel je heeft geholpen in je werkzaamheden. Ik wil graag geloven dat je, door het te lezen, net zoveel hebt geleerd als ik terwijl ik alles uitzocht wat ik erin heb beschreven.<\/p>\n<p><b>P.S.<\/b> In onze <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace#order\">op de marktplaats<\/a><\/noindex> is er een afbeelding <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace\/docker#advantages\">Docker<\/a><\/noindex>, die met \u00e9\u00e9n klik kan worden ge\u00efnstalleerd. U kunt de werking van de containers testen op <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/\">VPS<\/a><\/noindex>. Alle nieuwe klanten krijgen 3 dagen gratis om uit te proberen.<\/p>\n<p><b>Geachte lezers!<\/b> Maak je gebruik van CI\/CD-technologie\u00ebn in je projecten?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/#order\"><img decoding=\"async\" alt=\"Het opzetten van een CI\/CD-pijplijn en automatisering van het werk met Docker\" src=\"\/wp-content\/uploads\/2020\/02\/3fbfa90ea56a3d9b6b89ba945eb94570.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Bron: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":69352,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-69351","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=\"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e.\" \/>\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\/nl\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"nl_NL\" \/>\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\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 CI\/CD-\u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Docker | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker\" \/>\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=\"2020-02-18T11:44:06+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-03-03T13:15:00+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\udd47Het opzetten van een CI\/CD-keten en automatisering van het werken met Docker | ProHoster","description":"Ik schreef mijn eerste websites aan het einde van de jaren '90. Toen was het heel eenvoudig om ze operationeel te krijgen.","canonical_url":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"nl_NL","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\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 CI\/CD-\u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Docker | ProHoster","og:description":"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e.","og:url":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","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":"2020-02-18T11:44:06+00:00","article:modified_time":"2020-03-03T13:15:00+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"69351","title":null,"description":null,"keywords":null,"keyphrases":{"focus":[],"additional":[]},"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":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 19:24:45","updated":"2026-08-11 12:50:24","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/69351","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/comments?post=69351"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/69351\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media\/69352"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media?parent=69351"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/categories?post=69351"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/tags?post=69351"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}