Na navigaci | Klávesové zkratky

Translate to English… Ins Deutsche übersetzen…

FTP Deployment: nahrávejte přes FTP chytře

Není nic horšího, než uploadovat soubory na FTP ručně, například pomocí Total Commanderu. (Ačkoliv, ještě horší je editovat soubory přímo na serveru a pak se zoufale pokoušet o jakousi synchronizaci.) Jakmile totiž proces nezautomatizujete, stojí vás mnohem víc času a hrozí riziko chyby. Třeba, že některý soubor zapomenete nahrát.

Dnes už se používají sofistikované techniky nasazování aplikací na web, například pomocí Gitu, ale mnoho lidí stále zůstává u nahrávání jednotlivých souborů skrze FTP. Právě pro ně je určen nástroj FTP Deployment, který zautomatizuje a zjednoduší nahrávání aplikací přes FTP.

FTP Deployment je skript napsaný v PHP, který celý proces zautomatizuje. Stačí jen říct, který adresář (local) má kam nahrát (remote). Tyto údaje zapíšete do souboru deployment.ini, jehož odkliknutí můžete rovnou asociovat se spuštěním skriptu, takže deployment se stane věcí jednoho kliknutí:

php deployment deployment.ini

A jak vypadá soubor deployment.ini? Povinná je vlastně jen položka remote, všechny ostatní jsou nepovinné:

; remote FTP server
remote = ftp://user:secretpassword@ftp.example.com/directory

; local path (optional)
local = .

; run in test-mode? (can be enabled by option -t or --test too)
test = no

