{"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\/ro\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","title":{"rendered":"Crearea unui lan\u021b CI\/CD \u0219i automatizarea lucrului cu Docker.","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Am scris primele mele site-uri la sf\u00e2r\u0219itul anilor '90. Atunci era foarte simplu s\u0103 le pui \u00een func\u021biune. Era un server Apache pe un serviciu de hosting comun, la care se putea accesa prin FTP, tast\u00e2nd \u00een bara de adrese a browserului ceva de genul <code>ftp:\/\/ftp.example.com<\/code>. Apoi, trebuia s\u0103 introduci un nume de utilizator \u0219i o parol\u0103 \u0219i s\u0103 \u00eencarci fi\u0219ierele pe server. Alte vremuri, totul era mai simplu dec\u00e2t acum.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\"><img decoding=\"async\" alt=\"Crearea unui lan\u021b CI\/CD \u0219i automatizarea lucrului cu Docker.\" src=\"\/wp-content\/uploads\/2020\/02\/0861cdd82770ef4695ffac7a09777c52.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><\/p>\n<p>\u00cen cele dou\u0103 decenii care au trecut de atunci, multe s-au schimbat. Site-urile au devenit mai complexe, iar \u00eenainte de lansarea \u00een produc\u021bie, trebuie s\u0103 fie compilate. Un singur server a devenit o mul\u021bime de servere, lucr\u00e2nd prin echilibratoare de sarcin\u0103, iar utilizarea sistemelor de control al versiunilor a devenit normal.<\/p>\n<p>Pentru proiectul meu personal, am avut o configura\u021bie special\u0103. \u0218tiam c\u0103 am nevoie de posibilitatea de a desf\u0103\u0219ura site-ul \u00een produc\u021bie, efectu\u00e2nd o singur\u0103 ac\u021biune: s\u0103 scriu codul \u00een ramura <code>master<\/code> de pe GitHub. De asemenea, \u0219tiam c\u0103 nu vreau s\u0103 gestionez un cluster imens de Kubernetes pentru a asigura func\u021bionarea micii mele aplica\u021bii web, s\u0103 utilizez Docker Swarm sau s\u0103 men\u021bin un parc de servere cu module, agen\u021bi \u0219i alte complexit\u0103\u021bi. Pentru a atinge obiectivul de a simplifica munca la maxim, a trebuit s\u0103 m\u0103 familiarizez cu CI\/CD.<\/p>\n<p>Dac\u0103 ai un proiect mic (\u00een cazul nostru, vorbim despre un proiect Node.js) \u0219i vrei s\u0103 \u00eenve\u021bi cum s\u0103 automatizezi desf\u0103\u0219urarea acestuia, asigur\u00e2ndu-te c\u0103 ceea ce se afl\u0103 \u00een depozit corespunde exact cu ceea ce este \u00een produc\u021bie, cred c\u0103 te-ar putea interesa acest articol.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Cerin\u021be preliminare<\/h2>\n<p>\nSe a\u0219teapt\u0103 ca cititorul acestui articol s\u0103 aib\u0103 cuno\u0219tin\u021be de baz\u0103 despre utilizarea liniei de comand\u0103 \u0219i scrierea scripturilor Bash. De asemenea, va avea nevoie de conturi <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex> \u0219i <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/p>\n<h2>Obiective<\/h2>\n<p>\nNu a\u0219 spune c\u0103 acest articol poate fi considerat f\u0103r\u0103 ezitare un \u201eghid didactic\u201d. Este mai degrab\u0103 un document \u00een care \u00eemp\u0103rt\u0103\u0219esc ce am \u00eenv\u0103\u021bat \u0219i descriu procesul de testare \u0219i desf\u0103\u0219urare a codului \u00een produc\u021bie, realizat \u00eentr-o singur\u0103 trecere automatizat\u0103.<\/p>\n<p>Iat\u0103 cum a rezultat, \u00een cele din urm\u0103, procesul meu de lucru.<\/p>\n<p>Pentru codul trimis \u00een orice ramur\u0103 a depozitului, cu excep\u021bia <code>master<\/code>, se efectueaz\u0103 urm\u0103toarele ac\u021biuni:<\/p>\n<ul>\n<li>Se porne\u0219te construirea proiectului pe Travis CI.<\/li>\n<li>Se efectueaz\u0103 toate testele unitare, de integrare \u0219i func\u021bionale.<\/li>\n<\/ul>\n<p>\nNumai pentru codul care ajunge \u00een <code>master<\/code>, se execut\u0103 urm\u0103toarele:<\/p>\n<ul>\n<li>Tot ceea ce s-a spus mai sus, plus\u2026<\/li>\n<li>Construirea unei imagini Docker pe baza codului, set\u0103rilor \u0219i mediului curent.<\/li>\n<li>\u00cenc\u0103rcarea imaginii pe Docker Hub.<\/li>\n<li>Conectarea la serverul de produc\u021bie.<\/li>\n<li>Desc\u0103rcarea imaginii de pe Docker Hub pe server.<\/li>\n<li>Oprirea containerului curent \u0219i pornirea unuia nou, bazat pe noua imagine.<\/li>\n<\/ul>\n<p>\nDac\u0103 nu \u0219ti\u021bi nimic despre Docker, imagini \u0219i containere \u2014 nu v\u0103 face\u021bi griji. Voi explica totul despre acestea.<\/p>\n<h2>Ce este CI\/CD?<\/h2>\n<p>\nAbrevierea CI\/CD se traduce prin 'integrare continu\u0103\/implementare continu\u0103'.<\/p>\n<h3>\u258dIntegrare continu\u0103<\/h3>\n<p>\nIntegrarea continu\u0103 este un proces \u00een care dezvoltatorii fac commit-uri \u00een depozitul principal de cod surs\u0103 al proiectului (de obicei \u00een ramura <code>master<\/code>). Calitatea codului este asigurat\u0103 prin teste automatizate.<\/p>\n<h3>\u258dImplementare continu\u0103<\/h3>\n<p>\nImplementarea continu\u0103 este desf\u0103\u0219urarea frecvent\u0103 \u0219i automatizat\u0103 a codului \u00een produc\u021bie. A doua parte a abreviaturii CI\/CD este uneori dezv\u0103luit\u0103 ca 'livrare continu\u0103'. Aceasta este, \u00een general, acela\u0219i lucru cu 'implementare continu\u0103', dar 'livrarea continu\u0103' implic\u0103 necesitatea confirm\u0103rii manuale a modific\u0103rilor \u00eenainte de a lansa procesul de desf\u0103\u0219urare a proiectului.<\/p>\n<h2>\u00cenceperea utiliz\u0103rii<\/h2>\n<p>\nAplica\u021bia pe care am \u00eenv\u0103\u021bat totul aceasta se nume\u0219te <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/taniarascia\/takenote\">TakeNote<\/a><\/noindex>. Acesta este un proiect web la care lucrez, destinat pentru a face note. La \u00eenceput am \u00eencercat s\u0103 creez un <noindex><a rel=\"nofollow\" href=\"https:\/\/jamstack.org\/\">proiect JAMStack<\/a><\/noindex>- sau doar o aplica\u021bie frontend f\u0103r\u0103 server, pentru a profita de caracteristicile standard de g\u0103zduire \u0219i desf\u0103\u0219urare a proiectelor oferite de <noindex><a rel=\"nofollow\" href=\"https:\/\/www.netlify.com\/\">Netlify<\/a><\/noindex>. Pe m\u0103sur\u0103 ce complexitatea aplica\u021biei a crescut, a trebuit s\u0103 \u00eemi creez \u0219i partea serverului, ceea ce a \u00eensemnat c\u0103 ar fi trebuit s\u0103 formulez propria strategie de integrare automatizat\u0103 \u0219i desf\u0103\u0219urare automatizat\u0103 a proiectului.<\/p>\n<p>\u00cen cazul meu, aplica\u021bia reprezint\u0103 un server Express care func\u021bioneaz\u0103 \u00een mediu Node.js, servind o aplica\u021bie React de o pagin\u0103 \u0219i sus\u021bin\u00e2nd un API server protejat. Aceast\u0103 arhitectur\u0103 urmeaz\u0103 o strategie pe care o pute\u021bi g\u0103si \u00een <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/full-stack-cookies-localstorage-react-express\/\">acest<\/a><\/noindex> ghid de autentificare full-stack.<\/p>\n<p>Am consultat <noindex><a rel=\"nofollow\" href=\"http:\/\/qualitytesting.tech\/\">alt<\/a><\/noindex>, care este expert \u00een automatizare, \u0219i l-am \u00eentrebat despre ce ar trebui s\u0103 fac pentru ca toate acestea s\u0103 func\u021bioneze a\u0219a cum \u00eemi doresc. Mi-a sugerat o idee despre cum ar trebui s\u0103 arate fluxul de lucru automatizat, prezentat \u00een sec\u021biunea \u201eObiective\u201d a acestui articol. Faptul c\u0103 mi-am propus astfel de obiective \u00eenseamn\u0103 c\u0103 trebuie s\u0103 \u00eenv\u0103\u021b cum s\u0103 folosesc Docker.<\/p>\n<h2>Docker<\/h2>\n<p>\nDocker este un instrument care, datorit\u0103 tehnologiei de containerizare, permite distribuirea u\u0219oar\u0103 a aplica\u021biilor, precum \u0219i desf\u0103\u0219urarea \u0219i rularea lor \u00een acela\u0219i mediu, chiar dac\u0103 platforma Docker func\u021bioneaz\u0103 \u00een medii diverse. Pentru \u00eenceput, am avut nevoie s\u0103 ob\u021bin instrumentele de linie de comand\u0103 (CLI) Docker. <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/install\/\">Instruc\u021biunea<\/a><\/noindex> pentru instalarea Docker nu poate fi considerat\u0103 foarte clar\u0103 \u0219i u\u0219or de \u00een\u021beles, dar din ea se poate \u00eenv\u0103\u021ba c\u0103, pentru a face primul pas \u00een instalare, trebuie s\u0103 descarci Docker Desktop (pentru Mac sau Windows).<\/p>\n<p>Docker Hub este cam acela\u0219i lucru <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/\">GitHub<\/a><\/noindex> cu repositoriile git, sau un registru <noindex><a rel=\"nofollow\" href=\"https:\/\/www.npmjs.com\/\">npm<\/a><\/noindex> pentru pachetele JavaScript. Acesta este un repository online pentru imaginile Docker. La el se conecteaz\u0103 Docker Desktop.<\/p>\n<p>A\u0219adar, pentru a \u00eencepe lucrul cu Docker, trebuie s\u0103 faci dou\u0103 lucruri:<\/p>\n<ul>\n<li>Instala\u021bi <noindex><a rel=\"nofollow\" href=\"https:\/\/www.docker.com\/get-started\">Docker Desktop<\/a><\/noindex>.<\/li>\n<li>\u00censcrie-te pe <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nDup\u0103 aceasta, po\u021bi verifica func\u021bionalitatea Docker CLI, rul\u00e2nd urm\u0103toarea comand\u0103 pentru a verifica versiunea Docker:<\/p>\n<pre><code class=\"plaintext\">docker -v<\/code><\/pre>\n<p>\nApoi, conecteaz\u0103-te la Docker Hub, introduc\u00e2nd, atunci c\u00e2nd \u021bi se cere, numele de utilizator \u0219i parola:<\/p>\n<pre><code class=\"plaintext\">docker login<\/code><\/pre>\n<p>\nPentru a folosi Docker, trebuie s\u0103 \u00een\u021belegi conceptele de imagini \u0219i containere.<\/p>\n<h3>\u258dImagini<\/h3>\n<p>\nO imagine este ca un plan, con\u021bin\u00e2nd instruc\u021biuni pentru construirea unui container. Este o captur\u0103 de ecran nemodificat\u0103 a sistemului de fi\u0219iere \u0219i a set\u0103rilor aplica\u021biei. Dezvoltatorii pot face schimb de imagini cu u\u0219urin\u021b\u0103.<\/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>\nAceast\u0103 comand\u0103 va afi\u0219a un tabel cu urm\u0103toarea antet:<\/p>\n<pre><code class=\"plaintext\">REPOSITORY \u00a0 \u00a0 TAG     IMAGE ID \u00a0 \u00a0 CREATED     SIZE\n---<\/code><\/pre>\n<p>\nMai departe, vom analiza c\u00e2teva exemple de comenzi \u00eentr-un format similar \u2014 \u00eenceputul va fi comanda cu un comentariu, iar apoi un exemplu al output-ului s\u0103u.<\/p>\n<h3>\u258dContainere<\/h3>\n<p>\nUn container este un pachet executabil care con\u021bine tot ce este necesar pentru a rula o aplica\u021bie. Astfel, aplica\u021bia va func\u021biona \u00eentotdeauna la fel, indiferent de infrastructur\u0103: \u00eentr-un mediu izolat \u0219i \u00een aceea\u0219i mediu. Este vorba despre faptul c\u0103 instan\u021bele acelea\u0219i imagini sunt lansate \u00een diferite medii.<\/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>\u258dEtichete<\/h3>\n<p>\nO etichet\u0103 este o indica\u021bie a unei versiuni specifice a imaginii.<\/p>\n<h3>\u258dInforma\u021bii generale despre comenzile Docker<\/h3>\n<p>\nIat\u0103 o privire de ansamblu asupra unor comenzi Docker frecvent utilizate.<\/p>\n<p>Comanda<\/p>\n<p>Context<\/p>\n<p>Ac\u021biune<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">docker build<\/a><\/noindex><\/p>\n<p>Tip<\/p>\n<p>Construirea imaginii din Dockerfile<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/tag\/\">docker tag<\/a><\/noindex><\/p>\n<p>Tip<\/p>\n<p>Etichetarea imaginii<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/images\/\">docker images<\/a><\/noindex><\/p>\n<p>Tip<\/p>\n<p>Lista imaginilor<\/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>Pornirea containerului pe baza imaginii<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/push\/\">docker push<\/a><\/noindex><\/p>\n<p>Tip<\/p>\n<p>Trimiterea imaginii c\u0103tre registru<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/about:blank\">docker pull<\/a><\/noindex><\/p>\n<p>Tip<\/p>\n<p>Desc\u0103rcarea imaginii din registru<\/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>Lista containerelor<\/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>Imagine\/Container<\/p>\n<p>\u0218tergerea containerelor \u0219i imaginilor neutilizate<\/p>\n<h3>\u258dFi\u0219ier Dockerfile<\/h3>\n<p>\n\u0218tiu cum s\u0103 rulez local o aplica\u021bie pentru produc\u021bie. Am o configura\u021bie Webpack destinat\u0103 construirii unei aplica\u021bii React gata de utilizare. \u00cen continuare, am o comand\u0103 care porne\u0219te un server bazat pe Node.js pe portul <code>5000<\/code>. Asta arat\u0103 a\u0219a:<\/p>\n<pre><code class=\"plaintext\">npm i \u00a0 \u00a0 \u00a0 \u00a0 # instalarea dependen\u021belor\nnpm run build # construirea aplica\u021biei React\nnpm run start # pornirea serverului Node<\/code><\/pre>\n<p>\nTrebuie men\u021bionat c\u0103 nu am o aplica\u021bie de exemplu pentru acest material. Dar, pentru experimente, orice aplica\u021bie simpl\u0103 Node va func\u021biona.<\/p>\n<p>Pentru a folosi containerul, trebuie s\u0103 oferi\u021bi instruc\u021biuni Docker. Acest lucru se realizeaz\u0103 printr-un fi\u0219ier numit <code>Dockerfile<\/code>, aflat \u00een directorul r\u0103d\u0103cin\u0103 al proiectului. Acest fi\u0219ier pare, la \u00eenceput, destul de incomprehensibil.<\/p>\n<p>Dar ceea ce con\u021bine nu este dec\u00e2t o descriere, prin comenzi specifice, a unei configura\u021bii asem\u0103n\u0103toare cu setarea unui mediu de lucru. Iat\u0103 c\u00e2teva dintre aceste comenzi:<\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#from\">FROM<\/a><\/noindex> \u2014 Aceast\u0103 comand\u0103 \u00eencepe fi\u0219ierul. Se specific\u0103 imaginea de baz\u0103 pe care se bazeaz\u0103 containerul.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#copy\">COPIE<\/a><\/noindex> \u2014 Copierea fi\u0219ierelor dintr-o surs\u0103 local\u0103 \u00een container.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#workdir\">WORKDIR<\/a><\/noindex> \u2014 Setarea directorului de lucru pentru comenzile urm\u0103toare.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#run\">comenzi RUN dup\u0103 semnifica\u021bie.<\/a><\/noindex> \u2014 Executarea comenzilor.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#expose\">EXPOSE<\/a><\/noindex> \u2014 Configurarea portului.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#entrypoint\">ENTRYPOINT<\/a><\/noindex> \u2014 Specificarea comenzii care trebuie executat\u0103.<\/li>\n<\/ul>\n<p>\n<code>Dockerfile<\/code> poate ar\u0103ta cam a\u0219a:<\/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>\n\u00cen func\u021bie de imaginea de baz\u0103 aleas\u0103, este posibil s\u0103 fie necesar s\u0103 instala\u021bi dependen\u021be suplimentare. Unele imagini de baz\u0103 (precum Node Alpine Linux) sunt proiectate pentru a fi c\u00e2t mai compacte. Ca urmare, este posibil s\u0103 lipseasc\u0103 anumite programe pe care le-a\u021bi a\u0219teptat.<\/p>\n<h3>\u258dConstruirea, etichetarea \u0219i rularea containerului<\/h3>\n<p>\nConstruirea \u0219i rularea local\u0103 a containerului \u2014 asta se \u00eent\u00e2mpl\u0103 dup\u0103 ce avem <code>Dockerfile<\/code>, sarcinile fiind destul de simple. \u00cenainte de a trimite imaginea pe Docker Hub, trebuie testat\u0103 local.<\/p>\n<h3>\u258dConstruirea<\/h3>\n<p>\nPrimul pas este s\u0103 construim <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">imagine<\/a><\/noindex>, specific\u00e2nd un nume \u0219i, op\u021bional, un tag (dac\u0103 nu se specific\u0103 un tag, sistemul va atribui automat un tag imaginii) <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>\nDup\u0103 ce aceast\u0103 comand\u0103 este executat\u0103, pute\u021bi observa cum Docker construie\u0219te imaginea.<\/p>\n<pre><code class=\"plaintext\">Trimiterea contextului de construc\u021bie c\u0103tre daemonul Docker \u00a0 2.88MB\nPasul 1\/9 : FROM node:12-alpine\n\u00a0\u2014&gt; ...executarea etapelor de construc\u021bie...\nConstruire reu\u0219it\u0103 123456789123\nEtichetare reu\u0219it\u0103 &lt;imagine&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nConstruc\u021bia poate dura c\u00e2teva minute \u2014 totul depinde de c\u00e2te dependen\u021be ave\u021bi. Dup\u0103 finalizarea construc\u021biei, pute\u021bi executa comanda <code>docker images<\/code> \u0219i s\u0103 vizualiza\u021bi descrierea noii dumneavoastr\u0103 imagini.<\/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;imagine&gt; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 latest            123456789123\u00a0 \u00a0 \u00a0 \u00a0 Acum aproximativ un minut   x.xxGB<\/code><\/pre>\n<p><\/p>\n<h3>\u258dRularea<\/h3>\n<p>\nImaginea a fost creat\u0103. Asta \u00eenseamn\u0103 c\u0103 pe baza ei se poate rula un container. Deoarece vreau s\u0103 pot accesa aplica\u021bia care ruleaz\u0103 \u00een container la adresa <code>localhost:5000<\/code>, am setat \u00een partea st\u00e2ng\u0103 a perechii <code>5000:5000<\/code> \u00een urm\u0103toarea comand\u0103. <code>5000<\/code>. \u00cen partea dreapt\u0103 se afl\u0103 portul containerului.<\/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>\nAcum, c\u00e2nd containerul este creat \u0219i pornit, pute\u021bi folosi comanda <code>docker ps<\/code> pentru a vizualiza informa\u021biile despre acest container (sau pute\u021bi folosi comanda <code>docker ps -a<\/code>, care afi\u0219eaz\u0103 informa\u021bii despre toate containerele, nu doar despre cele \u00een func\u021biune).<\/p>\n<pre><code class=\"plaintext\">ID - CONTAINER\u00a0 \u00a0 \u00a0 \u00a0 IMAGE\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 COMMAND\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 CREATED\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0STATUS\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 PORTS\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 NAMES\n987654321234\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"\/bin\/sh -c 'npm run...\" \u00a0 6 secunde \u00een urm\u0103\u00a0 \u00a0\u00cen func\u021biune de 6 secunde\u00a0 \u00a00.0.0.0:5000-&gt;5000\/tcp\u00a0 \u00a0stoic_darwin<\/code><\/pre>\n<p>\nDac\u0103 accesa\u021bi acum adresa <code>localhost:5000<\/code> \u2014 pute\u021bi vedea pagina aplica\u021biei func\u021bionale, care arat\u0103 exact ca pagina aplica\u021biei din mediul de produc\u021bie.<\/p>\n<h3>\u258dAtribuirea etichetei \u0219i publicarea<\/h3>\n<p>\nPentru a folosi una dintre imaginile create pe serverul de produc\u021bie, trebuie s\u0103 avem posibilitatea de a \u00eenc\u0103rca aceast\u0103 imagine din Docker Hub. Aceasta \u00eenseamn\u0103 c\u0103 trebuie s\u0103 cre\u0103m mai \u00eent\u00e2i un repository pe Docker Hub pentru proiect. Dup\u0103 aceea, vom avea un loc unde putem trimite imaginea. Imaginea trebuie redenumit\u0103 astfel \u00eenc\u00e2t numele s\u0103 \u00eenceap\u0103 cu numele nostru de utilizator de pe Docker Hub. Urm\u0103torul lucru trebuie s\u0103 fie numele repository-ului. La finalul numelui poate fi plasat orice tag. Mai jos este un exemplu de denumire a imaginilor conform acestei scheme.<\/p>\n<p>Acum putem construi imaginea, acord\u00e2ndu-i un nou nume \u0219i execut\u00e2nd comanda <code>docker push<\/code> pentru a o trimite \u00een repository-ul Docker Hub.<\/p>\n<pre><code class=\"plaintext\">docker build -t \\\/ : .\ndocker tag \\\/ : \\\/ :latest\ndocker push \\\/ :\n\n# \u00cen practic\u0103, aceasta ar putea ar\u0103ta, de exemplu, astfel:\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>\nDac\u0103 totul decurge bine, imaginea va fi disponibil\u0103 pe Docker Hub \u0219i va putea fi u\u0219or \u00eenc\u0103rcat\u0103 pe server sau transmis\u0103 altor dezvoltatori.<\/p>\n<h2>Urm\u0103torii pa\u0219i<\/h2>\n<p>\nP\u00e2n\u0103 \u00een prezent, ne-am asigurat c\u0103 aplica\u021bia, sub form\u0103 de container Docker, func\u021bioneaz\u0103 local. Am \u00eenc\u0103rcat containerul pe Docker Hub. Tot acest lucru \u00eenseamn\u0103 c\u0103 am avansat semnificativ c\u0103tre obiectiv. Acum trebuie s\u0103 rezolv\u0103m \u00eenc\u0103 dou\u0103 \u00eentreb\u0103ri:<\/p>\n<ul>\n<li>Configurarea instrumentului CI pentru testarea \u0219i desf\u0103\u0219urarea codului.<\/li>\n<li>Configurarea serverului de produc\u021bie astfel \u00eenc\u00e2t s\u0103 poat\u0103 \u00eenc\u0103rca \u0219i rula codul nostru.<\/li>\n<\/ul>\n<p>\n\u00cen cazul nostru, ca solu\u021bie CI\/CD se utilizeaz\u0103 <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex>. Ca server \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex>.<\/p>\n<p>Trebuie men\u021bionat c\u0103 aici se poate folosi \u0219i o alt\u0103 combina\u021bie de servicii. De exemplu, \u00een loc de Travis CI, se poate utiliza CircleCI sau Github Actions. \u0218i \u00een loc de DigitalOcean \u2014 AWS sau Linode.<\/p>\n<p>Am decis s\u0103 lucr\u0103m cu Travis CI, iar \u00een acest serviciu am deja c\u00e2teva set\u0103ri realizate. A\u0219adar, acum voi explica pe scurt cum s\u0103-l preg\u0103tim pentru utilizare.<\/p>\n<h2>Travis CI<\/h2>\n<p>\nTravis CI este un instrument pentru testarea \u0219i desf\u0103\u0219urarea codului. Nu mi-a\u0219 dori s\u0103 intru \u00een detaliile configur\u0103rii Travis CI, deoarece fiecare proiect este unic \u0219i acest lucru nu ar aduce o valoare semnificativ\u0103. Totu\u0219i, voi vorbi despre elementele de baz\u0103 care v\u0103 vor permite s\u0103 \u00eencepe\u021bi s\u0103 lucra\u021bi \u00een cazul \u00een care decide\u021bi s\u0103 utiliza\u021bi Travis CI. Indiferent ce alege\u021bi \u2014 Travis CI, CircleCI, Jenkins sau altceva, metodele de configurare vor fi similare.<\/p>\n<p>Pentru a \u00eencepe s\u0103 lucra\u021bi cu Travis CI, vizita\u021bi <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">site-ul proiectului<\/a><\/noindex> \u0219i crea\u021bi un cont. Apoi, integra\u021bi Travis CI cu contul dumneavoastr\u0103 GitHub. \u00cen timpul configur\u0103rii sistemului, va trebui s\u0103 specifica\u021bi depozitul cu care dori\u021bi s\u0103 automatiza\u021bi lucrul \u0219i s\u0103 activa\u021bi accesul la acesta. (Eu folosesc GitHub, dar sunt sigur\u0103 c\u0103 Travis CI se poate integra \u0219i cu BitBucket, \u0219i cu GitLab, \u0219i cu alte servicii similare).<\/p>\n<p>De fiecare dat\u0103 c\u00e2nd Travis CI \u00eencepe s\u0103 lucreze, se lanseaz\u0103 un server care execut\u0103 comenzile specificate \u00een fi\u0219ierul de configurare, inclusiv desf\u0103\u0219urarea ramurilor corespunz\u0103toare ale depozitului.<\/p>\n<h3>\u258dCiclul de via\u021b\u0103 al sarcinii<\/h3>\n<p>\nFi\u0219ierul de configurare Travis CI, numit <code>.travis.yml<\/code> \u0219i stocat \u00een directorul r\u0103d\u0103cin\u0103 al proiectului, sus\u021bine conceptul de evenimente <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.travis-ci.com\/user\/job-lifecycle\/\">ciclului de via\u021b\u0103<\/a><\/noindex> al sarcinii. Iat\u0103 aceste evenimente, enumerate \u00een ordinea \u00een care apar:<\/p>\n<ul>\n<li><code>apt addons<\/code><\/li>\n<li><code>cache components<\/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 sau 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>\u258dTestare<\/h3>\n<p>\n\u00cen fi\u0219ierul de configurare, inten\u021bionez s\u0103 configurez un server local Travis CI. Ca limbaj, am ales Node versiunea 12 \u0219i am indicat sistemului s\u0103 instaleze dependen\u021bele necesare pentru utilizarea Docker.<\/p>\n<p>Tot ce este enumerat \u00een <code>.travis.yml<\/code>, va fi executat la fiecare pull request pe toate ramurile depozitului, cu excep\u021bia cazului \u00een care se specific\u0103 altceva. Aceasta este o caracteristic\u0103 util\u0103, deoarece \u00eenseamn\u0103 c\u0103 putem testa tot codul care intr\u0103 \u00een depozit. Aceasta ne permite s\u0103 \u0219tim dac\u0103 codul este preg\u0103tit pentru a fi \u00eenregistrat \u00een ramur\u0103 <code>master<\/code>, \u0219i dac\u0103 nu va afecta procesul de compilare al proiectului. \u00cen aceast\u0103 configura\u021bie global\u0103, instalez totul local, pornesc serverul dezvoltatorului Webpack \u00een fundal (aceasta este o caracteristic\u0103 a fluxului meu de lucru) \u0219i execut teste.<\/p>\n<p>Dac\u0103 dori\u021bi ca \u00een depozitul dumneavoastr\u0103 s\u0103 apar\u0103 insigne cu informa\u021bii despre acoperirea codului de teste, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/display-build-status-and-test-coverage\/\">aici<\/a><\/noindex> pute\u021bi g\u0103si un ghid scurt despre utilizarea Jest, Travis CI \u0219i Coveralls pentru a colecta \u0219i a afi\u0219a aceste informa\u021bii.<\/p>\n<p>A\u0219adar, iat\u0103 con\u021binutul fi\u0219ierului <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>\nAici se \u00eencheie acele ac\u021biuni care se execut\u0103 pentru toate ramurile depozitului \u0219i pentru pull requests.<\/p>\n<h3>\u258dDesf\u0103\u0219urare<\/h3>\n<p>\nPornind de la presupunerea c\u0103 toate testele automatizate s-au finalizat cu succes, noi, ceea ce nu este obligatoriu, putem desf\u0103\u0219ura codul pe serverul de produc\u021bie. Deoarece dorim s\u0103 facem acest lucru doar pentru codul din ramur\u0103 <code>master<\/code>, oferim sistemului instruc\u021biuni corespunz\u0103toare \u00een set\u0103rile de desf\u0103\u0219urare. \u00cenainte de a \u00eencerca s\u0103 folosi\u021bi \u00een proiectul dumneavoastr\u0103 codul pe care \u00eel vom analiza mai departe, a\u0219 dori s\u0103 v\u0103 aten\u021bionez c\u0103 trebuie s\u0103 ave\u021bi un script real, apelat pentru desf\u0103\u0219urare.<\/p>\n<pre><code class=\"plaintext\">deploy:\n\u00a0\u00a0# Construie\u0219te containerul Docker \u0219i trimite-l pe 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>\nScriptul de desf\u0103\u0219urare are dou\u0103 sarcini:<\/p>\n<ul>\n<li>Construirea, etichetarea \u0219i trimiterea imaginii pe Docker Hub cu ajutorul instrumentului CI (\u00een cazul nostru, este Travis CI).<\/li>\n<li>\u00cenc\u0103rcarea imaginii pe server, oprirea containerului vechi \u0219i pornirea celui nou (\u00een cazul nostru, serverul opereaz\u0103 pe platforma DigitalOcean).<\/li>\n<\/ul>\n<p>\nMai \u00eent\u00e2i, trebuie s\u0103 configur\u0103m procesul automat de construire, etichetare \u0219i trimitere a imaginii pe Docker Hub. Totul este foarte similar cu ceea ce am f\u0103cut manual, cu excep\u021bia faptului c\u0103 avem nevoie de o strategie pentru atribuirea de etichete unice imaginilor \u0219i automatizarea autentific\u0103rii. Am avut dificult\u0103\u021bi cu unele detalii ale scriptului de desf\u0103\u0219urare, cum ar fi strategia de etichetare, autentificarea, codificarea cheilor SSH \u0219i stabilirea conexiunii SSH. Dar, din fericire, prietenul meu se descurc\u0103 foarte bine cu bash, la fel ca \u0219i cu multe alte lucruri. El m-a ajutat s\u0103 scriu acest script.<\/p>\n<p>A\u0219adar, prima parte a scriptului este trimiterea imaginii pe Docker Hub. A face acest lucru este destul de simplu. Schema de etichetare pe care am folosit-o implic\u0103 combinarea hash-ului git \u0219i etichetei git, dac\u0103 exist\u0103. Acest lucru asigur\u0103 crearea unei etichete unice \u0219i simplific\u0103 identificarea compil\u0103rii pe care se bazeaz\u0103. <code>DOCKER_USERNAME<\/code> \u0219i <code>DOCKER_PASSWORD<\/code> \u2014 sunt variabile de mediu utilizator care pot fi setate prin intermediul interfe\u021bei Travis CI. Travis CI va gestiona automat datele sensibile astfel \u00eenc\u00e2t acestea s\u0103 nu fie expuse altora.<\/p>\n<p>Iat\u0103 prima parte a scriptului <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>\nCe va fi a doua parte a scriptului depinde \u00een totalitate de ce gazd\u0103 folosi\u021bi \u0219i de modul \u00een care este organizat\u0103 conectarea la aceasta. \u00cen cazul meu, deoarece folosesc Digital Ocean, comenzile utilizate pentru conectarea la server sunt <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/digitalocean\/doctl\">doctl<\/a><\/noindex>. La lucru cu AWS se va folosi utilitarul <code>aws<\/code>, \u0219i a\u0219a mai departe.<\/p>\n<p>Configurarea func\u021bion\u0103rii serverului nu a fost foarte complicat\u0103. A\u0219adar, am configurat un droplet bazat pe imaginea de baz\u0103. Trebuie men\u021bionat c\u0103 sistemul ales de mine necesit\u0103 o instalare manual\u0103 unic\u0103 a Docker \u0219i o pornire manual\u0103 unic\u0103 a Docker. Eu am folosit Ubuntu 18.04 pentru instalarea Docker, a\u0219a c\u0103, dac\u0103 folosi\u021bi \u0219i voi Ubuntu pentru a face acela\u0219i lucru, pute\u021bi pur \u0219i simplu s\u0103 urma\u021bi <noindex><a rel=\"nofollow\" href=\"https:\/\/phoenixnap.com\/kb\/how-to-install-docker-on-ubuntu-18-04\">acestei<\/a><\/noindex> ghidul simplu.<\/p>\n<p>Nu vorbesc aici despre comenzile specifice pentru serviciu, deoarece acest aspect poate s\u0103 varieze semnificativ \u00een diferite situa\u021bii. Voi prezenta doar un plan general de ac\u021biune, realizat dup\u0103 conectarea prin SSH la serverul pe care va fi desf\u0103\u0219urat proiectul:<\/p>\n<ul>\n<li>Trebuie s\u0103 g\u0103sim containerul care ruleaz\u0103 \u00een prezent \u0219i s\u0103-l oprim.<\/li>\n<li>Apoi, trebuie s\u0103 lans\u0103m un nou container \u00een fundal.<\/li>\n<li>Va trebui s\u0103 seta\u021bi portul local al serverului la valoarea <code>80<\/code> \u2014 acest lucru va permite accesul la site la adresa de tip <code>example.com<\/code>, f\u0103r\u0103 a specifica portul, \u0219i nu s\u0103 folosi\u021bi adresa de genul <code>example.com:5000<\/code>.<\/li>\n<li>\u0218i, \u00een cele din urm\u0103, trebuie s\u0103 \u0219terge\u021bi toate containerele \u0219i imaginile vechi.<\/li>\n<\/ul>\n<p>\nIat\u0103 continuarea scriptului.<\/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>C\u00e2teva lucruri la care merit\u0103 s\u0103 fi\u021bi aten\u021bi<\/h2>\n<p>\nEste posibil ca, atunci c\u00e2nd v\u0103 conecta\u021bi la server prin SSH din Travis CI, s\u0103 vede\u021bi un avertisment care nu va permite continuarea instal\u0103rii, deoarece sistemul va a\u0219tepta o reac\u021bie din partea utilizatorului.<\/p>\n<pre><code class=\"plaintext\">Autenticitatea gazdei '&lt;hostname&gt; (&lt;IP address&gt;)' nu poate fi stabilit\u0103.\nAmprenta cheii RSA este &lt;key fingerprint&gt;.\nSunte\u021bi sigur c\u0103 dori\u021bi s\u0103 continua\u021bi conectarea (da\/nu)?<\/code><\/pre>\n<p>\nAm aflat c\u0103 cheia pe \u0219ir poate fi codificat\u0103 \u00een base64 pentru a fi p\u0103strat\u0103 \u00eentr-o form\u0103 cu care s\u0103 fie u\u0219or \u0219i sigur de lucrat. \u00cen timpul instal\u0103rii, cheia public\u0103 poate fi decodificat\u0103 \u0219i scris\u0103 \u00eentr-un fi\u0219ier <code>known_hosts<\/code> pentru a sc\u0103pa de eroarea descris\u0103 mai sus.<\/p>\n<pre><code class=\"plaintext\">echo &lt;public key&gt; | base64 # produce &lt;cheia public\u0103, codificat\u0103 \u00een base64&gt;<\/code><\/pre>\n<p>\n\u00cen practic\u0103, acest\u0103 comand\u0103 poate ar\u0103ta a\u0219a:<\/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>\nIat\u0103 cum arat\u0103 ceea ce produce \u2014 un \u0219ir \u00een codificare base64:<\/p>\n<pre><code class=\"plaintext\">123.45.67.89 ssh-rsa AABAB3NzaC1y...QExExample.com<\/code><\/pre>\n<p>\nIat\u0103 comanda men\u021bionat\u0103 anterior<\/p>\n<pre><code class=\"plaintext\">install:\n\u00a0\u00a0- echo  | base64 -d &gt;&gt; $HOME\/.ssh\/known_hosts<\/code><\/pre>\n<p>\nAceea\u0219i abordare poate fi utilizat\u0103 cu cheia privat\u0103 atunci c\u00e2nd stabili\u021bi o leg\u0103tur\u0103, deoarece este posibil s\u0103 ave\u021bi nevoie de cheia privat\u0103 pentru a accesa serverul. C\u00e2nd lucra\u021bi cu cheia, trebuie doar s\u0103 asigura\u021bi stocarea sa sigur\u0103 \u00een variabila de mediu Travis CI, iar aceasta s\u0103 nu fie printat\u0103 nic\u0103ieri.<\/p>\n<p>Un alt aspect de care ar trebui s\u0103 \u021bine\u021bi cont este c\u0103 este posibil s\u0103 trebuiasc\u0103 s\u0103 rula\u021bi \u00eentregul script de desf\u0103\u0219urare, prezentat ca un singur \u0219ir, de exemplu \u2014 folosind <code>doctl<\/code>. Acest lucru poate necesita unele eforturi suplimentare.<\/p>\n<pre><code class=\"plaintext\">doctl compute ssh  --ssh-command \"toate comenzile vor fi aici &amp;&amp; aici\"<\/code><\/pre>\n<p><\/p>\n<h2>TLS\/SSL \u0219i echilibrarea \u00eenc\u0103rc\u0103rii<\/h2>\n<p>\nDup\u0103 ce am f\u0103cut tot ce am discutat mai sus, ultima problem\u0103 \u00eent\u00e2mpinat\u0103 a fost c\u0103 serverul nu avea SSL. Deoarece folosesc un server Node.js, pentru a face <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\">func\u021biona<\/a><\/noindex> un proxy invers Nginx \u0219i Let\u2019s Encrypt, trebuie s\u0103 munce\u0219ti destul.<\/p>\n<p>Nu am vrut deloc s\u0103 fac toate aceste configura\u021bii SSL manual, a\u0219a c\u0103 am creat doar un echilibrator de \u00eenc\u0103rcare \u0219i am salvat informa\u021biile despre acesta \u00een DNS. \u00cen cazul DigitalOcean, de exemplu, crearea automat\u0103 a unui certificat auto-semnat pe echilibratorul de \u00eenc\u0103rcare este o procedur\u0103 simpl\u0103, gratuit\u0103 \u0219i rapid\u0103. Aceast\u0103 abordare are \u0219i un avantaj suplimentar, \u00een m\u0103sura \u00een care permite s\u0103 configur\u0103m foarte simplu SSL pe mai multe servere care opereaz\u0103 \u00een spatele echilibratorului de \u00eenc\u0103rcare. Aceasta face ca serverele \u00een sine s\u0103 nu trebuiasc\u0103 s\u0103 \u201ese g\u00e2ndeasc\u0103\u201d la SSL, dar s\u0103 foloseasc\u0103, de obicei, portul <code>80<\/code>. A\u0219adar, configurarea SSL pe echilibratorul de \u00eenc\u0103rcare este mult mai simpl\u0103 \u0219i mai comod\u0103 dec\u00e2t metodele alternative de configurare SSL.<\/p>\n<p>Acum se pot \u00eenchide toate porturile de pe server care accept\u0103 conexiuni externe \u2014 \u00een afar\u0103 de portul <code>80<\/code>, utilizat pentru comunica\u021bia cu echilibratorul de \u00eenc\u0103rcare, \u0219i portul <code>22<\/code> pentru SSH. Prin urmare, \u00eencercarea de a accesa direct serverul pe orice porturi, cu excep\u021bia acestor dou\u0103, va e\u0219ua.<\/p>\n<h2>Concluzii<\/h2>\n<p>\nDup\u0103 ce am realizat tot ce am descris \u00een acest material, at\u00e2t platforma Docker, c\u00e2t \u0219i conceptele de fluxuri de lucru CI\/CD nu m\u0103 mai sperie. Am reu\u0219it s\u0103 configurez un flux de integrare continu\u0103, \u00een care codul este testat \u00eenainte de a ajunge \u00een produc\u021bie \u0219i este desf\u0103\u0219urat automat pe server. Toate acestea sunt \u00eenc\u0103 relativ noi pentru mine \u0219i sunt sigur\u0103 c\u0103 exist\u0103 modalit\u0103\u021bi de a \u00eembun\u0103t\u0103\u021bi fluxul meu de lucru automatizat \u0219i de a-l face mai eficient. A\u0219adar, dac\u0103 ave\u021bi idei despre acest subiect \u2014 spune\u021bi-mi <noindex><a rel=\"nofollow\" href=\"mailto:hello@taniarascia.com\">mi se pare<\/a><\/noindex> s\u0103 \u0219tiu. Sper c\u0103 acest articol v-a fost de ajutor \u00een activit\u0103\u021bile voastre. \u00cemi doresc s\u0103 cred c\u0103, citind-l, a\u021bi \u00eenv\u0103\u021bat la fel de mult c\u00e2t am \u00eenv\u0103\u021bat eu \u00een timp ce m\u0103 familiarizam cu tot ce am men\u021bionat aici.<\/p>\n<p><b>P.S.<\/b> \u00cen materialul nostru <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace#order\">pe marketplace<\/a><\/noindex> exist\u0103 o imagine <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace\/docker#advantages\">Docker<\/a><\/noindex>, care se instaleaz\u0103 cu un singur clic. Pute\u021bi verifica func\u021bionarea containerelor pe <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/\">VPS<\/a><\/noindex>. To\u021bi clien\u021bii noi beneficiaz\u0103 de 3 zile gratuite pentru testare.<\/p>\n<p><b>Stima\u021bi cititori!<\/b> Folosi\u021bi tehnologiile CI\/CD \u00een proiectele voastre?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/#order\"><img decoding=\"async\" alt=\"Crearea unui lan\u021b CI\/CD \u0219i automatizarea lucrului cu Docker.\" src=\"\/wp-content\/uploads\/2020\/02\/3fbfa90ea56a3d9b6b89ba945eb94570.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Sursa: <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.2.1 - 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\/ro\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"ro_RO\" \/>\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\/ro\/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\udd47Crearea unui flux CI\/CD \u0219i automatizarea lucrului cu Docker | ProHoster","description":"Am scris primele mele site-uri la sf\u00e2r\u0219itul anilor '90. Atunci, aducerea lor \u00een func\u021biune era foarte simpl\u0103.","canonical_url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"ro_RO","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\/ro\/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\/ro\/wp-json\/wp\/v2\/posts\/69351","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/comments?post=69351"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/69351\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media\/69352"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media?parent=69351"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/categories?post=69351"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/tags?post=69351"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}