Fáze požadavku: 11. FrankenPHP & Produkční běh
Modul 10 · Lekce 3

Produkce na Hetzneru: Reverzní proxy Caddy, sítě web/db, rozcestník a deploy.sh

💡 Shrnutí lekce

Detailní pohled na reálnou produkční architekturu na Hetzner serveru debian-hell (65.109.132.79): reverzní proxy Caddy s automatickým HTTPS, sdílená síť pro PostgreSQL 18 a atomický deployment přes deploy.sh.

Reálná produkční infrastruktura na serveru Hetzner

Naše výuková platforma běží na produkčním serveru Hetzner pod hostitelským jménem debian-hell (IP 65.109.132.79). Server provozuje více nezávislých služeb sdružených pod centrálním rozcestníkem https://heatsharing.cz.

Celá architektura je navržena podle principů striktní izolace a minimálních privilegií:

                  ┌──────────────────────────────────────────────┐
                  │                 Internet                     │
                  └──────────────────────┬───────────────────────┘
                                         │ HTTPS (443)
                                         ▼
                  ┌──────────────────────────────────────────────┐
                  │      Centrální Caddy Reverzní Proxy          │
                  │      (/srv/caddy — Automatické SSL)          │
                  └──────────────┬───────────────────────────────┘
                                 │
                 Docker Network: │ web (Bridge)
        ┌────────────────────────┴────────────────────────┐
        ▼                                                 ▼
┌──────────────────────────────┐      ┌──────────────────────────────┐
│  Aplikace agy-laravel        │      │  Další aplikace na serveru   │
│  (laravel.heatsharing.cz)    │      │  (spytihnev.heatsharing.cz)  │
│  FrankenPHP :80              │      │  (/srv/apps/obecni-web)      │
└──────────────┬───────────────┘      └──────────────┬───────────────┘
               │                                     │
               └──────────────┐       ┌──────────────┘
               Docker Network:│ db    │
                              ▼       ▼
                     ┌────────────────────────────────┐
                     │    Sdílený PostgreSQL 18       │
                     │    (/srv/postgres)             │
                     └────────────────────────────────┘

Proč využíváme externí Docker sítě web a db?

V souboru compose.prod.yaml jsou obě sítě deklarovány s příznakem external: true:

networks:
  db:
    external: true
  web:
    external: true

Tento vzor přináší zásadní výhody:

  1. Žádné vystavené porty do internetu: Kontejner agy-laravel nemá v produkci definovanou sekci ports:. Není přímo přístupný z veřejného internetu na žádném portu.
  2. Směrování přes Caddy: Reverzní proxy Caddy v /srv/caddy poslouchá na veřejných portech 80/443. Když přijde požadavek na hostitele laravel.heatsharing.cz, Caddy jej přes interní Docker DNS sítě web přepošle na kontejner s aliasem agy-laravel:80. Caddy se také automaticky stará o vystavení a obnovu Let's Encrypt certifikátů.
  3. Sdílená a izolovaná databáze: Sdílený PostgreSQL 18 kontejner běží v /srv/postgres na síti db. Naše aplikace s ním komunikuje po privátní síti pod uživatelem a databází agy_laravel, aniž by měla přístup k databázím jiných aplikací na serveru.

Nasazení pomocí skriptu deploy.sh

Proces aktualizace aplikace v produkci je plně automatizován v kořenovém skriptu deploy.sh:

#!/bin/sh
set -e

echo "==> Building production Docker image..."
docker compose -f compose.prod.yaml build app

echo "==> Deploying application..."
docker compose -f compose.prod.yaml up -d --wait

echo "==> Deployment complete. Service status:"
docker compose -f compose.prod.yaml ps

Kouzlo parametru --wait a Healthchecku:

Příkaz docker compose up -d --wait je klíčový. Bez parametru --wait by Docker kontejner spustil na pozadí a okamžitě ohlásil hotovo, aniž by tušil, zda aplikace skutečně naběhla.

S přepínačem --wait Docker sleduje náš definovaný Healthcheck:

