{"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\/cs\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","title":{"rendered":"Vytv\u00e1\u0159en\u00ed CI\/CD \u0159et\u011bzce a automatizace pr\u00e1ce s Dockerem.","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Sv\u016fj prvn\u00ed web jsem napsala na konci 90. let. Tehdy bylo jejich uveden\u00ed do provozu velmi jednoduch\u00e9. Byl tu server Apache na n\u011bjak\u00e9m sd\u00edlen\u00e9m hostingu, na kter\u00fd se dalo p\u0159ipojit p\u0159es FTP naps\u00e1n\u00edm n\u011b\u010deho jako <code>ftp:\/\/ftp.example.com<\/code>. Pot\u00e9 bylo nutn\u00e9 zadat jm\u00e9no a heslo a nahr\u00e1t soubory na server. Byly to jin\u00e9 \u010dasy, v\u0161echno bylo tehdy jednodu\u0161\u0161\u00ed ne\u017e dnes.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\"><img decoding=\"async\" alt=\"Vytv\u00e1\u0159en\u00ed CI\/CD \u0159et\u011bzce a automatizace pr\u00e1ce s Dockerem.\" src=\"\/wp-content\/uploads\/2020\/02\/0861cdd82770ef4695ffac7a09777c52.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><\/p>\n<p>B\u011bhem t\u011bch uplynul\u00fdch dvaceti let se toho hodn\u011b zm\u011bnilo. Weby se staly slo\u017eit\u011bj\u0161\u00edmi, p\u0159ed uveden\u00edm do produkce je pot\u0159eba je sestavit. Jeden jedin\u00fd server se stal mnoha servery pracuj\u00edc\u00edmi za load balancery, b\u011b\u017en\u00e9 je pou\u017e\u00edv\u00e1n\u00ed syst\u00e9m\u016f pro spr\u00e1vu verz\u00ed.<\/p>\n<p>Pro sv\u016fj osobn\u00ed projekt jsem m\u011bla speci\u00e1ln\u00ed konfiguraci. A v\u011bd\u011bla jsem, \u017ee pot\u0159ebuji mo\u017enost deployovat web do produkce jedin\u00fdm krokem: z\u00e1pisem k\u00f3du do v\u011btve <code>master<\/code> na GitHubu. Tak\u00e9 jsem v\u011bd\u011bla, \u017ee pro zaji\u0161t\u011bn\u00ed fungov\u00e1n\u00ed m\u00e9ho mal\u00e9ho webov\u00e9ho projektu se mi nechce zab\u00fdvat spr\u00e1vou obrovsk\u00e9ho Kubernetes klastru, pou\u017e\u00edvat technologii Docker Swarm, nebo spravovat park server\u016f s kontejnery, agenty a dal\u0161\u00edmi slo\u017eitostmi. Abych dos\u00e1hla c\u00edle maxim\u00e1ln\u00edho zjednodu\u0161en\u00ed pr\u00e1ce, musela jsem se sezn\u00e1mit s CI\/CD.<\/p>\n<p>Pokud m\u00e1te mal\u00fd projekt (v na\u0161em p\u0159\u00edpad\u011b se jedn\u00e1 o projekt Node.js) a cht\u011bli byste v\u011bd\u011bt, jak automatizovat jeho nasazen\u00ed, p\u0159i\u010dem\u017e to, co je ulo\u017eeno v repozit\u00e1\u0159i, by p\u0159esn\u011b odpov\u00eddalo tomu, co funguje v produkci, pak by v\u00e1s tento \u010dl\u00e1nek mohl zaj\u00edmat.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Po\u017eadavky<\/h2>\n<p>\nO\u010dek\u00e1v\u00e1 se, \u017ee \u010dten\u00e1\u0159 tohoto \u010dl\u00e1nku m\u00e1 z\u00e1kladn\u00ed znalosti o pr\u00e1ci s p\u0159\u00edkazov\u00fdm \u0159\u00e1dkem a psan\u00ed skript\u016f v Bash. D\u00e1le budou pot\u0159ebn\u00e9 \u00fa\u010dty <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex> a <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/p>\n<h2>C\u00edle<\/h2>\n<p>\nNehodl\u00e1m tvrdit, \u017ee tento \u010dl\u00e1nek m\u016f\u017ee b\u00fdt bezpodm\u00edne\u010dn\u011b pova\u017eov\u00e1n za \"v\u00fdukov\u00e9ho pr\u016fvodce\". Je to sp\u00ed\u0161e dokument, ve kter\u00e9m popisuji, co jsem se nau\u010dila, a zmi\u0148uji vyhovuj\u00edc\u00ed proces testov\u00e1n\u00ed a nasazov\u00e1n\u00ed k\u00f3du do produkce, kter\u00fd se prov\u00e1d\u00ed za jeden automatizovan\u00fd pr\u016fchod.<\/p>\n<p>Takto vypadal nakonec m\u016fj pracovn\u00ed proces.<\/p>\n<p>Pro k\u00f3d odeslan\u00fd do jak\u00e9koli v\u011btve repozit\u00e1\u0159e krom\u011b <code>master<\/code>, se prov\u00e1d\u011bj\u00ed n\u00e1sleduj\u00edc\u00ed akce:<\/p>\n<ul>\n<li>Spust\u00ed se sestaven\u00ed projektu na Travis CI.<\/li>\n<li>V\u0161echny modulov\u00e9, integra\u010dn\u00ed a end-to-end testy se provedou.<\/li>\n<\/ul>\n<p>\nPouze pro k\u00f3d, kter\u00fd spad\u00e1 do <code>master<\/code>, plat\u00ed n\u00e1sleduj\u00edc\u00ed:<\/p>\n<ul>\n<li>V\u0161echno, co bylo \u0159e\u010deno v\u00fd\u0161e, plus\u2026<\/li>\n<li>Vytvo\u0159en\u00ed Docker obrazu na z\u00e1klad\u011b aktu\u00e1ln\u00edho k\u00f3du, konfigurac\u00ed a prost\u0159ed\u00ed.<\/li>\n<li>Um\u00edst\u011bn\u00ed obrazu na Docker Hub.<\/li>\n<li>P\u0159ipojen\u00ed k produk\u010dn\u00edmu serveru.<\/li>\n<li>Sta\u017een\u00ed obrazu z Docker Hub na server.<\/li>\n<li>Zastaven\u00ed aktu\u00e1ln\u00edho kontejneru a spu\u0161t\u011bn\u00ed nov\u00e9ho, zalo\u017een\u00e9ho na nov\u00e9m obrazu.<\/li>\n<\/ul>\n<p>\nPokud o Dockeru, obrazech a kontejnerech nev\u00edte v\u016fbec nic \u2014 nebojte se. O v\u0161em v\u00e1m to podrobn\u011b pov\u00edm.<\/p>\n<h2>Co je CI\/CD?<\/h2>\n<p>\nZkratka CI\/CD znamen\u00e1 'continuous integration\/continuous deployment' \u2014 'pravideln\u00e1 integrace\/pravideln\u00e9 nasazen\u00ed'.<\/p>\n<h3>\u258dPravideln\u00e1 integrace<\/h3>\n<p>\nPravideln\u00e1 integrace je proces, p\u0159i kter\u00e9m v\u00fdvoj\u00e1\u0159i prov\u00e1d\u011bj\u00ed commity do hlavn\u00edho \u00falo\u017ei\u0161t\u011b zdrojov\u00e9ho k\u00f3du projektu (obvykle do v\u011btve <code>master<\/code>). Kvalita k\u00f3du je zaji\u0161t\u011bna prov\u00e1d\u011bn\u00edm automatizovan\u00fdch test\u016f.<\/p>\n<h3>\u258dPravideln\u00e9 nasazen\u00ed<\/h3>\n<p>\nPravideln\u00e9 nasazen\u00ed je \u010dast\u00e9 automatizovan\u00e9 nasazen\u00ed k\u00f3du do produkce. Druh\u00e1 \u010d\u00e1st zkratky CI\/CD se n\u011bkdy rozv\u00e1d\u00ed jako 'continuous delivery' ('pravideln\u00e1 dod\u00e1vka'). To je v podstat\u011b tot\u00e9\u017e jako 'pravideln\u00e9 nasazen\u00ed', ale 'pravideln\u00e1 dod\u00e1vka' implikuje pot\u0159ebu ru\u010dn\u00edho potvrzen\u00ed zm\u011bn p\u0159ed spu\u0161t\u011bn\u00edm procesu nasazen\u00ed projektu.<\/p>\n<h2>Za\u010d\u00edn\u00e1me<\/h2>\n<p>\nAplikace, na kter\u00e9 jsem to v\u0161echno osvojila, se jmenuje <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/taniarascia\/takenote\">TakeNote<\/a><\/noindex>. Je to webov\u00fd projekt, na kter\u00e9m pracuji, ur\u010den\u00fd k prov\u00e1d\u011bn\u00ed pozn\u00e1mek. Nejprve jsem se pokusila vytvo\u0159it <noindex><a rel=\"nofollow\" href=\"https:\/\/jamstack.org\/\">JAMStack<\/a><\/noindex>-projekt, nebo pouze frontendovou aplikaci bez serveru, abych vyu\u017eila standardn\u00ed mo\u017enosti hostingu a nasazen\u00ed projekt\u016f, kter\u00e9 nab\u00edz\u00ed <noindex><a rel=\"nofollow\" href=\"https:\/\/www.netlify.com\/\">Netlify<\/a><\/noindex>. Jak se slo\u017eitost aplikace zvy\u0161ovala, pot\u0159ebovala jsem vytvo\u0159it i jej\u00ed serverovou \u010d\u00e1st, co\u017e znamenalo, \u017ee jsem musela vytvo\u0159it vlastn\u00ed strategii pro automatizovanou integraci a automatizovan\u00e9 nasazen\u00ed projektu.<\/p>\n<p>V m\u00e9m p\u0159\u00edpad\u011b aplikace p\u0159edstavuje Express server b\u011b\u017e\u00edc\u00ed ve prost\u0159ed\u00ed Node.js, obsluhuj\u00edc\u00ed jednopageovou React aplikaci a podporuj\u00edc\u00ed zabezpe\u010den\u00e9 serverov\u00e9 API. Tato architektura n\u00e1sleduje strategii, kterou lze nal\u00e9zt v <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/full-stack-cookies-localstorage-react-express\/\">t\u00e9to<\/a><\/noindex> p\u0159\u00edru\u010dce pro full-stack autentizaci.<\/p>\n<p>Pora\u010fila jsem se s <noindex><a rel=\"nofollow\" href=\"http:\/\/qualitytesting.tech\/\">p\u0159\u00edtelem<\/a><\/noindex>, kter\u00fd je odborn\u00edkem na automatizaci, a zeptala jsem se ho, co mus\u00edm ud\u011blat, aby to v\u0161echno fungovalo, jak pot\u0159ebuji. Navrhl mi my\u0161lenku, jak by m\u011bl vypadat automatizovan\u00fd pracovn\u00ed proces, jak je uvedeno v \u010d\u00e1sti \u201eC\u00edle\u201c tohoto \u010dl\u00e1nku. To, \u017ee jsem si stanovila podobn\u00e9 c\u00edle, znamenalo, \u017ee se mus\u00edm nau\u010dit pou\u017e\u00edvat Docker.<\/p>\n<h2>Docker<\/h2>\n<p>\nDocker je n\u00e1stroj, kter\u00fd d\u00edky technologii kontejnerizace umo\u017e\u0148uje snadno distribuovat aplikace, stejn\u011b jako je nasazovat a spou\u0161t\u011bt v jednom a tom sam\u00e9m prost\u0159ed\u00ed, i kdy\u017e samotn\u00e1 platforma Docker funguje v r\u016fzn\u00fdch prost\u0159ed\u00edch. Nejd\u0159\u00edve jsem pot\u0159ebovala z\u00edskat sv\u00e9 n\u00e1stroje p\u0159\u00edkazov\u00e9 \u0159\u00e1dky (CLI) Docker. <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/install\/\">N\u00e1vod<\/a><\/noindex> na instalaci Dockeru nelze nazvat p\u0159\u00edli\u0161 jasn\u00fdm a srozumiteln\u00fdm, ale lze se z n\u011bj dozv\u011bd\u011bt, \u017ee abych provedla prvn\u00ed krok instalace, mus\u00edm st\u00e1hnout Docker Desktop (pro Mac nebo Windows).<\/p>\n<p>Docker Hub je zhruba to sam\u00e9, co <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/\">GitHub<\/a><\/noindex> pro git repozit\u00e1\u0159e, neboli registr <noindex><a rel=\"nofollow\" href=\"https:\/\/www.npmjs.com\/\">npm<\/a><\/noindex> pro JavaScript bal\u00ed\u010dky. Je to online repozit\u00e1\u0159 pro obrazy Dockeru. Pr\u00e1v\u011b do n\u011bj se p\u0159ipojuje Docker Desktop.<\/p>\n<p>Tak\u017ee abych za\u010dala pracovat s Dockerem, mus\u00edm ud\u011blat dv\u011b v\u011bci:<\/p>\n<ul>\n<li>Nainstalujte <noindex><a rel=\"nofollow\" href=\"https:\/\/www.docker.com\/get-started\">Docker Desktop<\/a><\/noindex>.<\/li>\n<li>Zaregistrujte se na <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nPot\u00e9 m\u016f\u017eete zkontrolovat funk\u010dnost Docker CLI, spu\u0161t\u011bn\u00edm n\u00e1sleduj\u00edc\u00edho p\u0159\u00edkazu pro kontrolu verze Dockeru:<\/p>\n<pre><code class=\"plaintext\">docker -v<\/code><\/pre>\n<p>\nPot\u00e9 se p\u0159ihlaste do Docker Hubu t\u00edm, \u017ee, a\u017e v\u00e1s o to po\u017e\u00e1daj\u00ed, zad\u00e1te sv\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no a heslo:<\/p>\n<pre><code class=\"plaintext\">docker login<\/code><\/pre>\n<p>\nAbych mohla pou\u017e\u00edvat Docker, mus\u00edm ch\u00e1pat koncepty obraz\u016f a kontejner\u016f.<\/p>\n<h3>\u258dObrazy<\/h3>\n<p>\nObraz je jak\u00fdsi pl\u00e1n obsahuj\u00edc\u00ed pokyny pro sestaven\u00ed kontejneru. Je to nem\u011bnn\u00fd sn\u00edmek souborov\u00e9ho syst\u00e9mu a nastaven\u00ed aplikace. V\u00fdvoj\u00e1\u0159i si mohou snadno vym\u011b\u0148ovat obrazy.<\/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>\nTento p\u0159\u00edkaz zobraz\u00ed tabulku s n\u00e1sleduj\u00edc\u00edm nadpisem:<\/p>\n<pre><code class=\"plaintext\">REPOSITORY \u00a0 \u00a0 TAG     IMAGE ID \u00a0 \u00a0 CREATED     SIZE\n---<\/code><\/pre>\n<p>\nD\u00e1le budeme zva\u017eovat n\u011bkter\u00e9 p\u0159\u00edklady p\u0159\u00edkaz\u016f ve stejn\u00e9m form\u00e1tu \u2014 nejprve jde p\u0159\u00edkaz s koment\u00e1\u0159em a pak \u2014 p\u0159\u00edklad toho, co m\u016f\u017ee vyprodukovat.<\/p>\n<h3>\u258dKontejnery<\/h3>\n<p>\nKontejner je exeku\u010dn\u00ed bal\u00ed\u010dek, kter\u00fd obsahuje v\u0161e pot\u0159ebn\u00e9 k b\u011bhu aplikace. Aplikace v tomto p\u0159\u00edstupu bude v\u017edy fungovat stejn\u011b, bez ohledu na infrastrukturu: v izolovan\u00e9m prost\u0159ed\u00ed a ve stejn\u00e9 prost\u0159ed\u00ed. Jde o to, \u017ee v r\u016fzn\u00fdch prost\u0159ed\u00edch b\u011b\u017e\u00ed instance jedn\u00e9 a t\u00e9\u017ee obrazce.<\/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>\u258dTagy<\/h3>\n<p>\nTag je ozna\u010den\u00ed pro konkr\u00e9tn\u00ed verzi obrazu.<\/p>\n<h3>\u258dStru\u010dn\u00fd p\u0159ehled p\u0159\u00edkaz\u016f Docker<\/h3>\n<p>\nZde je p\u0159ehled n\u011bkter\u00fdch \u010dasto pou\u017e\u00edvan\u00fdch p\u0159\u00edkaz\u016f Docker.<\/p>\n<p>T\u00fdm<\/p>\n<p>Kontext<\/p>\n<p>\u010cinnost<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">docker build<\/a><\/noindex><\/p>\n<p>Obraz<\/p>\n<p>Sestaven\u00ed obrazu z Dockerfile<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/tag\/\">docker tag<\/a><\/noindex><\/p>\n<p>Obraz<\/p>\n<p>Ozna\u010den\u00ed obrazu<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/images\/\">docker images<\/a><\/noindex><\/p>\n<p>Obraz<\/p>\n<p>V\u00fdstup seznamu obraz\u016f<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/run\/\">docker run<\/a><\/noindex><\/p>\n<p>Kontejner<\/p>\n<p>Spu\u0161t\u011bn\u00ed kontejneru na z\u00e1klad\u011b obrazu<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/push\/\">docker push<\/a><\/noindex><\/p>\n<p>Obraz<\/p>\n<p>Odesl\u00e1n\u00ed obrazu do registru<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/about:blank\">docker pull<\/a><\/noindex><\/p>\n<p>Obraz<\/p>\n<p>Sta\u017een\u00ed obrazu z registru<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/ps\/\">docker ps<\/a><\/noindex><\/p>\n<p>Kontejner<\/p>\n<p>V\u00fdstup seznamu kontejner\u016f<\/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>Obraz\/Kontejner<\/p>\n<p>Odstran\u011bn\u00ed nepou\u017e\u00edvan\u00fdch kontejner\u016f a obraz\u016f<\/p>\n<h3>\u258dSoubor Dockerfile<\/h3>\n<p>\nV\u00edm, jak lok\u00e1ln\u011b spustit aplikaci pro produk\u010dn\u00ed prost\u0159ed\u00ed. M\u00e1m konfiguraci Webpack, ur\u010denou k sestaven\u00ed hotov\u00e9 React aplikace. D\u00e1le m\u00e1m p\u0159\u00edkaz, kter\u00fd spou\u0161t\u00ed server zalo\u017een\u00fd na Node.js na portu <code>5000<\/code>. Vypad\u00e1 to takto:<\/p>\n<pre><code class=\"plaintext\">npm i         # instalace z\u00e1vislost\u00ed\nnpm run build # sestaven\u00ed React aplikace\nnpm run start  # spu\u0161t\u011bn\u00ed Node serveru<\/code><\/pre>\n<p>\nJe t\u0159eba poznamenat, \u017ee nem\u00e1m aplika\u010dn\u00ed p\u0159\u00edklad pro tento materi\u00e1l. Ale zde, pro experimenty, by vyhovoval jak\u00fdkoli jednoduch\u00fd Node.js aplikace.<\/p>\n<p>Abychom mohli vyu\u017e\u00edt kontejner, mus\u00edte d\u00e1t instrukce Dockeru. To se d\u011bje prost\u0159ednictv\u00edm souboru, kter\u00fd se naz\u00fdv\u00e1 <code>Dockerfile<\/code>, nach\u00e1zej\u00edc\u00edho se v ko\u0159enov\u00e9m adres\u00e1\u0159i projektu. Tento soubor se zpo\u010d\u00e1tku zd\u00e1 b\u00fdt pon\u011bkud nep\u0159ehledn\u00fd.<\/p>\n<p>Ale to, co obsahuje, pouze popisuje, pomoc\u00ed zvl\u00e1\u0161tn\u00edch p\u0159\u00edkaz\u016f, n\u011bco podobn\u00e9ho nastaven\u00ed pracovn\u00edho prost\u0159ed\u00ed. Zde jsou n\u011bkter\u00e9 z t\u011bchto p\u0159\u00edkaz\u016f:<\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#from\">FROM<\/a><\/noindex> \u2014 Tento p\u0159\u00edkaz za\u010d\u00edn\u00e1 soubor. Uv\u00e1d\u00ed z\u00e1kladn\u00ed obraz, na jeho\u017e z\u00e1klad\u011b se kontejner stav\u00ed.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#copy\">COPY<\/a><\/noindex> \u2014 Kop\u00edrov\u00e1n\u00ed soubor\u016f z m\u00edstn\u00edho zdroje do kontejneru.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#workdir\">WORKDIR<\/a><\/noindex> \u2014 Nastaven\u00ed pracovn\u00edho adres\u00e1\u0159e pro n\u00e1sleduj\u00edc\u00ed p\u0159\u00edkazy.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#run\">RUN<\/a><\/noindex> \u2014 Spu\u0161t\u011bn\u00ed p\u0159\u00edkaz\u016f.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#expose\">EXPOSE<\/a><\/noindex> \u2014 Nastaven\u00ed portu.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#entrypoint\">ENTRYPOINT<\/a><\/noindex> \u2014 Ur\u010den\u00ed vykon\u00e1van\u00e9ho p\u0159\u00edkazu.<\/li>\n<\/ul>\n<p>\n<code>Dockerfile<\/code> m\u016f\u017ee vypadat p\u0159ibli\u017en\u011b takto:<\/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>\nV z\u00e1vislosti na vybran\u00e9m z\u00e1kladn\u00edm obraze m\u016f\u017ee b\u00fdt nutn\u00e9 nainstalovat dal\u0161\u00ed z\u00e1vislosti. N\u011bkter\u00e9 z\u00e1kladn\u00ed obrazy (jako Node Alpine Linux) byly vytvo\u0159eny s c\u00edlem co nejv\u00edce zmen\u0161it jejich velikost. V d\u016fsledku toho mohou chyb\u011bt n\u011bkter\u00e9 programy, na kter\u00e9 spol\u00e9h\u00e1te.<\/p>\n<h3>\u258dSestaven\u00ed, ozna\u010den\u00ed a spu\u0161t\u011bn\u00ed kontejneru<\/h3>\n<p>\nLok\u00e1ln\u00ed sestaven\u00ed a spu\u0161t\u011bn\u00ed kontejneru \u2014 to je, jakmile m\u00e1me <code>Dockerfile<\/code>, \u00fakoly jsou docela jednoduch\u00e9. Ne\u017e po\u0161lete obraz na Docker Hub, mus\u00edte ho nejprve otestovat lok\u00e1ln\u011b.<\/p>\n<h3>\u258dSestaven\u00ed<\/h3>\n<p>\nNejprve mus\u00edte sestavit <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">obraz<\/a><\/noindex>, ur\u010dete n\u00e1zev a, pokud je to pot\u0159eba, tak\u00e9 ozna\u010den\u00ed (pokud ozna\u010den\u00ed nezad\u00e1te, syst\u00e9m automaticky p\u0159i\u0159ad\u00ed obrazu ozna\u010den\u00ed <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>\nPo proveden\u00ed tohoto p\u0159\u00edkazu m\u016f\u017eete sledovat, jak Docker prov\u00e1d\u00ed sestaven\u00ed obrazu.<\/p>\n<pre><code class=\"plaintext\">Odes\u00edl\u00e1n\u00ed kontextu sestaven\u00ed na Docker daemon \u00a0 2.88MB\nKrok 1\/9 : Z\u00c1KLAD node:12-alpine\n\u00a0---&gt; ...spou\u0161t\u011bn\u00ed krok\u016f sestaven\u00ed...\n\u00dasp\u011b\u0161n\u011b sestaveno 123456789123\n\u00dasp\u011b\u0161n\u011b ozna\u010deno &lt;image&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nSestaven\u00ed m\u016f\u017ee trvat n\u011bkolik minut \u2014 to z\u00e1vis\u00ed na tom, kolik m\u00e1te z\u00e1vislost\u00ed. Po dokon\u010den\u00ed sestaven\u00ed m\u016f\u017eete spustit p\u0159\u00edkaz <code>docker images<\/code> a pod\u00edvat se na popis sv\u00e9ho nov\u00e9ho obrazu.<\/p>\n<pre><code class=\"plaintext\">REPOSIT\u00c1\u0158\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 TAG               ID OBRAZU\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 VYTVO\u0158ENO              VELIKOST\n&lt;image&gt; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 latest            123456789123\u00a0 \u00a0 \u00a0 \u00a0 P\u0159ed asi minutou   x.xxGB<\/code><\/pre>\n<p><\/p>\n<h3>\u258dSpu\u0161t\u011bn\u00ed<\/h3>\n<p>\nObraz byl vytvo\u0159en. To znamen\u00e1, \u017ee na jeho z\u00e1klad\u011b lze spustit kontejner. Jeliko\u017e chci m\u00edt mo\u017enost p\u0159istupovat k aplikaci b\u011b\u017e\u00edc\u00ed v kontejneru na adrese <code>localhost:5000<\/code>, v lev\u00e9 \u010d\u00e1sti p\u00e1ru <code>5000:5000<\/code> v n\u00e1sleduj\u00edc\u00edm p\u0159\u00edkazu jsem nastavila <code>5000<\/code>. V prav\u00e9 \u010d\u00e1sti je port kontejneru.<\/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>\nNyn\u00ed, kdy\u017e je kontejner vytvo\u0159en a spu\u0161t\u011bn, m\u016f\u017eete pou\u017e\u00edt p\u0159\u00edkaz <code>docker ps<\/code> abyste se pod\u00edvali na informace o tomto kontejneru (nebo m\u016f\u017eete pou\u017e\u00edt p\u0159\u00edkaz <code>docker ps -a<\/code>, kter\u00fd zobrazuje informace o v\u0161ech kontejnerech, nikoli pouze o b\u011b\u017e\u00edc\u00edch).<\/p>\n<pre><code class=\"plaintext\">ID KONTEJNERU        OBRAZ               P\u0158\u00cdKAZ                     VYTVO\u0158ENO              STAV                       PORTY                    JM\u00c9NA\n987654321234                     \"\/bin\/sh -c 'npm run\u2026\"   p\u0159ed 6 sekundami        Aktivn\u00ed 6 sekund             0.0.0.0:5000-&gt;5000\/tcp   stoic_darwin<\/code><\/pre>\n<p>\nPokud nyn\u00ed p\u0159ejdete na adresu <code>localhost:5000<\/code> \u2014 m\u016f\u017eete vid\u011bt str\u00e1nku b\u011b\u017e\u00edc\u00ed aplikace, kter\u00e1 vypad\u00e1 \u00fapln\u011b stejn\u011b jako str\u00e1nka aplikace b\u011b\u017e\u00edc\u00ed v produk\u010dn\u00edm prost\u0159ed\u00ed.<\/p>\n<h3>\u258dP\u0159i\u0159azen\u00ed ozna\u010den\u00ed a publikace<\/h3>\n<p>\nAby skorzysta\u0107 z jednego z utworzonych obraz\u00f3w na serwerze produkcyjnym, musimy mie\u0107 mo\u017cliwo\u015b\u0107 za\u0142adowania tego obrazu z Docker Hub. Oznacza to, \u017ce najpierw musimy utworzy\u0107 na Docker Hub repozytorium dla projektu. Po tym b\u0119dziemy mie\u0107 miejsce, w kt\u00f3re mo\u017cemy wys\u0142a\u0107 obraz. Nale\u017cy zmieni\u0107 nazw\u0119 obrazu, tak aby zaczyna\u0142a si\u0119 od naszej nazwy u\u017cytkownika na Docker Hub. Nast\u0119pnie powinna by\u0107 podana nazwa repozytorium. Na ko\u0144cu nazwy mo\u017ce znajdowa\u0107 si\u0119 dowolny tag. Poni\u017cej przedstawiono przyk\u0142ad nazewnictwa obraz\u00f3w wed\u0142ug tej schemy.<\/p>\n<p>Teraz mo\u017cemy zbudowa\u0107 obraz, nadaj\u0105c mu now\u0105 nazw\u0119 i wykona\u0107 polecenie <code>docker push<\/code> aby wys\u0142a\u0107 go do repozytorium Docker Hub.<\/p>\n<pre><code class=\"plaintext\">docker build -t \/: .\ndocker tag \/: \/:latest\ndocker push \/:\n\n# W praktyce mo\u017ce to wygl\u0105da\u0107 na przyk\u0142ad tak:\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>\nJe\u015bli wszystko p\u00f3jdzie zgodnie z planem, obraz b\u0119dzie dost\u0119pny na Docker Hub i mo\u017cna go \u0142atwo za\u0142adowa\u0107 na serwer lub przekaza\u0107 innym programistom.<\/p>\n<h2>Dal\u0161\u00ed kroky<\/h2>\n<p>\nDo tej pory upewnili\u015bmy si\u0119, \u017ce aplikacja w postaci kontenera Docker dzia\u0142a lokalnie. Za\u0142adowali\u015bmy kontener do Docker Hub. To wszystko oznacza, \u017ce jeste\u015bmy ju\u017c na bardzo dobrej drodze do celu. Teraz musimy rozwi\u0105za\u0107 jeszcze dwa pytania:<\/p>\n<ul>\n<li>Konfiguracja narz\u0119dzia CI do testowania i wdra\u017cania kodu.<\/li>\n<li>Konfiguracja serwera produkcyjnego tak, aby m\u00f3g\u0142 pobiera\u0107 i uruchamia\u0107 nasz kod.<\/li>\n<\/ul>\n<p>\nW naszym przypadku jako rozwi\u0105zania CI\/CD u\u017cywamy <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex>. Jako serwera \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex>.<\/p>\n<p>Nale\u017cy zauwa\u017cy\u0107, \u017ce mo\u017cna skorzysta\u0107 z innej kombinacji us\u0142ug. Na przyk\u0142ad zamiast Travis CI mo\u017cna u\u017cy\u0107 CircleCI lub Github Actions. A zamiast DigitalOcean \u2014 AWS lub Linode.<\/p>\n<p>Postanowili\u015bmy pracowa\u0107 z Travis CI, a w tym serwisie mam ju\u017c co\u015b skonfigurowane. Dlatego teraz kr\u00f3tko opowiem, jak przygotowa\u0107 go do pracy.<\/p>\n<h2>Travis CI<\/h2>\n<p>\nTravis CI to narz\u0119dzie do testowania i wdra\u017cania kodu. Nie chcia\u0142bym zag\u0142\u0119bia\u0107 si\u0119 w szczeg\u00f3\u0142y konfiguracji Travis CI, poniewa\u017c ka\u017cdy projekt jest unikalny, a to nie przyniesie szczeg\u00f3lnych korzy\u015bci. Ale opowiem o podstawach, kt\u00f3re pozwol\u0105 Ci rozpocz\u0105\u0107 prac\u0119, je\u015bli zdecydujesz si\u0119 na korzystanie z Travis CI. Cokolwiek wybierzesz \u2014 Travis CI, CircleCI, Jenkins, czy co\u015b innego, wsz\u0119dzie b\u0119d\u0105 stosowane podobne metody konfiguracji.<\/p>\n<p>Abyste mohli za\u010d\u00edt pracovat s Travis CI, p\u0159ejd\u011bte na <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">webovou str\u00e1nku projektu<\/a><\/noindex> a vytvo\u0159te si \u00fa\u010det. Pot\u00e9 integrujte Travis CI s va\u0161\u00edm \u00fa\u010dtem na GitHubu. P\u0159i nastavov\u00e1n\u00ed syst\u00e9mu budete muset ur\u010dit repozit\u00e1\u0159, se kter\u00fdm chcete automatizovat pr\u00e1ci, a povolit k n\u011bmu p\u0159\u00edstup. (Pou\u017e\u00edv\u00e1m GitHub, ale jsem si jist\u00e1, \u017ee Travis CI m\u016f\u017ee b\u00fdt integrov\u00e1n tak\u00e9 s BitBucket, GitLab a dal\u0161\u00edmi podobn\u00fdmi slu\u017ebami).<\/p>\n<p>Ka\u017ed\u00fdkr\u00e1t, kdy\u017e Travis CI za\u010dne pracovat, spust\u00ed se server, kter\u00fd vykon\u00e1v\u00e1 p\u0159\u00edkazy uveden\u00e9 v konfigura\u010dn\u00edm souboru, v\u010detn\u011b nasazen\u00ed p\u0159\u00edslu\u0161n\u00fdch v\u011btv\u00ed repozit\u00e1\u0159e.<\/p>\n<h3>\u258dCyklus \u00falohy<\/h3>\n<p>\nKonfigura\u010dn\u00ed soubor Travis CI, naz\u00fdvan\u00fd <code>.travis.yml<\/code> a ulo\u017een\u00fd v ko\u0159enov\u00e9m adres\u00e1\u0159i projektu, podporuje koncept ud\u00e1lost\u00ed <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.travis-ci.com\/user\/job-lifecycle\/\">cyklu \u017eivota<\/a><\/noindex> \u00falohy. Zde jsou tyto ud\u00e1losti uvedeny v po\u0159ad\u00ed, v jak\u00e9m se vyskytuj\u00ed:<\/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 nebo 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>\u258dTestov\u00e1n\u00ed<\/h3>\n<p>\nV konfigura\u010dn\u00edm souboru hodl\u00e1m nastavit lok\u00e1ln\u00ed server Travis CI. Jako jazyk jsem zvolila Node verze 12 a ozna\u010dila jsem syst\u00e9mu, \u017ee je t\u0159eba nainstalovat z\u00e1vislosti pot\u0159ebn\u00e9 pro pou\u017e\u00edv\u00e1n\u00ed Dockeru.<\/p>\n<p>V\u0161e, co je uvedeno v <code>.travis.yml<\/code>, bude prov\u00e1d\u011bno p\u0159i v\u0161ech pull requestech do v\u0161ech v\u011btv\u00ed repozit\u00e1\u0159e, pokud nebude uvedeno jinak. To je u\u017eite\u010dn\u00e1 funkce, proto\u017ee to znamen\u00e1, \u017ee m\u016f\u017eeme testovat cel\u00fd k\u00f3d p\u0159ich\u00e1zej\u00edc\u00ed do repozit\u00e1\u0159e. To n\u00e1m umo\u017e\u0148uje v\u011bd\u011bt, zda je k\u00f3d p\u0159ipraven k z\u00e1znamu do v\u011btve <code>master<\/code>, a zda nenaru\u0161\u00ed proces sestaven\u00ed projektu. V t\u00e9to glob\u00e1ln\u00ed konfiguraci instaluji v\u0161e lok\u00e1ln\u011b, spou\u0161t\u00edm server v\u00fdvoj\u00e1\u0159e Webpack na pozad\u00ed (to je charakteristika m\u00e9ho pracovn\u00edho procesu) a prov\u00e1d\u00edm testy.<\/p>\n<p>Pokud chcete, aby ve va\u0161em repozit\u00e1\u0159i byly zobrazeny ikonky se statistikami pokryt\u00ed k\u00f3du testy, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/display-build-status-and-test-coverage\/\">tady<\/a><\/noindex> m\u016f\u017eete naj\u00edt stru\u010dn\u00fd n\u00e1vod k pou\u017eit\u00ed Jest, Travis CI a Coveralls ke shroma\u017e\u010fov\u00e1n\u00ed a zobrazen\u00ed t\u011bchto \u00fadaj\u016f.<\/p>\n<p>Tak\u017ee, zde je obsah souboru <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>\nT\u00edmto kon\u010d\u00ed akce, kter\u00e9 se prov\u00e1d\u011bj\u00ed pro v\u0161echny v\u011btve repozit\u00e1\u0159e a pro pull requesty.<\/p>\n<h3>\u258dNasazen\u00ed<\/h3>\n<p>\nP\u0159edpokl\u00e1d\u00e1me-li, \u017ee v\u0161echny automatizovan\u00e9 testy byly \u00fasp\u011b\u0161n\u011b dokon\u010deny, m\u016f\u017eeme (co\u017e nen\u00ed povinn\u00e9) nasadit k\u00f3d na produk\u010dn\u00ed server. Jeliko\u017e to chceme d\u011blat pouze pro k\u00f3d z v\u011btve <code>master<\/code>, d\u00e1v\u00e1me syst\u00e9mu odpov\u00eddaj\u00edc\u00ed pokyny v nastaven\u00ed nasazen\u00ed. Ne\u017e budete moci ve sv\u00e9m projektu pou\u017e\u00edt k\u00f3d, kter\u00fd budeme d\u00e1le zkoumat, cht\u011bla bych v\u00e1s varovat, \u017ee mus\u00edte m\u00edt skute\u010dn\u00fd skript, kter\u00fd se spou\u0161t\u00ed pro nasazen\u00ed.<\/p>\n<pre><code class=\"plaintext\">deploy:\n\u00a0\u00a0# Sestavit Docker kontejner a odeslat ho na 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>\nSkript pro nasazen\u00ed \u0159e\u0161\u00ed dv\u011b \u00falohy:<\/p>\n<ul>\n<li>Sestaven\u00ed, tagov\u00e1n\u00ed a odesl\u00e1n\u00ed obrazu na Docker Hub pomoc\u00ed CI n\u00e1stroje (v na\u0161em p\u0159\u00edpad\u011b Travis CI).<\/li>\n<li>Nahr\u00e1n\u00ed obrazu na server, zastaven\u00ed star\u00e9ho kontejneru a spu\u0161t\u011bn\u00ed nov\u00e9ho (v na\u0161em p\u0159\u00edpad\u011b server b\u011b\u017e\u00ed na platform\u011b DigitalOcean).<\/li>\n<\/ul>\n<p>\nNejprve je t\u0159eba nastavit automatick\u00fd proces sestavov\u00e1n\u00ed, tagov\u00e1n\u00ed a odes\u00edl\u00e1n\u00ed obrazu na Docker Hub. To v\u0161e je velmi podobn\u00e9 tomu, co jsme ji\u017e d\u011blali ru\u010dn\u011b, s v\u00fdjimkou toho, \u017ee pot\u0159ebujeme strategii p\u0159i\u0159azov\u00e1n\u00ed unik\u00e1tn\u00edch tag\u016f obraz\u016fm a automatizaci p\u0159ihl\u00e1\u0161en\u00ed. M\u011bla jsem pot\u00ed\u017ee s n\u011bkter\u00fdmi detaily skriptu nasazen\u00ed, jako jsou strategie tagov\u00e1n\u00ed, p\u0159ihl\u00e1\u0161en\u00ed, k\u00f3dov\u00e1n\u00ed SSH kl\u00ed\u010d\u016f, ustanoven\u00ed SSH spojen\u00ed. Ale na\u0161t\u011bst\u00ed se m\u016fj p\u0159\u00edtel velmi dob\u0159e vyzn\u00e1 v bash, stejn\u011b jako v mnoha dal\u0161\u00edch v\u011bcech. Pomohl mi napsat tento skript.<\/p>\n<p>Tak\u017ee prvn\u00ed \u010d\u00e1st skriptu je odesl\u00e1n\u00ed obrazu na Docker Hub. Je to docela jednoduch\u00e9. Sch\u00e9ma tagov\u00e1n\u00ed, kter\u00e9 pou\u017e\u00edv\u00e1m, zahrnuje kombinaci git hash a git tag, pokud existuje. To umo\u017e\u0148uje vytv\u00e1\u0159en\u00ed unik\u00e1tn\u00edho tagu a usnad\u0148uje identifikaci sestaven\u00ed, na kter\u00e9 je zalo\u017eeno. <code>DOCKER_USERNAME<\/code> a <code>DOCKER_PASSWORD<\/code> \u2014 to jsou u\u017eivatelsk\u00e9 prom\u011bnn\u00e9 prost\u0159ed\u00ed, kter\u00e9 lze nastavit pomoc\u00ed rozhran\u00ed Travis CI. Travis CI automaticky zpracov\u00e1v\u00e1 tajn\u00e9 \u00fadaje tak, aby se nedostaly do ciz\u00edch rukou.<\/p>\n<p>Zde je prvn\u00ed \u010d\u00e1st skriptu. <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>\nJak bude vypadat druh\u00e1 \u010d\u00e1st skriptu, z\u00e1vis\u00ed zcela na tom, jak\u00e9ho hostitele pou\u017e\u00edv\u00e1te a jak je p\u0159ipojen\u00ed k n\u011bmu uspo\u0159\u00e1d\u00e1no. V m\u00e9m p\u0159\u00edpad\u011b, proto\u017ee pou\u017e\u00edv\u00e1m Digital Ocean, se pro p\u0159\u00edstup k serveru pou\u017e\u00edvaj\u00ed p\u0159\u00edkazy <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/digitalocean\/doctl\">doctl<\/a><\/noindex>. P\u0159i pr\u00e1ci s AWS se bude pou\u017e\u00edvat n\u00e1stroj <code>aws<\/code>, a tak d\u00e1le.<\/p>\n<p>Nastaven\u00ed serveru nebylo p\u0159\u00edli\u0161 slo\u017eit\u00e9. Tak jsem nastavila droplet zalo\u017een\u00fd na z\u00e1kladn\u00edm obrazu. Je t\u0159eba poznamenat, \u017ee vybran\u00fd syst\u00e9m vy\u017eaduje jednor\u00e1zovou ru\u010dn\u00ed instalaci Dockeru a jednor\u00e1zov\u00e9 ru\u010dn\u00ed spu\u0161t\u011bn\u00ed Dockeru. Pro instalaci Dockeru jsem pou\u017eila Ubuntu 18.04, tak\u017ee pokud tak\u00e9 pou\u017e\u00edv\u00e1te Ubuntu, abyste ud\u011blali tot\u00e9\u017e, m\u016f\u017eete jednodu\u0161e n\u00e1sledovat <noindex><a rel=\"nofollow\" href=\"https:\/\/phoenixnap.com\/kb\/how-to-install-docker-on-ubuntu-18-04\">tohoto<\/a><\/noindex> jednoduch\u00fd n\u00e1vod.<\/p>\n<p>Nemluv\u00edm zde o konkr\u00e9tn\u00edch p\u0159\u00edkazech pro slu\u017ebu, proto\u017ee se tento aspekt v r\u016fzn\u00fdch p\u0159\u00edpadech m\u016f\u017ee v\u00fdrazn\u011b li\u0161it. Uvedu pouze obecn\u00fd pl\u00e1n \u010dinnost\u00ed, kter\u00e9 se prov\u00e1d\u011bj\u00ed po p\u0159ipojen\u00ed p\u0159es SSH k serveru, na kter\u00e9m bude projekt nasazen:<\/p>\n<ul>\n<li>Je t\u0159eba naj\u00edt kontejner, kter\u00fd je pr\u00e1v\u011b spu\u0161t\u011bn, a zastavit ho.<\/li>\n<li>Pot\u00e9 je nutn\u00e9 spustit nov\u00fd kontejner na pozad\u00ed.<\/li>\n<li>Budete pot\u0159ebovat nastavit m\u00edstn\u00ed port serveru na hodnotu <code>80<\/code> \u2014 to umo\u017en\u00ed p\u0159\u00edstup na webovou str\u00e1nku na adrese typu <code>example.com<\/code>, bez zad\u00e1n\u00ed portu, m\u00edsto adresy jako <code>example.com:5000<\/code>.<\/li>\n<li>A nakonec je t\u0159eba odstranit v\u0161echny star\u00e9 kontejnery a obrazy.<\/li>\n<\/ul>\n<p>\nToto je pokra\u010dov\u00e1n\u00ed skriptu.<\/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>N\u011bkter\u00e9 v\u011bci, na kter\u00e9 byste m\u011bli d\u00e1vat pozor<\/h2>\n<p>\nMo\u017en\u00e1, kdy\u017e se p\u0159ipoj\u00edte k serveru p\u0159es SSH z Travis CI, uvid\u00edte varov\u00e1n\u00ed, kter\u00e9 neumo\u017en\u00ed pokra\u010dovat v instalaci, proto\u017ee syst\u00e9m \u010dek\u00e1 na reakci u\u017eivatele.<\/p>\n<pre><code class=\"plaintext\">Ov\u011b\u0159en\u00ed hostitele '&lt;hostname&gt; (&lt;IP address&gt;)' nem\u016f\u017ee b\u00fdt provedeno.\nOtisk RSA kl\u00ed\u010de je &lt;key fingerprint&gt;.\nOpravdu chcete pokra\u010dovat v p\u0159ipojen\u00ed (ano\/nejde)?<\/code><\/pre>\n<p>\nZjistila jsem, \u017ee \u0159et\u011bzov\u00fd kl\u00ed\u010d lze zak\u00f3dovat do base64, aby bylo mo\u017en\u00e9 ho uchovat v takov\u00e9 podob\u011b, se kterou bude snadn\u00e9 a spolehliv\u00e9 pracovat. Ve f\u00e1zi instalace lze ve\u0159ejn\u00fd kl\u00ed\u010d de\u0161ifrovat a ulo\u017eit do souboru <code>known_hosts<\/code> aby se zbavili v\u00fd\u0161e uveden\u00e9 chyby.<\/p>\n<pre><code class=\"plaintext\">echo &lt;public key&gt; | base64 # zobraz\u00ed &lt;ve\u0159ejn\u00fd kl\u00ed\u010d zak\u00f3dovan\u00fd v base64&gt;<\/code><\/pre>\n<p>\nV praxi m\u016f\u017ee tento p\u0159\u00edkaz vypadat takto:<\/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>\nA takto vypad\u00e1 to, co to vyd\u00e1v\u00e1 \u2014 \u0159et\u011bzec v k\u00f3dov\u00e1n\u00ed base64:<\/p>\n<pre><code class=\"plaintext\">123.45.67.89 ssh-rsa AABAB3NzaC1y... (base64 encoded public key)<\/code><\/pre>\n<p>\nToto je p\u0159\u00edkaz, o kter\u00e9m se hovo\u0159ilo v\u00fd\u0161e<\/p>\n<pre><code class=\"plaintext\">install:\n  - echo  | base64 -d &gt;&gt; $HOME\/.ssh\/known_hosts<\/code><\/pre>\n<p>\nStejn\u00fd p\u0159\u00edstup lze pou\u017e\u00edt i s priv\u00e1tn\u00edm kl\u00ed\u010dem p\u0159i navazov\u00e1n\u00ed p\u0159ipojen\u00ed, proto\u017ee pro p\u0159\u00edstup na server m\u016f\u017ee b\u00fdt nutn\u00fd priv\u00e1tn\u00ed kl\u00ed\u010d. P\u0159i pr\u00e1ci s kl\u00ed\u010dem je d\u016fle\u017eit\u00e9 zajistit jeho bezpe\u010dn\u00e9 uchov\u00e1v\u00e1n\u00ed v prom\u011bnn\u00e9 prost\u0159ed\u00ed Travis CI, a aby se nikde nezobrazoval.<\/p>\n<p>Je\u0161t\u011b jedna v\u011bc, na kterou je dobr\u00e9 myslet, je, \u017ee budete mo\u017en\u00e1 pot\u0159ebovat spustit cel\u00fd skript pro nasazen\u00ed jako jeden \u0159\u00e1dek, nap\u0159\u00edklad pomoc\u00ed <code>doctl<\/code>. To m\u016f\u017ee vy\u017eadovat dodate\u010dn\u00e9 \u00fasil\u00ed.<\/p>\n<pre><code class=\"plaintext\">doctl compute ssh  --ssh-command \"v\u0161echny p\u0159\u00edkazy budou zde &amp;&amp; zde\"<\/code><\/pre>\n<p><\/p>\n<h2>TLS\/SSL a load balancing<\/h2>\n<p>\nPot\u00e9, co jsem ud\u011blala v\u0161echno, o \u010dem byla \u0159e\u010d v\u00fd\u0161e, byla posledn\u00edm probl\u00e9mem, kter\u00fd jsem \u010delila, absence SSL na serveru. Proto\u017ee pou\u017e\u00edv\u00e1m Node.js server, abych p\u0159im\u011bla <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\">pracovat<\/a><\/noindex> Nginx reverse proxy a Let\u2019s Encrypt, je t\u0159eba se s t\u00edm tro\u0161ku poprat.<\/p>\n<p>Necht\u011bla jsem v\u0161echny tyto SSL nastaven\u00ed prov\u00e1d\u011bt ru\u010dn\u011b, tak\u017ee jsem jednodu\u0161e vytvo\u0159ila load balancer a zapsala jeho informace do DNS. V p\u0159\u00edpad\u011b DigitalOcean nap\u0159\u00edklad, vytvo\u0159en\u00ed automaticky obnoviteln\u00e9ho self-signed certifik\u00e1tu na load balanceru je jednoduch\u00fd, bezplatn\u00fd a rychl\u00fd postup. Tento p\u0159\u00edstup m\u00e1 tak\u00e9 dal\u0161\u00ed v\u00fdhodu, kterou je to, \u017ee velmi snadno umo\u017e\u0148uje nastavit SSL na mnoha serverech pracuj\u00edc\u00edch za load balancerem. To umo\u017e\u0148uje samotn\u00fdm server\u016fm \u201enen\u00ed p\u0159em\u00fd\u0161let\u201c o SSL, ale st\u00e1le pou\u017e\u00edvat port <code>80<\/code>. Tak\u017ee nastaven\u00ed SSL na load balanceru je mnohem jednodu\u0161\u0161\u00ed a pohodln\u011bj\u0161\u00ed ne\u017e alternativn\u00ed metody nastaven\u00ed SSL.<\/p>\n<p>Nyn\u00ed lze na serveru zav\u0159\u00edt v\u0161echny porty, kter\u00e9 p\u0159ij\u00edmaj\u00ed p\u0159\u00edchoz\u00ed p\u0159ipojen\u00ed \u2014 krom\u011b portu <code>80<\/code>, pou\u017e\u00edvan\u00e9ho pro komunikaci s load balancerem, a portu <code>22<\/code> pro SSH. V d\u016fsledku toho pokus o p\u0159\u00edm\u00fd p\u0159\u00edstup k serveru na jak\u00fdchkoli portech, krom\u011b t\u011bchto dvou, bude ne\u00fasp\u011b\u0161n\u00fd.<\/p>\n<h2>Z\u00e1v\u011bry<\/h2>\n<p>\nJakmile jsem ud\u011blala v\u0161echno, o \u010dem jsem v tomto \u010dl\u00e1nku psala, u\u017e m\u011b nelekala ani platforma Docker, ani koncept automatizovan\u00fdch CI\/CD \u0159et\u011bzc\u016f. Dok\u00e1zala jsem nastavit \u0159et\u011bzec kontinu\u00e1ln\u00ed integrace, b\u011bhem kter\u00e9ho se prov\u00e1d\u00ed testov\u00e1n\u00ed k\u00f3du p\u0159ed jeho nasazen\u00edm do produkce a automatick\u00e9 nasazen\u00ed k\u00f3du na serveru. To v\u0161echno je pro m\u011b zat\u00edm relativn\u011b nov\u00e9 a jsem si jist\u00e1, \u017ee existuj\u00ed zp\u016fsoby, jak zlep\u0161it m\u016fj automatizovan\u00fd pracovn\u00ed proces a u\u010dinit ho efektivn\u011bj\u0161\u00edm. Tak\u017ee pokud m\u00e1te n\u011bjak\u00e9 n\u00e1pady, dejte <noindex><a rel=\"nofollow\" href=\"mailto:hello@taniarascia.com\">mi<\/a><\/noindex> v\u011bd\u011bt. Douf\u00e1m, \u017ee v\u00e1m tento \u010dl\u00e1nek pomohl ve va\u0161ich projektech. Chci v\u011b\u0159it, \u017ee jste se z n\u011bj nau\u010dili tolik, kolik jsem se nau\u010dila j\u00e1, kdy\u017e jsem se zab\u00fdvala v\u0161\u00edm, o \u010dem v n\u011bm p\u00ed\u0161u.<\/p>\n<p><b>P.S.<\/b> V na\u0161em <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace#order\">marketplace<\/a><\/noindex> existuje obraz <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace\/docker#advantages\">Docker<\/a><\/noindex>, kter\u00fd se instaluje jedn\u00edm kliknut\u00edm. M\u016f\u017eete zkontrolovat funk\u010dnost kontejner\u016f na <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/\">VPS<\/a><\/noindex>. V\u0161em nov\u00fdm klient\u016fm se poskytuj\u00ed 3 dny testov\u00e1n\u00ed zdarma.<\/p>\n<p><b>V\u00e1\u017een\u00ed \u010dten\u00e1\u0159i!<\/b> Pou\u017e\u00edv\u00e1te technologie CI\/CD ve sv\u00fdch projektech?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/#order\"><img decoding=\"async\" alt=\"Vytv\u00e1\u0159en\u00ed CI\/CD \u0159et\u011bzce a automatizace pr\u00e1ce s Dockerem.\" src=\"\/wp-content\/uploads\/2020\/02\/3fbfa90ea56a3d9b6b89ba945eb94570.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Zdroj: <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\/cs\/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=\"cs_CZ\" \/>\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\/cs\/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\udd47Vytv\u00e1\u0159en\u00ed CI\/CD \u0159et\u011bzce a automatizace pr\u00e1ce s Dockerem | ProHoster","description":"Sv\u016fj prvn\u00ed web jsem napsala na konci 90. let. Tehdy bylo jejich uveden\u00ed do provozu velmi jednoduch\u00e9.","canonical_url":"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"cs_CZ","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\/cs\/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\/cs\/wp-json\/wp\/v2\/posts\/69351","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/comments?post=69351"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts\/69351\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/media\/69352"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/media?parent=69351"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/categories?post=69351"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/tags?post=69351"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}