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 ; you can use ftps:// or sftp:// protocols (sftp requires SSH2 extension) ; do not like to specify user & password in 'remote'? Use these options: ;user = ... ;password = ... ; FTP passive mode passiveMode = yes ; 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 uploading before[] = local: lessc assets/combined.less assets/combined.css before[] = http://example.com/deployment.php?before ; jobs to run after uploading and before uploaded files are renamed afterUpload[] = http://example.com/deployment.php?afterUpload ; directories to purge after uploading purge[] = temp/cache ; jobs to run after everything (upload, rename, delete, purge) is done after[] = remote: unzip api.zip after[] = remote: chmod 0777 temp/cache ; change permissions after[] = http://example.com/deployment.php?after ; files to preprocess (defaults to *.js *.css) preprocess = no ; file which contains hashes of all uploaded files (defaults to .htdeployment) deploymentFile = .deployment ; default permissions for new files ;filePermissions = 0644 ; default permissions for new directories ;dirPermissions = 0755
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ářelog
, i uvnitř všech podsložek/log
– ignoruje soubor či adresářlog
v kořenovém adresářiapp/log
– ignoruje soubor či adresářlog
v podsložceapp
kořenového adresáředata/*
– ignoruje vše uvnitř složkydata
, ale samotnou služku na FTP vytvoří!data/session
– z předchozího pravidla učiní výjimku pro soubor či složkusession
project.pp[jx]
– ignoruje soubory či složkyproject.ppj
aproject.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
Honza Lilák #1
Chápu to správně tak, že při spuštění nahraje jen soubory, jejichž verze se na lokále a produkci liší?
Filip Procházka (@HosipLan) #2
#1 Honza Lilák, 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.
Teyras #3
Hehe, kdybys to zveřejnil o dva roky dřív, nemusel jsem psát tohle
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.
Filip Procházka (@HosipLan) #5
#4 cabadaj, 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?
pek #6
Hoho, kdybys to zveřejnil o rok dřív, nemusel jsem psát tohle :^)
cabadaj #7
#5 Filip Procházka (@HosipLan), to máš pravdu. Ale v každým případě bych v článku netvrdil, že to je transakční. Protože není.
Jan Panschab #8
Má to nějaké výhody oproti https://web.archive.org/…esmo/git-ftp ? Tohle používám už asi na 3. soukromém projektu a jsem s tím naprosto spokojenej.
Kindlista #9
#8 Jan Panschab, Řekl bych že má, protože někdo ani git nemusí používat a u nějakých projektů má zdrojáky jen tak.
JSifalda #10
#8 Jan Panschab, Jako hlavní výhodu vidím v tom, že https://web.archive.org/…esmo/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.
Roman Matěna #11
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é.
enumag #12
Dalo by se to použít s nějakým bezpečnějším protokolem (SCP, SFTP)?
Povinné #13
a co srovnání s phingem?
Jan Panschab #14
#9 Kindlista, Ano, nemusí, ale neni problém to v gitu mít. Stojí to za to a ne jenom kvůli deployování 🙂
#10 JSifalda, 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.
Jan Chorvat #15
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?
David Grudl #16
#12 enumag, 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 Jan Chorvat, test mode by nic nahrávat neměl. Zobrazí se po spuštění informace, že to běží v „Test mode“?
Jan Chorvat #17
#16 David Grudl, ked v konzole napisem php d:\tools\Deployment\deployment.php deployment.ini -t, tak pole $options, ktore naplna funkcia getopt je prazdna.
Filip Procházka (@HosipLan) #18
#17 Jan Chorvat, Je standardem dávat options před argumenty (opačně to funguje jen výjimečně)
php deployment.php -t deployment.ini
Jan Chorvat #19
#18 Filip Procházka (@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.
David Grudl #20
#17 Jan Chorvat, #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
uestla #21
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.
David Grudl #22
#21 uestla, možná by se mohl event „before“ volat těsně před přejmenováváním, co?
uestla #23
#22 David Grudl, To by bylo tzv. ideální :)
uestla #24
#22 David Grudl, 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ší.
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.
Tomáš Kafka #26
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.
David #27
Proč já si tohle nepřečetl dříve? 😁 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 :(
Petr Homoky #28
Tohle je nejlepší nástroj, který používám. Děkuji.
martin #29
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?
pawouk #30
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.
martin #31
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.
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.
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.
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í:
Na většinu projektů mi to takto zatím stačí, ale samozřejmě záleží na konkrétním projektu.
Jiří Novák #35
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?
David Grudl #36
#35 Jiří Novák, tohle se skutečně nedělá pohodlně, zkusil jsem kód upravit.
Jiří Novák #37
#36 David Grudl, 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 🙂
chikeet #38
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.
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ě?
David #40
Ahoj, je možné nastavit více remote najednou ?
Petr #41
#39 Herb, Mám stejný problém. Neví někdo co s tím?
Petr #42
#39 Herb, 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.
David Grudl #43
#40 David, můžeš mít v jednom INI souboru více sekcí a v každé jiný remote:
David Grudl #44
#39 Herb, #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.Petr Polák #45
#25 MK, 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.
David Grudl #46
#25 MK, #45 Petr Polák název i složku pro deployovací soubor lze nastavit v konfigu:
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!
David Grudl #48
#47 Ja, a liší se ty „stejne“ soubory nějak? Například kvůli rozdílným znakům pro konce řádků na Windows vs Linux?
Ja #49
#48 David Grudl, 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).
David Grudl #50
#49 Ja, č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.).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 ;).
Pikl #52
Díky Davide, super věc!
BuMoRi #53
Zkouším něco takového poprvé, takže asi dělám nějakou stupidní chybu: nahraju si deployment.ini + deployment.php do projektu, ini nastavím (v podstatě jen remote) a spustím v příkazové řádce „php deployment deployment.ini“? Zkouším zadávat i relativní a absolutní cesty, ale stále stejná chyba: „Could not open input file: deployment“. Poradíte?
BuMoRi #54
#53 BuMoRi, OK, už mi to došlo. Takže jsem si vytvořil projekt ftp-deployment přes composer a spouštím to v něm. A teď bych se chtěl zeptat, jak to používáte v praxi? V tomto projektu máte několik ini a php souborů vytvořených pro každý projekt? Nebo přes composer nainstalujete ftp-deployment do každého projektu?
Martin #55
Ahoj.
Na PHP 7.1 dostávám chybu:
Error: Argument 1 passed to Deployment\FtpServer::protect() must be callable, string given, called in FtpServer.php on line 64
Martin #56
V phpinfo přitom mám:
FTP support enabled
FTPS support enabled
Ale pokus o přímé volání ftp_connect místo callbacku dává chybu:
Error: Call to undefined function ftp_connect()
#55 Martin
Martin #57
#56 Martin,
Aha, tak rovnou odpovím sám: Na WAMPu ve Windows je potřeba upravit ještě php.ini v adresáři PHP podobně jako php.ini v adresáři apache, aby pracovalo i z příkazové řádky se stejnými rozšířeními jako na localhostu.
Tomáš #58
Ahoj, prosím o pomoc. Vše v pořádku proběhne, ale na ostré verzi mi zůstanou .deploytmp soubory (žáden soubor se nepřepíše a zůstanou ke všemu .deploytmp soubory, originalni soubory na ftp zustanou beze změny). Purge mi také nesmaže po nahrání cache. Co dělám špatně? mám teď server zaspamovaný .deploytmp soubory… Díky za pomoc.
Tomáš #59
Tak už jsem na to přišel, kdyby měl někdo stejný problém, tak si zkontrolujte, co máte v before[] afterUpload[] after[] (případně vše zakomentujte středníkem). V mém případě jsem tam zapomněl afterUpload[] = http://example.com/deployment.php?… a to způsobilo chybu, díky které to nepokračovalo.
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.