healthcheck:
  test: ["CMD-SHELL", "curl -fsS http://localhost/up >/dev/null || exit 1"]
  interval: 30s
  timeout: 5s
  start_period: 60s
  retries: 3

Kontejner provolává náš Laravel endpoint /up. Teprve ve chvíli, kdy:

  1. entrypoint.sh úspěšně počkal na databázi,
  2. proběhly databázové migrace a seedy,
  3. zahřála se cache přes optimize,
  4. a FrankenPHP začal odpovídat kódem 200 OK na /up,

Docker označí kontejner za healthy a starý kontejner bezpečně přepojí na nový bez jediného výpadku pro uživatele (Zero-Downtime Deployment).

💻 Klíčová konstrukce z praxe
# compose.prod.yaml — Reálná produkční konfigurace
name: agylaravel

services:
  app:
    image: agylaravel/course:prod
    build:
      context: .
      dockerfile: docker/Dockerfile
      target: prod
    env_file: .env
    restart: unless-stopped
    environment:
      PHP_OPCACHE_VALIDATE_TIMESTAMPS: "0"
      RUN_MIGRATIONS: "1"
      RUN_SEEDERS: "1"
    volumes:
      - storage:/app/storage
    networks:
      db: {}
      web:
        aliases:
          - agy-laravel
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://localhost/up >/dev/null || exit 1"]
      interval: 30s
      timeout: 5s
      start_period: 60s
      retries: 3

networks:
  db:
    external: true
  web:
    external: true

volumes:
  storage:

🎯 Ověření znalostí z lekce

Kvíz otestuje pochopení architektury a reálného kódu probíraného v této lekci.

Otázka 1 z 3

Jak je na našem Hetzner serveru zajištěno automatické vystavení a obnova SSL/TLS certifikátů pro laravel.heatsharing.cz?

Otázka 2 z 3

Proč má služba app v souboru compose.prod.yaml připojení do dvou externích sítí web a db?

Otázka 3 z 3

Jakou roli hraje přepínač --wait v příkazu docker compose -f compose.prod.yaml up -d --wait ve skriptu deploy.sh?

Zodpovězeno: 0 / 3
🔬 Živá anatomie v této aplikaci
2 soubory
Na co se v kódu zaměřit: V compose.prod.yaml prozkoumej konfiguraci networks s externími sítěmi web a db. Díky tomu je agy-laravel na síti web dostupný centrální reverzní proxy Caddy a na síti db komunikuje se sdíleným postgres kontejnerem.
cesta: compose.prod.yaml
37 řádků · 0.7 KB
Produkční Docker Compose pro nasazení na Hetzner serveru s připojením na sdílenou síť db a caddy.
1 name: agylaravel
2
3 services:
4 app:
5 image: agylaravel/course:prod
6 build:
7 context: .
8 dockerfile: docker/Dockerfile
9 target: prod
10 env_file: .env
11 restart: unless-stopped
12 environment:
13 PHP_OPCACHE_VALIDATE_TIMESTAMPS: "0"
14 RUN_MIGRATIONS: "1"
15 RUN_SEEDERS: "1"
16 volumes:
17 - storage:/app/storage
18 networks:
19 db: {}
20 web:
21 aliases:
22 - agy-laravel
23 healthcheck:
24 test: ["CMD-SHELL", "curl -fsS http://localhost/up >/dev/null || exit 1"]
25 interval: 30s
26 timeout: 5s
27 start_period: 60s
28 retries: 3
29
30 networks:
31 db:
32 external: true
33 web:
34 external: true
35
36 volumes:
37 storage:
38
cesta: deploy.sh
11 řádků · 0.3 KB
Produkční nasazovací skript pro Hetzner server s migracemi, cacheováním a zero-downtime reloadem.
1 #!/bin/sh
2 set -e
3
4 echo "==> Building production Docker image..."
5 docker compose -f compose.prod.yaml build app
6
7 echo "==> Deploying application..."
8 docker compose -f compose.prod.yaml up -d --wait
9
10 echo "==> Deployment complete. Service status:"
11 docker compose -f compose.prod.yaml ps
12
Zdroj: skutečné soubory této aplikace Plný průzkumník anatomie →