; files and directories to ignore
ignore = "
    .git*
    project.pp[jx]
    /deployment.*
    /log
    temp/*
    !temp/.htaccess
"

; is allowed to delete remote files? (defaults to yes)
allowdelete = yes

; jobs to run before file upload
before[] = http://example.com/deployment.php?before

; jobs to run after file upload
after[] = http://example.com/deployment.php?after

; directories to purge after file upload
purge[] = temp/cache

; files to preprocess (defaults to *.js *.css)
preprocess = no

; file which contains hashes of all uploaded files (defaults to .htdeployment)
deploymentfile = .deployment

V testovacím režimu (při spuštění s parametrem -t) k uploadu nebo mazání souborů na FTP nedochází, můžete jej tedy použít k ověření, zda máte všechny hodnoty dobře nastavené.

Položka ignore používá stejný formát jako .gitignore:

  • log – ignoruje všechny soubory či adresáře log, i uvnitř všech podsložek
  • /log – ignoruje soubor či adresář log v kořenovém adresáři
  • app/log – ignoruje soubor či adresář log v podsložce app kořenového adresáře
  • data/* – ignoruje vše uvnitř složky data, ale samotnou služku na FTP vytvoří
  • !data/session – z předchozího pravidla učiní výjimku pro soubor či složku session
  • project.pp[jx] – ignoruje soubory či složky project.ppjproject.ppx

Před započetím uploadu a po jeho skončení můžete nechat zavolat skripty na vašem serveru (viz before a after), které mohou například server přepnout do maintenance režimu, kdy bude odesílat hlavičku 503.

Aby synchronizace i velkého množství souborů proběhla (v rámci možností) transakčně, všechny soubory se nejprve nahrají s příponou .deploytmp a poté, což už je rychlé, přejmenují. Zároveň se na server uloží soubor .htdeployment, kde jsou uloženy md5 otisky všech souborů a právě pomocí něj se nadále web synchronizuje.

Při dalším spuštění tedy nahrává pouze změněné soubory a maže smazané (pokud to nezakážeme direktivou allowdelete).

Nahrávané soubory je možné nechat zpracovat preprocesorem. Standardně jsou nastaveny pravidla, že všechny .css soubory se zkomprimují pomocí Clean-CSS a .js pomocí Google Closure Compiler. Před samotnou komprimací se ještě expandují základní mod_include direktivy Apache. Můžete tedy vytvořit například soubor combined.js:

<!--#include file="jquery.js" -->
<!--#include file="jquery.fancybox.js" -->
<!--#include file="main.js" -->

Který vám bude Apache na lokálním serveru za běhu sestavovat spojením tří uvedených souborů. Říci si o to můžete takto:

<FilesMatch "combined\.(js|css)$">
    Options +Includes
    SetOutputFilter INCLUDES
</FilesMatch>

Přičemž na server se nahraje už ve spojené a zkomprimované podobě. Vaše HTML stránka tak bude šetřit zdroje a načítat jediný JavaScriptový soubor.

V konfiguračním souboru deployment.ini můžete vytvořit i více sekcí, případně si udělat jeden konfigurák zvlášť pro data a jeden pro aplikaci, aby synchronizace byla co nejrychlejší a nemusel se vždy počítat otisk velkého množství souborů.

Nástroj FTP Deployment jsem si vytvořil před mnoha lety a plně pokrývá mé požadavky na deployovací nástroj. Zároveň je třeba zdůraznit, že FTP protokol tím, že přenáší heslo v čitelné podobě, představuje bezpečnostní riziko a rozhodně byste jej neměli používat třeba na veřejných Wi-Fi.

Komentáře

  1. Honza Lilák http://blog.honzalilak.cz #1

    avatar

    Chápu to správně tak, že při spuštění nahraje jen soubory, jejichž verze se na lokále a produkci liší?

  2. Filip Procházka (@HosipLan) http://filip-prochazka.com #2

    avatar

    #1 Honzo Liláku, Ano. Nahrává jen to co se změnilo.

    V současnosti na jednom projektu velice spokojeně používám. Jenom mi strašně dlouho zpracovává ignore pravidla. Možná by to chtělo ukecanější výpis? Kamarád mi tuhle psal, že si myslel, že se mu to zaseklo a přitom to stále pracovalo.

    před 4 lety | odpovědět
  3. Teyras #3

    avatar

    Hehe, kdybys to zveřejnil o dva roky dřív, nemusel jsem psát tohle

    před 4 lety | odpovědět
  4. cabadaj #4

    Pojem „proběhla transakčně“ je trochu něco jiného než „proběhne rychle“. Ten trik s „.deploytmp“ ti rozhodně atomicitu nezaručí.

    Lepší by bylo mít zdrojáky například v adresáři „/home/src/projekt-1.0“ a například ve „/var/www“ mít jenom symlink („/var/www/projekt“). Pak je možné nahrát novou verzi například do adresáře „/home/src/projekt-1.1“ a atomicky změnit symlink.

  5. Filip Procházka (@HosipLan) http://filip-prochazka.com #5

    avatar

    #4 cabadaji, to je sice hezké, ale tento nástroj zjevně cílí na sdílené hostingy ;) Viděl jsi někdy na sdíleným, že by si uživatel mohl dělat symlinky?

    před 4 lety | odpovědět | reagoval [7] cabadaj
  6. pek http://zonglovani.info #6

    avatar

    Hoho, kdybys to zveřejnil o rok dřív, nemusel jsem psát tohle :^)

    před 4 lety | odpovědět
  7. cabadaj #7

    #5 Filipe Procházko (@HosipLan), to máš pravdu. Ale v každým případě bych v článku netvrdil, že to je transakční. Protože není.

    před 4 lety | odpovědět
  8. Jan Panschab #8

    Má to nějaké výhody oproti https://github.com/resmo/git-ftp ? Tohle používám už asi na 3. soukromém projektu a jsem s tím naprosto spokojenej.

    před 4 lety | odpovědět | reagoval [9] Kindlista [10] JSifalda
  9. Kindlista http://www.amazon-kindle.cz #9

    #8 Jane Panschabe, Řekl bych že má, protože někdo ani git nemusí používat a u nějakých projektů má zdrojáky jen tak.

    před 4 lety | odpovědět | reagoval [14] Jan Panschab
  10. JSifalda http://www.jsifalda.name #10

    avatar

    #8 Jane Panschabe, Jako hlavní výhodu vidím v tom, že https://github.com/resmo/git-ftp ti na hosting nenahraje libs (pokud je nemáš v indexu – což by byla většinou pěkná pakárna). Zatímco https://github.com/…p-deployment by si s tím měl díky .ignore hrabě poradit.

    před 4 lety | odpovědět | reagoval [14] Jan Panschab
  11. Roman Matěna http://www.webfaq.cz #11

    avatar

    Nemáte už někdo nějaké GUI rozšíření? kde by se dali uložit nové profily, a tedy ftp klíče, rozhodnutí jestli to pojede přes ftp nebo jestli je to lokal > lokal transfer, naklikat adresaře, které jsou aktivní a které se mají ignorovat atd.

    Jestli nic nefunguje uvažuji, že se do toho po víkendu pustíme, prohlédl jsem spousty dalších nástrojů a přišlo mi to všechno moc složité.

    před 4 lety | odpovědět
  12. enumag http://m33.cz #12

    avatar

    Dalo by se to použít s nějakým bezpečnějším protokolem (SCP, SFTP)?

    před 4 lety | odpovědět | reagoval [16] David Grudl
  13. Povinné #13

    avatar

    a co srovnání s phingem?

    před 4 lety | odpovědět
  14. Jan Panschab #14

    #9 Kindlisto, Ano, nemusí, ale neni problém to v gitu mít. Stojí to za to a ne jenom kvůli deployování :-)

    #10 JSifaldo, To nechápu. Nahrává to tam přesně to co potřebuju. A svůj vlastní ignore to má taky.

    Ale koukám, že git-ftp asi nemá pre a post deploy hooky.

    před 4 lety | odpovědět
  15. Jan Chorvat #15

    avatar

    Ako presne to funguje s testovacim modom? V .ini subore som ho mal vypnutý, ale v konzole som pridal parameter -t, ale skript mi nahral vsetky subory na server.

    Robim niečo zle, alebo je to chyba?

    před 4 lety | odpovědět | reagoval [16] David Grudl
  16. David Grudl http://davidgrudl.com #16

    avatar

    #12 enumagu, dal by se použít bezpečnější protokol SSH2, ale můj hosting to nepodporuje (jen FTPS) abych to mohl odladit, funkci ftp-ssl-connect zase nemám k dispozici pod Windows, tudíž jsem na implementaci zatím rezignoval.

    #15 Jane Chorvate, test mode by nic nahrávat neměl. Zobrazí se po spuštění informace, že to běží v „Test mode“?

    před 4 lety | odpovědět | reagoval [17] Jan Chorvat
  17. Jan Chorvat #17

    avatar

    #16 Davide Grudle, ked v konzole napisem php d:\tools\Deployment\deployment.php deployment.ini -t, tak pole $options, ktore naplna funkcia getopt je prazdna.

  18. Filip Procházka (@HosipLan) http://filip-prochazka.com #18

    avatar

    #17 Jane Chorvate, Je standardem dávat options před argumenty (opačně to funguje jen výjimečně)

    php deployment.php -t deployment.ini

    před 4 lety | odpovědět | reagoval [19] Jan Chorvat [20] David Grudl
  19. Jan Chorvat #19

    avatar

    #18 Filipe Procházko (@HosipLan),
    php d:\tools\Deployment\deployment.php -t deployment.ini

    FTP deployment
    Missing config file -t

    Viem čo je štandard, ale držal som sa návodu na tomto blogu. A taktiež som vyskúšal parametre prehodiť. Nefunguje to ani jedným spôsobom.

    před 4 lety | odpovědět | reagoval [20] David Grudl
  20. David Grudl http://davidgrudl.com #20

    avatar

    #17 Jane Chorvate, #18 Filip Procházka (@HosipLan)#19 Jan Chorvat tohle je dost drsný nedostatek getopt(), který jsem si neuvědomil :-(

    Opraveno ve verzi 0.9.1 https://github.com/…ter/releases

    před 4 lety | odpovědět
  21. uestla http://kesspess.1991.cz #21

    avatar

    Bylo by možné přidat event, který by byl volán před závěrečným přejmenováním deploynutých souborů? Maintenance stav by IMHO mohlo být vhodné zapnout až tam – do té doby by se mohly nové soubory v klidu nahrávat, aplikace by běžela postaru, a jen na přejmenování (což nebývá dlouhá doba), by se zmaintenancovatěla, po přejmenování pak releasnula.

    před 4 lety | odpovědět | reagoval [22] David Grudl
  22. David Grudl http://davidgrudl.com #22

    avatar

    #21 uestlo, možná by se mohl event „before“ volat těsně před přejmenováváním, co?

    před 4 lety | odpovědět | reagoval [23] uestla [24] uestla
  23. uestla http://kesspess.1991.cz #23

    avatar

    #22 Davide Grudle, To by bylo tzv. ideální :)

    před 4 lety | odpovědět
  24. uestla http://kesspess.1991.cz #24

    avatar

    #22 Davide Grudle, I když si na druhou stranu dokáži představit, že v ‚before‘ eventu může někdo např. nahrazovat knihovny jejich minifikovanými verzemi, což je žádoucí ještě před samotným uploadem… 3 eventy proto vidím jako vhodnější.

    před 4 lety | odpovědět
  25. MK #25

    Je možné nějak nastavit, aby se soubor .htdeployment nahrával na ftp do specifického adresáře a ne do /. Například do adresáře /private.

    před 4 lety | odpovědět | reagoval [45] Petr Polák [46] David Grudl
  26. Tomáš Kafka #26

    avatar

    Hezký, díky :).

    Doteď jsem používal sitecopy, které dělá (až na minifikaci) přesně to samé, kdyby někdo potřeboval hýbat s daty i opačným směrem (jen změněné fetchnout ze serveru, upravit u sebe, a zase jen změněné nahrát na server), mohlo by se vám sitecopy hodit.

    před 4 lety | odpovědět
  27. David http://www.saja.cz #27

    avatar

    Proč já si tohle nepřečetl dříve? :-D Dnešním dnem zavírám TC a otevřu ho jen v největší nouzi. I když jak se znám, bude to brzo :(

    před 4 lety | odpovědět
  28. Petr Homoky http://www.wpkoder.cz #28

    avatar

    Tohle je nejlepší nástroj, který používám. Děkuji.

    před 3 lety | odpovědět
  29. martin #29

    avatar

    Díky, vypadá to hezky. Patrně to zkusíme nějak používat. Začínáme s Git systémem a objevujeme Ameriku :-)
    Omlouvám se možná za trochu začátečnický dotaz. Jak se řeší obecně deployment celého projektu, včetně updatu databáze?

    před 3 lety | odpovědět
  30. pawouk #30

    avatar

    Nevím co znamená slovo obecně? Ale jinak klidně asi takto. Tím, že nahrává jen změněné soubory je právě vhodný na celý projekt. Databáze se většinou řeší manuálně pomocí ALTER.

    před 3 lety | odpovědět
  31. martin #31

    avatar

    asi je to nějaká kravina, ale nějak to nešlape :-)
    Error: php_network_getaddresses: getaddrinfo failed: Není znám žádný takový hostitel.
    přitom ten host po otestovaní např. v total cmd jede.

    před 3 lety | odpovědět
  32. Michal #32

    Pri komprimácii css súborov pomocou YUI Compressor sa súbory len spoja do jedného? Čakal som, že to bude rovnako ako pri .js súboroch, a teda ze sa vymažú komenty aj všetky bíle znaky… Alebo niečo nerobím správne ? Ďakujem.

    před 3 lety | odpovědět
  33. Vojta #33

    Jak řešíte rozdílný nastavení na lokálu a na remotu?
    Třeba nějaký konfigy pro db? Nejde udělat něco jako database.deployment.php, kam bych si napsal nastavení mysql na remotu? Chápu, že si můžu na lokálu vymyslet vlastní db, ale tohle by se mohlo hodit i v jiných případech.

    před 3 lety | odpovědět
  34. takyhonza #34

    Osobně to na některých projektech řeším tak, že mám nějaký deploy.sh script, pak soubor local.neon (s lokálním nastavením) a local.remote.neon (s nastavením serveru, o ten se na lokálu nestarám a k ničemu mi vlastně není). Při deploymentu pak spustím daný script a ten mi provede přejmenování:

    • local.neon ⇒ local.local.neon
    • local.remote.neon ⇒ local.neon
    • nahrání na FTP přes ftp-deployment
    • local.neon ⇒ local.remote.neon
    • local.local.neon ⇒ local.neon

    Na většinu projektů mi to takto zatím stačí, ale samozřejmě záleží na konkrétním projektu.

    před 3 lety | odpovědět
  35. Jiří Novák #35

    avatar

    Dost bojuji s těmi ignore a pravděpodobně jsem se dostal do slepé uličky. Potřeboval bych ignorovat složku např data, ale z ní povolit některé soubory, které jsou zanořené v různých úrovních. Např.:

    data/*
    !data/images/product/watermark.png
    !data/files/product/loremipsum.pdf

    Zběžně jsem se koukl do zdrojových souborů a řekl bych, že to bez úprav nepůjde. Je to tak, nebo to lze nějak nastavit?

    před rokem | odpovědět | reagoval [36] David Grudl
  36. David Grudl http://davidgrudl.com #36

    avatar

    #35 Jiří Nováku, tohle se skutečně nedělá pohodlně, zkusil jsem kód upravit.

    před rokem | odpovědět | reagoval [37] Jiří Novák
  37. Jiří Novák #37

    avatar

    #36 Davide Grudle, Díky moc, jak bude trochu času tak to vyzkouším. Knihovnu jsem si kvůli tomu také upravil (přidával jsem ty výjimky na konci do listu souborů), ale toto vypadá lépe a hlavně můžu do budoucna aktualizovat. PS: poslal jsem na kávičku :-)

    před rokem | odpovědět
  38. chikeet #38

    avatar

    Na win 8 mi deployment padá na „proc_open(): CreateProcess failed, error code – 2“. Dá se s tím něco dělat? Předpokládám, že chyba je na mém přijímači.

    před rokem | odpovědět
  39. Herb #39

    Skvělá věc, toto, diky moc. Taky jsme přešli na git a pro upload na server je to parádička.

    Trochu bojuji s těmi ignore.

    Řekneme, ze na serveru mám složku img a tam jsou data, který nechci smazat ani nijak přepsat. Složku chci nechat jak je.
    U sebe tu složku mám také, ale jsou tam jiná data (testovací).

    Když do ignore napíšu /img nebo /img/ nebo /img/* tak mi to složku na serveru vždycky smaže.

    Co prosím dělám špatně?

  40. David #40

    avatar

    Ahoj, je možné nastavit více remote najednou ?

    před rokem | odpovědět | reagoval [43] David Grudl
  41. Petr #41

    #39 Herbe, Mám stejný problém. Neví někdo co s tím?

    před rokem | odpovědět | reagoval [44] David Grudl
  42. Petr #42

    #39 Herbe, Teda takhle, jde to obejít tak, že při prvním uploadu nechám ignorovanou složku nenahrát. Tím se vytvoří záznam o serveru že tam ta složka není a tak si jí následně ani nevšímá. Dodám jí tam ručně. Když se pak na serveru mění, ftp-deployment ji nechá být a nemaže ji. Ale je to takové přes ruku.

    před rokem | odpovědět | reagoval [44] David Grudl
  43. David Grudl http://davidgrudl.com #43

    avatar

    #40 Davide, můžeš mít v jednom INI souboru více sekcí a v každé jiný remote:

    [first]
    remote = ....
    
    [second]
    remote = ....
    před rokem | odpovědět
  44. David Grudl http://davidgrudl.com #44

    avatar

    #39 Herbe, #41 Petr#42 Petr pokud synchronizovanou složku dáš ignorovat, tak ji smaže i na serveru. Aby se tak nestalo, přidej allowdelete = no. Později to můžeš zase zrušit.

    před rokem | odpovědět
  45. Petr Polák #45

    avatar

    #25 MKu, Mám stejný dotaz. Je možné v deployment.ini nastavit, aby se soubor .htdeployment nahrával do volitelného podadresáře, např. do /private? Pokud je hosting spravovaný přes ISPConfig , nemají uživatelé práva zápisu do svého uživatelského kořenového adresáře (který je nadřazen adresáři web, což je uživatelův kořenový adresář webu). Je to omezení ISPConfigu, ale asi se to hned tak nezmění. Do adresáře /private (nebo /home, …) už klient zapisovat může. Dalo by se tím omezení ISPConfigu pěkně obejít.

    před rokem | odpovědět | reagoval [46] David Grudl
  46. David Grudl http://davidgrudl.com #46

    avatar

    #25 MKu, #45 Petr Polák název i složku pro deployovací soubor lze nastavit v konfigu:

    deploymentfile = private/.deployment
    před rokem | odpovědět
  47. Ja #47

    Zdravim,
    mam takovy problem, ktery jsem uz zacal resit. Kdyz vyvijim na vice pocitacich a delam z nich deploy, tak se mi stava, ze i kdyz jsou identicke soubory na pocitaci [a] i [b], tak v pripade stridaveho spousteni deploye (v jeden cas bezi pochopitelne jen jeden) na obou pocitacich se vzdy nahraje urcita mnozina souboru (na pocitaci [a] je to okolo 800 souboru, na [b] je to okolo 600 souboru).

    Jiny projekt delal dokonce to, ze nahral vsechno, psal pri stridavem deployi ‚already synchronized‘, ale pri zmene na pocitaci [a] a naslednem deployi (soubor se nahral OK), tak jsem zkusil rovnou hned deploy na pocitaci [b] (kde byla starsi, neaktualizovana, nepullnuta verze souboru), skript mi tedy napsal ‚already synchronized‘, aniz by nahral soubor, ktery byl evidentne jiny, nez soubor nahrany z pocitace [a]. (sorry za dlouhy popis)

    Stalo se to nekomu? Pise se tu, ze by se deploy mel ridit podle md5 otisku, ale skoro to vypada, jako by toto melo co docineni s poslednim casem editace souboru.

    Diky za pripadny nasmerovani!

    před rokem | odpovědět | reagoval [48] David Grudl
  48. David Grudl http://davidgrudl.com #48

    avatar

    #47 Jo, a liší se ty „stejne“ soubory nějak? Například kvůli rozdílným znakům pro konce řádků na Windows vs Linux?

    před rokem | odpovědět | reagoval [49] Ja
  49. Ja #49

    #48 Davide Grudle, Oboje jsou to pocitace s win 10, soubory editovane v phpstormu, synchronizovano gitem.
    Kdyby se neco delo (napr. se radky souboru) po nahrani souboru na server, pak by druhy deploy ze stejneho PC znovu zacal nahravat soubory, coz se ale nestane, napise to ‚up to date‘.
    Pustim deploy z druheho PC (nad syncnutymi daty z gitu), deploy zacne nahravat nejake soubory (nevysledoval jsem zde zadnou souvislost, maximalne cas zmeny souboru muze byt jiny).

    před rokem | odpovědět | reagoval [50] David Grudl
  50. David Grudl http://davidgrudl.com #50

    avatar

    #49 Jo, čas změny souboru nehraje žádnou roli, používá se hash generovaný metodou Deployment\Deployer::hashFile. Takže zkus ověřit, jestli se ty hashe liší, a pokud ano, proč (může to být rozdílný znak pro konce řádku apod.).

    před rokem | odpovědět
  51. dsigy #51

    V dokumentaci výše je psáno, že skript se spustí pomocí
    php deployment.php deployment.ini nicméně to zřejmě patří k nějaké starší verzi. Mě funguje pouze
    php deployment deployment.ini , tedy deployment bez .php.
    Třeba to někomu ušetří čas ;).

    před měsícem | odpovědět

Zanechat komentář

Text komentáře
Kontakt

(kvůli gravataru)



*kurzíva* **tučné** "odkaz":http://example.com /--php phpkod(); \--