Docker na jednom serveru není orchestrace ani cloud. Je to způsob, jak instalovat software bez instalace: jeden příkaz stáhne otestovaný image, spustí ho jako izolovaný strom procesů s vlastním souborovým systémem a v operačním systému nezůstane nic kromě Dockeru. Nic se za tebe nenasazuje, nic se neškáluje, nic se samo nevyléčí. Dostaneš opakovatelnost - stejný kontejner se chová stejně na notebooku i na stroji v Německu - a možnost provozovat dvě verze téže databáze bez sporů.
Celá práce na jednom stroji se redukuje na pět rozhodnutí u každého kontejneru: který image a který tag, které porty se publikují a na jakou adresu, kde leží data, co se stane, když skončí, a co mu zabrání zaplnit disk. Tento článek je projde v tomto pořadí, s příkazy, a pak ukáže, jak lidé o data přicházejí.
Co Docker na jednom serveru mění#
Upřímné shrnutí toho, co získáš:
- Instalace se mění ve stažení. Žádné PPA, žádná kompilace, žádné „tohle chce Python 3.9 a na stroji je 3.11". Image si nese vlastní userland.
- Verze se přestanou prát. PostgreSQL 15 a 17 mohou běžet vedle sebe na jednom stroji, každý s vlastním datovým adresářem a vlastním portem, a jeden o druhém neví.
- Odstranění je úplné.
docker rmvezme proces i souborový systém. V/usr,/etcani v databázi balíčků nezůstane nic. - Konfigurace se stává příkazem, který lze zapsat. Řádek
docker runjsou instalační pokyny a můžeš ho držet v souboru - což je přesně to, co je Docker Compose pro malé stacky.
A co nedává, což je důležitější, protože lidé předpokládají opak. Kontejnery nejsou virtuální stroje: sdílejí jádro hostitele, takže kernel panic nebo nedostatek paměti na hostiteli stáhne všechno s sebou. Nejsou bezpečnostní hranicí vůči rootovi - kdokoli, kdo může spustit docker, může připojit souborový systém hostitele do kontejneru a číst cokoli. Software nezrychlují; režie je skoro nulová, ale stejně tak přínos. A nic nezálohují.
Jeden předpoklad: potřebuješ skutečné vlastní jádro. Na KVM, VMware, Hyper-V nebo Xen Docker funguje. Na kontejnerové virtualizaci jako OpenVZ nebo LXC je to od nepohodlného po nemožné. Než s tím začneš počítat, spusť systemd-detect-virt - VPS, VDS nebo dedikovaný server rozebírá, co ti to jedno slovo výstupu říká o tarifu, který jsi koupil.
Instalace Dockeru a skupina, která je vlastně root#
Dvě podporované cesty. Pohodlný skript je v pořádku na stroji, který vlastníš:
$ curl -fsSL https://get.docker.com -o get-docker.sh$ sudo sh get-docker.sh$ sudo systemctl enable --now docker$ docker versionMetoda přes repozitář jsou stejné balíčky s více kroky a je to to, co chceš u všeho, co budeš opakovat. Tak či tak skončíš s těmito částmi: docker-ce (démon), docker-ce-cli (příkaz), containerd.io (runtime pod tím) a podpříkazy docker-buildx-plugin a docker-compose-plugin. Vyhni se balíčku docker.io z vlastní distribuce: zaostává a spolehlivě nepřináší plugin Compose, který budeš chtít do týdne.
Pak část, kterou všichni přeskočí:
$ sudo usermod -aG docker deploy$ newgrp docker$ docker run --rm hello-worldPokud docker run hello-world vypíše uvítací text, démon běží, síť funguje a stahování images funguje. To jsou tři testy v jednom příkazu a stojí za to ho udělat, než začneš ladit něco složitějšího.
Images a kontejnery: první spuštění#
Image je zásobník vrstev souborového systému jen pro čtení plus metadata o tom, co spustit. Kontejner je jedna běžící instance image s tenkou zapisovatelnou vrstvou navrchu. Smazáním kontejneru se smaže i tato zapisovatelná vrstva - proto vše, na čem ti záleží, musí ležet ve volume, o tři oddíly níž.
$ docker run -d \ --name web \ --restart unless-stopped \ -p 127.0.0.1:8080:80 \ -v /srv/web/html:/usr/share/nginx/html:ro \ nginx:1.27-alpineČteno zleva doprava: spusti odpojeně, pojmenuj web, vrať po restartu, publikuj port kontejneru 80 jako port 8080 jen na loopbacku, připoj adresář hostitele jen pro čtení, z image nginx s tagem 1.27-alpine.
Tag je rozhodnutí, ne dekorace:
| Styl tagu | Příklad | Použij, když |
|---|---|---|
latest | nginx | Na serveru nikdy |
| Major | nginx:1 | Věříš slibu projektu o kompatibilitě |
| Minor | nginx:1.27 | Rozumná výchozí volba: bezpečnostní opravy, žádná překvapení |
| Přesný | nginx:1.27.2 | Chceš si vybírat každý upgrade sám |
| Digest | nginx@sha256:... | Build musí být pokaždé bajt po bajtu stejný |
latest není kanál a nic neslibuje. Je to prostě tag použitý, když žádný nikdo neuvedl, a posouvá se, kdykoli to správce uzná za vhodné. docker pull o půl roku později ti může podat novou hlavní verzi a první, co se dozvíš, je kontejner, který nenastartuje. Na všem, co drží data, připni alespoň minor verzi.
Každodenní příkazy, které budeš používat po hodině:
$ docker ps # running containers$ docker ps -a # including the dead ones$ docker logs -f --tail 100 web # follow the output$ docker exec -it web sh # a shell inside it$ docker stop web && docker rm web$ docker inspect web # every setting, as JSONdocker stop pošle SIGTERM, deset sekund počká a pak pošle SIGKILL. Pokud tvoje aplikace potřebuje na čisté ukončení víc času - dokončit zápis, dokončit požadavek, uložit svět - prodlouži ho pomocí docker stop -t 60 a ujisti se, že proces SIGTERM skutečně zpracovává. Řádné ukončení a health checky ukazuje, jak vypadá správné zpracování v kódu aplikace.
Porty, publikování a firewall#
-p nastaví destination NAT z portu hostitele na port kontejneru. Zapsáno jako -p 8080:80 se naváže na všechny adresy stroje, včetně veřejné. Zapsáno jako -p 127.0.0.1:8080:80 se naváže jen na loopback.
Dávej přednost druhé podobě. Skoro všechno, co provozuješ, patří za něco jiného - reverse proxy pro cokoli, co mluví HTTP, a vůbec nic pro databázi, kterou používá jen tvoje aplikace. Pravidlo zní: publikuj port, jen pokud se k němu má internet dostat přímo.
Není to estetická preference. Publikované porty obcházejí UFW. Kontejner publikovaný přes -p 8080:80 je dosažitelný z internetu, i když ufw status hlásí výchozí zákaz bez pravidla pro 8080, protože paket se přeposílá do kontejneru místo doručení hostiteli a pravidla přeposílání Dockeru se vyhodnotí první. Celé vysvětlení a čtyři cesty, jak to obejít, jsou v průvodci firewallem UFW; verze na jeden řádek zní: publikuj na 127.0.0.1 a veřejnou stranu nech na proxy na hostiteli.
Kontejnery, které spolu musí mluvit, by měly místo publikování sdílet uživatelsky definovanou síť:
$ docker network create appnet$ docker run -d --name db --network appnet -e POSTGRES_PASSWORD=... postgres:17$ docker run -d --name app --network appnet -p 127.0.0.1:3000:3000 my/app:1.4V uživatelsky definované síti Docker spouští vestavěný DNS server, takže aplikace se připojí na hostname db na portu 5432 a nic není nikde vystaveno. Výchozí síť bridge toto překládání jmen nedělá, a proto „v Compose to funguje, ale s docker run ne" je téměř vždy chybějící docker network create.
Volumes a kde data skutečně leží#
Zapisovatelná vrstva kontejneru zaniká s kontejnerem. Data se dají udržet dvěma způsoby a slouží k různé práci.
Pojmenované volumes spravuje Docker a ukládají se pod /var/lib/docker/volumes/. Použij je pro cokoli, co vlastní nějaký program a ty se toho dotýkáš jen přes něj: soubory databáze, herní svět, stavový adresář aplikace.
$ docker volume create pgdata$ docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:17$ docker volume ls$ docker volume inspect pgdataBind mounty mapují cestu na hostiteli do kontejneru. Použij je pro věci, které chceš upravovat vlastním editorem: konfigurační soubory, HTML statického webu, adresář nahraných médií, který zároveň servíruješ z nginxu.
$ docker run -d --name web -v /srv/web/html:/usr/share/nginx/html:ro nginx:1.27-alpineTři praktické poznámky. Přípona :ro udělá z mountu připojení jen pro čtení a nic nestojí - použij ji všude, kde kontejner nemá co zapisovat. Bind mounty přenášejí vlastnictví hostitele do kontejneru, takže proces běžící uvnitř jako UID 1000 nemůže zapisovat do adresáře vlastněného rootem venku; buď adresáři přes chown nastav správné číselné ID, nebo spusť kontejner s --user "$(id -u):$(id -g)". A pojmenovaný volume, který počáteční obsah dostává z image, ho dostane jen když je prázdný, a proto změna výchozí konfigurace image po prvním startu zdánlivě nic neudělá.
Záloha volume je kontejner, který ho připojí a zapíše tarball jinam:
$ docker run --rm -v pgdata:/data:ro -v /srv/backups:/out alpine \ tar czf /out/pgdata-$(date +%F).tar.gz -C /data .U databáze dej přednost vlastnímu dump nástroji databáze před souborovou kopií živého datového adresáře - pg_dump přes docker exec dá soubor, který se spolehlivě obnoví, což tarball souborů, do nichž se právě zapisuje, nedá. pg_dump a pg_restore obsahuje přepínače.
Restart policy, restarty stroje a zdraví#
Kontejner se sám nevrátí. Ve výchozím stavu zůstane po skončení procesu nebo restartu stroje vypnutý.
| Politika | Při pádu | Při docker stop | Po restartu stroje |
|---|---|---|---|
no (výchozí) | zůstane vypnutý | zůstane vypnutý | zůstane vypnutý |
on-failure:5 | restartuje, nejvýše 5krát | zůstane vypnutý | restartuje, pokud padal |
always | restartuje | zůstane vypnutý do restartu démona | restartuje |
unless-stopped | restartuje | zůstane vypnutý | zůstane vypnutý, pokud jsi ho zastavil |
unless-stopped je správná odpověď téměř pro všechno na malém serveru. Přežije restarty i pády a respektuje, že jsi kontejner zastavil schválně - což always nedělá a při dalším startu démona vesele vrátí kontejner, který jsi před hodinou zastavil.
Politiku můžeš změnit i na běžícím kontejneru, bez znovuvytvoření:
$ docker update --restart unless-stopped webRestart policy závisí na tom, že služba Docker startuje při bootu, takže ověř, že systemctl is-enabled docker říká enabled. A pamatuj, čím restart policy není: není to health check. Kontejner, jehož proces žije, ale je zaseknutý - aplikace v Node, která přestala přijímat spojení, herní server uvízlý na ukládání - splňuje restart policy dokonale. Vlastní HEALTHCHECK Dockeru takový kontejner označí za nezdravý, ale nerestartuje ho. Pokud chceš „restartuj, když přestane odpovídat", je to práce pro watchdog nebo pro jednotku systemd, která kontejner obalí logikou zdraví, jakou chceš; služby systemd pro tvoje aplikace obsahuje soubor jednotky pro tento tvar.
Limity a logy: dvě věci, které potichu zaplní stroj#
Ve výchozím stavu může kontejner použít veškerou paměť stroje a veškerý procesor. Na serveru, který provozuje jedinou věc, je to v pořádku. Na serveru se šesti věcmi vezme jeden zdivočelý proces ostatní s sebou.
$ docker run -d --name app \ --memory=1g --memory-swap=1g --cpus=1.5 \ my/app:1.4$ docker stats --no-streamNastavení --memory-swap rovné --memory vypne swap pro daný kontejner, což obvykle chceš: proces zatlačený do swapu je pomalý způsobem, který se diagnostikuje hůř než přímé selhání. Když kontejner překročí limit paměti, jádro proces uvnitř zabije, kontejner skončí s kódem 137 a docker inspect to zaznamená:
$ docker inspect -f '{{.State.OOMKilled}}' apptrueKód ukončení 137 je 128 + 9, tedy SIGKILL. Kód 143 je 128 + 15, čisté SIGTERM - obvykle ty, jak ho zastavuješ. Tato dvě čísla samy zodpoví většinu otázek „proč to umřelo". Swap v Linuxu a OOM killer vysvětluje, o čem jádro rozhoduje, když vybírá oběť.
Tišším problémem jsou logy. Výchozí logovací ovladač Dockeru zapisuje vše, co kontejner vypíše, do souboru JSON pod /var/lib/docker/containers/, bez jakéhokoli omezení velikosti. Upovídaná aplikace zaplní disk během týdnů nebo měsíců a příznakem je server, který selhává na několika nesouvisejících místech naráz. Oprav to globálně:
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }}Pak sudo systemctl restart docker. Pozor, platí to pro kontejnery vytvořené až potom; existující si ponechají nastavení, se kterým vznikly, takže je znovu vytvoř nebo u každého nastav --log-opt max-size=10m. Co teď nosíš, zjistíš přes sudo du -sh /var/lib/docker/containers/*.
Aktualizace, prune a místo na disku#
Aktualizace kontejneru není upgrade na místě. Stáhneš nový image, zničíš kontejner a vytvoříš nový ze stejných volumes a stejného příkazu run:
$ docker pull nginx:1.27$ docker stop web && docker rm web$ docker run -d --name web --restart unless-stopped \ -p 127.0.0.1:8080:80 -v /srv/web/html:/usr/share/nginx/html:ro nginx:1.27Právě proto záleží na tom, aby byl řádek run zapsaný, a právě proto existuje Compose - docker compose pull && docker compose up -d udělá stejné tři kroky ze souboru, který už máš. Předchozí tag image si nech, dokud nový kontejner neběží den; rollback je pak jen spuštění starého tagu.
Využití disku roste na čtyřech místech najednou. Tohle ti řekne, kde:
$ docker system dfTYPE TOTAL ACTIVE SIZE RECLAIMABLEImages 14 4 6.2GB 4.9GB (79%)Containers 7 4 112MB 43MB (38%)Local Volumes 6 3 2.1GB 820MB (38%)Build Cache 41 0 3.3GB 3.3GBdocker image prune -a odstraní images, které nepoužívá žádný kontejner. docker builder prune vyčistí build cache, která bývá na stroji, jenž si sám sestavuje images, často největší jednotlivou položkou. docker container prune odstraní zastavené kontejnery. Spouštěj tyto tři podle plánu a na téma už skoro nepomyslíš. Spouštěj nedbale docker system prune --volumes a jednou smažeš databázi. Ten rozdíl stojí za to mít v prstech.
Když kontejner nenastartuje#
Okamžitě skončí a `docker ps` neukazuje nic. Podívej se do docker ps -a na kód ukončení a do docker logs <name> na důvod. Kontejner bez dlouho běžícího procesu v popředí skončí, jakmile jeho příkaz doběhne - to není chyba, to je návrh.
Port is already allocated. Port na hostiteli má něco jiného. Pojmenuje to sudo ss -lntp | grep 8080. Buď to zastav, nebo publikuj na jiném portu hostitele; port kontejneru se měnit nemusí.
Permission denied na připojeném adresáři. Nesouhlasí UID u bind mountu. Zkontroluj ls -ln na adresáři hostitele a USER, pod kterým image běží, a srovnej je.
Image se nestáhne. Ověř, že tag skutečně existuje, a pak jestli tě nezastavuje rate limit - registry omezují anonymní stahování podle adresy a docker login to uvolní. Na čerstvém stroji zkontroluj i DNS: kontejnery ve výchozím stavu používají resolver hostitele a rozbitý /etc/resolv.conf se projeví nejdřív tady.
Včera to fungovalo a dnes je to jiná verze. Použil jsi latest. Viz tabulka tagů.
Všechno je pomalé a disk je plný. Logy nebo build cache. Viz předchozí oddíl.
Spravované tarify RE:NODE jsou také kontejnery - panel je upravený Pterodactyl s jedním kontejnerem na server, tvrdým omezením procesoru na podíl, který jsi koupil, a stejným chováním při nedostatku paměti, jaké je popsané výše, až na to, že celý kontejner se na limitu zastaví a čistě restartuje, místo aby se nechal swapovat. Co tam nejde, je přinést vlastní image, takže pokud software, který chceš spustit, není jednou z řad katalogu, je poctivou odpovědí VDS s rootem a Dockerem. Volba mezi VDS a herním panelem dává obojí vedle sebe a co hostingový panel vlastně je pokrývá spravovanou stranu.
FAQ#
Potřebuju Docker na malém VDS?
Ne, a pro jednu aplikaci to může být další vrstva k naučení za málo užitku. Své místo si získá ve chvíli, kdy provozuješ druhou věc, potřebuješ verzi, kterou distribuce nebalí, nebo chceš přenést stejné nastavení na jiný stroj beze změny.
Kolik režie kontejner přidává?
U procesoru a paměti skoro nic - je to totéž jádro, které spouští týž proces s jinými namespaces. Síť kontejnerů přidává přes NAT malé zpoždění a zapisovatelný overlay souborový systém je při intenzivních náhodných zápisech pomalejší než bind mount, což je další důvod, proč databáze patří na volumes.
Kde jsou moje data, když smažu kontejner?
Ve volumes, které přežijí. Cokoli zapsané uvnitř kontejneru, ale mimo volume, zmizí se zapisovatelnou vrstvou. Co kontejner připojuje, ověř přes docker inspect, než ho odstraníš.
Můžu spouštět Docker uvnitř spravovaného hostingového tarifu?
Ne. Spravovaný herní nebo aplikační tarif je už kontejner a uvnitř nemáš vlastní jádro ani démon. Provozování Dockeru je jedním z konkrétních důvodů, proč místo toho vzít VDS.
Proč je můj kontejner dosažitelný z internetu, když je firewall zapnutý?
Protože jsi publikoval port a publikované porty se přeposílají mimo vstupní pravidla firewallu hostitele. Naváž publikování na 127.0.0.1, pokud port opravdu nemá být veřejný, a před všechno, co veřejné je, dej reverse proxy.
Mají se kontejnery aktualizovat automaticky?
Na serveru, na kterém ti záleží, ne. Automatické aktualizace image znamenají bezobslužnou změnu hlavní verze v hodinu, kdy se nikdo nedívá. Připni minor tag, stahuj záměrně a předchozí tag si nech dost dlouho na rollback.




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.