Celý smysl hostingu aplikací je v tom, že bys nikdy neměl nahrávat zip soubor. Aplikace Node se nasazuje z repozitáře na GitHubu pomocí tří nastavení: který repozitář a větev, jaký příkaz ji spouští a zda push do té větve má spustit nové nasazení. Všechno ostatní v tomto článku jsou podrobnosti kolem těch tří - jak musí repozitář vypadat, aby to fungovalo, na který port se navázat, kam patří tajné hodnoty a osm věcí, které se rozbijí při něčím prvním nasazení.
Píšu to pro hosting založený na kontejnerech s panelem, což dnes většina lidí myslí pod hostingem aplikací. Stejný tvar platí na obyčejném VDS se systemd jednotkou a webhookem, a je to úplně jiná práce než build pipeline, která produkuje image kontejneru - tady žádný image není, jen tvůj zdrojový kód a příkaz.
Co nasazení z Gitu vlastně dělá#
Nasazení z Gitu je clone nebo pull do vlastního souborového systému serveru, následovaný startovacím příkazem. To je celý mechanismus a když mu porozumíš, zmizí většina zmatků.
Z toho vyplývá:
- Neexistuje build server. Pokud tvůj projekt potřebuje krok buildu, běží na stejném kontejneru, který obsluhuje provoz, se stejnou pamětí a stejným podílem CPU. Build v TypeScriptu nebo Next.js na tarifu s 1 GB je nejčastější důvod, proč první nasazení umře.
- Soubory na serveru jsou pracovní kopie. Cokoli, co se za běhu zapíše do adresáře repozitáře - uploady, soubor SQLite, vygenerovaná konfigurace - je ohrožené příštím pullem. Drž data za běhu mimo checkout, nebo aspoň mimo cokoli, co Git sleduje.
- Tvoje větev je nasazený stav. Neexistuje oddělené „vydání“. Návrat zpět znamená pushnout revert, nebo nasměrovat server na jinou větev a restartovat.
- Soukromý repozitář potřebuje autentizovaný pull. K tomu existuje GitHub App a proto je vkládání osobního přístupového tokenu do konfiguračního souboru špatná odpověď.
Pokud chceš samostatné místo, kde tenhle postup vyzkoušíš dřív, než se dostane k hráčům nebo zákazníkům, stačí druhý levný server na stejném účtu nasměrovaný na větev staging. Staging a produkce na jednom účtu popisuje, jak zabránit tomu, aby se oba rozjeli.
Příprava repozitáře#
Většina neúspěšných prvních nasazení jsou problémy repozitáře, ne hostingu. Pět věcí ke kontrole, než cokoli připojíš.
Commitni lockfile. package-lock.json patří do repozitáře. Bez něj neexistuje reprodukovatelná instalace a npm ci nemůže vůbec běžet.
Ignoruj `node_modules`. Staví se znovu na serveru, je obrovský a commitnutá kopie postavená na Windows nebo macOS bude obsahovat nativní binárky, které na Linuxu nepoběží.
Měj skutečný start script a ujisti se, že spouští sestavený výstup, ne vývojový server. next dev, nodemon a ts-node nejsou produkční příkazy: sledují souborový systém, používají víc paměti a některé z nich nepřežijí restart čistě.
Deklaruj verzi Node. Pole engines je dokumentace pro tebe stejně jako pro instalátor a je to první věc ke kontrole, když něco lokálně funguje a na serveru ne.
Nenech tam `.env`. Přidej .env do .gitignore a commitni .env.example s klíči a bez hodnot, aby další člověk věděl, co vyplnit.
{ "name": "example-api", "private": true, "type": "module", "engines": { "node": ">=20" }, "scripts": { "build": "tsc -p tsconfig.json", "start": "node dist/server.js" }, "dependencies": { "express": "^4.21.2" }, "devDependencies": { "typescript": "^5.7.2" }}Připojení GitHubu k serveru#
Na záložce GitHub svého serveru nainstaluj aplikaci na svůj účet nebo organizaci, povol jí repozitáře, které mají být dosažitelné, a pak vyber jeden repozitář a jednu větev.
Panel komunikuje s GitHubem přes GitHub App s krátce žijícími instalačními tokeny, místo aby po tobě chtěl vložit osobní přístupový token. Záleží na tom ze dvou důvodů. Soukromé repozitáře fungují, aniž by přihlašovací údaj ležel v konfiguraci tvého serveru, a token, který si musíš pamatovat odvolat, je token, který se nikdy neodvolá. Odebrání přístupu aplikace k repozitáři na GitHubu se projeví okamžitě a na straně serveru není co uklízet.
GitHub je tu jediný poskytovatel. Pokud tvůj kód leží na GitLabu, Gitea nebo soukromém serveru, zrcadli větev na GitHub, nebo se vrať k nahrávání přes SFTP - SFTP a správce souborů popisuje, jak to udělat, aniž by vznikl nepořádek.
Startovací příkaz#
Startovací příkaz se spouští při každém startu kontejneru, ne jen při nasazení. Pro zkompilovaný projekt s commitnutým výstupem buildu je to celé:
npm ci --omit=dev && node dist/server.jsPoužívej npm ci, ne npm install. npm ci nainstaluje přesně to, co říká lockfile, a hlasitě selže, když se lockfile a package.json rozcházejí, což je přesně to, co na serveru chceš. npm install potichu vyřeší něco jiného, než jsi testoval, a pak zapíše změněný lockfile, který není ve tvém repozitáři. Přepínač --omit=dev přeskočí devDependencies, což obvykle zhruba zpolovičí čas instalace i využité místo na disku.
Pokud se build musí provést na serveru, tvar je:
npm ci && npm run build && npm prune --omit=dev && node dist/server.jsTohle nainstaluje všechno včetně kompilátoru, sestaví, znovu odstraní vývojové balíčky a spustí. Je to správně a je to pomalé: počítej s 30 až 90 sekundami, než proces začne poslouchat, při každém restartu, a s paměťovou špičkou během buildu, která bývá dvakrát až třikrát vyšší, než potřebuje běžící aplikace. Na tarifu s 1 GB je build TypeScriptu nebo bundleru nejpravděpodobnější věcí, která narazí na strop. Dvě cesty ven, podle preference: sestav v GitHub Actions a commitni nebo vydej výstup, nebo na dobu buildů přejdi o stupeň výš kvůli paměti a pak zase zpátky, pokud zjistíš, že ji nepotřebuješ. Limity paměti Node vysvětleny popisuje přepínače haldy a co limit kontejneru skutečně dělá.
Port a prostředí kolem něj#
Tvůj tarif přichází s přidělením - adresou a portem zobrazenými na záložce Network - a na tomto portu musí tvoje aplikace poslouchat. Dvě pravidla, na která lidé narážejí oba:
Čti port z prostředí, nikdy ho nepiš napevno. Panely předávají přidělený port procesu jako proměnnou prostředí a aplikace Node tradičně čtou PORT. Přijmi obě a pro lokální vývoj se vrať k něčemu rozumnému:
import express from "express";const app = express();const port = Number(process.env.PORT ?? process.env.SERVER_PORT ?? 3000);app.set("trust proxy", 1);app.get("/healthz", (req, res) => res.status(200).send("ok"));const server = app.listen(port, "0.0.0.0", () => { console.log(`listening on ${port}`);});process.on("SIGTERM", () => { server.close(() => process.exit(0));});Naváž se na `0.0.0.0`, ne na `127.0.0.1`. To je příčina číslo jedna případu „běží to, log říká listening a nic se nedokáže připojit“. Uvnitř kontejneru znamená localhost kontejner a nic jiného. Express při vynechání hostu poslouchá na všech rozhraních, ale mnoho frameworků a příkladů má výchozí loopback a preview server Vite i Next.js mají na to přepínače.
Proměnné prostředí jsou na záložce Startup. Nastavují se na kontejneru, takže je tvůj kód čte obvyklým způsobem a žádné tajemství se nikdy nezapíše do něčeho, co pushuješ. Přihlašovací údaje k databázi, API klíče, URL webhooků a tokeny botů patří sem - proměnné prostředí a tajné hodnoty pokrývá celý seznam a co dělat, když už byla některá commitnuta.
Selži rychle, když nějaká chybí, místo abys nastartoval a zjistil to až při prvním požadavku:
const required = ["DATABASE_URL", "SESSION_SECRET"];const missing = required.filter((name) => !process.env[name]);if (missing.length) { console.error(`missing environment variables: ${missing.join(", ")}`); process.exit(1);}Tarif pro aplikace zde zahrnuje i sloty pro databáze, vytvářené v panelu s vygenerovaným hostem, uživatelem a heslem, takže připojovací řetězec zkopíruješ na záložku Startup, místo abys ho vymýšlel.
Auto update a deploy on push#
Jsou tu dva nezávislé přepínače a dělají různé věci.
Auto update stáhne větev při každém startu kontejneru. Restart z jakéhokoli důvodu - stiskl jsi tlačítko, spustil se rozvrh, proces spadl - přinese nejnovější commit. Je to užitečné a stojí za to to vědět, protože to znamená, že rozbitý commit ve tvé větvi může zachytit restart, který bys za nasazení nepovažoval.
Deploy on push reaguje na to, že GitHub oznámí push do té větve: stáhne ji a restartuje server. Restartuje jen server, který už běžel, takže server, který jsi záměrně zastavil, zůstane zastavený. Tento detail ušetří spoustu zmatků, když pracuješ na něčem, co ještě nemá být živé.
Panel vede jeden záznam na každé nasazení, otevřený, když push dorazí, a uzavřený, jakmile je kontejner znovu viděn běžet. To je rozdíl mezi nasazením, které se povedlo, a nasazením, které spadlo, a je to první místo, kam se podívat, když web pořád servíruje včerejší kód: buď žádný záznam neexistuje, což znamená, že je špatně webhook nebo název větve, nebo je záznam otevřený a nikdy se neuzavřel, což znamená, že kontejner nenastartuje znovu a důvod je v konzoli.
Rozumné výchozí nastavení pro malý projekt: deploy on push zapnutý na produkční větvi a zapnutý i auto update, aby tě restart po pádu nevrátil na zastaralý checkout. Pokud pushuješ do main mnohokrát denně, deploy on push vypni a použij větev release, do které slučuješ záměrně.
Doména před aplikací#
Tarify pro aplikace a web zahrnují slot reverzní proxy. Nasměruj hostname záznamem A na adresu zobrazenou u slotu a certifikát se vydá a obnovuje automaticky - obnova probíhá v okně 21 dní, takže není co si pamatovat jednou ročně.
Tři věci, které ve své aplikaci udělat, jakmile je za proxy:
- Důvěřuj proxy. Skutečná IP klienta dorazí v
X-Forwarded-For. Dokud frameworku neřekneš, aby té hlavičce věřil, každý požadavek vypadá, že přichází z proxy, což rozbije omezování rychlosti, geolokaci i tvoje logy. V Expressu je toapp.set("trust proxy", 1). - Nevynucuj HTTPS v aplikaci. Proxy ukončuje TLS a s tvým kontejnerem mluví obyčejným HTTP. Aplikace, která přesměrovává každý požadavek bez HTTPS na HTTPS, se zacyklí navždy. Pokud to potřebuješ vědět, zkontroluj
X-Forwarded-Proto. - Generuj absolutní URL z veřejného hostname, ne z hlavičky host požadavku, jinak budou callbacky OAuth a odkazy v e-mailech mířit na vnitřní adresu.
Nasměruj doménu na server a získej certifikát SSL je kompletní postup včetně záznamů DNS a důvodů, proč vydání selže, a co dělá reverzní proxy je základ, pokud je pojem nový. Pokud tvoje aplikace používá WebSockets, přečti si WebSockets za reverzní proxy, než začneš ladit odpojení každých šedesát sekund.
Když první nasazení selže#
Zhruba podle toho, jak často se která věc stává:
Při pushi se nic neděje. Zkontroluj, že název větve přesně sedí, včetně velikosti písmen. Zkontroluj, že GitHub App má pořád přístup k repozitáři. Zkontroluj, že server běžel - deploy on push zastavený server nespustí.
`npm ci` končí s `EUSAGE`. Lockfile a package.json se rozcházejí, nebo lockfile chybí. Spusť lokálně npm install, commitni aktualizovaný lockfile a pushni znovu.
Build je zabit bez chyby. To je limit paměti. Kontejner se na stropu zastaví a čistě restartuje, místo aby se mohl swapovat, takže dostaneš useknutý log místo stack trace. Sestav jinde, nebo přejdi o stupeň výš.
Log říká listening, nic se nepřipojí. Navázáno na 127.0.0.1, nebo na jiný port než přidělený. Občas obojí.
`Error: Cannot find module` u něčeho, co je v package.json. Téměř vždy --omit=dev odstranil balíček, který tvůj runtime skutečně potřebuje - aliasy cest v TypeScriptu a některá ORM za běhu načítají nástroje. Přesuň balíček do dependencies.
Nativní modul se nenačte. bcrypt, sharp, better-sqlite3 a další se kompilují pro platformu. Nikdy necommituj node_modules; nech je sestavit npm ci na serveru. Pokud na serveru není dostupný kompilátor, přepni na čistě JS alternativu (bcryptjs) nebo balíček s předkompilovanými binárkami.
Proces se restartuje každých pár sekund. Čti konzoli od začátku, ne od konce - užitečná chyba je ta první a smyčka ji zavaluje. Čtení konzole je přesně o tomhle a proč se tvůj herní server pořád restartuje popisuje ochranu před smyčkou pádů, která nakonec zasáhne.
`EADDRINUSE`. Předchozí proces neskončil. To je chybějící obsluha SIGTERM; viz blok se server.close() výše a řádné vypnutí a health checky.
FAQ#
Potřebuji k takovému nasazení aplikace Node Docker?
Ne. Server už tvůj proces v kontejneru provozuje; dodáváš zdrojový kód a startovací příkaz, ne image. Vlastní image nejsou součástí tohoto postupu. Pokud chceš výslovně řídit runtime image, je to práce pro VDS - viz volba mezi VDS a herním panelem.
Můžu nasazovat z GitLabu nebo soukromého serveru Git?
Přímo ne - integrace je zde jen pro GitHub, přes GitHub App. Obvyklé obejití je zrcadlo: pushuj na oba remote repozitáře, nebo přidej úlohu GitLab CI, která pushuje do repozitáře na GitHubu, jejž server sleduje. Jinak nahraj sestavenou aplikaci přes SFTP.
Jak vrátím špatné nasazení?
Pushni revert. Udělej git revert toho commitu a pushni; deploy on push ho nasadí stejně, jako nasadil chybu. Pokud aplikace nejede a potřebuješ ji hned, rychlejší je nasměrovat server na předchozí větev nebo tag a restartovat, ale nezapomeň ho potom vrátit, jinak bude příští push vypadat, že nedělá nic.
Spouští se npm ci při každém restartu?
Ano, pokud je součástí startovacího příkazu, a to obvykle chceš - zaručuje, že nainstalované odpovídá lockfile. Cena je 20 až 60 sekund startu na malém tarifu. Pokud jsou tvé závislosti stabilní a na době startu ti záleží víc, můžeš npm ci ze startovacího příkazu vyjmout a spouštět ho ručně po změně závislostí, ale pak pull, který změní závislosti, nastartuje s nainstalovanými špatnými.
Co se děje s uploady a soubory, které moje aplikace zapisuje?
Leží na disku serveru a přežijí restarty, ale cokoli uvnitř checkoutu z Gitu může pull přepsat. Zapisuj uploady uživatelů do adresáře mimo cestu repozitáře, nebo do adresáře uvedeného v .gitignore, a zahrň ho do záloh - backup sloty jsou součástí tarifu a dají se spouštět podle rozvrhu.
Kolik paměti aplikace Node potřebuje?
Malé API nebo bot na Discordu se v klidu vejde do 512 MB až 1 GB; rezervu potřebuje krok buildu. Začni na 1 GB, týden sleduj graf v konzoli a přejdi výš, pokud se strop dosáhne při buildech, ne za provozu. Volba tarifu pro bota na Discordu a dimenzování webové aplikace na den spuštění procházejí čísla.




Komentáře
Zcela anonymně: žádný účet, žádný e-mail, žádné cookie. Ukládáme jen jméno, které napíšeš, text a čas - nic dalšího. Počet odkazů je omezený a značkování se nevykresluje.